From 4618773be685488e081bebb397db32851dc16782 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 3 Dec 2010 17:11:03 +0100 Subject: milestone: added support for CEE-variables to RainerScript --- doc/rscript_abnf.html | 53 ++++++++++++++++++++++++++++++++++++++++++++++- runtime/ctok.c | 3 +++ runtime/ctok_token.h | 1 + runtime/expr.c | 5 +++++ runtime/msg.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++ runtime/msg.h | 2 ++ runtime/stringbuf.c | 29 ++++++++++++++++++++++++++ runtime/stringbuf.h | 2 ++ runtime/var.h | 1 + runtime/vm.c | 27 ++++++++++++++++++++++++ runtime/vmop.h | 1 + 11 files changed, 180 insertions(+), 1 deletion(-) diff --git a/doc/rscript_abnf.html b/doc/rscript_abnf.html index d60edb5c..9172d945 100644 --- a/doc/rscript_abnf.html +++ b/doc/rscript_abnf.html @@ -21,7 +21,58 @@ and many other languages).

Below is the formal language definitionin ABNF (RFC 2234) format:

-
; all of this is a working document and may change! -- rgerhards, 2008-02-24

script := *stmt
stmt := (if_stmt / block / vardef / run_s / load_s)
vardef := "var" ["scope" = ("global" / "event")]
block := "begin" stmt "end"
load_s := "load" constraint ("module") modpath params ; load mod only if expr is true
run_s := "run" constraint ("input") name
constraint:= "if" expr ; constrains some one-time commands
modpath := expr
params := ["params" *1param *("," param) "endparams"]
param := paramname) "=" expr
paramname := [*(obqualifier ".") name]
modpath:= ; path to module
?line? := cfsysline / cfli
cfsysline:= BOL "$" *char EOL ; how to handle the first line? (no EOL in front!)
BOL := ; Begin of Line - implicitely set on file beginning and after each EOL
EOL := 0x0a ;LF
if_stmt := "if" expr "then"
old_filter:= BOL facility "." severity ; no whitespace allowed between BOL and facility!
facility := "*" / "auth" / "authpriv" / "cron" / "daemon" / "kern" / "lpr" /
"mail" / "mark" / "news" / "security" / "syslog" / "user" / "uucp" /
"local0" .. "local7" / "mark"
; The keyword security should not be used anymore
; mark is just internal
severity := TBD ; not really relevant in this context

; and now the actual expression
expr := e_and *("or" e_and)
e_and := e_cmp *("and" e_cmp)
e_cmp := val 0*1(cmp_op val)
val := term *(("+" / "-" / "&") term)
term := factor *(("*" / "/" / "%") factor)
factor := ["not"] ["-"] terminal
terminal := var / constant / function / ( "(" expr ")" )
function := name "(" *("," expr) ")"
var := "$" varname
varname := msgvar / sysvar
msgvar := name
sysvar := "$" name
name := alpha *(alnum)
constant := string / number
string := simpstr / tplstr ; tplstr will be implemented in next phase
simpstr := "'" *char "'" ; use your imagination for char ;)
tplstr := '"' template '"' ; not initially implemented
number := ["-"] 1*digit ; 0nn = octal, 0xnn = hex, nn = decimal
cmp_op := "==" / "!=" / "<>" / "<" / ">" / "<=" / ">=" / "contains" / "contains_i" / "startswith" / "startswith_i"
digit := %x30-39
alpha := "a" ... "z" # all letters
alnum :* alpha / digit / "_" /"-" # "-" necessary to cover currently-existing message properties
+
; all of this is a working document and may change! -- rgerhards, 2008-02-24
+
+script := *stmt
+stmt := (if_stmt / block / vardef / run_s / load_s)
+vardef := "var" ["scope" = ("global" / "event")]
+block := "begin" stmt "end"
+load_s := "load" constraint ("module") modpath params ; load mod only if expr is true
+run_s := "run" constraint ("input") name
+constraint:= "if" expr ; constrains some one-time commands
+modpath := expr
+params := ["params" *1param *("," param) "endparams"]
+param := paramname) "=" expr
+paramname := [*(obqualifier ".") name]
+modpath:= ; path to module
+?line? := cfsysline / cfli
+cfsysline:= BOL "$" *char EOL ; how to handle the first line? (no EOL in front!)
+BOL := ; Begin of Line - implicitely set on file beginning and after each EOL
+EOL := 0x0a ;LF
+if_stmt := "if" expr "then"
+old_filter:= BOL facility "." severity ; no whitespace allowed between BOL and facility!
+facility := "*" / "auth" / "authpriv" / "cron" / "daemon" / "kern" / "lpr" /
+"mail" / "mark" / "news" / "security" / "syslog" / "user" / "uucp" /
+"local0" .. "local7" / "mark"
+; The keyword security should not be used anymore
+; mark is just internal
+severity := TBD ; not really relevant in this context
+
+; and now the actual expression
+expr := e_and *("or" e_and)
+e_and := e_cmp *("and" e_cmp)
+e_cmp := val 0*1(cmp_op val)
+val := term *(("+" / "-" / "&") term)
+term := factor *(("*" / "/" / "%") factor)
+factor := ["not"] ["-"] terminal
+terminal := var / constant / function / ( "(" expr ")" )
+function := name "(" *("," expr) ")"
+var := "$" varname
+varname := msgvar / sysvar / ceevar
+msgvar := name
+ceevar := "!" name
+sysvar := "$" name
+name := alpha *(alnum)
+constant := string / number
+string := simpstr / tplstr ; tplstr will be implemented in next phase
+simpstr := "'" *char "'" ; use your imagination for char ;)
+tplstr := '"' template '"' ; not initially implemented
+number := ["-"] 1*digit ; 0nn = octal, 0xnn = hex, nn = decimal
+cmp_op := "==" / "!=" / "<>" / "<" / ">" / "<=" / ">=" / "contains" / "contains_i" / "startswith" / "startswith_i"
+digit := %x30-39
+alpha := "a" ... "z" # all letters
+alnum :* alpha / digit / "_" /"-" # "-" necessary to cover currently-existing message properties
+

Samples

Some samples of RainerScript:

define function IsLinux
begin
    if $environ contains "linux" then return true else return false
end

load if IsLinux() 'imklog.so' params name='klog' endparams /* load klog under linux only */
run if IsLinux() input 'klog'
load 'ommysql.so'

