summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRainer Gerhards <rgerhards@adiscon.com>2012-09-17 09:34:01 +0200
committerRainer Gerhards <rgerhards@adiscon.com>2012-09-17 09:34:01 +0200
commitb3eb2f9b81a3cfc2d179aea364501fca4134e480 (patch)
tree082f9951df5c113d6495c029e00dbb42d441365c
parent92564dc5560aff06062a612058865f2a8a8bdff3 (diff)
parent5ab936a92d636390026d466350464ec4182075ac (diff)
downloadrsyslog-b3eb2f9b81a3cfc2d179aea364501fca4134e480.tar.gz
rsyslog-b3eb2f9b81a3cfc2d179aea364501fca4134e480.tar.xz
rsyslog-b3eb2f9b81a3cfc2d179aea364501fca4134e480.zip
Merge branch 'master-newruleset'
-rw-r--r--grammar/grammar.y4
-rw-r--r--grammar/lexer.l6
-rw-r--r--grammar/rainerscript.c38
-rw-r--r--grammar/rainerscript.h11
-rw-r--r--runtime/msg.c108
-rw-r--r--runtime/msg.h7
-rw-r--r--runtime/rsyslog.h2
-rw-r--r--runtime/ruleset.c41
8 files changed, 200 insertions, 17 deletions
diff --git a/grammar/grammar.y b/grammar/grammar.y
index f455a21f..074d2abd 100644
--- a/grammar/grammar.y
+++ b/grammar/grammar.y
@@ -65,6 +65,8 @@ extern int yyerror(char*);
%token BEGIN_CONSTANT
%token BEGIN_TPL
%token STOP
+%token SET
+%token UNSET
%token <s> LEGACY_ACTION
%token <s> LEGACY_RULESET
%token <s> PRIFILT
@@ -153,6 +155,8 @@ stmt: actlst { $$ = $1; }
$$->d.s_if.expr = $2;
$$->d.s_if.t_then = $4;
$$->d.s_if.t_else = $6; }
+ | SET VAR '=' expr ';' { $$ = cnfstmtNewSet($2, $4); }
+ | UNSET VAR ';' { $$ = cnfstmtNewUnset($2); }
| PRIFILT block { $$ = cnfstmtNewPRIFILT($1, $2); }
| PROPFILT block { $$ = cnfstmtNewPROPFILT($1, $2); }
block: stmt { $$ = $1; }
diff --git a/grammar/lexer.l b/grammar/lexer.l
index 869b34fe..657c817f 100644
--- a/grammar/lexer.l
+++ b/grammar/lexer.l
@@ -96,9 +96,11 @@ int fileno(FILE *stream);
/* keywords */
"if" { BEGIN EXPR; return IF; }
<EXPR>"then" { BEGIN INITIAL; return THEN; }
+<EXPR>";" { BEGIN INITIAL; return ';'; }
<EXPR>"or" { return OR; }
<EXPR>"and" { return AND; }
<EXPR>"not" { return NOT; }
+<EXPR>"=" |
<EXPR>"," |
<EXPR>"*" |
<EXPR>"/" |
@@ -121,7 +123,7 @@ int fileno(FILE *stream);
<EXPR>0[0-7]+ | /* octal number */
<EXPR>0x[0-7a-f] | /* hex number, following rule is dec; strtoll handles all! */
<EXPR>([1-9][0-9]*|0) { yylval.n = strtoll(yytext, NULL, 0); return NUMBER; }
-<EXPR>\$[$!]{0,1}[a-z][a-z0-9\-_\.]* { yylval.s = strdup(yytext); return VAR; }
+<EXPR>\$[$!]{0,1}[a-z][!a-z0-9\-_\.]* { yylval.s = strdup(yytext); return VAR; }
<EXPR>\'([^'\\]|\\['"\\$bntr]|\\x[0-9a-f][0-9a-f]|\\[0-7][0-7][0-7])*\' {
yytext[yyleng-1] = '\0';
unescapeStr((uchar*)yytext+1, yyleng-2);
@@ -145,6 +147,8 @@ int fileno(FILE *stream);
*/
"stop" { return STOP; }
"else" { return ELSE; }
+"set" { BEGIN EXPR; return SET; }
+"unset" { BEGIN EXPR; return UNSET; }
"preprocfilelinenumber(" { BEGIN LINENO; }
<LINENO>[0-9]+ { yylineno = atoi(yytext) - 1; }
<LINENO>")" { BEGIN INITIAL; }
diff --git a/grammar/rainerscript.c b/grammar/rainerscript.c
index b0a79458..c7344628 100644
--- a/grammar/rainerscript.c
+++ b/grammar/rainerscript.c
@@ -1466,6 +1466,16 @@ cnfstmtPrint(struct cnfstmt *root, int indent)
}
doIndent(indent); dbgprintf("END IF\n");
break;
+ case S_SET:
+ doIndent(indent); dbgprintf("SET %s =\n",
+ stmt->d.s_set.varname);
+ cnfexprPrint(stmt->d.s_set.expr, indent+1);
+ doIndent(indent); dbgprintf("END SET\n");
+ break;
+ case S_UNSET:
+ doIndent(indent); dbgprintf("UNSET %s\n",
+ stmt->d.s_unset.varname);
+ break;
case S_PRIFILT:
doIndent(indent); dbgprintf("PRIFILT '%s'\n", stmt->printable);
cnfstmtPrint(stmt->d.s_prifilt.t_then, indent+1);
@@ -1569,6 +1579,13 @@ cnfstmtDestruct(struct cnfstmt *root)
cnfstmtDestruct(stmt->d.s_if.t_else);
}
break;
+ case S_SET:
+ free(stmt->d.s_set.varname);
+ cnfexprDestruct(stmt->d.s_set.expr);
+ break;
+ case S_UNSET:
+ free(stmt->d.s_set.varname);
+ break;
case S_PRIFILT:
cnfstmtDestruct(stmt->d.s_prifilt.t_then);
break;
@@ -1594,6 +1611,27 @@ cnfstmtDestruct(struct cnfstmt *root)
}
struct cnfstmt *
+cnfstmtNewSet(char *var, struct cnfexpr *expr)
+{
+ struct cnfstmt* cnfstmt;
+ if((cnfstmt = cnfstmtNew(S_SET)) != NULL) {
+ cnfstmt->d.s_set.varname = (uchar*) var;
+ cnfstmt->d.s_set.expr = expr;
+ }
+ return cnfstmt;
+}
+
+struct cnfstmt *
+cnfstmtNewUnset(char *var)
+{
+ struct cnfstmt* cnfstmt;
+ if((cnfstmt = cnfstmtNew(S_UNSET)) != NULL) {
+ cnfstmt->d.s_unset.varname = (uchar*) var;
+ }
+ return cnfstmt;
+}
+
+struct cnfstmt *
cnfstmtNewPRIFILT(char *prifilt, struct cnfstmt *t_then)
{
struct cnfstmt* cnfstmt;
diff --git a/grammar/rainerscript.h b/grammar/rainerscript.h
index 7637ae48..51d96e51 100644
--- a/grammar/rainerscript.h
+++ b/grammar/rainerscript.h
@@ -116,6 +116,8 @@ struct nvlst {
#define S_IF 4003
#define S_ACT 4004
#define S_NOP 4005 /* usually used to disable some statement */
+#define S_SET 4006
+#define S_UNSET 4007
enum cnfFiltType { CNFFILT_NONE, CNFFILT_PRI, CNFFILT_PROP, CNFFILT_SCRIPT };
static inline char*
@@ -146,6 +148,13 @@ struct cnfstmt {
struct cnfstmt *t_else;
} s_if;
struct {
+ uchar *varname;
+ struct cnfexpr *expr;
+ } s_set;
+ struct {
+ uchar *varname;
+ } s_unset;
+ struct {
uchar pmask[LOG_NFACILITIES+1]; /* priority mask */
struct cnfstmt *t_then;
} s_prifilt;
@@ -288,6 +297,8 @@ struct cnfstmt * cnfstmtNewPRIFILT(char *prifilt, struct cnfstmt *t_then);
struct cnfstmt * cnfstmtNewPROPFILT(char *propfilt, struct cnfstmt *t_then);
struct cnfstmt * cnfstmtNewAct(struct nvlst *lst);
struct cnfstmt * cnfstmtNewLegaAct(char *actline);
+struct cnfstmt * cnfstmtNewSet(char *var, struct cnfexpr *expr);
+struct cnfstmt * cnfstmtNewUnset(char *var);
void cnfstmtDestruct(struct cnfstmt *root);
char* getFIOPName(unsigned iFIOP);
rsRetVal initRainerscript(void);
diff --git a/runtime/msg.c b/runtime/msg.c
index f1f7997c..2f6606bb 100644
--- a/runtime/msg.c
+++ b/runtime/msg.c
@@ -2420,7 +2420,6 @@ getCEEPropVal(msg_t *pM, es_str_t *propName, uchar **pRes, int *buflen, unsigned
if(*pbMustBeFreed)
free(*pRes);
*pRes = NULL;
-dbgprintf("AAAA: enter getCEEPropVal\n");
// TODO: mutex?
if(pM->json == NULL) goto finalize_it;
@@ -2428,9 +2427,7 @@ dbgprintf("AAAA: enter getCEEPropVal\n");
field = pM->json;
} else {
name = (uchar*)es_str2cstr(propName, NULL);
-dbgprintf("AAAA: name to search '%s'\n", name);
leaf = jsonPathGetLeaf(name, ustrlen(name));
-dbgprintf("AAAA: leaf '%s'\n", leaf);
CHKiRet(jsonPathFindParent(pM, name, leaf, &parent, 1));
field = json_object_object_get(parent, (char*)leaf);
}
@@ -2439,7 +2436,6 @@ dbgprintf("AAAA: leaf '%s'\n", leaf);
*pbMustBeFreed = 0;
} else {
*pRes = (uchar*) strdup(json_object_get_string(field));
-dbgprintf("AAAA: json_object_get_string() returns '%s'\n", *pRes);
*buflen = (int) ustrlen(*pRes);
*pbMustBeFreed = 1;
}
@@ -2465,7 +2461,6 @@ msgGetCEEPropJSON(msg_t *pM, es_str_t *propName, struct json_object **pjson)
struct json_object *parent;
DEFiRet;
-dbgprintf("AAAA: enter getCEEPropJSON\n");
// TODO: mutex?
if(pM->json == NULL) {
ABORT_FINALIZE(RS_RET_NOT_FOUND);
@@ -2476,9 +2471,7 @@ dbgprintf("AAAA: enter getCEEPropJSON\n");
FINALIZE;
}
name = (uchar*)es_str2cstr(propName, NULL);
-dbgprintf("AAAA: name to search '%s'\n", name);
leaf = jsonPathGetLeaf(name, ustrlen(name));
-dbgprintf("AAAA: leaf '%s'\n", leaf);
CHKiRet(jsonPathFindParent(pM, name, leaf, &parent, 1));
*pjson = json_object_object_get(parent, (char*)leaf);
if(*pjson == NULL) {
@@ -3685,9 +3678,9 @@ jsonPathFindNext(struct json_object *root, uchar **name, uchar *leaf,
++p;
for(i = 0 ; *p && *p != '!' && p != leaf && i < sizeof(namebuf)-1 ; ++i, ++p)
namebuf[i] = *p;
- if(i == 0) {
+ if(i > 0) {
namebuf[i] = '\0';
- dbgprintf("AAAA: next JSONP elt: '%s'\n", namebuf);
+ dbgprintf("AAAA: next JSONPath elt: '%s'\n", namebuf);
json = json_object_object_get(root, (char*)namebuf);
} else
json = root;
@@ -3713,7 +3706,6 @@ jsonPathFindParent(msg_t *pM, uchar *name, uchar *leaf, struct json_object **par
*parent = pM->json;
while(name < leaf-1) {
jsonPathFindNext(*parent, &name, leaf, parent, bCreate);
-dbgprintf("AAAA: name %p, leaf %p\n", name, leaf);
}
RETiRet;
}
@@ -3726,7 +3718,7 @@ jsonMerge(struct json_object *existing, struct json_object *json)
struct json_object_iter it;
json_object_object_foreachC(json, it) {
-dbgprintf("AAAA jsonMerge adds '%s'\n", it.key);
+DBGPRINTF("AAAA jsonMerge adds '%s'\n", it.key);
json_object_object_add(existing, it.key,
json_object_get(it.val));
}
@@ -3760,10 +3752,74 @@ msgAddJSON(msg_t *pM, uchar *name, struct json_object *json)
leaf = jsonPathGetLeaf(name, ustrlen(name));
CHKiRet(jsonPathFindParent(pM, name, leaf, &parent, 1));
leafnode = json_object_object_get(parent, (char*)leaf);
- if(leafnode == NULL)
+ if(leafnode == NULL) {
json_object_object_add(parent, (char*)leaf, json);
- else
- CHKiRet(jsonMerge(pM->json, json));
+ } else {
+ if(json_object_get_type(json) == json_type_object) {
+ CHKiRet(jsonMerge(pM->json, json));
+ } else {
+//dbgprintf("AAAA: leafnode already exists, type is %d, update with %d\n", (int)json_object_get_type(leafnode), (int)json_object_get_type(json));
+ /* TODO: improve the code below, however, the current
+ * state is not really bad */
+ if(json_object_get_type(leafnode) == json_type_object) {
+ DBGPRINTF("msgAddJSON: trying to update a container "
+ "node with a leaf, name is '%s' - "
+ "forbidden\n", name);
+ json_object_put(json);
+ ABORT_FINALIZE(RS_RET_INVLD_SETOP);
+ }
+ /* json-c code indicates we can simply replace a
+ * json type. Unfortunaltely, this is not documented
+ * as part of the interface spec. We still use it,
+ * because it speeds up processing. If it does not work
+ * at some point, use
+ * json_object_object_del(parent, (char*)leaf);
+ * before adding. rgerhards, 2012-09-17
+ */
+ json_object_object_add(parent, (char*)leaf, json);
+ }
+ }
+ }
+
+finalize_it:
+ MsgUnlock(pM);
+ RETiRet;
+}
+
+rsRetVal
+msgDelJSON(msg_t *pM, uchar *name)
+{
+ struct json_object *parent, *leafnode;
+ uchar *leaf;
+ DEFiRet;
+
+dbgprintf("AAAA: unset variable '%s'\n", name);
+ MsgLock(pM);
+ if(name[0] == '!' && name[1] == '\0') {
+ /* strange, but I think we should permit this. After all,
+ * we trust rsyslog.conf to be written by the admin.
+ */
+ DBGPRINTF("unsetting JSON root object\n");
+ json_object_put(pM->json);
+ pM->json = NULL;
+ } else {
+ if(pM->json == NULL) {
+ /* now we need a root obj */
+ pM->json = json_object_new_object();
+ }
+ leaf = jsonPathGetLeaf(name, ustrlen(name));
+ CHKiRet(jsonPathFindParent(pM, name, leaf, &parent, 1));
+ leafnode = json_object_object_get(parent, (char*)leaf);
+DBGPRINTF("AAAA: unset found JSON value path '%s', " "leaf '%s', leafnode %p\n", name, leaf, leafnode);
+ if(leafnode == NULL) {
+ DBGPRINTF("unset JSON: could not find '%s'\n", name);
+ ABORT_FINALIZE(RS_RET_JNAME_NOTFOUND);
+ } else {
+ DBGPRINTF("deleting JSON value path '%s', "
+ "leaf '%s', type %d\n",
+ name, leaf, json_object_get_type(leafnode));
+ json_object_object_del(parent, (char*)leaf);
+ }
}
finalize_it:
@@ -3771,6 +3827,30 @@ finalize_it:
RETiRet;
}
+rsRetVal
+msgSetJSONFromVar(msg_t *pMsg, uchar *varname, struct var *var)
+{
+ struct json_object *json = NULL;
+ char *cstr;
+ DEFiRet;
+ switch(var->datatype) {
+ case 'S':/* string */
+ cstr = es_str2cstr(var->d.estr, NULL);
+ json = json_object_new_string(cstr);
+ free(cstr);
+ break;
+ case 'N':/* number (integer) */
+ json = json_object_new_int((int) var->d.n);
+ break;
+ default:DBGPRINTF("msgSetJSONFromVar: unsupported datatype %c\n",
+ var->datatype);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ msgAddJSON(pMsg, varname+1, json);
+finalize_it:
+ RETiRet;
+}
+
/* dummy */
rsRetVal msgQueryInterface(void) { return RS_RET_NOT_IMPLEMENTED; }
diff --git a/runtime/msg.h b/runtime/msg.h
index 857eb673..477d1f1a 100644
--- a/runtime/msg.h
+++ b/runtime/msg.h
@@ -202,6 +202,13 @@ uchar *getRcvFrom(msg_t *pM);
rsRetVal propNameToID(cstr_t *pCSPropName, propid_t *pPropID);
uchar *propIDToName(propid_t propID);
rsRetVal msgGetCEEPropJSON(msg_t *pM, es_str_t *propName, struct json_object **pjson);
+rsRetVal msgSetJSONFromVar(msg_t *pMsg, uchar *varname, struct var *var);
+rsRetVal msgDelJSON(msg_t *pMsg, uchar *varname);
+
+static inline rsRetVal
+msgUnsetJSON(msg_t *pMsg, uchar *varname) {
+ return msgDelJSON(pMsg, varname+1);
+}
/* The MsgPrepareEnqueue() function is a macro for performance reasons.
diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h
index fe9bb4cc..ceb277d0 100644
--- a/runtime/rsyslog.h
+++ b/runtime/rsyslog.h
@@ -387,6 +387,8 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth
RS_RET_JNAME_INVALID = -2302, /**< JSON path is invalid */
RS_RET_JSON_PARSE_ERR = -2303, /**< we had a problem parsing JSON (or extra data) */
RS_RET_BSD_BLOCKS_UNSUPPORTED = -2304, /**< BSD-style config blocks are no longer supported */
+ RS_RET_JNAME_NOTFOUND = -2305, /**< JSON name not found (does not exist) */
+ RS_RET_INVLD_SETOP = -2305, /**< invalid variable set operation, incompatible type */
/* RainerScript error messages (range 1000.. 1999) */
RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */
diff --git a/runtime/ruleset.c b/runtime/ruleset.c
index faec122c..a40e2ed1 100644
--- a/runtime/ruleset.c
+++ b/runtime/ruleset.c
@@ -206,6 +206,7 @@ static inline sbool *newActive(batch_t *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
@@ -218,6 +219,38 @@ dbgprintf("RRRR: execAct: batch of %d elements, active %p\n", batchNumMsgs(pBatc
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.
@@ -302,7 +335,6 @@ execPRIFILT(struct cnfstmt *stmt, batch_t *pBatch, sbool *active)
DBGPRINTF("batch: item %d PRIFILT %d\n", i, thenAct[i]);
}
-dbgprintf("RRRR: PRIFILT calling %p\n", stmt->d.s_prifilt.t_then);
scriptExec(stmt->d.s_prifilt.t_then, pBatch, thenAct);
freeActive(thenAct);
}
@@ -417,7 +449,6 @@ execPROPFILT(struct cnfstmt *stmt, batch_t *pBatch, sbool *active)
DBGPRINTF("batch: item %d PROPFILT %d\n", i, thenAct[i]);
}
-dbgprintf("RRRR: PROPFILT calling %p\n", stmt->d.s_propfilt.t_then);
scriptExec(stmt->d.s_propfilt.t_then, pBatch, thenAct);
freeActive(thenAct);
}
@@ -449,6 +480,12 @@ dbgprintf("RRRR: scriptExec: batch of %d elements, active %p, stmt %p, nodetype
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_IF:
execIf(stmt, pBatch, active);
break;