/* 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_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 */ if (*(configGetval(RC_LOC_VAL) + strlen(configGetval(RC_LOC_VAL)) - sizeof (char)) != '/') { 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) scriptAppend(pScript, sBuf, nRet); 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; 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)) 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 * sizeof (char)) = '\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 (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) * 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); } /* 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)); }