mirror of
https://github.com/rsyslog/rsyslog.git
synced 2025-12-21 13:40:42 +01:00
398 lines
9.9 KiB
C
398 lines
9.9 KiB
C
/* cfsysline.c
|
|
* Implementation of the configuration system line object.
|
|
*
|
|
* File begun on 2007-07-30 by RGerhards
|
|
*
|
|
* Copyright 2007 Rainer Gerhards and Adiscon GmbH.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* A copy of the GPL can be found in the file "COPYING" in this distribution.
|
|
*/
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <pwd.h>
|
|
#include <grp.h>
|
|
|
|
#include "rsyslog.h"
|
|
#include "syslogd.h" /* TODO: when the module interface & library design is done, this should be able to go away */
|
|
#include "cfsysline.h"
|
|
#include "srUtils.h"
|
|
|
|
|
|
/* static data */
|
|
cslCmd_t *pCmdListRoot = NULL; /* The list of known configuration commands. */
|
|
cslCmd_t *pCmdListLast = NULL;
|
|
|
|
/* --------------- START functions for handling canned syntaxes --------------- */
|
|
|
|
|
|
/* parse a character from the config line
|
|
* added 2007-07-17 by rgerhards
|
|
* TODO: enhance this function to handle different classes of characters
|
|
* HINT: check if char is ' and, if so, use 'c' where c may also be things
|
|
* like \t etc.
|
|
*/
|
|
//static void doControlCharEscPrefix(uchar **pp)
|
|
rsRetVal doGetChar(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *pVal)
|
|
{
|
|
rsRetVal iRet = RS_RET_OK;
|
|
|
|
assert(pp != NULL);
|
|
assert(*pp != NULL);
|
|
|
|
skipWhiteSpace(pp); /* skip over any whitespace */
|
|
|
|
/* if we are not at a '\0', we have our new char - no validity checks here... */
|
|
if(**pp == '\0') {
|
|
logerror("No character available");
|
|
iRet = RS_RET_NOT_FOUND;
|
|
} else {
|
|
if(pSetHdlr == NULL) {
|
|
/* we should set value directly to var */
|
|
*((uchar*)pVal) = **pp;
|
|
} else {
|
|
/* we set value via a set function */
|
|
CHKiRet(pSetHdlr(pVal, **pp));
|
|
}
|
|
++(*pp); /* eat processed char */
|
|
}
|
|
|
|
finalize_it:
|
|
return iRet;
|
|
}
|
|
|
|
|
|
/* Parse and interpet a $FileCreateMode and $umask line. This function
|
|
* pulls the creation mode and, if successful, stores it
|
|
* into the global variable so that the rest of rsyslogd
|
|
* opens files with that mode. Any previous value will be
|
|
* overwritten.
|
|
* HINT: if we store the creation mode in selector_t, we
|
|
* can even specify multiple modes simply be virtue of
|
|
* being placed in the right section of rsyslog.conf
|
|
* rgerhards, 2007-07-4 (happy independence day to my US friends!)
|
|
* Parameter **pp has a pointer to the current config line.
|
|
* On exit, it will be updated to the processed position.
|
|
*/
|
|
rsRetVal doFileCreateMode(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *pVal)
|
|
{
|
|
uchar *p;
|
|
rsRetVal iRet = RS_RET_OK;
|
|
uchar errMsg[128]; /* for dynamic error messages */
|
|
int iVal;
|
|
|
|
assert(pp != NULL);
|
|
assert(*pp != NULL);
|
|
|
|
skipWhiteSpace(pp); /* skip over any whitespace */
|
|
p = *pp;
|
|
|
|
/* for now, we parse and accept only octal numbers
|
|
* Sequence of tests is important, we are using boolean shortcuts
|
|
* to avoid addressing invalid memory!
|
|
*/
|
|
if(!( (*p == '0')
|
|
&& (*(p+1) && *(p+1) >= '0' && *(p+1) <= '7')
|
|
&& (*(p+2) && *(p+2) >= '0' && *(p+2) <= '7')
|
|
&& (*(p+3) && *(p+3) >= '0' && *(p+3) <= '7') ) ) {
|
|
snprintf((char*) errMsg, sizeof(errMsg)/sizeof(uchar),
|
|
"value must be octal (e.g 0644).");
|
|
errno = 0;
|
|
logerror((char*) errMsg);
|
|
iRet = RS_RET_INVALID_VALUE;
|
|
goto finalize_it;
|
|
}
|
|
|
|
/* we reach this code only if the octal number is ok - so we can now
|
|
* compute the value.
|
|
*/
|
|
iVal = (*(p+1)-'0') * 64 + (*(p+2)-'0') * 8 + (*(p+3)-'0');
|
|
|
|
if(pSetHdlr == NULL) {
|
|
/* we should set value directly to var */
|
|
*((int*)pVal) = iVal;
|
|
} else {
|
|
/* we set value via a set function */
|
|
CHKiRet(pSetHdlr(pVal, iVal));
|
|
}
|
|
|
|
p += 4; /* eat the octal number */
|
|
*pp = p;
|
|
|
|
finalize_it:
|
|
return iRet;
|
|
}
|
|
|
|
|
|
/* Parse and interpret an on/off inside a config file line. This is most
|
|
* often used for boolean options, but of course it may also be used
|
|
* for other things. The passed-in pointer is updated to point to
|
|
* the first unparsed character on exit. Function emits error messages
|
|
* if the value is neither on or off. It returns 0 if the option is off,
|
|
* 1 if it is on and another value if there was an error.
|
|
* rgerhards, 2007-07-15
|
|
*/
|
|
static int doParseOnOffOption(uchar **pp)
|
|
{
|
|
uchar *pOptStart;
|
|
uchar szOpt[32];
|
|
|
|
assert(pp != NULL);
|
|
assert(*pp != NULL);
|
|
|
|
pOptStart = *pp;
|
|
skipWhiteSpace(pp); /* skip over any whitespace */
|
|
|
|
if(getSubString(pp, (char*) szOpt, sizeof(szOpt) / sizeof(uchar), ' ') != 0) {
|
|
logerror("Invalid $-configline - could not extract on/off option");
|
|
return -1;
|
|
}
|
|
|
|
if(!strcmp((char*)szOpt, "on")) {
|
|
return 1;
|
|
} else if(!strcmp((char*)szOpt, "off")) {
|
|
return 0;
|
|
} else {
|
|
logerrorSz("Option value must be on or off, but is '%s'", (char*)pOptStart);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
/* extract a groupname and return its gid.
|
|
* rgerhards, 2007-07-17
|
|
*/
|
|
rsRetVal doGetGID(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *pVal)
|
|
{
|
|
struct group *pgBuf;
|
|
struct group gBuf;
|
|
rsRetVal iRet = RS_RET_OK;
|
|
uchar szName[256];
|
|
char stringBuf[2048]; /* I hope this is large enough... */
|
|
|
|
assert(pp != NULL);
|
|
assert(*pp != NULL);
|
|
|
|
if(getSubString(pp, (char*) szName, sizeof(szName) / sizeof(uchar), ' ') != 0) {
|
|
logerror("could not extract group name");
|
|
iRet = RS_RET_NOT_FOUND;
|
|
goto finalize_it;
|
|
}
|
|
|
|
getgrnam_r((char*)szName, &gBuf, stringBuf, sizeof(stringBuf), &pgBuf);
|
|
|
|
if(pgBuf == NULL) {
|
|
logerrorSz("ID for group '%s' could not be found or error", (char*)szName);
|
|
iRet = RS_RET_NOT_FOUND;
|
|
} else {
|
|
if(pSetHdlr == NULL) {
|
|
/* we should set value directly to var */
|
|
*((gid_t*)pVal) = pgBuf->gr_gid;
|
|
} else {
|
|
/* we set value via a set function */
|
|
CHKiRet(pSetHdlr(pVal, pgBuf->gr_gid));
|
|
}
|
|
dprintf("gid %d obtained for group '%s'\n", pgBuf->gr_gid, szName);
|
|
}
|
|
|
|
skipWhiteSpace(pp); /* skip over any whitespace */
|
|
|
|
finalize_it:
|
|
return iRet;
|
|
}
|
|
|
|
|
|
/* extract a username and return its uid.
|
|
* rgerhards, 2007-07-17
|
|
*/
|
|
rsRetVal doGetUID(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *pVal)
|
|
{
|
|
struct passwd *ppwBuf;
|
|
struct passwd pwBuf;
|
|
rsRetVal iRet = RS_RET_OK;
|
|
uchar szName[256];
|
|
char stringBuf[2048]; /* I hope this is large enough... */
|
|
|
|
assert(pp != NULL);
|
|
assert(*pp != NULL);
|
|
|
|
if(getSubString(pp, (char*) szName, sizeof(szName) / sizeof(uchar), ' ') != 0) {
|
|
logerror("could not extract user name");
|
|
iRet = RS_RET_NOT_FOUND;
|
|
goto finalize_it;
|
|
}
|
|
|
|
getpwnam_r((char*)szName, &pwBuf, stringBuf, sizeof(stringBuf), &ppwBuf);
|
|
|
|
if(ppwBuf == NULL) {
|
|
logerrorSz("ID for user '%s' could not be found or error", (char*)szName);
|
|
iRet = RS_RET_NOT_FOUND;
|
|
} else {
|
|
if(pSetHdlr == NULL) {
|
|
/* we should set value directly to var */
|
|
*((uid_t*)pVal) = ppwBuf->pw_uid;
|
|
} else {
|
|
/* we set value via a set function */
|
|
CHKiRet(pSetHdlr(pVal, ppwBuf->pw_uid));
|
|
}
|
|
dprintf("uid %d obtained for user '%s'\n", ppwBuf->pw_uid, szName);
|
|
}
|
|
|
|
skipWhiteSpace(pp); /* skip over any whitespace */
|
|
|
|
finalize_it:
|
|
return iRet;
|
|
}
|
|
|
|
|
|
/* Parse and process an binary cofig option. pVal must be
|
|
* a pointer to an integer which is to receive the option
|
|
* value.
|
|
* rgerhards, 2007-07-15
|
|
*/
|
|
rsRetVal doBinaryOptionLine(uchar **pp, rsRetVal (*pSetHdlr)(void*, int), void *pVal)
|
|
{
|
|
int iOption;
|
|
rsRetVal iRet = RS_RET_OK;
|
|
|
|
assert(pp != NULL);
|
|
assert(*pp != NULL);
|
|
|
|
if((iOption = doParseOnOffOption(pp)) == -1)
|
|
return RS_RET_ERR; /* nothing left to do */
|
|
|
|
if(pSetHdlr == NULL) {
|
|
/* we should set value directly to var */
|
|
*((int*)pVal) = iOption;
|
|
} else {
|
|
/* we set value via a set function */
|
|
CHKiRet(pSetHdlr(pVal, iOption));
|
|
}
|
|
|
|
skipWhiteSpace(pp); /* skip over any whitespace */
|
|
|
|
finalize_it:
|
|
return iRet;
|
|
}
|
|
|
|
|
|
/* --------------- END functions for handling canned syntaxes --------------- */
|
|
|
|
/* destructor for cslCmdHdlr
|
|
*/
|
|
rsRetVal cslchDestruct(cslCmdHdlr_t *pThis)
|
|
{
|
|
assert(pThis != NULL);
|
|
free(pThis);
|
|
|
|
return RS_RET_OK;
|
|
}
|
|
|
|
|
|
/* constructor for cslCmdHdlr
|
|
*/
|
|
rsRetVal cslchConstruct(cslCmdHdlr_t **ppThis)
|
|
{
|
|
cslCmdHdlr_t *pThis;
|
|
rsRetVal iRet = RS_RET_OK;
|
|
|
|
assert(ppThis != NULL);
|
|
if((pThis = calloc(1, sizeof(cslCmdHdlr_t))) == NULL) {
|
|
iRet = RS_RET_OUT_OF_MEMORY;
|
|
goto abort_it;
|
|
}
|
|
|
|
abort_it:
|
|
*ppThis = pThis;
|
|
return iRet;
|
|
}
|
|
|
|
/* set data members for this object
|
|
*/
|
|
rsRetVal cslchSetEntry(cslCmdHdlr_t *pThis, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData)
|
|
{
|
|
assert(pThis != NULL);
|
|
assert(eType != eCmdHdlrInvalid);
|
|
|
|
pThis->eType = eType;
|
|
pThis->cslCmdHdlr = pHdlr;
|
|
pThis->pData = pData;
|
|
|
|
return RS_RET_OK;
|
|
}
|
|
|
|
|
|
/* call the specified handler
|
|
*/
|
|
rsRetVal cslchCallHdlr(cslCmdHdlr_t *pThis, uchar **ppConfLine)
|
|
{
|
|
assert(pThis != NULL);
|
|
assert(ppConfLine != NULL);
|
|
|
|
|
|
return RS_RET_OK;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- *
|
|
* now come the handlers for cslCmd_t
|
|
* ---------------------------------------------------------------------- */
|
|
|
|
/* destructor for cslCmd
|
|
*/
|
|
rsRetVal cslcDestruct(cslCmd_t *pThis)
|
|
{
|
|
cslCmdHdlr_t *pHdlr;
|
|
cslCmdHdlr_t *pHdlrPrev;
|
|
assert(pThis != NULL);
|
|
|
|
for(pHdlr = pThis->pHdlrRoot ; pHdlr != NULL ; pHdlr = pHdlrPrev->pNext) {
|
|
pHdlrPrev = pHdlr; /* else we get a segfault */
|
|
cslchDestruct(pHdlr);
|
|
}
|
|
|
|
free(pThis->pszCmdString);
|
|
free(pThis);
|
|
|
|
return RS_RET_OK;
|
|
}
|
|
|
|
|
|
/* constructor for cslCmdHdlr
|
|
*/
|
|
rsRetVal cslcConstruct(cslCmd_t **ppThis)
|
|
{
|
|
cslCmd_t *pThis;
|
|
rsRetVal iRet = RS_RET_OK;
|
|
|
|
assert(ppThis != NULL);
|
|
if((pThis = calloc(1, sizeof(cslCmd_t))) == NULL) {
|
|
iRet = RS_RET_OUT_OF_MEMORY;
|
|
goto abort_it;
|
|
}
|
|
|
|
abort_it:
|
|
*ppThis = pThis;
|
|
return iRet;
|
|
}
|
|
/*
|
|
* vi:set ai:
|
|
*/
|