diff options
Diffstat (limited to 'runtime/conf.c')
-rw-r--r-- | runtime/conf.c | 233 |
1 files changed, 194 insertions, 39 deletions
diff --git a/runtime/conf.c b/runtime/conf.c index 529142ed..9fecc34d 100644 --- a/runtime/conf.c +++ b/runtime/conf.c @@ -12,7 +12,7 @@ * the selector lines (e.g. *.info). That code is scheduled for removal * as part of RainerScript. After this is done, we can change licenses. * - * Copyright 2008 Rainer Gerhards and Adiscon GmbH. + * Copyright 2008-2011 Rainer Gerhards and Adiscon GmbH. * * This file is part of rsyslog. * @@ -52,7 +52,6 @@ #endif #include "rsyslog.h" -#include "../tools/syslogd.h" /* TODO: this must be removed! */ #include "dirty.h" #include "parse.h" #include "action.h" @@ -71,6 +70,7 @@ #include "ctok_token.h" #include "rule.h" #include "ruleset.h" +#include "rsconf.h" #include "unicode-helper.h" #ifdef OS_SOLARIS @@ -78,8 +78,8 @@ #endif /* forward definitions */ -static rsRetVal cfline(uchar *line, rule_t **pfCurr); -static rsRetVal processConfFile(uchar *pConfFile); +static rsRetVal cfline(rsconf_t *conf, uchar *line, rule_t **pfCurr); +static rsRetVal processConfFile(rsconf_t *conf, uchar *pConfFile); /* static data */ @@ -93,7 +93,9 @@ DEFobjCurrIf(net) DEFobjCurrIf(rule) DEFobjCurrIf(ruleset) -static int iNbrActions = 0; /* number of currently defined actions */ +ecslConfObjType currConfObj = eConfObjGlobal; /* to support scoping - which config object is currently active? */ +int bConfStrictScoping = 0; /* force strict scoping during config processing? */ + /* The following module-global variables are used for building * tag and host selector lines during startup and config reload. @@ -114,7 +116,7 @@ static cstr_t *pDfltProgNameCmp = NULL; * indeed a directory. * rgerhards, 2007-08-01 */ -static rsRetVal doIncludeDirectory(uchar *pDirName) +static rsRetVal doIncludeDirectory(rsconf_t *conf, uchar *pDirName) { DEFiRet; int iEntriesDone = 0; @@ -164,7 +166,7 @@ static rsRetVal doIncludeDirectory(uchar *pDirName) memcpy(szFullFileName + iDirNameLen, res->d_name, iFileNameLen); *(szFullFileName + iDirNameLen + iFileNameLen) = '\0'; dbgprintf("including file '%s'\n", szFullFileName); - processConfFile(szFullFileName); + processConfFile(conf, szFullFileName); /* we deliberately ignore the iRet of processConfFile() - this is because * failure to process one file does not mean all files will fail. By ignoring, * we retry with the next file, which is the best thing we can do. -- rgerhards, 2007-08-01 @@ -193,7 +195,7 @@ finalize_it: * rgerhards, 2007-08-01 */ rsRetVal -doIncludeLine(uchar **pp, __attribute__((unused)) void* pVal) +doIncludeLine(rsconf_t *conf, uchar **pp, __attribute__((unused)) void* pVal) { DEFiRet; char pattern[MAXFNAME]; @@ -231,10 +233,10 @@ doIncludeLine(uchar **pp, __attribute__((unused)) void* pVal) if(S_ISREG(fileInfo.st_mode)) { /* config file */ dbgprintf("requested to include config file '%s'\n", cfgFile); - iRet = processConfFile(cfgFile); + iRet = processConfFile(conf, cfgFile); } else if(S_ISDIR(fileInfo.st_mode)) { /* config directory */ dbgprintf("requested to include directory '%s'\n", cfgFile); - iRet = doIncludeDirectory(cfgFile); + iRet = doIncludeDirectory(conf, cfgFile); } else { /* TODO: shall we handle symlinks or not? */ dbgprintf("warning: unable to process IncludeConfig directive '%s'\n", cfgFile); } @@ -250,7 +252,7 @@ finalize_it: /* process a $ModLoad config line. */ rsRetVal -doModLoad(uchar **pp, __attribute__((unused)) void* pVal) +doModLoad(rsconf_t * conf, uchar **pp, __attribute__((unused)) void* pVal) { DEFiRet; uchar szName[512]; @@ -322,7 +324,7 @@ doNameLine(uchar **pp, void* pVal) switch(eDir) { case DIR_TEMPLATE: - tplAddLine(szName, &p); + tplAddLine(loadConf, szName, &p); break; case DIR_OUTCHANNEL: ochAddLine(szName, &p); @@ -353,7 +355,7 @@ finalize_it: * 2004-11-17 rgerhards */ rsRetVal -cfsysline(uchar *p) +cfsysline(rsconf_t *conf, uchar *p) { DEFiRet; uchar szCmd[64]; @@ -394,7 +396,7 @@ finalize_it: * started with code from init() by rgerhards on 2007-07-31 */ static rsRetVal -processConfFile(uchar *pConfFile) +processConfFile(rsconf_t *conf, uchar *pConfFile) { int iLnNbr = 0; FILE *cf; @@ -461,7 +463,7 @@ processConfFile(uchar *pConfFile) /* we now have the complete line, and are positioned at the first non-whitespace * character. So let's process it */ - if(cfline(cbuf, &pCurrRule) != RS_RET_OK) { + if(cfline(conf, cbuf, &pCurrRule) != RS_RET_OK) { /* we log a message, but otherwise ignore the error. After all, the next * line can be correct. -- rgerhards, 2007-08-02 */ @@ -476,7 +478,7 @@ processConfFile(uchar *pConfFile) /* we probably have one selector left to be added - so let's do that now */ if(pCurrRule != NULL) { - CHKiRet(ruleset.AddRule(rule.GetAssRuleset(pCurrRule), &pCurrRule)); + CHKiRet(ruleset.AddRule(conf, rule.GetAssRuleset(pCurrRule), &pCurrRule)); } /* close the configuration file */ @@ -871,6 +873,14 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register rule_t *f) rsParsDestruct(pPars); return(iRet); } + if(f->f_filterData.prop.propID == PROP_CEE) { + /* in CEE case, we need to preserve the actual property name */ + if((f->f_filterData.prop.propName = + es_newStrFromBuf((char*)cstrGetSzStrNoNULL(pCSPropName)+2, cstrLen(pCSPropName)-2)) == NULL) { + cstrDestruct(&pCSPropName); + return(RS_RET_ERR); + } + } cstrDestruct(&pCSPropName); /* read operation */ @@ -899,10 +909,13 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register rule_t *f) iOffset = 0; } +dbgprintf("XXX: offset is %d, string '%s'\n", iOffset, rsCStrGetSzStrNoNULL(pCSCompOp)); if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (uchar*) "contains", 8)) { f->f_filterData.prop.operation = FIOP_CONTAINS; } else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (uchar*) "isequal", 7)) { f->f_filterData.prop.operation = FIOP_ISEQUAL; + } else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (uchar*) "isempty", 7)) { + f->f_filterData.prop.operation = FIOP_ISEMPTY; } else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (uchar*) "startswith", 10)) { f->f_filterData.prop.operation = FIOP_STARTSWITH; } else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (unsigned char*) "regex", 5)) { @@ -915,12 +928,15 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register rule_t *f) } rsCStrDestruct(&pCSCompOp); /* no longer needed */ - /* read compare value */ - iRet = parsQuotedCStr(pPars, &f->f_filterData.prop.pCSCompValue); - if(iRet != RS_RET_OK) { - errmsg.LogError(0, iRet, "error %d compare value property - ignoring selector", iRet); - rsParsDestruct(pPars); - return(iRet); +dbgprintf("XXX: fiop is %u\n", (unsigned) f->f_filterData.prop.operation); + if(f->f_filterData.prop.operation != FIOP_ISEMPTY) { + /* read compare value */ + iRet = parsQuotedCStr(pPars, &f->f_filterData.prop.pCSCompValue); + if(iRet != RS_RET_OK) { + errmsg.LogError(0, iRet, "error %d compare value property - ignoring selector", iRet); + rsParsDestruct(pPars); + return(iRet); + } } /* skip to action part */ @@ -943,7 +959,7 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register rule_t *f) * from the config file ("+/-hostname"). It stores it for further reference. * rgerhards 2005-10-19 */ -static rsRetVal cflineProcessHostSelector(uchar **pline) +static rsRetVal cflineProcessHostSelector(rsconf_t *conf, uchar **pline) { DEFiRet; @@ -993,7 +1009,7 @@ finalize_it: * from the config file ("!tagname"). It stores it for further reference. * rgerhards 2005-10-18 */ -static rsRetVal cflineProcessTagSelector(uchar **pline) +static rsRetVal cflineProcessTagSelector(rsconf_t *conf, uchar **pline) { DEFiRet; @@ -1062,7 +1078,6 @@ static rsRetVal cflineDoFilter(uchar **pp, rule_t *f) * and, if so, we copy them over. rgerhards, 2005-10-18 */ if(pDfltProgNameCmp != NULL) { -RUNLOG_STR("dflt ProgNameCmp != NULL, setting opCSProgNameComp"); CHKiRet(rsCStrConstructFromCStr(&(f->pCSProgNameComp), pDfltProgNameCmp)); } @@ -1079,7 +1094,7 @@ finalize_it: /* process the action part of a selector line * rgerhards, 2007-08-01 */ -static rsRetVal cflineDoAction(uchar **p, action_t **ppAction) +static rsRetVal cflineDoAction(rsconf_t *conf, uchar **p, action_t **ppAction) { DEFiRet; modInfo_t *pMod; @@ -1102,16 +1117,21 @@ static rsRetVal cflineDoAction(uchar **p, action_t **ppAction) iRet = pMod->mod.om.parseSelectorAct(p, &pModData, &pOMSR); dbgprintf("tried selector action for %s: %d\n", module.GetName(pMod), iRet); if(iRet == RS_RET_OK || iRet == RS_RET_SUSPENDED) { + /* advance our config parser state: we now only accept an $End as valid, + * no more action statments. + */ + if(currConfObj == eConfObjAction) + currConfObj = eConfObjActionWaitEnd; if((iRet = addAction(&pAction, pMod, pModData, pOMSR, (iRet == RS_RET_SUSPENDED)? 1 : 0)) == RS_RET_OK) { /* now check if the module is compatible with select features */ if(pMod->isCompatibleWithFeature(sFEATURERepeatedMsgReduction) == RS_RET_OK) - pAction->f_ReduceRepeated = bReduceRepeatMsgs; + pAction->f_ReduceRepeated = loadConf->globals.bReduceRepeatMsgs; else { dbgprintf("module is incompatible with RepeatedMsgReduction - turned off\n"); pAction->f_ReduceRepeated = 0; } pAction->eState = ACT_STATE_RDY; /* action is enabled */ - iNbrActions++; /* one more active action! */ + conf->actions.nbrActions++; /* one more active action! */ } break; } @@ -1139,7 +1159,7 @@ static rsRetVal cflineDoAction(uchar **p, action_t **ppAction) * of the master config file!). */ static rsRetVal -cflineClassic(uchar *p, rule_t **ppRule) +cflineClassic(rsconf_t *conf, uchar *p, rule_t **ppRule) { DEFiRet; action_t *pAction; @@ -1161,15 +1181,15 @@ cflineClassic(uchar *p, rule_t **ppRule) * all. -- rgerhards, 2007-08-01 */ if(*ppRule != NULL) { - CHKiRet(ruleset.AddRule(rule.GetAssRuleset(*ppRule), ppRule)); + CHKiRet(ruleset.AddRule(conf, rule.GetAssRuleset(*ppRule), ppRule)); } CHKiRet(rule.Construct(ppRule)); /* create "fresh" selector */ - CHKiRet(rule.SetAssRuleset(*ppRule, ruleset.GetCurrent())); /* create "fresh" selector */ + CHKiRet(rule.SetAssRuleset(*ppRule, ruleset.GetCurrent(conf))); /* create "fresh" selector */ CHKiRet(rule.ConstructFinalize(*ppRule)); /* create "fresh" selector */ CHKiRet(cflineDoFilter(&p, *ppRule)); /* pull filters */ } - CHKiRet(cflineDoAction(&p, &pAction)); + CHKiRet(cflineDoAction(conf, &p, &pAction)); CHKiRet(llAppend(&(*ppRule)->llActList, NULL, (void*) pAction)); finalize_it: @@ -1182,7 +1202,7 @@ finalize_it: * rgerhards, 2007-08-01 */ static rsRetVal -cfline(uchar *line, rule_t **pfCurr) +cfline(rsconf_t *conf, uchar *line, rule_t **pfCurr) { DEFiRet; @@ -1193,18 +1213,18 @@ cfline(uchar *line, rule_t **pfCurr) /* check type of line and call respective processing */ switch(*line) { case '!': - iRet = cflineProcessTagSelector(&line); + iRet = cflineProcessTagSelector(conf, &line); break; case '+': case '-': - iRet = cflineProcessHostSelector(&line); + iRet = cflineProcessHostSelector(conf, &line); break; case '$': ++line; /* eat '$' */ - iRet = cfsysline(line); + iRet = cfsysline(conf, line); break; default: - iRet = cflineClassic(line, pfCurr); + iRet = cflineClassic(conf, line, pfCurr); break; } @@ -1216,11 +1236,11 @@ cfline(uchar *line, rule_t **pfCurr) * rgerhards, 2008-07-28 */ static rsRetVal -GetNbrActActions(int *piNbrActions) +GetNbrActActions(rsconf_t *conf, int *piNbrActions) { DEFiRet; assert(piNbrActions != NULL); - *piNbrActions = iNbrActions; + *piNbrActions = conf->actions.nbrActions; RETiRet; } @@ -1251,6 +1271,136 @@ finalize_it: ENDobjQueryInterface(conf) +/* switch to a new action scope. This means that we switch the current + * mode to action, but it also means we need to clear all scope variables, + * so that we have a new environment. + * rgerhards, 2010-07-23 + */ +static inline rsRetVal +setActionScope(void) +{ + DEFiRet; + modInfo_t *pMod; + + currConfObj = eConfObjAction; + DBGPRINTF("entering action scope\n"); + CHKiRet(actionNewScope()); + + /* now tell each action to start the scope */ + pMod = NULL; + while((pMod = module.GetNxtType(pMod, eMOD_OUT)) != NULL) { + DBGPRINTF("beginning scope on module %s\n", pMod->pszName); + pMod->mod.om.newScope(); + } + +finalize_it: + RETiRet; +} + + +/* switch back from action scope. + * rgerhards, 2010-07-27 + */ +static inline rsRetVal +unsetActionScope(void) +{ + DEFiRet; + modInfo_t *pMod; + + currConfObj = eConfObjAction; + DBGPRINTF("exiting action scope\n"); + CHKiRet(actionRestoreScope()); + + /* now tell each action to restore the scope */ + pMod = NULL; + while((pMod = module.GetNxtType(pMod, eMOD_OUT)) != NULL) { + DBGPRINTF("exiting scope on module %s\n", pMod->pszName); + pMod->mod.om.restoreScope(); + } + +finalize_it: + RETiRet; +} + + +/* This method is called by our own handlers to begin a new config + * object ($Begin statement). This also implies a new scope. + * rgerhards, 2010-07-23 + */ +static rsRetVal +beginConfObj(void __attribute__((unused)) *pVal, uchar *pszName) +{ + DEFiRet; + + if(currConfObj != eConfObjGlobal) { + errmsg.LogError(0, RS_RET_CONF_NOT_GLBL, "not in global scope - can not nest $Begin"); + ABORT_FINALIZE(RS_RET_CONF_NOT_GLBL); + } + + if(!strcasecmp((char*)pszName, "action")) { + setActionScope(); + } else { + errmsg.LogError(0, RS_RET_INVLD_CONF_OBJ, "invalid config object \"%s\" in $Begin", pszName); + ABORT_FINALIZE(RS_RET_INVLD_CONF_OBJ); + } + +finalize_it: + free(pszName); /* no longer needed */ + RETiRet; +} + + +/* This method is called to end a config scope and switch + * back to global scope. + * rgerhards, 2010-07-23 + */ +static rsRetVal +endConfObj(void __attribute__((unused)) *pVal, uchar *pszName) +{ + DEFiRet; + + if(currConfObj == eConfObjGlobal) { + errmsg.LogError(0, RS_RET_CONF_NOT_GLBL, "already in global scope - dangling $End"); + ABORT_FINALIZE(RS_RET_CONF_IN_GLBL); + } + + if(!strcasecmp((char*)pszName, "action")) { + if(currConfObj == eConfObjAction) { + errmsg.LogError(0, RS_RET_CONF_END_NO_ACT, "$End action but not action specified"); + /* this is a warning, we continue processing in that case (unscope) */ + } else if(currConfObj != eConfObjActionWaitEnd) { + errmsg.LogError(0, RS_RET_CONF_INVLD_END, "$End not for active config object - " + "nesting error?"); + ABORT_FINALIZE(RS_RET_CONF_INVLD_END); + } + currConfObj = eConfObjGlobal; + CHKiRet(unsetActionScope()); + } else { + errmsg.LogError(0, RS_RET_INVLD_CONF_OBJ, "invalid config object \"%s\" in $End", pszName); + ABORT_FINALIZE(RS_RET_INVLD_CONF_OBJ); + } + +finalize_it: + free(pszName); /* no longer needed */ + RETiRet; +} + + +/* Reset config variables to default values. Note that + * when we are inside an scope, we simply reset this to global. + * However, $ResetConfigVariables is a global directive, and as such + * will not be honored inside a scope! + * rgerhards, 2010-07-23 + */ +static rsRetVal +resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal) +{ + currConfObj = eConfObjGlobal; + bConfStrictScoping = 0; + return RS_RET_OK; +} + + /* exit our class * rgerhards, 2008-03-11 */ @@ -1291,6 +1441,11 @@ BEGINAbstractObjClassInit(conf, 1, OBJ_IS_CORE_MODULE) /* class, version - CHANG CHKiRet(objUse(net, LM_NET_FILENAME)); /* TODO: make this dependcy go away! */ CHKiRet(objUse(rule, CORE_COMPONENT)); CHKiRet(objUse(ruleset, CORE_COMPONENT)); + + CHKiRet(regCfSysLineHdlr((uchar *)"begin", 0, eCmdHdlrGetWord, beginConfObj, NULL, NULL, eConfObjGlobal)); + CHKiRet(regCfSysLineHdlr((uchar *)"end", 0, eCmdHdlrGetWord, endConfObj, NULL, NULL, eConfObjAlways)); + CHKiRet(regCfSysLineHdlr((uchar *)"strictscoping", 0, eCmdHdlrBinary, NULL, &bConfStrictScoping, NULL, eConfObjGlobal)); + CHKiRet(regCfSysLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, NULL, eConfObjAction)); ENDObjClassInit(conf) /* vi:set ai: |