/* OSSP rc - Run-command processor ** Copyright (c) 2002 Ralf S. Engelschall ** Copyright (c) 2002 Cable & Wireless Deutschland GmbH ** Copyright (c) 2002 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_script.c: Run-command processor ISO C source file */ #include #include #include /* For mkstemp(3) */ #include /* For open(2) */ #include /* For isspace(3) */ #include /* 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)); *pNew = malloc((strlen(scriptTostring(pOrig)) + 1) * sizeof(char)); if (pNew) strcpy(*pNew, scriptTostring(pOrig)); 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)); *pScript = NULL; return(pScript); } /**************************************************** * scriptAppend(rc_script_t *, const char *, size_t) * * Append text to a script * ****************************************************/ rc_return_t scriptAppend(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 to end of nResize to ensure that a \0 precedes any strings */ nResize = (*pScript != NULL ? strlen(*pScript) : 0) + Size + 2; /* 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); } else strncpy(pvRealloc, szInbuf, Size); /* Cleanup and deallocate memory */ if (*pScript) { free(*pScript); *pScript = NULL; } *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 */ nTmp = (int)piSubtemp + strlen(RC_DEF_PRG); 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 */ nTmp = (int)piSubtemp + strlen(RC_DEF_UIG); nUid = strtol((char *)nTmp, &piSep, 10); if ((char *)nTmp == piSep) /* No userid number follows */ { nUserbytes = (strcspn(piSep, " \t\n") + sizeof (char)) * 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 */ } /************************************************ * scriptTostring(rc_script_t *) * * Return the private script data as a string * ************************************************/ const char *scriptTostring(rc_script_t *pScript) { /* Don't remove this! It encapsulates the script object, */ /* which might not be a simple string */ if (pScript) return(*pScript); else return(NULL); } /************************************************ * 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", *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", *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) { if (*pScript) free(*pScript); else { RC_THROW(RC_WRN_NUL); } free(pScript); return(RC_THROW(RC_OK)); }