diff options
author | Rainer Gerhards <rgerhards@adiscon.com> | 2008-02-25 13:27:10 +0000 |
---|---|---|
committer | Rainer Gerhards <rgerhards@adiscon.com> | 2008-02-25 13:27:10 +0000 |
commit | 5ebc0db1a6d4c75ce9c26449ef2a2e3d7b340e10 (patch) | |
tree | d9f34f80e487d70da9c764f7c050086eceaff5bf | |
parent | a24cee11b718603fbc681e4a7a23f50c8d785ad7 (diff) | |
download | rsyslog-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.c | 13 | ||||
-rw-r--r-- | debug.h | 3 | ||||
-rw-r--r-- | msg.c | 47 | ||||
-rw-r--r-- | msg.h | 3 | ||||
-rw-r--r-- | syslogd.c | 55 | ||||
-rw-r--r-- | var.c | 36 | ||||
-rw-r--r-- | var.h | 1 | ||||
-rw-r--r-- | vm.c | 73 | ||||
-rw-r--r-- | vm.h | 4 |
9 files changed, 213 insertions, 22 deletions
@@ -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; @@ -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 @@ -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); @@ -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. @@ -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; @@ -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) @@ -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! */ @@ -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) @@ -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! */ |