ossp-pkg/rc/rc_script.c
/* OSSP rc - Run-Command Processor
** Copyright (c) 2002-2003 Ralf S. Engelschall
** Copyright (c) 2002-2003 Cable & Wireless Deutschland GmbH
** Copyright (c) 2002-2003 The OSSP Project <http://www.ossp.org/>
**
** This file is part of OSSP rc, a portable run-command processor
** which can be found at http://www.ossp.org/pkg/lib/rc/
**
** Permission to use, copy, modify, and distribute this software for
** any purpose with or without fee is hereby granted, provided that
** the above copyright notice and this permission notice appear in all
** copies.
**
** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE.
**
** rc_script.c: Run-Command Processor ISO C source file
*/
#include <string.h>
#include <stdlib.h>
#include <unistd.h> /* For mkstemp(3) */
#include <fcntl.h> /* For open(2) */
#include <ctype.h> /* For isspace(3) */
#include <pwd.h> /* For getpwuid(3) */
#include "rc.h" /* Public Rc interface */
#include "rc_pcre.h" /* For section parsing */
#include "rc_config.h" /* For configuration access */
#include "rc_const.h" /* For configuration defaults */
/************************************************
* scriptCopy(rc_script_t *) *
* Copy constructor *
************************************************/
rc_script_t *scriptCopy(rc_script_t *pOrig)
{
rc_script_t *pNew = NULL;
pNew = (rc_script_t *)malloc(sizeof(rc_script_t));
if (pNew) {
*pNew = malloc((strlen(scriptGetdata(pOrig)) + 1) * sizeof(char));
if (*pNew)
strcpy(*pNew, scriptGetdata(pOrig));
else
RC_THROW(RC_ERR_MEM);
}
else
RC_THROW(RC_ERR_MEM);
return(pNew);
}
/************************************************
* scriptNew(void) *
* Construct a script *
************************************************/
rc_script_t *scriptNew(void)
{
rc_script_t *pScript = NULL;
pScript = (rc_script_t *)malloc(sizeof(rc_script_t));
if (pScript) {
*pScript = malloc(sizeof(char));
if (*pScript)
**pScript = NULL;
else
RC_THROW(RC_ERR_MEM);
}
else
RC_THROW(RC_ERR_MEM);
return(pScript);
}
/****************************************************
* scriptAdd(rc_script_t *, rc_script_t *) *
* Add one script to another *
****************************************************/
rc_return_t scriptAdd(rc_script_t *pDest, rc_script_t *pSource)
{
int nResize = 0;
void *pvRealloc = NULL;
assert(pDest && pSource); /* Parameters must be valid */
/* Add 2 chars to ensure that a \0 precedes any strings */
nResize = strlen(*pDest) + strlen(*pSource) + sizeof (char);
/* Don't trust realloc(3) in this case */
if ((pvRealloc = calloc(1, (size_t)nResize)) == NULL)
return(RC_THROW(RC_ERR_MEM));
strcpy(pvRealloc, *pDest);
strcat(pvRealloc, *pSource);
/* Cleanup and deallocate */
if (*pDest) {
free(*pDest);
*pDest = NULL;
}
*pDest = pvRealloc; /* Put the new data in */
return(RC_THROW(RC_OK));
}
/*****************************************************
* scriptnAppend(rc_script_t *, const char *, size_t) *
* Append n characters of text to a script *
*****************************************************/
rc_return_t scriptnAppend(rc_script_t *pScript, const char *szInbuf, size_t Size)
{
int nResize = 0;
void *pvRealloc = NULL;
assert(pScript); /* Script parameter must be valid */
if (!szInbuf)
return(RC_THROW(RC_ERR_USE));
/* Short circuit in case of dumb noop call */
if (Size == 0)
return(RC_THROW(RC_OK));
/* Add 2 chars to ensure that a \0 precedes any strings */
nResize = strlen(*pScript) + Size + sizeof (char);
/* Don't trust realloc(3) in this case */
if ((pvRealloc = calloc(1, (size_t)nResize)) == NULL)
return(RC_THROW(RC_ERR_MEM));
/* Coerce strings into one Script again */
if (*pScript)
strcpy(pvRealloc, *pScript);
strncat(pvRealloc, szInbuf, Size + 1);
/* Cleanup and deallocate memory */
if (*pScript)
free(*pScript);
*pScript = pvRealloc;
return(RC_THROW(RC_OK));
}
/************************************************
* scriptSection(rc_script_t *, const char *) *
* Parse a script for a given section *
************************************************/
rc_section_t *scriptSection(rc_script_t *pScript, const char *kszSecname)
{
rc_section_t *pSec = NULL;
char *piLabstart = NULL;
int nLabsize = 0;
char *piSubtemp = NULL; /* To find priority and userid in substrings */
char *piBlocend = NULL; /* Misnomer used to control section looping */
char *piStart = NULL;
char *piEnd = NULL;
char *piSep = NULL;
char *szUser = NULL;
char *szTemp = NULL; /* Holds temporarily the pri and user strings */
struct passwd *pPwd = NULL;
long nPri = 0;
long nUid = 0;
int nUserbytes = 0;
int nTmp = 0;
int nOffset = 0;
int nFound = 0;
int nVecsize = 0;
int nSubstrings = 0;
int *pnVec = NULL;
const char *kszErr = NULL;
const int kiRegopt = PCRE_DOTALL | PCRE_MULTILINE;
/* const int kiRegopt = PCRE_DOTALL | PCRE_MULTILINE | PCRE_UNGREEDY;*/
pcre *pRegex = NULL; /* Perl Compatible Regular Expression */
pcre_extra *pExtra = NULL; /* Used for studying an expression */
assert(pScript); /* Check for a valid incoming script */
assert(configGetval(RC_DEF_VAL));
if (!kszSecname) /* If we get a NULL section label, then throw up */
RC_THROW(RC_ERR_USE);
if ((pRegex = pcre_compile(configGetval(RC_DEF_VAL), kiRegopt, &kszErr, &nOffset, NULL)) == NULL) {
RC_THROW(RC_ERR_SYS);
}
pExtra = pcre_study(pRegex, 0, &kszErr); /* Study the FSM */
if (kszErr) { /* Variable contains a string reference in case of errors */
free(pRegex);
RC_THROW(RC_ERR_SYS);
}
if (pcre_fullinfo(pRegex, pExtra, PCRE_INFO_CAPTURECOUNT, &nSubstrings))
RC_THROW(RC_ERR_SYS);
/* Use multiples of six, because PCRE needs 2x multiples of three */
nVecsize = 6 * (nSubstrings > 0 ? nSubstrings : 1);
nVecsize *= RC_GOOD_MEASURE; /* Add redundancy factor for error margin */
/* pAwesome += RC_GOOD_VIBRATIONS;*/ /* Add good vibes for super action */
/***********************************************************************/
/* Reminder: PCRE writes vectors to help identify substrings. */
/* That means that in the following code we can */
/* execute a compiled PCRE regex (ab)(\s)(.*)$ */
/* */
/* piBlocend + pnVec[0] = 'start of whole matched string' */
/* piBlocend + pnVec[1] = 'end of whole matched string' */
/* piBlocend + pnVec[2] = 'start of first substring (ab)' */
/* piBlocend + pnVec[3] = 'end of first substring (ab)' */
/* piBlocend + pnVec[4] = 'start of second substring (\s)' */
/* piBlocend + pnVec[5] = 'end of second substring (\s)' */
/* piBlocend + pnVec[6] = 'start of third substring (.*)' */
/* piBlocend + pnVec[7] = 'end of third substring (.*)' */
/***********************************************************************/
/* Filter the rc file for the section label, do it here the first time */
pnVec = calloc(nVecsize, sizeof(int)); /* 2/3 vec 1/3 scrapinfo */
nFound = pcre_exec(pRegex, pExtra, *pScript,\
strlen(*pScript), 0, 0, pnVec, nVecsize);
piBlocend = *pScript; /* Start piBlocend pointing to the script object */
while (nFound > 1) { /* Loop as long as we have more sections */
piLabstart = piBlocend + *(pnVec + 2);
nLabsize = *(pnVec + 3) - *(pnVec + 2);
/* Test the substring. If it matches our label, make a new section */
if (strncmp(piLabstart, kszSecname, nLabsize) == 0 \
&& nLabsize == strlen(kszSecname) * sizeof (char)) {
/* Handle the section body */
piStart = piBlocend + *(pnVec + 6);
piEnd = piBlocend + *(pnVec + 7);
pSec = sectionNew(kszSecname);
sectionSetndata(pSec, piStart, piEnd - piStart);
/* FIXME: Implement --ParseSectionParam for extra gravy */
/* Handle the section priority */
piStart = piBlocend + *(pnVec + 4);
piEnd = piBlocend + *(pnVec + 5);
szTemp = malloc((piEnd - piStart + 1) * sizeof (char));
strncpy(szTemp, piStart, piEnd - piStart + 1);
piSubtemp = strstr(szTemp, RC_DEF_PRG);
if (piSubtemp) { /* Priority pattern found */
for (nTmp = (int)piSubtemp + strlen(RC_DEF_PRG); \
isspace(*(char *)nTmp); nTmp += sizeof (char)); /* Strip */
nPri = strtol((char *)nTmp, &piSep, 10);
if ((char *)nTmp == piSep) /* No priority number follows */
RC_THROW(RC_ERR_USE); /* which is an error */
else
sectionSetpri(pSec, nPri); /* Found a priority value */
}
else /* Fallback to default value */
sectionSetpri(pSec, RC_DEF_PRI);
/* Handle the section userid */
piSubtemp = strstr(szTemp, RC_DEF_UIG);
if (piSubtemp) { /* Userid pattern found */
for (nTmp = (int)piSubtemp + strlen(RC_DEF_UIG); \
isspace(*(char *)nTmp); nTmp += sizeof (char)); /* Strip */
nUid = strtol((char *)nTmp, &piSep, 10);
if ((char *)nTmp == piSep) /* No userid number follows */
{
nUserbytes = strspn((strcspn(piSep, " \t\n") * sizeof (char) + piSep), " \t\n");
nUserbytes = (strlen(piSep) - nUserbytes + 1) * sizeof (char);
szUser = malloc(nUserbytes);
if (!szUser)
RC_THROW(RC_ERR_MEM);
strncpy(szUser, (const char *)nTmp, nUserbytes);
strtok(szUser, " \t\n");
pPwd = getpwnam(szUser);
if (pPwd) {
sectionSetuid(pSec, pPwd->pw_uid); /* Set to given */
sectionSetlogin(pSec, szUser); /* uid and login */
}
else
sectionSetuid(pSec, RC_DEF_UID); /* Set to default */
free(szUser);
}
else {
pPwd = getpwuid(nUid);
if (pPwd) {
sectionSetuid(pSec, nUid); /* Found a value */
sectionSetlogin(pSec, pPwd->pw_name); /* uid and login */
}
else
sectionSetuid(pSec, RC_DEF_UID); /* Set to default */
}
}
else /* Fallback to default value */
sectionSetuid(pSec, RC_DEF_UID);
/* Cleanup */
free(szTemp);
szTemp = NULL;
return(pSec); /* Section found, so return the text */
}
/* Looks like we didn't find the section yet, so keep trying */
piBlocend += *(pnVec + 1); /* Find end of section block */
nFound = pcre_exec(pRegex, pExtra, piBlocend,\
strlen(piBlocend), 0, 0, pnVec, nVecsize);
}
/* Under correct conditions, the section subscript was returned in loop */
if (nFound == 1) /* User gave no klammern */
RC_THROW(RC_ERR_USE); /* so complain about it */
else if (nFound < PCRE_ERROR_NOMATCH) /* Generic problem so */
RC_THROW(RC_ERR_SYS); /* return an error */
return(NULL); /* Probably not found */
}
/************************************************
* scriptGetdata(rc_script_t *) *
* Return the private script data as a string *
************************************************/
const char *scriptGetdata(rc_script_t *pScript)
{
assert(pScript);
/* Don't remove this! It encapsulates the script object, */
/* which might not be a simple string */
return((const char *)*pScript);
}
/************************************************
* scriptSetdata(rc_script_t *, const char *) *
* Set the private script data from a string *
************************************************/
rc_return_t scriptSetdata(rc_script_t *pScript, const char *kszIn)
{
assert(pScript && kszIn); /* Avoid idiots */
if (*pScript) {
free(*pScript);
*pScript = NULL;
}
*pScript = malloc((strlen(kszIn) + 1) * sizeof(char));
strcpy(*pScript, kszIn);
return(RC_THROW(RC_OK));
}
/************************************************
* scriptDump(rc_script_t *) *
* Print a script to standard out *
************************************************/
rc_return_t scriptDump(rc_script_t *pScript)
{
/* Don't remove this! It encapsulates the script object, */
/* which might not be a simple string */
if (pScript) {
fprintf(stdout, "%s", scriptGetdata(pScript));
return(RC_THROW(RC_OK));
}
else
return(RC_THROW(RC_ERR_USE));
}
/************************************************
* scriptWrite(rc_script_t *, const char *) *
* Print a script to a file *
************************************************/
rc_return_t scriptWrite(rc_script_t *pScript, const char *szPath)
{
int nFdtmp = open(szPath, O_WRONLY | O_CREAT, 0600);
FILE *pStream = NULL;
/* Initial sanity checks */
if (!pScript || nFdtmp < 0)
return(RC_THROW(RC_ERR_USE));
else
pStream = fdopen(nFdtmp, "w");
/* Don't remove this! It encapsulates the script object, */
/* which might not be a simple string */
if (pStream) {
fprintf(pStream, "%s", scriptGetdata(pScript));
fclose(pStream);
return(RC_THROW(RC_OK));
}
else
return(RC_THROW(RC_ERR_USE));
}
/************************************************
* scriptDelete(rc_script_t *) *
* Destruct a script *
************************************************/
rc_return_t scriptDelete(rc_script_t *pScript)
{
assert(pScript); /* Check for a valid incoming script */
if (*pScript) {
free(*pScript);
*pScript = NULL;
}
else
RC_THROW(RC_WRN_NUL);
free(pScript); /* Free the script itself */
pScript = NULL;
return(RC_THROW(RC_OK));
}