/* 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
**
** 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_file.c: Run-command processor ISO C source file
*/
#include /* For string copy and such data ops */
#include /* For memory ops */
#include /* For reading rc files */
#include /* For reading rc files */
#include /* For isspace(3) */
#include /* For getpwuid(3) */
#include "rc.h" /* Public Rc interface */
#include "rc_const.h" /* For configuration defaults */
#include "rc_config.h" /* Option definitions */
#include "rc_pcre.h" /* For section parsing */
/************************************************
* rcfileNew(void) *
* Construct a new rcfile *
************************************************/
rc_file_t *rcfileNew(const char *szName)
{
rc_file_t *pRcfile = NULL;
pRcfile = (rc_file_t *)calloc(1, sizeof(rc_file_t)); /* Allocate and clear */
if (!pRcfile) /* Ensure storage */
RC_THROW(RC_ERR_MEM);
if (szName) {
pRcfile->m_szName = malloc((strlen(szName) + 1) * sizeof(char));
strcpy(pRcfile->m_szName, szName);
}
else
pRcfile->m_szName = NULL;
/* Start section array with one empty allocated tail item */
pRcfile->m_nSecs = 0;
pRcfile->m_ppSecvec = malloc(sizeof(rc_section_t *));
*pRcfile->m_ppSecvec = NULL;
return(pRcfile);
}
/****************************************************
* rcfileParse(rc_file_t *) *
* Parse rcfile contents, initialize any sections *
****************************************************/
rc_return_t rcfileParse(rc_file_t *pRcf)
{
rc_script_t *pScript = NULL;
char *szLocex = NULL;
char *sBuf = NULL;
int nFdrc = -1;
int nRet = 0;
if (pRcf->m_nSecs > 0)
RC_THROW(RC_ERR_USE); /* Only virgin rcfiles can be parsed */
/* Build the location path name */
if (!configGetval(RC_LOC_VAL)) /* Config should have */
RC_THROW(RC_ERR_INT); /* given a locs default */
else { /* Only enter block with valid string, strdup can't handle NULL */
/* FIXME mlelstv -- crashes on zero-length RC_LOC_VAL string */
if (*(configGetval(RC_LOC_VAL) + strlen(configGetval(RC_LOC_VAL)) - 1) != '/') {
/* FIXME mlelstv -- void pointer arithmetic from hell */
szLocex = malloc(strlen(configGetval(RC_LOC_VAL)) + \
sizeof (char) + \
strlen("rc.") + \
strlen(pRcf->m_szName) + \
sizeof (char));
strcpy(szLocex, configGetval(RC_LOC_VAL));
strcat(szLocex, "/");
strcat(szLocex, "rc."); /* FIXME: Make the prefix configurable */
strcat(szLocex, pRcf->m_szName);
}
else {
szLocex = malloc(strlen(configGetval(RC_LOC_VAL)) + \
strlen("rc.") + \
strlen(pRcf->m_szName) + \
sizeof (char));
strcpy(szLocex, configGetval(RC_LOC_VAL));
strcat(szLocex, "rc."); /* FIXME: Make the prefix configurable */
strcat(szLocex, pRcf->m_szName);
}
}
/* Open the rc file unconditionally */
if ((nFdrc = open(szLocex, O_RDONLY)) == -1)
RC_THROW(RC_ERR_RCF);
/* Read data from the rcfile into a temporary script */
pScript = scriptNew();
sBuf = (char *)calloc(1, RC_READ_BUFSIZE);
while ((nRet = read(nFdrc, sBuf, RC_READ_BUFSIZE)) > 0)
scriptnAppend(pScript, sBuf, nRet);
/* FIXME mlelstv -- errno is lost ? */
if (nRet == -1) /* Handle read errors */
RC_THROW(RC_ERR_IO);
{ /* Extract sections from script, and neatly place in rcfile */
rc_section_t *pSec = NULL;
char *piSecname = NULL;
int nLabsize = 0;
int nPribytes = 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 *szName = 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;
char *pTmp = NULL;
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 */
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)) {
free(pRegex);
RC_THROW(RC_ERR_SYS);
}
/***********************************************************************/
/* 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 (.*)' */
/***********************************************************************/
/* 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 */
/* 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 */
piSecname = piBlocend + *(pnVec + 2);
nLabsize = *(pnVec + 3) - *(pnVec + 2);
/* Handle the section name and body */
piStart = piBlocend + *(pnVec + 6);
piEnd = piBlocend + *(pnVec + 7);
szName = malloc((nLabsize + 1) * sizeof (char));
strncpy(szName, piSecname, nLabsize);
*(szName + nLabsize) = '\0';
pSec = sectionNew(szName);
free(szName); /* Get rid of the temporary, actually */
szName = NULL; /* just for making a new section string */
sectionSetndata(pSec, piStart, piEnd - piStart);
/* Handle the section parent */
sectionSetparent(pSec, pRcf->m_szName);
/* FIXME: Implement --ParseSectionParam for extra gravy */
/* Handle the section priority */
piStart = piBlocend + *(pnVec + 4);
piEnd = piBlocend + *(pnVec + 5);
nPribytes = piEnd - piStart;
szTemp = malloc(nPribytes + sizeof (char));
strncpy(szTemp, piStart, nPribytes);
*(szTemp + nPribytes) = '\0';
piSubtemp = strstr(szTemp, RC_DEF_PRG);
if (piSubtemp) { /* Priority pattern found */
for (pTmp = piSubtemp + strlen(RC_DEF_PRG); \
isspace(*pTmp); pTmp += 1)
continue; /* Strip */
nPri = strtol(pTmp, &piSep, 10);
if (pTmp == 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) * sizeof (char);
szUser = malloc(nUserbytes + sizeof (char));
if (!szUser)
RC_THROW(RC_ERR_MEM);
strncpy(szUser, (const char *)nTmp, nUserbytes);
*(szUser + nUserbytes) = '\0';
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;
/* Copy a new section to that found in the script object */
pRcf->m_ppSecvec = realloc(pRcf->m_ppSecvec, sizeof(rc_section_t *) \
* (pRcf->m_nSecs + 2));
pRcf->m_ppSecvec[pRcf->m_nSecs] = sectionCopy(pSec);
pRcf->m_ppSecvec[pRcf->m_nSecs + 1] = NULL;
pRcf->m_nSecs++;
/* Clean up our temporary section */
sectionDelete(pSec);
pSec = NULL;
/* Find end of section block */
piBlocend += *(pnVec + 1);
/* Try to match another section */
nFound = pcre_exec(pRegex, pExtra, piBlocend,\
strlen(piBlocend), 0, 0, pnVec, nVecsize);
}
/* release compiled regex */
free(pRegex);
/* Handle errors */
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 */
}
/* Deallocate and clean */
scriptDelete(pScript);
pScript = NULL;
free(szLocex);
szLocex = NULL;
free(sBuf);
sBuf = NULL;
close(nFdrc);
return(RC_OK);
}
/****************************************************
* rcfileGetsec(rc_file_t *, const char *) *
* Get a section from the rcfile, and return it *
****************************************************/
rc_section_t *rcfileGetsec(rc_file_t *pRcf, const char *szSec)
{
int nIter = 0;
assert(pRcf && szSec);
/* Return the section if it is found */
for (nIter = 0; nIter < pRcf->m_nSecs; nIter++) {
if (!strcmp(pRcf->m_ppSecvec[nIter]->m_szName, szSec))
return(pRcf->m_ppSecvec[nIter]);
}
return NULL;
}
/****************************************************
* rcfileAppendsec(rc_file_t *, rc_section_t *) *
* Append a section to this rcfile *
****************************************************/
rc_return_t rcfileAppendsec(rc_file_t *pRcfile, rc_section_t *pInsec)
{
assert(pRcfile && pInsec); /* Parameters must be valid */
/* Grow the number of sections */
pRcfile->m_nSecs++;
/* First make our vector larger to hold one more section */
pRcfile->m_ppSecvec = realloc(pRcfile->m_ppSecvec, sizeof(rc_section_t *) \
* (pRcfile->m_nSecs + 1));
pRcfile->m_ppSecvec[pRcfile->m_nSecs] = sectionNew(pInsec->m_szName);
pRcfile->m_ppSecvec[pRcfile->m_nSecs + 1] = NULL;
/* Simple value copy */
pRcfile->m_ppSecvec[pRcfile->m_nSecs]->m_nPri = pInsec->m_nPri;
pRcfile->m_ppSecvec[pRcfile->m_nSecs]->m_nUid = pInsec->m_nUid;
/* Deep copy of parent */
if (pInsec->m_szParent) {
pRcfile->m_ppSecvec[pRcfile->m_nSecs]->m_szParent = \
malloc((strlen(pInsec->m_szParent) + sizeof(char)) * sizeof(char));
strcpy(pRcfile->m_ppSecvec[pRcfile->m_nSecs]->m_szParent, pInsec->m_szParent);
}
/* Deep copy of user name */
if (pInsec->m_szLogin) {
pRcfile->m_ppSecvec[pRcfile->m_nSecs]->m_szLogin = \
malloc((strlen(pInsec->m_szLogin) + sizeof(char)) * sizeof(char));
strcpy(pRcfile->m_ppSecvec[pRcfile->m_nSecs]->m_szLogin, pInsec->m_szLogin);
}
/* Deep copy of section text */
if (scriptGetdata(pInsec->m_pData))
scriptSetdata(pRcfile->m_ppSecvec[pRcfile->m_nSecs]->m_pData, scriptGetdata(pInsec->m_pData));
return(RC_THROW(RC_OK));
}
/************************************************
* rcfileGetXXX(rc_file_t *) *
* Accessor methods *
************************************************/
const char *rcfileGetname(rc_file_t *pRcfile)
{ /* Name of rcfile, used for display during verbose */
if (pRcfile)
return(pRcfile->m_szName);
else
RC_THROW(RC_ERR_USE);
return(0); /* Not reached */
}
/************************************************
* rcfileSetXXX(rc_file_t *, const ... *) *
* Accessor methods *
************************************************/
rc_return_t rcfileSetname(rc_file_t *pRcfile, const char *szName)
{ /* Name of rcfile, used for display during verbose */
if (pRcfile) {
pRcfile->m_szName = malloc((strlen(szName) + 1) * sizeof (char));
strcpy(pRcfile->m_szName, szName);
return(RC_THROW(RC_OK));
}
return(RC_THROW(RC_ERR_USE));
}
/************************************************
* rcfileExists(rc_file_t *) *
* Tests for an existing rcfile object *
************************************************/
short rcfileExists(rc_file_t *pRcfile)
{ /* Key on name of rcfile */
if (pRcfile) {
if (pRcfile->m_szName)
return(TRUE); /* Object does indeed exist */
else
return(FALSE); /* Object does not exist */
}
return(RC_THROW(RC_ERR_USE));
}
/************************************************
* rcfileDelete(rc_file_t *) *
* Destruct a rcfile *
************************************************/
rc_return_t rcfileDelete(rc_file_t *pRcfile)
{
int nSecs = pRcfile->m_nSecs;
assert(pRcfile); /* Stupidity check */
/* Cleanup our junk */
pRcfile->m_nSecs = 0; /* Blank the section count */
if (pRcfile->m_szName) { /* Destroy the rc name */
free(pRcfile->m_szName);
pRcfile->m_szName = NULL;
}
while (--nSecs >= 0) { /* Destroy the section vector */
if (pRcfile->m_ppSecvec[nSecs]) {
sectionDelete(pRcfile->m_ppSecvec[nSecs]);
pRcfile->m_ppSecvec[nSecs] = NULL;
}
}
free(pRcfile->m_ppSecvec);
pRcfile->m_ppSecvec = NULL;
free(pRcfile);
pRcfile = NULL;
return(RC_THROW(RC_OK));
}