summaryrefslogtreecommitdiffstats
path: root/runtime/ruleset.c
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/ruleset.c')
-rw-r--r--runtime/ruleset.c629
1 files changed, 504 insertions, 125 deletions
diff --git a/runtime/ruleset.c b/runtime/ruleset.c
index 5cb34148..8d2bb924 100644
--- a/runtime/ruleset.c
+++ b/runtime/ruleset.c
@@ -11,27 +11,24 @@
*
* Module begun 2009-06-10 by Rainer Gerhards
*
- * Copyright 2009-2011 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2009-2012 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of the rsyslog runtime library.
*
- * The rsyslog runtime library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The rsyslog runtime library 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>.
- *
- * A copy of the GPL can be found in the file "COPYING" in this distribution.
- * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
-
#include "config.h"
#include <stdlib.h>
#include <assert.h>
@@ -42,22 +39,36 @@
#include "cfsysline.h"
#include "msg.h"
#include "ruleset.h"
-#include "rule.h"
#include "errmsg.h"
#include "parser.h"
#include "batch.h"
#include "unicode-helper.h"
#include "rsconf.h"
+#include "action.h"
+#include "rainerscript.h"
+#include "srUtils.h"
+#include "modules.h"
#include "dirty.h" /* for main ruleset queue creation */
/* static data */
DEFobjStaticHelpers
DEFobjCurrIf(errmsg)
-DEFobjCurrIf(rule)
DEFobjCurrIf(parser)
+/* tables for interfacing with the v6 config system (as far as we need to) */
+static struct cnfparamdescr rspdescr[] = {
+ { "name", eCmdHdlrString, CNFPARAM_REQUIRED },
+ { "parser", eCmdHdlrArray, 0 }
+};
+static struct cnfparamblk rspblk =
+ { CNFPARAMBLK_VERSION,
+ sizeof(rspdescr)/sizeof(struct cnfparamdescr),
+ rspdescr
+ };
+
/* forward definitions */
static rsRetVal processBatch(batch_t *pBatch);
+static rsRetVal scriptExec(struct cnfstmt *root, batch_t *pBatch, sbool *active);
/* ---------- linked-list key handling functions (ruleset) ---------- */
@@ -73,45 +84,61 @@ rulesetKeyDestruct(void __attribute__((unused)) *pData)
/* ---------- END linked-list key handling functions (ruleset) ---------- */
+/* iterate over all actions in a script (stmt subtree) */
+static void
+scriptIterateAllActions(struct cnfstmt *root, rsRetVal (*pFunc)(void*, void*), void* pParam)
+{
+ struct cnfstmt *stmt;
+ for(stmt = root ; stmt != NULL ; stmt = stmt->next) {
+ switch(stmt->nodetype) {
+ case S_NOP:
+ case S_STOP:
+ case S_CALL:/* call does not need to do anything - done in called ruleset! */
+ break;
+ case S_ACT:
+ DBGPRINTF("iterateAllActions calling into action %p\n", stmt->d.act);
+ pFunc(stmt->d.act, pParam);
+ break;
+ case S_IF:
+ if(stmt->d.s_if.t_then != NULL)
+ scriptIterateAllActions(stmt->d.s_if.t_then,
+ pFunc, pParam);
+ if(stmt->d.s_if.t_else != NULL)
+ scriptIterateAllActions(stmt->d.s_if.t_else,
+ pFunc, pParam);
+ break;
+ case S_PRIFILT:
+ if(stmt->d.s_prifilt.t_then != NULL)
+ scriptIterateAllActions(stmt->d.s_prifilt.t_then,
+ pFunc, pParam);
+ if(stmt->d.s_prifilt.t_else != NULL)
+ scriptIterateAllActions(stmt->d.s_prifilt.t_else,
+ pFunc, pParam);
+ break;
+ case S_PROPFILT:
+ scriptIterateAllActions(stmt->d.s_propfilt.t_then,
+ pFunc, pParam);
+ break;
+ default:
+ dbgprintf("error: unknown stmt type %u during iterateAll\n",
+ (unsigned) stmt->nodetype);
+ break;
+ }
+ }
+}
/* driver to iterate over all of this ruleset actions */
typedef struct iterateAllActions_s {
rsRetVal (*pFunc)(void*, void*);
void *pParam;
} iterateAllActions_t;
-DEFFUNC_llExecFunc(doIterateRulesetActions)
-{
- DEFiRet;
- rule_t* pRule = (rule_t*) pData;
- iterateAllActions_t *pMyParam = (iterateAllActions_t*) pParam;
- iRet = rule.IterateAllActions(pRule, pMyParam->pFunc, pMyParam->pParam);
- RETiRet;
-}
-/* iterate over all actions of THIS rule set.
- */
-static rsRetVal
-iterateRulesetAllActions(ruleset_t *pThis, rsRetVal (*pFunc)(void*, void*), void* pParam)
-{
- iterateAllActions_t params;
- DEFiRet;
- assert(pFunc != NULL);
-
- params.pFunc = pFunc;
- params.pParam = pParam;
- CHKiRet(llExecFunc(&(pThis->llRules), doIterateRulesetActions, &params));
-
-finalize_it:
- RETiRet;
-}
-
-
/* driver to iterate over all actions */
DEFFUNC_llExecFunc(doIterateAllActions)
{
DEFiRet;
ruleset_t* pThis = (ruleset_t*) pData;
iterateAllActions_t *pMyParam = (iterateAllActions_t*) pParam;
- iRet = iterateRulesetAllActions(pThis, pMyParam->pFunc, pMyParam->pParam);
+ scriptIterateAllActions(pThis->root, pMyParam->pFunc, pMyParam->pParam);
RETiRet;
}
/* iterate over ALL actions present in the WHOLE system.
@@ -134,30 +161,10 @@ finalize_it:
}
-
-/* helper to processBatch(), used to call the configured actions. It is
- * executed from within llExecFunc() of the action list.
- * rgerhards, 2007-08-02
- */
-DEFFUNC_llExecFunc(processBatchDoRules)
-{
- rsRetVal iRet;
- ISOBJ_TYPE_assert(pData, rule);
- DBGPRINTF("Processing next rule\n");
- iRet = rule.ProcessBatch((rule_t*) pData, (batch_t*) pParam);
- DBGPRINTF("ruleset: get iRet %d from rule.ProcessMsg()\n", iRet);
- return iRet;
-}
-
-
-
/* This function is similar to processBatch(), but works on a batch that
* contains rules from multiple rulesets. In this case, we can not push
* the whole batch through the ruleset. Instead, we examine it and
* partition it into sub-rulesets which we then push through the system.
- * Note that when we evaluate which message must be processed, we do NOT need
- * to look at bFilterOK, because this value is only set in a later processing
- * stage. Doing so caused a bug during development ;)
* rgerhards, 2010-06-15
*/
static inline rsRetVal
@@ -207,6 +214,324 @@ finalize_it:
RETiRet;
}
+/* return a new "active" structure for the batch. Free with freeActive(). */
+static inline sbool *newActive(batch_t *pBatch)
+{
+ return malloc(sizeof(sbool) * batchNumMsgs(pBatch));
+
+}
+static inline void freeActive(sbool *active) { free(active); }
+
+
+/* for details, see scriptExec() header comment! */
+/* call action for all messages with filter on */
+static rsRetVal
+execAct(struct cnfstmt *stmt, batch_t *pBatch, sbool *active)
+{
+ DEFiRet;
+dbgprintf("RRRR: execAct [%s]: batch of %d elements, active %p\n", modGetName(stmt->d.act->pMod), batchNumMsgs(pBatch), active);
+ pBatch->active = active;
+ stmt->d.act->submitToActQ(stmt->d.act, pBatch);
+ RETiRet;
+}
+
+static rsRetVal
+execSet(struct cnfstmt *stmt, batch_t *pBatch, sbool *active)
+{
+ int i;
+ struct var result;
+ DEFiRet;
+ for(i = 0 ; i < batchNumMsgs(pBatch) && !*(pBatch->pbShutdownImmediate) ; ++i) {
+ if( pBatch->pElem[i].state != BATCH_STATE_DISC
+ && (active == NULL || active[i])) {
+ cnfexprEval(stmt->d.s_set.expr, &result, pBatch->pElem[i].pUsrp);
+ msgSetJSONFromVar((msg_t*)pBatch->pElem[i].pUsrp, stmt->d.s_set.varname,
+ &result);
+ varDelete(&result);
+ }
+ }
+ RETiRet;
+}
+
+static rsRetVal
+execUnset(struct cnfstmt *stmt, batch_t *pBatch, sbool *active)
+{
+ int i;
+ DEFiRet;
+ for(i = 0 ; i < batchNumMsgs(pBatch) && !*(pBatch->pbShutdownImmediate) ; ++i) {
+ if( pBatch->pElem[i].state != BATCH_STATE_DISC
+ && (active == NULL || active[i])) {
+ msgUnsetJSON((msg_t*)pBatch->pElem[i].pUsrp, stmt->d.s_unset.varname);
+ }
+ }
+ RETiRet;
+}
+
+/* for details, see scriptExec() header comment! */
+/* "stop" simply discards the filtered items - it's just a (hopefully more intuitive
+ * shortcut for users.
+ */
+static rsRetVal
+execStop(batch_t *pBatch, sbool *active)
+{
+ int i;
+ DEFiRet;
+ for(i = 0 ; i < batchNumMsgs(pBatch) && !*(pBatch->pbShutdownImmediate) ; ++i) {
+ if( pBatch->pElem[i].state != BATCH_STATE_DISC
+ && (active == NULL || active[i])) {
+ pBatch->pElem[i].state = BATCH_STATE_DISC;
+ }
+ }
+ RETiRet;
+}
+
+/* for details, see scriptExec() header comment! */
+// save current filter, evaluate new one
+// perform then (if any message)
+// if ELSE given:
+// set new filter, inverted
+// perform else (if any messages)
+static rsRetVal
+execIf(struct cnfstmt *stmt, batch_t *pBatch, sbool *active)
+{
+ sbool *newAct;
+ int i;
+ sbool bRet;
+ DEFiRet;
+ newAct = newActive(pBatch);
+ for(i = 0 ; i < batchNumMsgs(pBatch) && !*(pBatch->pbShutdownImmediate) ; ++i) {
+ if(pBatch->pElem[i].state == BATCH_STATE_DISC)
+ continue; /* will be ignored in any case */
+ if(active == NULL || active[i]) {
+ bRet = cnfexprEvalBool(stmt->d.s_if.expr,
+ (msg_t*)(pBatch->pElem[i].pUsrp));
+ } else
+ bRet = 0;
+ newAct[i] = bRet;
+ DBGPRINTF("batch: item %d: expr eval: %d\n", i, bRet);
+ }
+
+ if(stmt->d.s_if.t_then != NULL) {
+ scriptExec(stmt->d.s_if.t_then, pBatch, newAct);
+ }
+ if(stmt->d.s_if.t_else != NULL) {
+ for(i = 0 ; i < batchNumMsgs(pBatch) && !*(pBatch->pbShutdownImmediate)
+ ; ++i)
+ if(pBatch->pElem[i].state != BATCH_STATE_DISC)
+ newAct[i] = !newAct[i];
+ scriptExec(stmt->d.s_if.t_else, pBatch, newAct);
+ }
+ freeActive(newAct);
+ RETiRet;
+}
+
+/* for details, see scriptExec() header comment! */
+static void
+execPRIFILT(struct cnfstmt *stmt, batch_t *pBatch, sbool *active)
+{
+ sbool *newAct;
+ msg_t *pMsg;
+ int bRet;
+ int i;
+ newAct = newActive(pBatch);
+ for(i = 0 ; i < batchNumMsgs(pBatch) && !*(pBatch->pbShutdownImmediate) ; ++i) {
+ if(pBatch->pElem[i].state == BATCH_STATE_DISC)
+ continue; /* will be ignored in any case */
+ pMsg = (msg_t*)(pBatch->pElem[i].pUsrp);
+ if(active == NULL || active[i]) {
+ if( (stmt->d.s_prifilt.pmask[pMsg->iFacility] == TABLE_NOPRI) ||
+ ((stmt->d.s_prifilt.pmask[pMsg->iFacility]
+ & (1<<pMsg->iSeverity)) == 0) )
+ bRet = 0;
+ else
+ bRet = 1;
+ } else
+ bRet = 0;
+ newAct[i] = bRet;
+ DBGPRINTF("batch: item %d PRIFILT %d\n", i, newAct[i]);
+ }
+
+ if(stmt->d.s_prifilt.t_then != NULL) {
+ scriptExec(stmt->d.s_prifilt.t_then, pBatch, newAct);
+ }
+ if(stmt->d.s_prifilt.t_else != NULL) {
+ for(i = 0 ; i < batchNumMsgs(pBatch) && !*(pBatch->pbShutdownImmediate)
+ ; ++i)
+ if(pBatch->pElem[i].state != BATCH_STATE_DISC)
+ newAct[i] = !newAct[i];
+ scriptExec(stmt->d.s_prifilt.t_else, pBatch, newAct);
+ }
+ freeActive(newAct);
+}
+
+
+/* helper to execPROPFILT(), as the evaluation itself is quite lengthy */
+static int
+evalPROPFILT(struct cnfstmt *stmt, msg_t *pMsg)
+{
+ unsigned short pbMustBeFreed;
+ uchar *pszPropVal;
+ int bRet = 0;
+ rs_size_t propLen;
+
+ if(stmt->d.s_propfilt.propID == PROP_INVALID)
+ goto done;
+
+ pszPropVal = MsgGetProp(pMsg, NULL, stmt->d.s_propfilt.propID,
+ stmt->d.s_propfilt.propName, &propLen, &pbMustBeFreed);
+
+ /* Now do the compares (short list currently ;)) */
+ switch(stmt->d.s_propfilt.operation ) {
+ case FIOP_CONTAINS:
+ if(rsCStrLocateInSzStr(stmt->d.s_propfilt.pCSCompValue, (uchar*) pszPropVal) != -1)
+ bRet = 1;
+ break;
+ case FIOP_ISEMPTY:
+ if(propLen == 0)
+ bRet = 1; /* process message! */
+ break;
+ case FIOP_ISEQUAL:
+ if(rsCStrSzStrCmp(stmt->d.s_propfilt.pCSCompValue,
+ pszPropVal, ustrlen(pszPropVal)) == 0)
+ bRet = 1; /* process message! */
+ break;
+ case FIOP_STARTSWITH:
+ if(rsCStrSzStrStartsWithCStr(stmt->d.s_propfilt.pCSCompValue,
+ pszPropVal, ustrlen(pszPropVal)) == 0)
+ bRet = 1; /* process message! */
+ break;
+ case FIOP_REGEX:
+ if(rsCStrSzStrMatchRegex(stmt->d.s_propfilt.pCSCompValue,
+ (unsigned char*) pszPropVal, 0, &stmt->d.s_propfilt.regex_cache) == RS_RET_OK)
+ bRet = 1;
+ break;
+ case FIOP_EREREGEX:
+ if(rsCStrSzStrMatchRegex(stmt->d.s_propfilt.pCSCompValue,
+ (unsigned char*) pszPropVal, 1, &stmt->d.s_propfilt.regex_cache) == RS_RET_OK)
+ bRet = 1;
+ break;
+ default:
+ /* here, it handles NOP (for performance reasons) */
+ assert(stmt->d.s_propfilt.operation == FIOP_NOP);
+ bRet = 1; /* as good as any other default ;) */
+ break;
+ }
+
+ /* now check if the value must be negated */
+ if(stmt->d.s_propfilt.isNegated)
+ bRet = (bRet == 1) ? 0 : 1;
+
+ if(Debug) {
+ char *cstr;
+ if(stmt->d.s_propfilt.propID == PROP_CEE) {
+ cstr = es_str2cstr(stmt->d.s_propfilt.propName, NULL);
+ DBGPRINTF("Filter: check for CEE property '%s' (value '%s') ",
+ cstr, pszPropVal);
+ free(cstr);
+ } else {
+ DBGPRINTF("Filter: check for property '%s' (value '%s') ",
+ propIDToName(stmt->d.s_propfilt.propID), pszPropVal);
+ }
+ if(stmt->d.s_propfilt.isNegated)
+ DBGPRINTF("NOT ");
+ if(stmt->d.s_propfilt.operation == FIOP_ISEMPTY) {
+ DBGPRINTF("%s : %s\n",
+ getFIOPName(stmt->d.s_propfilt.operation),
+ bRet ? "TRUE" : "FALSE");
+ } else {
+ DBGPRINTF("%s '%s': %s\n",
+ getFIOPName(stmt->d.s_propfilt.operation),
+ rsCStrGetSzStrNoNULL(stmt->d.s_propfilt.pCSCompValue),
+ bRet ? "TRUE" : "FALSE");
+ }
+ }
+
+ /* cleanup */
+ if(pbMustBeFreed)
+ free(pszPropVal);
+done:
+ return bRet;
+}
+
+/* for details, see scriptExec() header comment! */
+static void
+execPROPFILT(struct cnfstmt *stmt, batch_t *pBatch, sbool *active)
+{
+ sbool *thenAct;
+ sbool bRet;
+ int i;
+ thenAct = newActive(pBatch);
+ for(i = 0 ; i < batchNumMsgs(pBatch) && !*(pBatch->pbShutdownImmediate) ; ++i) {
+ if(pBatch->pElem[i].state == BATCH_STATE_DISC)
+ continue; /* will be ignored in any case */
+ if(active == NULL || active[i]) {
+ bRet = evalPROPFILT(stmt, (msg_t*)(pBatch->pElem[i].pUsrp));
+ } else
+ bRet = 0;
+ thenAct[i] = bRet;
+ DBGPRINTF("batch: item %d PROPFILT %d\n", i, thenAct[i]);
+ }
+
+ scriptExec(stmt->d.s_propfilt.t_then, pBatch, thenAct);
+ freeActive(thenAct);
+}
+
+/* The rainerscript execution engine. It is debatable if that would be better
+ * contained in grammer/rainerscript.c, HOWEVER, that file focusses primarily
+ * on the parsing and object creation part. So as an actual executor, it is
+ * better suited here.
+ * param active: if NULL, all messages are active (to be processed), if non-null
+ * this is an array of the same size as the batch. If 1, the message
+ * is to be processed, otherwise not.
+ * NOTE: this function must receive batches which contain a single ruleset ONLY!
+ * rgerhards, 2012-09-04
+ */
+static rsRetVal
+scriptExec(struct cnfstmt *root, batch_t *pBatch, sbool *active)
+{
+ DEFiRet;
+ struct cnfstmt *stmt;
+
+ for(stmt = root ; stmt != NULL ; stmt = stmt->next) {
+dbgprintf("RRRR: scriptExec: batch of %d elements, active %p, stmt %p, nodetype %u\n", batchNumMsgs(pBatch), active, stmt, stmt->nodetype);
+ switch(stmt->nodetype) {
+ case S_NOP:
+ break;
+ case S_STOP:
+ execStop(pBatch, active);
+ break;
+ case S_ACT:
+ execAct(stmt, pBatch, active);
+ break;
+ case S_SET:
+ execSet(stmt, pBatch, active);
+ break;
+ case S_UNSET:
+ execUnset(stmt, pBatch, active);
+ break;
+ case S_CALL:
+ DBGPRINTF("calling ruleset\n"); // TODO: add Name
+ scriptExec(stmt->d.s_call.stmt, pBatch, active);
+ break;
+ case S_IF:
+ execIf(stmt, pBatch, active);
+ break;
+ case S_PRIFILT:
+ execPRIFILT(stmt, pBatch, active);
+ break;
+ case S_PROPFILT:
+ execPROPFILT(stmt, pBatch, active);
+ break;
+ default:
+ dbgprintf("error: unknown stmt type %u during exec\n",
+ (unsigned) stmt->nodetype);
+ break;
+ }
+ }
+ RETiRet;
+}
+
+
/* Process (consume) a batch of messages. Calls the actions configured.
* If the whole batch uses a singel ruleset, we can process the batch as
* a whole. Otherwise, we need to process it slower, on a message-by-message
@@ -226,7 +551,7 @@ processBatch(batch_t *pBatch)
if(pThis == NULL)
pThis = ourConf->rulesets.pDflt;
ISOBJ_TYPE_assert(pThis, ruleset);
- CHKiRet(llExecFunc(&pThis->llRules, processBatchDoRules, pBatch));
+ CHKiRet(scriptExec(pThis->root, pBatch, NULL));
} else {
CHKiRet(processBatchMultiRuleset(pBatch));
}
@@ -248,34 +573,21 @@ GetParserList(rsconf_t *conf, msg_t *pMsg)
}
-/* Add a new rule to the end of the current rule set. We do a number
- * of checks and ignore the rule if it does not pass them.
- */
-static rsRetVal
-addRule(ruleset_t *pThis, rule_t **ppRule)
+/* Add a script block to the current ruleset */
+static void
+addScript(ruleset_t *pThis, struct cnfstmt *script)
{
- int iActionCnt;
- DEFiRet;
-
- ISOBJ_TYPE_assert(pThis, ruleset);
- ISOBJ_TYPE_assert(*ppRule, rule);
-
- CHKiRet(llGetNumElts(&(*ppRule)->llActList, &iActionCnt));
- if(iActionCnt == 0) {
- errmsg.LogError(0, NO_ERRCODE, "warning: selector line without actions will be discarded");
- rule.Destruct(ppRule);
- } else {
- CHKiRet(llAppend(&pThis->llRules, NULL, *ppRule));
- DBGPRINTF("selector line successfully processed, %d actions\n", iActionCnt);
+ if(pThis->last == NULL)
+ pThis->root = pThis->last = script;
+ else {
+ pThis->last->next = script;
+ pThis->last = script;
}
-
-finalize_it:
- RETiRet;
}
/* set name for ruleset */
-static rsRetVal setName(ruleset_t *pThis, uchar *pszName)
+static rsRetVal rulesetSetName(ruleset_t *pThis, uchar *pszName)
{
DEFiRet;
free(pThis->pszName);
@@ -344,8 +656,7 @@ finalize_it:
}
-/* Set a new current rule set. If the ruleset can not be found, no change happens.
- */
+/* Set a new current rule set. If the ruleset can not be found, no change happens */
static rsRetVal
SetCurrRuleset(rsconf_t *conf, uchar *pszName)
{
@@ -362,23 +673,11 @@ finalize_it:
}
-/* destructor we need to destruct rules inside our linked list contents.
- */
-static rsRetVal
-doRuleDestruct(void *pData)
-{
- rule_t *pRule = (rule_t *) pData;
- DEFiRet;
- rule.Destruct(&pRule);
- RETiRet;
-}
-
-
/* Standard-Constructor
*/
BEGINobjConstruct(ruleset) /* be sure to specify the object type also in END macro! */
- CHKiRet(llInit(&pThis->llRules, doRuleDestruct, NULL, NULL));
-finalize_it:
+ pThis->root = NULL;
+ pThis->last = NULL;
ENDobjConstruct(ruleset)
@@ -399,9 +698,6 @@ rulesetConstructFinalize(rsconf_t *conf, ruleset_t *pThis)
CHKmalloc(keyName = ustrdup(pThis->pszName));
CHKiRet(llAppend(&(conf->rulesets.llRulesets), keyName, pThis));
- /* this now also is the new current ruleset */
- conf->rulesets.pCurr = pThis;
-
/* and also the default, if so far none has been set */
if(conf->rulesets.pDflt == NULL)
conf->rulesets.pDflt = pThis;
@@ -421,8 +717,8 @@ CODESTARTobjDestruct(ruleset)
if(pThis->pParserLst != NULL) {
parser.DestructParserList(&pThis->pParserLst);
}
- llDestroy(&pThis->llRules);
free(pThis->pszName);
+ cnfstmtDestruct(pThis->root);
ENDobjDestruct(ruleset)
@@ -456,16 +752,13 @@ rulesetDestructForLinkedList(void *pData)
return rulesetDestruct(&pThis);
}
-/* helper for debugPrint(), initiates rule printing */
-DEFFUNC_llExecFunc(doDebugPrintRule)
-{
- return rule.DebugPrint((rule_t*) pData);
-}
/* debugprint for the ruleset object */
BEGINobjDebugPrint(ruleset) /* be sure to specify the object type also in END and CODESTART macros! */
CODESTARTobjDebugPrint(ruleset)
dbgoprint((obj_t*) pThis, "rsyslog ruleset %s:\n", pThis->pszName);
- llExecFunc(&pThis->llRules, doDebugPrintRule, NULL);
+ cnfstmtPrint(pThis->root, 0);
+ dbgoprint((obj_t*) pThis, "ruleset %s assigned parser list:\n", pThis->pszName);
+ printParserList(pThis->pParserLst);
ENDobjDebugPrint(ruleset)
@@ -486,6 +779,40 @@ debugPrintAll(rsconf_t *conf)
RETiRet;
}
+static inline void
+rulesetOptimize(ruleset_t *pRuleset)
+{
+ if(Debug) {
+ dbgprintf("ruleset '%s' before optimization:\n",
+ pRuleset->pszName);
+ rulesetDebugPrint((ruleset_t*) pRuleset);
+ }
+ cnfstmtOptimize(pRuleset->root);
+ if(Debug) {
+ dbgprintf("ruleset '%s' after optimization:\n",
+ pRuleset->pszName);
+ rulesetDebugPrint((ruleset_t*) pRuleset);
+ }
+}
+
+/* helper for rulsetOptimizeAll(), optimizes a single ruleset */
+DEFFUNC_llExecFunc(doRulesetOptimizeAll)
+{
+ rulesetOptimize((ruleset_t*) pData);
+ return RS_RET_OK;
+}
+/* optimize all rulesets
+ */
+rsRetVal
+rulesetOptimizeAll(rsconf_t *conf)
+{
+ DEFiRet;
+ dbgprintf("begin ruleset optimization phase\n");
+ llExecFunc(&(conf->rulesets.llRulesets), doRulesetOptimizeAll, NULL);
+ dbgprintf("ruleset optimization phase finished.\n");
+ RETiRet;
+}
+
/* Create a ruleset-specific "main" queue for this ruleset. If one is already
* defined, an error message is emitted but nothing else is done.
@@ -539,13 +866,11 @@ rulesetCreateQueue(void __attribute__((unused)) *pVal, int *pNewVal)
* rgerhards, 2009-11-04
*/
static rsRetVal
-doRulesetAddParser(rsconf_t *conf, uchar *pName)
+doRulesetAddParser(ruleset_t *pRuleset, uchar *pName)
{
parser_t *pParser;
DEFiRet;
- assert(conf->rulesets.pCurr != NULL);
-
CHKiRet(objUse(parser, CORE_COMPONENT));
iRet = parser.FindParser(&pParser, pName);
if(iRet == RS_RET_PARSER_NOT_FOUND) {
@@ -557,9 +882,9 @@ doRulesetAddParser(rsconf_t *conf, uchar *pName)
FINALIZE;
}
- CHKiRet(parser.AddParserToList(&conf->rulesets.pCurr->pParserLst, pParser));
+ CHKiRet(parser.AddParserToList(&pRuleset->pParserLst, pParser));
- DBGPRINTF("added parser '%s' to ruleset '%s'\n", pName, conf->rulesets.pCurr->pszName);
+ DBGPRINTF("added parser '%s' to ruleset '%s'\n", pName, pRuleset->pszName);
finalize_it:
d_free(pName); /* no longer needed */
@@ -570,7 +895,63 @@ finalize_it:
static rsRetVal
rulesetAddParser(void __attribute__((unused)) *pVal, uchar *pName)
{
- return doRulesetAddParser(ourConf, pName);
+ return doRulesetAddParser(ourConf->rulesets.pCurr, pName);
+}
+
+
+/* Process ruleset() objects */
+rsRetVal
+rulesetProcessCnf(struct cnfobj *o)
+{
+ struct cnfparamvals *pvals;
+ rsRetVal localRet;
+ uchar *rsName = NULL;
+ uchar *parserName;
+ int nameIdx, parserIdx;
+ ruleset_t *pRuleset;
+ struct cnfarray *ar;
+ int i;
+ DEFiRet;
+
+ pvals = nvlstGetParams(o->nvlst, &rspblk, NULL);
+ if(pvals == NULL) {
+ ABORT_FINALIZE(RS_RET_CONFIG_ERROR);
+ }
+ DBGPRINTF("ruleset param blk after rulesetProcessCnf:\n");
+ cnfparamsPrint(&rspblk, pvals);
+ nameIdx = cnfparamGetIdx(&rspblk, "name");
+ rsName = (uchar*)es_str2cstr(pvals[nameIdx].val.d.estr, NULL);
+ localRet = rulesetGetRuleset(loadConf, &pRuleset, rsName);
+ if(localRet == RS_RET_OK) {
+ errmsg.LogError(0, RS_RET_RULESET_EXISTS,
+ "error: ruleset '%s' specified more than once",
+ rsName);
+ cnfstmtDestruct(o->script);
+ ABORT_FINALIZE(RS_RET_RULESET_EXISTS);
+ } else if(localRet != RS_RET_NOT_FOUND) {
+ ABORT_FINALIZE(localRet);
+ }
+ CHKiRet(rulesetConstruct(&pRuleset));
+ CHKiRet(rulesetSetName(pRuleset, rsName));
+ CHKiRet(rulesetConstructFinalize(loadConf, pRuleset));
+ addScript(pRuleset, o->script);
+
+ /* we have only two params, so we do NOT do the usual param loop */
+ parserIdx = cnfparamGetIdx(&rspblk, "parser");
+ if(parserIdx == -1 || !pvals[parserIdx].bUsed)
+ FINALIZE;
+
+ ar = pvals[parserIdx].val.d.ar;
+ for(i = 0 ; i < ar->nmemb ; ++i) {
+ parserName = (uchar*)es_str2cstr(ar->arr[i], NULL);
+ doRulesetAddParser(pRuleset, parserName);
+ free(parserName);
+ }
+
+finalize_it:
+ free(rsName);
+ cnfparamvalsDestruct(pvals, &rspblk);
+ RETiRet;
}
@@ -595,9 +976,9 @@ CODESTARTobjQueryInterface(ruleset)
pIf->IterateAllActions = iterateAllActions;
pIf->DestructAllActions = destructAllActions;
- pIf->AddRule = addRule;
+ pIf->AddScript = addScript;
pIf->ProcessBatch = processBatch;
- pIf->SetName = setName;
+ pIf->SetName = rulesetSetName;
pIf->DebugPrintAll = debugPrintAll;
pIf->GetCurrent = GetCurrent;
pIf->GetRuleset = rulesetGetRuleset;
@@ -614,7 +995,6 @@ ENDobjQueryInterface(ruleset)
*/
BEGINObjClassExit(ruleset, OBJ_IS_CORE_MODULE) /* class, version */
objRelease(errmsg, CORE_COMPONENT);
- objRelease(rule, CORE_COMPONENT);
objRelease(parser, CORE_COMPONENT);
ENDObjClassExit(ruleset)
@@ -626,7 +1006,6 @@ ENDObjClassExit(ruleset)
BEGINObjClassInit(ruleset, 1, OBJ_IS_CORE_MODULE) /* class, version */
/* request objects we use */
CHKiRet(objUse(errmsg, CORE_COMPONENT));
- CHKiRet(objUse(rule, CORE_COMPONENT));
/* set our own handlers */
OBJSetMethodHandler(objMethod_DEBUGPRINT, rulesetDebugPrint);