summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRainer Gerhards <rgerhards@adiscon.com>2008-02-25 13:27:10 +0000
committerRainer Gerhards <rgerhards@adiscon.com>2008-02-25 13:27:10 +0000
commit5ebc0db1a6d4c75ce9c26449ef2a2e3d7b340e10 (patch)
treed9f34f80e487d70da9c764f7c050086eceaff5bf
parenta24cee11b718603fbc681e4a7a23f50c8d785ad7 (diff)
downloadrsyslog-5ebc0db1a6d4c75ce9c26449ef2a2e3d7b340e10.tar.gz
rsyslog-5ebc0db1a6d4c75ce9c26449ef2a2e3d7b340e10.tar.xz
rsyslog-5ebc0db1a6d4c75ce9c26449ef2a2e3d7b340e10.zip
- added PUSHMSGVAR operation
- included expression support in filter module (and it works ;))
-rw-r--r--conf.c13
-rw-r--r--debug.h3
-rw-r--r--msg.c47
-rw-r--r--msg.h3
-rw-r--r--syslogd.c55
-rw-r--r--var.c36
-rw-r--r--var.h1
-rw-r--r--vm.c73
-rw-r--r--vm.h4
9 files changed, 213 insertions, 22 deletions
diff --git a/conf.c b/conf.c
index a537d18a..d2d1fbcf 100644
--- a/conf.c
+++ b/conf.c
@@ -59,6 +59,7 @@ DEFobjCurrIf(expr)
DEFobjCurrIf(ctok)
#include "vm.h"
DEFobjCurrIf(vm) // TODO: remove, testing aid! rgerhards, 2008-02-25
+DEFobjCurrIf(var) // TODO: remove, testing aid! rgerhards, 2008-02-25
uchar *pModDir = NULL; /* read-only after startup */
@@ -792,6 +793,12 @@ dbgprintf("calling expression parser, pp %p ('%s')\n", *pline, *pline);
CHKiRet(ctok.Getpp(tok, pline));
CHKiRet(ctok.Destruct(&tok));
+ /* we now need to skip whitespace to the action part, else we confuse
+ * the legacy rsyslog conf parser. -- rgerhards, 2008-02-25
+ */
+ while(isspace(**pline))
+ ++(*pline);
+
dbgprintf("expression parser successfully ended, pp %p ('%s')\n", *pline, *pline);
/* debug aid, try to exec - just now for testing the vm... -- rgerhards, 2008-02-25 */
@@ -801,8 +808,9 @@ CHKiRet(vm.Construct(&pVM));
CHKiRet(vm.ConstructFinalize(pVM));
CHKiRet(vm.ExecProg(pVM, f->f_filterData.f_expr->pVmprg));
-CHKiRet(vm.PopBoolFromStack(pVM, &pResult));
-dbgprintf("result of expression run: %lld\n", pResult->val.num);
+CHKiRet(vm.PopVarFromStack(pVM, &pResult));
+dbgprintf("result of expression run:\n");
+var.DebugPrint(pResult);
CHKiRet(vm.Destruct(&pVM));
/* ...end testing aid... */
@@ -1191,6 +1199,7 @@ rsRetVal confClassInit(void)
CHKiRet(objUse(expr));
CHKiRet(objUse(ctok));
CHKiRet(objUse(vm)); // TODO: remove, testing aid! rgerhards, 2008-02-25
+ CHKiRet(objUse(var)); // TODO: remove, testing aid! rgerhards, 2008-02-25
finalize_it:
RETiRet;
diff --git a/debug.h b/debug.h
index bcc0dda5..f39f12a8 100644
--- a/debug.h
+++ b/debug.h
@@ -102,7 +102,8 @@ void dbgPrintAllDebugInfo(void);
#ifdef RTINST
# define BEGINfunc static dbgFuncDB_t dbgFuncDB=dbgFuncDB_t_INITIALIZER; int dbgCALLStaCK_POP_POINT = dbgEntrFunc(&dbgFuncDB,__LINE__);
# define ENDfunc dbgExitFunc(&dbgFuncDB, dbgCALLStaCK_POP_POINT);
-# define ASSERT(x) do { if(!(x)) dbgPrintAllDebugInfo(); assert(x); } while(0);
+// # define ASSERT(x) do { if(!(x)) dbgPrintAllDebugInfo(); assert(x); } while(0);
+# define ASSERT(x) assert(x)
#else
# define BEGINfunc
# define ENDfunc
diff --git a/msg.c b/msg.c
index 5a271580..b2ea1870 100644
--- a/msg.c
+++ b/msg.c
@@ -41,8 +41,11 @@
#include "stringbuf.h"
#include "template.h"
#include "msg.h"
+#include "var.h"
+/* static data */
DEFobjStaticHelpers
+DEFobjCurrIf(var)
static syslogCODE rs_prioritynames[] =
{
@@ -2074,6 +2077,46 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
}
+/* The returns a message variable suitable for use with RainerScript. Most importantly, this means
+ * that the value is returned in a var_t object. The var_t is constructed inside this function and
+ * MUST be freed by the caller.
+ * rgerhards, 2008-02-25
+ */
+rsRetVal
+msgGetMsgVar(msg_t *pThis, cstr_t *pstrPropName, var_t **ppVar)
+{
+ DEFiRet;
+ var_t *pVar;
+ uchar *pszProp = NULL;
+ cstr_t *pstrProp;
+ unsigned short bMustBeFreed = 0;
+
+ ISOBJ_TYPE_assert(pThis, msg);
+ ASSERT(pstrPropName != NULL);
+ ASSERT(ppVar != NULL);
+
+ /* make sure we have a var_t instance */
+ CHKiRet(var.Construct(&pVar));
+ CHKiRet(var.ConstructFinalize(pVar));
+
+ /* always call MsgGetProp() without a template specifier */
+ pszProp = (uchar*) MsgGetProp(pThis, NULL, pstrPropName, &bMustBeFreed);
+
+ /* now create a string object out of it and hand that over to the var */
+ CHKiRet(rsCStrConstructFromszStr(&pstrProp, pszProp));
+ CHKiRet(var.SetString(pVar, pstrProp));
+
+ /* finally store var */
+ *ppVar = pVar;
+
+finalize_it:
+ if(bMustBeFreed)
+ free(pszProp);
+
+ RETiRet;
+}
+
+
/* This function can be used as a generic way to set properties.
* We have to handle a lot of legacy, so our return value is not always
* 100% correct (called functions do not always provide one, should
@@ -2158,6 +2201,10 @@ MsgGetSeverity(obj_t *pThis, int *piSeverity)
* rgerhards, 2008-01-04
*/
BEGINObjClassInit(msg, 1)
+ /* request objects we use */
+ CHKiRet(objUse(var));
+
+ /* set our own handlers */
OBJSetMethodHandler(objMethod_SERIALIZE, MsgSerialize);
OBJSetMethodHandler(objMethod_SETPROPERTY, MsgSetProperty);
OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, msgConstructFinalizer);
diff --git a/msg.h b/msg.h
index b4135533..dd12b77c 100644
--- a/msg.h
+++ b/msg.h
@@ -22,6 +22,8 @@
*
* A copy of the GPL can be found in the file "COPYING" in this distribution.
*/
+#include "template.h" /* this is a quirk, but these two are too interdependant... */
+
#ifndef MSG_H_INCLUDED
#define MSG_H_INCLUDED 1
@@ -154,6 +156,7 @@ char *getMSGID(msg_t *pM);
char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
cstr_t *pCSPropName, unsigned short *pbMustBeFreed);
char *textpri(char *pRes, size_t pResLen, int pri);
+rsRetVal msgGetMsgVar(msg_t *pThis, cstr_t *pstrPropName, var_t **ppVar);
rsRetVal MsgEnableThreadSafety(void);
/* The MsgPrepareEnqueue() function is a macro for performance reasons.
diff --git a/syslogd.c b/syslogd.c
index 4a4506b3..b9400500 100644
--- a/syslogd.c
+++ b/syslogd.c
@@ -178,6 +178,7 @@
/* definitions for objects we access */
DEFobjCurrIf(expr)
+DEFobjCurrIf(vm)
/* We define our own set of syslog defintions so that we
@@ -1444,12 +1445,17 @@ finalize_it:
* decision code to grow more complex over time AND logmsg() is already
* a very lengthy function, I thought a separate function is more appropriate.
* 2005-09-19 rgerhards
+ * 2008-02-25 rgerhards: changed interface, now utilizes iRet, bProcessMsg
+ * returns is message should be procesed.
*/
-int shouldProcessThisMessage(selector_t *f, msg_t *pMsg)
+static rsRetVal shouldProcessThisMessage(selector_t *f, msg_t *pMsg, int *bProcessMsg)
{
+ DEFiRet;
unsigned short pbMustBeFreed;
char *pszPropVal;
- int iRet = 0;
+ int bRet = 0;
+ vm_t *pVM;
+ var_t *pResult;
assert(f != NULL);
assert(pMsg != NULL);
@@ -1467,14 +1473,14 @@ int shouldProcessThisMessage(selector_t *f, msg_t *pMsg)
/* not equal, so we are already done... */
dbgprintf("hostname filter '+%s' does not match '%s'\n",
rsCStrGetSzStrNoNULL(f->pCSHostnameComp), getHOSTNAME(pMsg));
- return 0;
+ FINALIZE;
}
} else { /* must be -hostname */
if(!rsCStrSzStrCmp(f->pCSHostnameComp, (uchar*) getHOSTNAME(pMsg), getHOSTNAMELen(pMsg))) {
/* not equal, so we are already done... */
dbgprintf("hostname filter '-%s' does not match '%s'\n",
rsCStrGetSzStrNoNULL(f->pCSHostnameComp), getHOSTNAME(pMsg));
- return 0;
+ FINALIZE;
}
}
@@ -1495,7 +1501,7 @@ int shouldProcessThisMessage(selector_t *f, msg_t *pMsg)
/* not equal or inverted selection, so we are already done... */
dbgprintf("programname filter '%s' does not match '%s'\n",
rsCStrGetSzStrNoNULL(f->pCSProgNameComp), getProgramName(pMsg));
- return 0;
+ FINALIZE;
}
}
@@ -1505,9 +1511,18 @@ int shouldProcessThisMessage(selector_t *f, msg_t *pMsg)
/* skip messages that are incorrect priority */
if ( (f->f_filterData.f_pmask[pMsg->iFacility] == TABLE_NOPRI) || \
((f->f_filterData.f_pmask[pMsg->iFacility] & (1<<pMsg->iSeverity)) == 0) )
- iRet = 0;
+ bRet = 0;
else
- iRet = 1;
+ bRet = 1;
+ } else if(f->f_filter_type == FILTER_EXPR) {
+ CHKiRet(vm.Construct(&pVM));
+ CHKiRet(vm.ConstructFinalize(pVM));
+ CHKiRet(vm.SetMsg(pVM, pMsg));
+ CHKiRet(vm.ExecProg(pVM, f->f_filterData.f_expr->pVmprg));
+ CHKiRet(vm.PopBoolFromStack(pVM, &pResult));
+ dbgprintf("result of expression evaluation: %lld\n", pResult->val.num);
+ CHKiRet(vm.Destruct(&pVM));
+ bRet = (pResult->val.num) ? 1 : 0;
} else {
assert(f->f_filter_type == FILTER_PROP); /* assert() just in case... */
pszPropVal = MsgGetProp(pMsg, NULL, f->f_filterData.prop.pCSPropName, &pbMustBeFreed);
@@ -1516,33 +1531,33 @@ int shouldProcessThisMessage(selector_t *f, msg_t *pMsg)
switch(f->f_filterData.prop.operation ) {
case FIOP_CONTAINS:
if(rsCStrLocateInSzStr(f->f_filterData.prop.pCSCompValue, (uchar*) pszPropVal) != -1)
- iRet = 1;
+ bRet = 1;
break;
case FIOP_ISEQUAL:
if(rsCStrSzStrCmp(f->f_filterData.prop.pCSCompValue,
(uchar*) pszPropVal, strlen(pszPropVal)) == 0)
- iRet = 1; /* process message! */
+ bRet = 1; /* process message! */
break;
case FIOP_STARTSWITH:
if(rsCStrSzStrStartsWithCStr(f->f_filterData.prop.pCSCompValue,
(uchar*) pszPropVal, strlen(pszPropVal)) == 0)
- iRet = 1; /* process message! */
+ bRet = 1; /* process message! */
break;
case FIOP_REGEX:
if(rsCStrSzStrMatchRegex(f->f_filterData.prop.pCSCompValue,
(unsigned char*) pszPropVal) == 0)
- iRet = 1;
+ bRet = 1;
break;
default:
/* here, it handles NOP (for performance reasons) */
assert(f->f_filterData.prop.operation == FIOP_NOP);
- iRet = 1; /* as good as any other default ;) */
+ bRet = 1; /* as good as any other default ;) */
break;
}
/* now check if the value must be negated */
if(f->f_filterData.prop.isNegated)
- iRet = (iRet == 1) ? 0 : 1;
+ bRet = (bRet == 1) ? 0 : 1;
if(Debug) {
dbgprintf("Filter: check for property '%s' (value '%s') ",
@@ -1553,7 +1568,7 @@ int shouldProcessThisMessage(selector_t *f, msg_t *pMsg)
dbgprintf("%s '%s': %s\n",
getFIOPName(f->f_filterData.prop.operation),
rsCStrGetSzStrNoNULL(f->f_filterData.prop.pCSCompValue),
- iRet ? "TRUE" : "FALSE");
+ bRet ? "TRUE" : "FALSE");
}
/* cleanup */
@@ -1561,7 +1576,9 @@ int shouldProcessThisMessage(selector_t *f, msg_t *pMsg)
free(pszPropVal);
}
- return(iRet);
+finalize_it:
+ *bProcessMsg = bRet;
+ RETiRet;
}
@@ -1610,7 +1627,9 @@ processMsg(msg_t *pMsg)
{
selector_t *f;
int bContinue;
+ int bProcessMsg;
processMsgDoActions_t DoActData;
+ rsRetVal iRet;
BEGINfunc
assert(pMsg != NULL);
@@ -1620,7 +1639,8 @@ processMsg(msg_t *pMsg)
bContinue = 1;
for (f = Files; f != NULL && bContinue ; f = f->f_next) {
/* first check the filters... */
- if(!shouldProcessThisMessage(f, pMsg)) {
+ iRet = shouldProcessThisMessage(f, pMsg, &bProcessMsg);
+ if(!bProcessMsg) {
continue;
}
@@ -2669,6 +2689,8 @@ static void dbgPrintInitInfo(void)
dbgprintf(" X ");
else
dbgprintf("%2X ", f->f_filterData.f_pmask[i]);
+ } else if(f->f_filter_type == FILTER_EXPR) {
+ dbgprintf("EXPRESSION-BASED Filter: can currently not be displayed");
} else {
dbgprintf("PROPERTY-BASED Filter:\n");
dbgprintf("\tProperty.: '%s'\n",
@@ -3571,6 +3593,7 @@ static rsRetVal InitGlobalClasses(void)
/* request objects we use */
CHKiRet(objUse(expr));
+ CHKiRet(objUse(vm));
finalize_it:
RETiRet;
diff --git a/var.c b/var.c
index 3c6a101d..4df56a16 100644
--- a/var.c
+++ b/var.c
@@ -89,6 +89,41 @@ CODESTARTobjDebugPrint(var)
ENDobjDebugPrint(var)
+/* duplicates a var instance
+ * rgerhards, 2008-02-25
+ */
+static rsRetVal
+Duplicate(var_t *pThis, var_t **ppNew)
+{
+ DEFiRet;
+ var_t *pNew = NULL;
+ cstr_t *pstr;
+
+ ISOBJ_TYPE_assert(pThis, var);
+ assert(ppNew != NULL);
+
+ CHKiRet(varConstruct(&pNew));
+ CHKiRet(varConstructFinalize(pNew));
+
+ /* we have the object, now copy value */
+ pNew->varType = pThis->varType;
+ if(pThis->varType == VARTYPE_NUMBER) {
+ pNew->val.num = pThis->val.num;
+ } else if(pThis->varType == VARTYPE_STR) {
+ CHKiRet(rsCStrConstructFromCStr(&pstr, pThis->val.pStr));
+ pNew->val.pStr = pstr;
+ }
+
+ *ppNew = pNew;
+
+finalize_it:
+ if(iRet != RS_RET_OK && pNew != NULL)
+ varDestruct(&pNew);
+
+ RETiRet;
+}
+
+
/* free the current values (destructs objects if necessary)
*/
static rsRetVal
@@ -340,6 +375,7 @@ CODESTARTobjQueryInterface(var)
pIf->ConvToNumber = ConvToNumber;
pIf->ConvToBool = ConvToBool;
pIf->ConvToString = ConvToString;
+ pIf->Duplicate = Duplicate;
finalize_it:
ENDobjQueryInterface(var)
diff --git a/var.h b/var.h
index 3ebc1021..7daf2f27 100644
--- a/var.h
+++ b/var.h
@@ -58,6 +58,7 @@ BEGINinterface(var) /* name must also be changed in ENDinterface macro! */
rsRetVal (*ConvToNumber)(var_t *pThis);
rsRetVal (*ConvToBool)(var_t *pThis);
rsRetVal (*ConvToString)(var_t *pThis);
+ rsRetVal (*Duplicate)(var_t *pThis, var_t **ppNew);
ENDinterface(var)
#define varCURR_IF_VERSION 1 /* increment whenever you change the interface above! */
diff --git a/vm.c b/vm.c
index 76f06328..389a3cd1 100644
--- a/vm.c
+++ b/vm.c
@@ -219,11 +219,41 @@ ENDop(UNARY_MINUS)
BEGINop(PUSHCONSTANT) /* remember to set the instruction also in the ENDop macro! */
+ var_t *pVarDup; /* we need to duplicate the var, as we need to hand it over */
CODESTARTop(PUSHCONSTANT)
- vmstk.Push(pThis->pStk, pOp->operand.pVar);
+ CHKiRet(var.Duplicate(pOp->operand.pVar, &pVarDup));
+ vmstk.Push(pThis->pStk, pVarDup);
+finalize_it:
ENDop(PUSHCONSTANT)
+BEGINop(PUSHMSGVAR) /* remember to set the instruction also in the ENDop macro! */
+ var_t *pVal; /* the value to push */
+ cstr_t *pstrVal;
+CODESTARTop(PUSHMSGVAR)
+ if(pThis->pMsg == NULL) {
+ /* TODO: flag an error message! As a work-around, we permit
+ * execution to continue here with an empty string
+ */
+ /* TODO: create a method in var to create a string var? */
+ CHKiRet(var.Construct(&pVal));
+ CHKiRet(var.ConstructFinalize(pVal));
+ CHKiRet(rsCStrConstructFromszStr(&pstrVal, (uchar*)""));
+ CHKiRet(var.SetString(pVal, pstrVal));
+ } else {
+ /* we have a message, so pull value from there */
+var.DebugPrint(pOp->operand.pVar);
+ CHKiRet(msgGetMsgVar(pThis->pMsg, pOp->operand.pVar->val.pStr, &pVal));
+ }
+
+ /* if we reach this point, we have a valid pVal and can push it */
+ vmstk.Push(pThis->pStk, pVal);
+RUNLOG_STR("msgvar:");
+var.DebugPrint(pVal);
+finalize_it:
+ENDop(PUSHMSGVAR)
+
+
/* ------------------------------ end instruction set implementation ------------------------------ */
@@ -292,6 +322,7 @@ execProg(vm_t *pThis, vmprg_t *pProg)
// TODO: implement: doOP(CMP_STARTSWITH);
doOP(NOT);
doOP(PUSHCONSTANT);
+ doOP(PUSHMSGVAR);
doOP(PLUS);
doOP(MINUS);
doOP(TIMES);
@@ -317,6 +348,41 @@ finalize_it:
}
+/* Set the current message object for the VM. It *is* valid to set a
+ * NULL message object, what simply means there is none. Message
+ * objects are properly reference counted.
+ */
+static rsRetVal
+SetMsg(vm_t *pThis, msg_t *pMsg)
+{
+ DEFiRet;
+ if(pThis->pMsg != NULL) {
+ msgDestruct(&pThis->pMsg);
+ }
+
+ if(pMsg != NULL) {
+ pThis->pMsg = MsgAddRef(pMsg);
+ }
+
+ RETiRet;
+}
+
+
+/* Pop a var from the stack and return it to caller. The variable type is not
+ * changed, it is taken from the stack as is. This functionality is
+ * partly needed. We may (or may not ;)) be able to remove it once we have
+ * full RainerScript support. -- rgerhards, 2008-02-25
+ */
+static rsRetVal
+PopVarFromStack(vm_t *pThis, var_t **ppVar)
+{
+ DEFiRet;
+ CHKiRet(vmstk.Pop(pThis->pStk, ppVar));
+finalize_it:
+ RETiRet;
+}
+
+
/* Pop a boolean from the stack and return it to caller. This functionality is
* partly needed. We may (or may not ;)) be able to remove it once we have
* full RainerScript support. -- rgerhards, 2008-02-25
@@ -325,13 +391,12 @@ static rsRetVal
PopBoolFromStack(vm_t *pThis, var_t **ppVar)
{
DEFiRet;
-
CHKiRet(vmstk.PopBool(pThis->pStk, ppVar));
-
finalize_it:
RETiRet;
}
+
/* queryInterface function
* rgerhards, 2008-02-21
*/
@@ -354,6 +419,8 @@ CODESTARTobjQueryInterface(vm)
pIf->DebugPrint = vmDebugPrint;
pIf->ExecProg = execProg;
pIf->PopBoolFromStack = PopBoolFromStack;
+ pIf->PopVarFromStack = PopVarFromStack;
+ pIf->SetMsg = SetMsg;
finalize_it:
ENDobjQueryInterface(vm)
diff --git a/vm.h b/vm.h
index 1234349e..44db9c35 100644
--- a/vm.h
+++ b/vm.h
@@ -32,6 +32,7 @@
#ifndef INCLUDED_VM_H
#define INCLUDED_VM_H
+#include "msg.h"
#include "vmstk.h"
#include "vmprg.h"
@@ -39,6 +40,7 @@
typedef struct vm_s {
BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
vmstk_t *pStk; /* The stack */
+ msg_t *pMsg; /* the current message (or NULL, if we have none) */
} vm_t;
@@ -50,6 +52,8 @@ BEGINinterface(vm) /* name must also be changed in ENDinterface macro! */
rsRetVal (*Destruct)(vm_t **ppThis);
rsRetVal (*ExecProg)(vm_t *pThis, vmprg_t *pProg);
rsRetVal (*PopBoolFromStack)(vm_t *pThis, var_t **ppVar); /* there are a few cases where we need this... */
+ rsRetVal (*PopVarFromStack)(vm_t *pThis, var_t **ppVar); /* there are a few cases where we need this... */
+ rsRetVal (*SetMsg)(vm_t *pThis, msg_t *pMsg); /* there are a few cases where we need this... */
ENDinterface(vm)
#define vmCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */