From 4618773be685488e081bebb397db32851dc16782 Mon Sep 17 00:00:00 2001
From: Rainer Gerhards
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
+
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