summaryrefslogtreecommitdiffstats
path: root/grammar/rainerscript.c
diff options
context:
space:
mode:
authorRainer Gerhards <rgerhards@adiscon.com>2011-07-07 08:22:40 +0200
committerRainer Gerhards <rgerhards@adiscon.com>2011-07-07 08:22:40 +0200
commit5710b413963d2fde9d062127ed72672b8a58a07e (patch)
tree35cd364e62273da3a239f1c72f1d50d4e69ce21b /grammar/rainerscript.c
parent30f2b5b094d282af6f601aa4e8fa88c1baf187f4 (diff)
downloadrsyslog-5710b413963d2fde9d062127ed72672b8a58a07e.tar.gz
rsyslog-5710b413963d2fde9d062127ed72672b8a58a07e.tar.xz
rsyslog-5710b413963d2fde9d062127ed72672b8a58a07e.zip
milestone/[PARTWORK]: integrted script filter, but var access is missing
Diffstat (limited to 'grammar/rainerscript.c')
-rw-r--r--grammar/rainerscript.c658
1 files changed, 658 insertions, 0 deletions
diff --git a/grammar/rainerscript.c b/grammar/rainerscript.c
new file mode 100644
index 00000000..801a52b2
--- /dev/null
+++ b/grammar/rainerscript.c
@@ -0,0 +1,658 @@
+/* rainerscript.c - routines to support RainerScript config language
+ *
+ * Module begun 2011-07-01 by Rainer Gerhards
+ *
+ * Copyright 2011 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of the rsyslog runtime library.
+ *
+ * The rsyslog runtime library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The rsyslog runtime library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <libestr.h>
+#include "rainerscript.h"
+#include "parserif.h"
+#include "grammar.h"
+
+void
+readConfFile(FILE *fp, es_str_t **str)
+{
+ char ln[10240];
+ char buf[512];
+ int lenBuf;
+ int bWriteLineno = 0;
+ int len, i;
+ int start; /* start index of to be submitted text */
+ int bContLine = 0;
+ int lineno = 0;
+
+ *str = es_newStr(4096);
+
+ while(fgets(ln, sizeof(ln), fp) != NULL) {
+ ++lineno;
+ if(bWriteLineno) {
+ bWriteLineno = 0;
+ lenBuf = sprintf(buf, "PreprocFileLineNumber(%d)\n", lineno);
+ es_addBuf(str, buf, lenBuf);
+ }
+ len = strlen(ln);
+ /* if we are continuation line, we need to drop leading WS */
+ if(bContLine) {
+ for(start = 0 ; start < len && isspace(ln[start]) ; ++start)
+ /* JUST SCAN */;
+ } else {
+ start = 0;
+ }
+ for(i = len - 1 ; i >= start && isspace(ln[i]) ; --i)
+ /* JUST SCAN */;
+ if(i >= 0) {
+ if(ln[i] == '\\') {
+ --i;
+ bContLine = 1;
+ } else {
+ if(bContLine) /* write line number if we had cont line */
+ bWriteLineno = 1;
+ bContLine = 0;
+ }
+ /* add relevant data to buffer */
+ es_addBuf(str, ln+start, i+1 - start);
+ }
+ if(!bContLine)
+ es_addChar(str, '\n');
+ }
+ /* indicate end of buffer to flex */
+ es_addChar(str, '\0');
+ es_addChar(str, '\0');
+}
+
+struct nvlst*
+nvlstNew(es_str_t *name, es_str_t *value)
+{
+ struct nvlst *lst;
+
+ if((lst = malloc(sizeof(struct nvlst))) != NULL) {
+ lst->next = NULL;
+ lst->name = name;
+ lst->value = value;
+ }
+
+ return lst;
+}
+
+void
+nvlstDestruct(struct nvlst *lst)
+{
+ struct nvlst *toDel;
+
+ while(lst != NULL) {
+ toDel = lst;
+ lst = lst->next;
+ es_deleteStr(toDel->name);
+ es_deleteStr(toDel->value);
+ free(toDel);
+ }
+}
+
+void
+nvlstPrint(struct nvlst *lst)
+{
+ char *name, *value;
+ dbgprintf("nvlst %p:\n", lst);
+ while(lst != NULL) {
+ name = es_str2cstr(lst->name, NULL);
+ value = es_str2cstr(lst->value, NULL);
+ dbgprintf("\tname: '%s', value '%s'\n", name, value);
+ free(name);
+ free(value);
+ lst = lst->next;
+ }
+}
+
+struct cnfobj*
+cnfobjNew(enum cnfobjType objType, struct nvlst *lst)
+{
+ struct cnfobj *o;
+
+ if((o = malloc(sizeof(struct nvlst))) != NULL) {
+ o->objType = objType;
+ o->nvlst = lst;
+ }
+
+ return o;
+}
+
+void
+cnfobjDestruct(struct cnfobj *o)
+{
+ if(o != NULL) {
+ nvlstDestruct(o->nvlst);
+ free(o);
+ }
+}
+
+void
+cnfobjPrint(struct cnfobj *o)
+{
+ dbgprintf("obj: '%s'\n", cnfobjType2str(o->objType));
+ nvlstPrint(o->nvlst);
+}
+
+
+struct cnfactlst*
+cnfactlstNew(enum cnfactType actType, struct nvlst *lst, char *actLine)
+{
+ struct cnfactlst *actlst;
+
+ if((actlst = malloc(sizeof(struct cnfactlst))) != NULL) {
+ actlst->next = NULL;
+ actlst->syslines = NULL;
+ actlst->actType = actType;
+ if(actType == CNFACT_V2)
+ actlst->data.lst = lst;
+ else
+ actlst->data.legActLine = actLine;
+ }
+ return actlst;
+}
+
+struct cnfactlst*
+cnfactlstAddSysline(struct cnfactlst* actlst, char *line)
+{
+ struct cnfcfsyslinelst *cflst;
+
+ if((cflst = malloc(sizeof(struct cnfcfsyslinelst))) != NULL) {
+ cflst->next = NULL;
+ cflst->line = line;
+ if(actlst->syslines == NULL) {
+ actlst->syslines = cflst;
+ } else {
+ cflst->next = actlst->syslines;
+ actlst->syslines = cflst;
+ }
+ }
+ return actlst;
+}
+
+void
+cnfactlstDestruct(struct cnfactlst *actlst)
+{
+ struct cnfactlst *toDel;
+
+ while(actlst != NULL) {
+ toDel = actlst;
+ actlst = actlst->next;
+ if(toDel->actType == CNFACT_V2)
+ nvlstDestruct(toDel->data.lst);
+ else
+ free(toDel->data.legActLine);
+ free(toDel);
+ }
+
+}
+
+static inline struct cnfcfsyslinelst*
+cnfcfsyslinelstReverse(struct cnfcfsyslinelst *lst)
+{
+ struct cnfcfsyslinelst *curr, *prev;
+ if(lst == NULL)
+ return NULL;
+ prev = NULL;
+ while(lst != NULL) {
+ curr = lst;
+ lst = lst->next;
+ curr->next = prev;
+ prev = curr;
+ }
+ return prev;
+}
+
+struct cnfactlst*
+cnfactlstReverse(struct cnfactlst *actlst)
+{
+ struct cnfactlst *curr, *prev;
+
+ prev = NULL;
+ while(actlst != NULL) {
+ //dbgprintf("reversing: %s\n", actlst->data.legActLine);
+ curr = actlst;
+ actlst = actlst->next;
+ curr->syslines = cnfcfsyslinelstReverse(curr->syslines);
+ curr->next = prev;
+ prev = curr;
+ }
+ return prev;
+}
+
+void
+cnfactlstPrint(struct cnfactlst *actlst)
+{
+ struct cnfcfsyslinelst *cflst;
+
+ while(actlst != NULL) {
+ dbgprintf("aclst %p: ", actlst);
+ if(actlst->actType == CNFACT_V2) {
+ dbgprintf("V2 action type: ");
+ nvlstPrint(actlst->data.lst);
+ } else {
+ dbgprintf("legacy action line: '%s'\n",
+ actlst->data.legActLine);
+ }
+ for( cflst = actlst->syslines
+ ; cflst != NULL ; cflst = cflst->next) {
+ dbgprintf("action:cfsysline: '%s'\n", cflst->line);
+ }
+ actlst = actlst->next;
+ }
+}
+
+struct cnfexpr*
+cnfexprNew(unsigned nodetype, struct cnfexpr *l, struct cnfexpr *r)
+{
+ struct cnfexpr *expr;
+
+ /* optimize some constructs during parsing */
+ if(nodetype == 'M' && r->nodetype == 'N') {
+ ((struct cnfnumval*)r)->val *= -1;
+ expr = r;
+ goto done;
+ }
+
+ if((expr = malloc(sizeof(struct cnfexpr))) != NULL) {
+ expr->nodetype = nodetype;
+ expr->l = l;
+ expr->r = r;
+ }
+done:
+ return expr;
+}
+
+/* ensure that retval is a number; if string is no number,
+ * emit error message and set number to 0.
+ */
+static inline long long
+exprret2Number(struct exprret *r)
+{
+ if(r->datatype == 'S') {
+ dbgprintf("toNumber CONVERSION MISSING\n"); abort();
+ }
+ return r->d.n;
+}
+
+/* ensure that retval is a string; if string is no number,
+ * emit error message and set number to 0.
+ */
+static inline es_str_t *
+exprret2String(struct exprret *r)
+{
+ if(r->datatype == 'N') {
+ dbgprintf("toString CONVERSION MISSING\n"); abort();
+ }
+ return r->d.estr;
+}
+
+#define COMP_NUM_BINOP(x) \
+ cnfexprEval(expr->l, &l); \
+ cnfexprEval(expr->r, &r); \
+ ret->datatype = 'N'; \
+ ret->d.n = exprret2Number(&l) x exprret2Number(&r)
+
+/* evaluate an expression.
+ * Note that we try to avoid malloc whenever possible (because on
+ * the large overhead it has, especially on highly threaded programs).
+ * As such, the each caller level must provide buffer space for the
+ * result on its stack during recursion. This permits the callee to store
+ * the return value without malloc. As the value is a somewhat larger
+ * struct, we could otherwise not return it without malloc.
+ * Note that we implement boolean shortcut operations. For our needs, there
+ * simply is no case where full evaluation would make any sense at all.
+ */
+void
+cnfexprEval(struct cnfexpr *expr, struct exprret *ret)
+{
+ struct exprret r, l; /* memory for subexpression results */
+
+ //dbgprintf("eval expr %p, type '%c'(%u)\n", expr, expr->nodetype, expr->nodetype);
+ switch(expr->nodetype) {
+ case CMP_EQ:
+ COMP_NUM_BINOP(==);
+ break;
+ case CMP_NE:
+ COMP_NUM_BINOP(!=);
+ break;
+ case CMP_LE:
+ COMP_NUM_BINOP(<=);
+ break;
+ case CMP_GE:
+ COMP_NUM_BINOP(>=);
+ break;
+ case CMP_LT:
+ COMP_NUM_BINOP(<);
+ break;
+ case CMP_GT:
+ COMP_NUM_BINOP(>);
+ break;
+ case OR:
+ cnfexprEval(expr->l, &l);
+ ret->datatype = 'N';
+ if(exprret2Number(&l)) {
+ ret->d.n = 1ll;
+ } else {
+ cnfexprEval(expr->r, &r);
+ if(exprret2Number(&r))
+ ret->d.n = 1ll;
+ else
+ ret->d.n = 0ll;
+ }
+ break;
+ case AND:
+ cnfexprEval(expr->l, &l);
+ ret->datatype = 'N';
+ if(exprret2Number(&l)) {
+ cnfexprEval(expr->r, &r);
+ if(exprret2Number(&r))
+ ret->d.n = 1ll;
+ else
+ ret->d.n = 0ll;
+ } else {
+ ret->d.n = 0ll;
+ }
+ break;
+ case NOT:
+ cnfexprEval(expr->r, &r);
+ ret->datatype = 'N';
+ ret->d.n = !exprret2Number(&r);
+ break;
+ case 'N':
+ ret->datatype = 'N';
+ ret->d.n = ((struct cnfnumval*)expr)->val;
+ break;
+ case '+':
+ COMP_NUM_BINOP(+);
+ break;
+ case '-':
+ COMP_NUM_BINOP(-);
+ break;
+ case '*':
+ COMP_NUM_BINOP(*);
+ break;
+ case '/':
+ COMP_NUM_BINOP(/);
+ break;
+ case '%':
+ COMP_NUM_BINOP(%);
+ break;
+ case 'M':
+ cnfexprEval(expr->r, &r);
+ ret->datatype = 'N';
+ ret->d.n = -exprret2Number(&r);
+ break;
+ default:
+ ret->datatype = 'N';
+ ret->d.n = 0ll;
+ dbgprintf("eval error: unknown nodetype %u\n",
+ (unsigned) expr->nodetype);
+ break;
+ }
+}
+
+/* Evaluate an expression as a bool. This is added because expressions are
+ * mostly used inside filters, and so this function is quite common and
+ * important.
+ */
+int
+cnfexprEvalBool(struct cnfexpr *expr)
+{
+ struct exprret ret;
+ cnfexprEval(expr, &ret);
+ return exprret2Number(&ret);
+}
+
+inline static void
+doIndent(int indent)
+{
+ int i;
+ for(i = 0 ; i < indent ; ++i)
+ dbgprintf(" ");
+}
+void
+cnfexprPrint(struct cnfexpr *expr, int indent)
+{
+ struct cnffparamlst *param;
+ //dbgprintf("expr %p, indent %d, type '%c'\n", expr, indent, expr->nodetype);
+ switch(expr->nodetype) {
+ case CMP_EQ:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf("==\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case CMP_NE:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf("!=\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case CMP_LE:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf("<=\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case CMP_GE:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf(">=\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case CMP_LT:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf("<\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case CMP_GT:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf(">\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case CMP_CONTAINS:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf("CONTAINS\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case CMP_CONTAINSI:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf("CONTAINS_I\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case CMP_STARTSWITH:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf("STARTSWITH\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case CMP_STARTSWITHI:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf("STARTSWITH_I\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case OR:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf("OR\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case AND:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf("AND\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case NOT:
+ doIndent(indent);
+ dbgprintf("NOT\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case 'S':
+ doIndent(indent);
+ cstrPrint("string '", ((struct cnfstringval*)expr)->estr);
+ dbgprintf("'\n");
+ break;
+ case 'N':
+ doIndent(indent);
+ dbgprintf("%lld\n", ((struct cnfnumval*)expr)->val);
+ break;
+ case 'V':
+ doIndent(indent);
+ dbgprintf("var '%s'\n", ((struct cnfvar*)expr)->name);
+ break;
+ case 'F':
+ doIndent(indent);
+ cstrPrint("function '", ((struct cnffunc*)expr)->fname);
+ dbgprintf("'\n");
+ for( param = ((struct cnffunc*)expr)->paramlst
+ ; param != NULL
+ ; param = param->next) {
+ cnfexprPrint(param->expr, indent+1);
+ }
+ break;
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '%':
+ case 'M':
+ if(expr->l != NULL)
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf("%c\n", (char) expr->nodetype);
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ default:
+ dbgprintf("error: unknown nodetype %u\n",
+ (unsigned) expr->nodetype);
+ break;
+ }
+}
+
+struct cnfnumval*
+cnfnumvalNew(long long val)
+{
+ struct cnfnumval *numval;
+ if((numval = malloc(sizeof(struct cnfnumval))) != NULL) {
+ numval->nodetype = 'N';
+ numval->val = val;
+ }
+ return numval;
+}
+
+struct cnfstringval*
+cnfstringvalNew(es_str_t *estr)
+{
+ struct cnfstringval *strval;
+ if((strval = malloc(sizeof(struct cnfstringval))) != NULL) {
+ strval->nodetype = 'S';
+ strval->estr = estr;
+ }
+ return strval;
+}
+
+struct cnfvar*
+cnfvarNew(char *name)
+{
+ struct cnfvar *var;
+ if((var = malloc(sizeof(struct cnfvar))) != NULL) {
+ var->nodetype = 'V';
+ var->name = name;
+ }
+ return var;
+}
+
+struct cnfrule *
+cnfruleNew(enum cnfFiltType filttype, struct cnfactlst *actlst)
+{
+ struct cnfrule* cnfrule;
+ if((cnfrule = malloc(sizeof(struct cnfrule))) != NULL) {
+ cnfrule->nodetype = 'R';
+ cnfrule->filttype = filttype;
+ cnfrule->actlst = cnfactlstReverse(actlst);
+ }
+ return cnfrule;
+}
+
+void
+cnfrulePrint(struct cnfrule *rule)
+{
+ dbgprintf("------ start rule %p:\n", rule);
+ dbgprintf("%s: ", cnfFiltType2str(rule->filttype));
+ switch(rule->filttype) {
+ case CNFFILT_NONE:
+ break;
+ case CNFFILT_PRI:
+ case CNFFILT_PROP:
+ dbgprintf("%s\n", rule->filt.s);
+ break;
+ case CNFFILT_SCRIPT:
+ dbgprintf("\n");
+ cnfexprPrint(rule->filt.expr, 0);
+ break;
+ }
+ cnfactlstPrint(rule->actlst);
+ dbgprintf("------ end rule %p\n", rule);
+}
+
+struct cnffparamlst *
+cnffparamlstNew(struct cnfexpr *expr, struct cnffparamlst *next)
+{
+ struct cnffparamlst* lst;
+ if((lst = malloc(sizeof(struct cnffparamlst))) != NULL) {
+ lst->nodetype = 'P';
+ lst->expr = expr;
+ lst->next = next;
+ }
+ return lst;
+}
+
+struct cnffunc *
+cnffuncNew(es_str_t *fname, struct cnffparamlst* paramlst)
+{
+ struct cnffunc* func;
+ if((func = malloc(sizeof(struct cnffunc))) != NULL) {
+ func->nodetype = 'F';
+ func->fname = fname;
+ func->paramlst = paramlst;
+ }
+ return func;
+}
+
+void
+cstrPrint(char *text, es_str_t *estr)
+{
+ char *str;
+ str = es_str2cstr(estr, NULL);
+ dbgprintf("%s%s", text, str);
+ free(str);
+}