if $message contains "error" then
  action
    type='ommysql.so', queue.mode='disk', queue.highwatermark = 300,
    action.dbname='events', action.dbuser='uid',
    [?action.template='templatename'?] or [?action.sql='insert into diff --git a/runtime/ctok.c b/runtime/ctok.c index 18ddaed2..6d97568e 100644 --- a/runtime/ctok.c +++ b/runtime/ctok.c @@ -277,6 +277,9 @@ ctokGetVar(ctok_t *pThis, ctok_token_t *pToken) if(c == '$') { /* second dollar, we have a system variable */ pToken->tok = ctok_SYSVAR; CHKiRet(ctokGetCharFromStream(pThis, &c)); /* "eat" it... */ + } else if(c == '!') { /* cee variable indicator */ + pToken->tok = ctok_CEEVAR; + CHKiRet(ctokGetCharFromStream(pThis, &c)); /* "eat" it... */ } else { pToken->tok = ctok_MSGVAR; } diff --git a/runtime/ctok_token.h b/runtime/ctok_token.h index d36689fa..1413c699 100644 --- a/runtime/ctok_token.h +++ b/runtime/ctok_token.h @@ -54,6 +54,7 @@ typedef struct { ctok_FUNCTION = 17, ctok_THEN = 18, ctok_STRADD = 19, + ctok_CEEVAR = 20, ctok_CMP_EQ = 100, /* all compare operations must be in a row */ ctok_CMP_NEQ = 101, ctok_CMP_LT = 102, diff --git a/runtime/expr.c b/runtime/expr.c index e449d1c7..01431474 100644 --- a/runtime/expr.c +++ b/runtime/expr.c @@ -151,6 +151,11 @@ terminal(expr_t *pThis, ctok_t *tok) CHKiRet(ctok_token.UnlinkVar(pToken, &pVar)); CHKiRet(vmprg.AddVarOperation(pThis->pVmprg, opcode_PUSHMSGVAR, pVar)); /* add to program */ break; + case ctok_CEEVAR: + dbgoprint((obj_t*) pThis, "SYSVAR\n"); + CHKiRet(ctok_token.UnlinkVar(pToken, &pVar)); + CHKiRet(vmprg.AddVarOperation(pThis->pVmprg, opcode_PUSHCEEVAR, pVar)); /* add to program */ + break; case ctok_SYSVAR: dbgoprint((obj_t*) pThis, "SYSVAR\n"); CHKiRet(ctok_token.UnlinkVar(pToken, &pVar)); diff --git a/runtime/msg.c b/runtime/msg.c index cca9d5f6..65ea101f 100644 --- a/runtime/msg.c +++ b/runtime/msg.c @@ -3074,6 +3074,61 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, } +/* The function returns a cee 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. + * Note that we need to do a lot of conversions between es_str_t and cstr -- this will go away once + * we have moved larger parts of rsyslog to es_str_t. Acceptable for the moment, especially as we intend + * to rewrite the script engine as well! + * rgerhards, 2010-12-03 + */ +rsRetVal +msgGetCEEVar(msg_t *pMsg, cstr_t *propName, var_t **ppVar) +{ + DEFiRet; + var_t *pVar; + cstr_t *pstrProp; + es_str_t *str = NULL; + es_str_t *epropName = NULL; + struct ee_field *field; + + ISOBJ_TYPE_assert(pMsg, msg); + ASSERT(propName != NULL); + ASSERT(ppVar != NULL); + + /* make sure we have a var_t instance */ + CHKiRet(var.Construct(&pVar)); + CHKiRet(var.ConstructFinalize(pVar)); + + epropName = es_newStrFromBuf((char*)propName->pBuf, propName->iStrLen); + if((field = ee_getEventField(pMsg->event, epropName)) != NULL) { + /* right now, we always extract data from the first field value. A reason for this + * is that as of now (2010-12-01) liblognorm never populates more than one ;) + */ + str = ee_getFieldValueAsStr(field, 0); + } + + if(str == NULL) { + CHKiRet(cstrConstruct(&pstrProp)); + CHKiRet(cstrFinalize(pstrProp)); + } else { + CHKiRet(cstrConstructFromESStr(&pstrProp, str)); + } + + /* now create a string object out of it and hand that over to the var */ + CHKiRet(var.SetString(pVar, pstrProp)); + es_deleteStr(str); + + /* finally store var */ + *ppVar = pVar; + +finalize_it: + if(epropName != NULL) + es_deleteStr(epropName); + RETiRet; +} + + /* 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. @@ -3116,6 +3171,8 @@ finalize_it: 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 diff --git a/runtime/msg.h b/runtime/msg.h index 9d9df8d0..1fd95994 100644 --- a/runtime/msg.h +++ b/runtime/msg.h @@ -29,6 +29,7 @@ #define MSG_H_INCLUDED 1 #include +#include #include "obj.h" #include "syslogd-types.h" #include "template.h" @@ -174,6 +175,7 @@ void getTAG(msg_t *pM, uchar **ppBuf, int *piLen); char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt); char *getPRI(msg_t *pMsg); void getRawMsg(msg_t *pM, uchar **pBuf, int *piLen); +rsRetVal msgGetCEEVar(msg_t *pThis, cstr_t *propName, var_t **ppVar); /* TODO: remove these five (so far used in action.c) */ diff --git a/runtime/stringbuf.c b/runtime/stringbuf.c index ccf115c1..f4a9caae 100644 --- a/runtime/stringbuf.c +++ b/runtime/stringbuf.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "rsyslog.h" #include "stringbuf.h" #include "srUtils.h" @@ -104,6 +105,34 @@ finalize_it: RETiRet; } + +/* construct from es_str_t string + * rgerhards 2010-12-03 + */ +rsRetVal cstrConstructFromESStr(cstr_t **ppThis, es_str_t *str) +{ + DEFiRet; + cstr_t *pThis; + + assert(ppThis != NULL); + + CHKiRet(rsCStrConstruct(&pThis)); + + pThis->iBufSize = pThis->iStrLen = es_strlen(str); + if((pThis->pBuf = (uchar*) MALLOC(sizeof(uchar) * pThis->iStrLen)) == NULL) { + RSFREEOBJ(pThis); + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + } + + /* we do NOT need to copy the \0! */ + memcpy(pThis->pBuf, es_getBufAddr(str), pThis->iStrLen); + + *ppThis = pThis; + +finalize_it: + RETiRet; +} + /* construct from CStr object. only the counted string is * copied, not the szString. * rgerhards 2005-10-18 diff --git a/runtime/stringbuf.h b/runtime/stringbuf.h index c5130238..df234012 100644 --- a/runtime/stringbuf.h +++ b/runtime/stringbuf.h @@ -36,6 +36,7 @@ #define _STRINGBUF_H_INCLUDED__ 1 #include +#include /** * The dynamic string buffer object. @@ -57,6 +58,7 @@ typedef struct cstr_s */ rsRetVal cstrConstruct(cstr_t **ppThis); #define rsCStrConstruct(x) cstrConstruct((x)) +rsRetVal cstrConstructFromESStr(cstr_t **ppThis, es_str_t *str); rsRetVal rsCStrConstructFromszStr(cstr_t **ppThis, uchar *sz); rsRetVal rsCStrConstructFromCStr(cstr_t **ppThis, cstr_t *pFrom); diff --git a/runtime/var.h b/runtime/var.h index 6d890ec9..ae971bb5 100644 --- a/runtime/var.h +++ b/runtime/var.h @@ -40,6 +40,7 @@ typedef struct var_s { varType_t varType; union { number_t num; + es_str_t *str; cstr_t *pStr; syslogTime_t vSyslogTime; diff --git a/runtime/vm.c b/runtime/vm.c index 0ed174d1..c5521c31 100644 --- a/runtime/vm.c +++ b/runtime/vm.c @@ -448,6 +448,7 @@ BEGINop(PUSHMSGVAR) /* remember to set the instruction also in the ENDop macro! var_t *pVal; /* the value to push */ cstr_t *pstrVal; CODESTARTop(PUSHMSGVAR) +dbgprintf("XXX: pushMSGVAR, var '%s'\n", rsCStrGetSzStr(pOp->operand.pVar->val.pStr)); if(pThis->pMsg == NULL) { /* TODO: flag an error message! As a work-around, we permit * execution to continue here with an empty string @@ -468,6 +469,31 @@ finalize_it: ENDop(PUSHMSGVAR) +BEGINop(PUSHCEEVAR) /* remember to set the instruction also in the ENDop macro! */ + var_t *pVal; /* the value to push */ + cstr_t *pstrVal; +CODESTARTop(PUSHCEEVAR) +dbgprintf("XXX: pushCEEVAR, var '%s'\n", rsCStrGetSzStr(pOp->operand.pVar->val.pStr)); + if(pThis->pMsg == NULL) { + /* TODO: flag an error message! As a work-around, we permit + * execution to continue here with an empty string + */ + 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 */ + CHKiRet(msgGetCEEVar(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); +dbgprintf("XXX: pushCEEVAR, result '%s'\n", rsCStrGetSzStr(pVal->val.pStr)); +finalize_it: +ENDop(PUSHCEEVAR) + + BEGINop(PUSHSYSVAR) /* remember to set the instruction also in the ENDop macro! */ var_t *pVal; /* the value to push */ CODESTARTop(PUSHSYSVAR) @@ -685,6 +711,7 @@ execProg(vm_t *pThis, vmprg_t *pProg) doOP(NOT); doOP(PUSHCONSTANT); doOP(PUSHMSGVAR); + doOP(PUSHCEEVAR); doOP(PUSHSYSVAR); doOP(STRADD); doOP(PLUS); diff --git a/runtime/vmop.h b/runtime/vmop.h index 67048c26..c085a940 100644 --- a/runtime/vmop.h +++ b/runtime/vmop.h @@ -59,6 +59,7 @@ typedef enum { /* do NOT start at 0 to detect uninitialized types after calloc( opcode_PUSHSYSVAR = 1001, /* requires var operand */ opcode_PUSHMSGVAR = 1002, /* requires var operand */ opcode_PUSHCONSTANT = 1003, /* requires var operand */ + opcode_PUSHCEEVAR = 1004, /* requires var operand */ opcode_UNARY_MINUS = 1010, opcode_FUNC_CALL = 1012, opcode_END_PROG = 2000 -- cgit v1.2.3