summaryrefslogtreecommitdiffstats
path: root/runtime/conf.c
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/conf.c')
-rw-r--r--runtime/conf.c233
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: