From 641e383b8ad13ad6ee7fd9241214e24e6a983500 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 5 Jul 2011 08:21:04 +0200 Subject: milestone: grammar integrated in rsyslog build system --- Makefile.am | 2 +- configure.ac | 6 +- grammar/Makefile.am | 7 ++ grammar/makefile | 14 --- grammar/makefile.stand-alone | 14 +++ grammar/rscript-lex.l | 241 +++++++++++++++++++++++++++++++++++++++++++ grammar/rscript.l | 233 ----------------------------------------- grammar/utils.c | 4 +- tools/Makefile.am | 2 +- 9 files changed, 272 insertions(+), 251 deletions(-) create mode 100644 grammar/Makefile.am delete mode 100644 grammar/makefile create mode 100644 grammar/makefile.stand-alone create mode 100644 grammar/rscript-lex.l delete mode 100644 grammar/rscript.l diff --git a/Makefile.am b/Makefile.am index d689b9ee..7bf9dd0e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -66,7 +66,7 @@ EXTRA_DIST = \ contrib/gnutls/key.pem \ rsyslog.service.in -SUBDIRS = doc runtime . plugins/immark plugins/imuxsock plugins/imtcp plugins/imudp plugins/omtesting +SUBDIRS = doc runtime grammar . plugins/immark plugins/imuxsock plugins/imtcp plugins/imudp plugins/omtesting if ENABLE_RSYSLOGD SUBDIRS += tools diff --git a/configure.ac b/configure.ac index b0e71c20..1a239ca4 100644 --- a/configure.ac +++ b/configure.ac @@ -24,6 +24,8 @@ if test x"$HAVE_JAVAC" = x""; then fi # Checks for programs. +AC_PROG_LEX +AC_PROG_YACC AC_PROG_CC AM_PROG_CC_C_O if test "$GCC" = "yes" @@ -707,6 +709,7 @@ AC_ARG_ENABLE(rsyslogrt, if test "x$enable_rsyslogrt" = "xyes"; then RSRT_CFLAGS="-I\$(top_srcdir)/runtime -I\$(top_srcdir)" RSRT_LIBS="\$(top_builddir)/runtime/librsyslog.la" + CNF_LIBS="\$(top_builddir)/grammar/libgrammar.la" fi AM_CONDITIONAL(ENABLE_RSYSLOGRT, test x$enable_rsyslogrt = xyes) AC_SUBST(RSRT_CFLAGS) @@ -779,7 +782,7 @@ AM_CONDITIONAL(ENABLE_MAIL, test x$enable_mail = xyes) # would complicate things if we first needed to tell them how to enable imdiag. # rgerhards, 2008-07-25 AC_ARG_ENABLE(imdiag, - [AS_HELP_STRING([--enable-imdiag],[Enable imdiag @<:@default=yes@:>@])], + [AS_HELP_STRING([--enable-imdiag],[Enable imdiag @<:@default=no@:>@])], [case "${enableval}" in yes) enable_imdiag="yes" ;; no) enable_imdiag="no" ;; @@ -1224,6 +1227,7 @@ AM_CONDITIONAL(ENABLE_OMMONGODB, test x$enable_ommongodb = xyes) AC_CONFIG_FILES([Makefile \ runtime/Makefile \ + grammar/Makefile \ tools/Makefile \ doc/Makefile \ plugins/imudp/Makefile \ diff --git a/grammar/Makefile.am b/grammar/Makefile.am new file mode 100644 index 00000000..b2bd3430 --- /dev/null +++ b/grammar/Makefile.am @@ -0,0 +1,7 @@ +noinst_LTLIBRARIES = libgrammar.la + +libgrammar_la_SOURCES = \ + utils.c \ + utils.h \ + rscript-lex.l \ + rscript.y diff --git a/grammar/makefile b/grammar/makefile deleted file mode 100644 index eb6c9522..00000000 --- a/grammar/makefile +++ /dev/null @@ -1,14 +0,0 @@ -rscript: lex.yy.c utils.o rscript.tab.h utils.h - gcc -g -o rscript lex.yy.c rscript.tab.c utils.o -lestr - -lex.yy.c: rscript.l rscript.tab.h - flex rscript.l - -rscript.tab.h: rscript.y - bison -d rscript.y - -utils.o: utils.c utils.h - gcc -g -Wall -c utils.c - -clean: - rm *.o diff --git a/grammar/makefile.stand-alone b/grammar/makefile.stand-alone new file mode 100644 index 00000000..b998a39d --- /dev/null +++ b/grammar/makefile.stand-alone @@ -0,0 +1,14 @@ +rscript: lex.yy.c utils.o rscript.tab.h utils.h + gcc -DSTAND_ALONE -g -o rscript lex.yy.c rscript.tab.c utils.o -lestr + +lex.yy.c: rscript.l rscript.tab.h + flex rscript.l + +rscript.tab.h: rscript.y + bison -d rscript.y + +utils.o: utils.c utils.h + gcc -g -DSTAND_ALONE -Wall -c utils.c + +clean: + rm *.o diff --git a/grammar/rscript-lex.l b/grammar/rscript-lex.l new file mode 100644 index 00000000..1c7b963e --- /dev/null +++ b/grammar/rscript-lex.l @@ -0,0 +1,241 @@ + /* Lex file for rsyslog config format v2 (RainerScript). + * Please note: this file introduces the new config format, but maintains + * backward compatibility. In order to do so, the grammar is not 100% clean, + * but IMHO still sufficiently easy both to understand for programmers + * maitaining the code as well as users writing the config file. Users are, + * of course, encouraged to use new constructs only. But it needs to be noted + * that some of the legacy constructs (specifically the in-front-of-action + * PRI filter) are very hard to beat in ease of use, at least for simpler + * cases. So while we hope that cfsysline support can be dropped some time in + * the future, we will probably keep these useful constructs. + * + * Copyright (C) 2011 by Rainer Gerhards and Adiscon GmbH + * Released under the GNU GPL v3. For details see LICENSE file. + */ + +%option noyywrap nodefault case-insensitive yylineno + /*%option noyywrap nodefault case-insensitive */ + +/* avoid compiler warning: `yyunput' defined but not used */ +%option nounput noinput + + +%x INOBJ + /* INOBJ is selected if we are inside an object (name/value pairs!) */ +%x COMMENT + /* COMMENT is "the usual trick" to handle C-style comments */ +%x INCL + /* INCL is in $IncludeConfig processing (skip to include file) */ +%x LINENO + /* LINENO: support for setting the linenumber */ +%x EXPR + /* EXPR is a bit ugly, but we need it to support pre v6-syntax. The problem + * is that cfsysline statement start with $..., the same like variables in + * an expression. However, cfsysline statements can never appear inside an + * expression. So we create a specific expr mode, which is turned on after + * we lexed a keyword that needs to be followed by an expression (using + * knowledge from the upper layer...). In expr mode, we strictly do + * expression-based parsing. Expr mode is stopped when we reach a token + * that can not be part of an expression (currently only "then"). As I + * wrote this ugly, but the price needed to pay in order to remain + * compatible to the previous format. + */ +%{ +#include +#include +#include "utils.h" +#include "rscript.tab.h" +static int preCommentState; /* save for lex state before a comment */ + +struct bufstack { + struct bufstack *prev; + YY_BUFFER_STATE bs; + int lineno; + char *fn; +} *currbs = NULL; + +char *currfn; /* name of currently processed file */ + +int popfile(void); +int cnfSetLexFile(char *fname); +%} + +%% + + /* keywords */ +"if" { BEGIN EXPR; return IF; } +"then" { BEGIN INITIAL; return THEN; } +"or" { return OR; } +"and" { return AND; } +"not" { return NOT; } +"," | +"*" | +"/" | +"%" | +"+" | +"-" | +"(" | +")" { return yytext[0]; } +"==" { return CMP_EQ; } +"<=" { return CMP_LE; } +">=" { return CMP_GE; } +"!=" | +"<>" { return CMP_NE; } +"<" { return CMP_LT; } +">" { return CMP_GT; } +"contains" { return CMP_CONTAINS; } +"contains_i" { return CMP_CONTAINSI; } +"startswith" { return CMP_STARTSWITH; } +"startswith_i" { return CMP_STARTSWITHI; } +0[0-7]+ | /* octal number */ +0x[0-7a-f] | /* hex number, following rule is dec; strtoll handles all! */ +([1-9][0-9]*|0) { yylval.n = strtoll(yytext, NULL, 0); return NUMBER; } +\$[$!]{0,1}[a-z][a-z0-9\-_\.]* { yylval.s = strdup(yytext); return VAR; } +\'([^'\\]|\\['])*\' { yylval.estr = es_newStrFromBuf(yytext+1, yyleng-2); + return STRING; } +\"([^"\\]|\\["])*\" { yylval.estr = es_newStrFromBuf(yytext+1, yyleng-2); + return STRING; } +[ \t\n] +[a-z][a-z0-9_]* { yylval.estr = es_newStrFromCStr(yytext, yyleng); + return FUNC; } +. { printf("invalid char in expr: %s\n", yytext); } +"&" { return '&'; } +"{" { return '{'; } +"}" { return '}'; } +"ruleset" { printf("RULESET\n"); } + /* line number support because the "preprocessor" combines lines and so needs + * to tell us the real source line. + */ +"preprocfilelinenumber(" { BEGIN LINENO; } +[0-9]+ { yylineno = atoi(yytext) - 1; } +")" { BEGIN INITIAL; } +.|\n + /* $IncludeConfig must be detected as part of CFSYSLINE, because this is + * always the longest match :-( + */ +.|\n +[^ \t\n]+ { if(cnfSetLexFile(yytext) != 0) + yyterminate(); + BEGIN INITIAL; } +"global"[ \n\t]*"(" { yylval.objType = CNFOBJ_GLOBAL; + BEGIN INOBJ; return BEGINOBJ; } +"input"[ \n\t]*"(" { yylval.objType = CNFOBJ_INPUT; + BEGIN INOBJ; return BEGINOBJ; } +"module"[ \n\t]*"(" { yylval.objType = CNFOBJ_MODULE; + BEGIN INOBJ; return BEGINOBJ; } +"action"[ \n\t]*"(" { BEGIN INOBJ; return BEGIN_ACTION; } +^[ \t]*:\$?[a-z]+[ ]*,[ ]*!?[a-z]+[ ]*,[ ]*\".*\" { + yylval.s = strdup(yytext); return PROPFILT; } +^[ \t]*[,\*a-z]+\.[,!=;\.\*a-z]+ { yylval.s = strdup(yytext); return PRIFILT; } +"*" | +\-\/[^*][^\n]* | +\/[^*][^\n]* | +:[a-z0-9]+:[^\n]* | +[\|\.\-\@~][^\n]+ | +[a-z0-9_][a-z0-9_\-\+]* { yylval.s = strdup(yytext); + // printf("lex: LEGA ACT: '%s'\n", yytext); + return LEGACY_ACTION; } +")" { BEGIN INITIAL; return ENDOBJ; } +[a-z][a-z0-9_\.]* { yylval.estr = es_newStrFromCStr(yytext, yyleng); + return NAME; } +"=" { return(yytext[0]); } +\"([^"\\]|\\['"?\\abfnrtv]|\\[0-7]{1,3})*\" { + yylval.estr = es_newStrFromBuf(yytext+1, yyleng-2); + return VALUE; } +"/*" { preCommentState = YY_START; BEGIN COMMENT; } +"/*" { preCommentState = YY_START; BEGIN COMMENT; } +"*/" { BEGIN preCommentState; } +([^*]|\n)+|. +#.*$ /* skip comments in input */ +[ \n\t] +. { printf("INOBJ: invalid char '%s'\n", yytext); } +\$[a-z]+.*$ { /* see common on $IncludeConfig above */ + if(!strncasecmp(yytext, "$includeconfig ", 14)) { + yyless(14); + BEGIN INCL; + } else { + yylval.s = strdup(yytext); + return CFSYSLINE; + } + } +![^ \t\n]+[ \t]*$ { yylval.s = strdup(yytext); return BSD_TAG_SELECTOR; } +[+-]\*[ \t\n]*#.*$ { yylval.s = strdup(yytext); return BSD_HOST_SELECTOR; } +[+-]\*[ \t\n]*$ { yylval.s = strdup(yytext); return BSD_HOST_SELECTOR; } +^[ \t]*[+-][a-z0-9.:-]+[ \t]*$ { yylval.s = strdup(yytext); return BSD_HOST_SELECTOR; } +\#.*\n /* skip comments in input */ +[\n\t ] /* drop whitespace */ +. { printf("invalid char: %s\n", yytext); + } +<> { if(popfile() != 0) yyterminate(); } + +%% +/* set a new buffers. Returns 0 on success, something else otherwise. */ +int +cnfSetLexFile(char *fname) +{ + es_str_t *str = NULL; + FILE *fp; + int r = 0; + struct bufstack *bs; + + if(fname == NULL) { + fp = stdin; + } else { + if((fp = fopen(fname, "r")) == NULL) { + r = 1; + goto done; + } + } + readConfFile(fp, &str); + if(fp != stdin) + fclose(fp); + + /* maintain stack */ + if((bs = malloc(sizeof(struct bufstack))) == NULL) { + r = 1; + goto done; + } + + if(currbs != NULL) + currbs->lineno = yylineno; + bs->prev = currbs; + bs->fn = strdup(fname); + bs->bs = yy_scan_buffer((char*)es_getBufAddr(str), es_strlen(str)); + currbs = bs; + currfn = bs->fn; + yylineno = 1; + +done: + if(r != 0) { + if(str != NULL) + es_deleteStr(str); + } + return r; +} + + +/* returns 0 on success, something else otherwise */ +int +popfile(void) +{ + struct bufstack *bs = currbs; + + if(bs == NULL) + return 1; + + /* delte current entry */ + yy_delete_buffer(bs->bs); + free(bs->fn); + + /* switch back to previous */ + currbs = bs->prev; + free(bs); + + if(currbs == NULL) + return 1; /* all processed */ + + yy_switch_to_buffer(currbs->bs); + yylineno = currbs->lineno; + currfn = currbs->fn; + return 0; +} diff --git a/grammar/rscript.l b/grammar/rscript.l deleted file mode 100644 index a7410b15..00000000 --- a/grammar/rscript.l +++ /dev/null @@ -1,233 +0,0 @@ - /* Lex file for rsyslog config format v2 (RainerScript). - * Please note: this file introduces the new config format, but maintains - * backward compatibility. In order to do so, the grammar is not 100% clean, - * but IMHO still sufficiently easy both to understand for programmers - * maitaining the code as well as users writing the config file. Users are, - * of course, encouraged to use new constructs only. But it needs to be noted - * that some of the legacy constructs (specifically the in-front-of-action - * PRI filter) are very hard to beat in ease of use, at least for simpler - * cases. So while we hope that cfsysline support can be dropped some time in - * the future, we will probably keep these useful constructs. - * - * Copyright (C) 2011 by Rainer Gerhards and Adiscon GmbH - * Released under the GNU GPL v3. For details see LICENSE file. - */ - -%option noyywrap nodefault case-insensitive yylineno - /*%option noyywrap nodefault case-insensitive */ - -%x INOBJ - /* INOBJ is selected if we are inside an object (name/value pairs!) */ -%x COMMENT - /* COMMENT is "the usual trick" to handle C-style comments */ -%x INCL - /* INCL is in $IncludeConfig processing (skip to include file) */ -%x LINENO - /* LINENO: support for setting the linenumber */ -%x EXPR - /* EXPR is a bit ugly, but we need it to support pre v6-syntax. The problem - * is that cfsysline statement start with $..., the same like variables in - * an expression. However, cfsysline statements can never appear inside an - * expression. So we create a specific expr mode, which is turned on after - * we lexed a keyword that needs to be followed by an expression (using - * knowledge from the upper layer...). In expr mode, we strictly do - * expression-based parsing. Expr mode is stopped when we reach a token - * that can not be part of an expression (currently only "then"). As I - * wrote this ugly, but the price needed to pay in order to remain - * compatible to the previous format. - */ -%{ -#include -#include -#include "utils.h" -#include "rscript.tab.h" -static int preCommentState; /* save for lex state before a comment */ - -struct bufstack { - struct bufstack *prev; - YY_BUFFER_STATE bs; - int lineno; - char *fn; -} *currbs = NULL; - -char *currfn; /* name of currently processed file */ -%} - -%% - - /* keywords */ -"if" { BEGIN EXPR; return IF; } -"then" { BEGIN INITIAL; return THEN; } -"or" { return OR; } -"and" { return AND; } -"not" { return NOT; } -"," | -"*" | -"/" | -"%" | -"+" | -"-" | -"(" | -")" { return yytext[0]; } -"==" { return CMP_EQ; } -"<=" { return CMP_LE; } -">=" { return CMP_GE; } -"!=" | -"<>" { return CMP_NE; } -"<" { return CMP_LT; } -">" { return CMP_GT; } -"contains" { return CMP_CONTAINS; } -"contains_i" { return CMP_CONTAINSI; } -"startswith" { return CMP_STARTSWITH; } -"startswith_i" { return CMP_STARTSWITHI; } -0[0-7]+ | /* octal number */ -0x[0-7a-f] | /* hex number, following rule is dec; strtoll handles all! */ -([1-9][0-9]*|0) { yylval.n = strtoll(yytext, NULL, 0); return NUMBER; } -\$[$!]{0,1}[a-z][a-z0-9\-_\.]* { yylval.s = strdup(yytext); return VAR; } -\'([^'\\]|\\['])*\' { yylval.estr = es_newStrFromBuf(yytext+1, yyleng-2); - return STRING; } -\"([^"\\]|\\["])*\" { yylval.estr = es_newStrFromBuf(yytext+1, yyleng-2); - return STRING; } -[ \t\n] -[a-z][a-z0-9_]* { yylval.estr = es_newStrFromCStr(yytext, yyleng); - return FUNC; } -. { printf("invalid char in expr: %s\n", yytext); } -"&" { return '&'; } -"{" { return '{'; } -"}" { return '}'; } -"ruleset" { printf("RULESET\n"); } - /* line number support because the "preprocessor" combines lines and so needs - * to tell us the real source line. - */ -"preprocfilelinenumber(" { BEGIN LINENO; } -[0-9]+ { yylineno = atoi(yytext) - 1; } -")" { BEGIN INITIAL; } -.|\n - /* $IncludeConfig must be detected as part of CFSYSLINE, because this is - * always the longest match :-( - */ -.|\n -[^ \t\n]+ { if(cnfSetLexFile(yytext) != 0) - yyterminate(); - BEGIN INITIAL; } -"global"[ \n\t]*"(" { yylval.objType = CNFOBJ_GLOBAL; - BEGIN INOBJ; return BEGINOBJ; } -"input"[ \n\t]*"(" { yylval.objType = CNFOBJ_INPUT; - BEGIN INOBJ; return BEGINOBJ; } -"module"[ \n\t]*"(" { yylval.objType = CNFOBJ_MODULE; - BEGIN INOBJ; return BEGINOBJ; } -"action"[ \n\t]*"(" { BEGIN INOBJ; return BEGIN_ACTION; } -^[ \t]*:\$?[a-z]+[ ]*,[ ]*!?[a-z]+[ ]*,[ ]*\".*\" { - yylval.s = strdup(yytext); return PROPFILT; } -^[ \t]*[,\*a-z]+\.[,!=;\.\*a-z]+ { yylval.s = strdup(yytext); return PRIFILT; } -"*" | -\-\/[^*][^\n]* | -\/[^*][^\n]* | -:[a-z0-9]+:[^\n]* | -[\|\.\-\@~][^\n]+ | -[a-z0-9_][a-z0-9_\-\+]* { yylval.s = strdup(yytext); - // printf("lex: LEGA ACT: '%s'\n", yytext); - return LEGACY_ACTION; } -")" { BEGIN INITIAL; return ENDOBJ; } -[a-z][a-z0-9_\.]* { yylval.estr = es_newStrFromCStr(yytext, yyleng); - return NAME; } -"=" { return(yytext[0]); } -\"([^"\\]|\\['"?\\abfnrtv]|\\[0-7]{1,3})*\" { - yylval.estr = es_newStrFromBuf(yytext+1, yyleng-2); - return VALUE; } -"/*" { preCommentState = YY_START; BEGIN COMMENT; } -"/*" { preCommentState = YY_START; BEGIN COMMENT; } -"*/" { BEGIN preCommentState; } -([^*]|\n)+|. -#.*$ /* skip comments in input */ -[ \n\t] -. { printf("INOBJ: invalid char '%s'\n", yytext); } -\$[a-z]+.*$ { /* see common on $IncludeConfig above */ - if(!strncasecmp(yytext, "$includeconfig ", 14)) { - yyless(14); - BEGIN INCL; - } else { - yylval.s = strdup(yytext); - return CFSYSLINE; - } - } -![^ \t\n]+[ \t]*$ { yylval.s = strdup(yytext); return BSD_TAG_SELECTOR; } -[+-]\*[ \t\n]*#.*$ { yylval.s = strdup(yytext); return BSD_HOST_SELECTOR; } -[+-]\*[ \t\n]*$ { yylval.s = strdup(yytext); return BSD_HOST_SELECTOR; } -^[ \t]*[+-][a-z0-9.:-]+[ \t]*$ { yylval.s = strdup(yytext); return BSD_HOST_SELECTOR; } -\#.*\n /* skip comments in input */ -[\n\t ] /* drop whitespace */ -. { printf("invalid char: %s\n", yytext); - } -<> { if(popfile() != 0) yyterminate(); } - -%% -/* set a new buffers. Returns 0 on success, something else otherwise. */ -int cnfSetLexFile(char *fname) -{ - es_str_t *str = NULL; - FILE *fp; - int r = 0; - struct bufstack *bs; - - if(fname == NULL) { - fp = stdin; - } else { - if((fp = fopen(fname, "r")) == NULL) { - r = 1; - goto done; - } - } - readConfFile(fp, &str); - if(fp != stdin) - fclose(fp); - - /* maintain stack */ - if((bs = malloc(sizeof(struct bufstack))) == NULL) { - r = 1; - goto done; - } - - if(currbs != NULL) - currbs->lineno = yylineno; - bs->prev = currbs; - bs->fn = strdup(fname); - bs->bs = yy_scan_buffer(es_getBufAddr(str), es_strlen(str)); - currbs = bs; - currfn = bs->fn; - yylineno = 1; - -done: - if(r != 0) { - if(str != NULL) - es_deleteStr(str); - } - return r; -} - - -/* returns 0 on success, something else otherwise */ -int -popfile(void) -{ - struct bufstack *bs = currbs; - - if(bs == NULL) - return 1; - - /* delte current entry */ - yy_delete_buffer(bs->bs); - free(bs->fn); - - /* switch back to previous */ - currbs = bs->prev; - free(bs); - - if(currbs == NULL) - return 1; /* all processed */ - - yy_switch_to_buffer(currbs->bs); - yylineno = currbs->lineno; - currfn = currbs->fn; - return 0; -} diff --git a/grammar/utils.c b/grammar/utils.c index 16cad201..e488ebda 100644 --- a/grammar/utils.c +++ b/grammar/utils.c @@ -389,7 +389,7 @@ cnfexprEval(struct cnfexpr *expr, struct exprret *ret) } inline static void -doIndent(indent) +doIndent(int indent) { int i; for(i = 0 ; i < indent ; ++i) @@ -611,6 +611,7 @@ cnffuncNew(es_str_t *fname, struct cnffparamlst* paramlst) } /* debug helper */ +#ifdef STAND_ALONE void cstrPrint(char *text, es_str_t *estr) { @@ -632,4 +633,5 @@ main(int argc, char *argv[]) printf("yyparse() returned %d\n", r); return r; } +#endif /* #ifdef STAND_ALONE */ diff --git a/tools/Makefile.am b/tools/Makefile.am index 5c3f7a40..f3b176f2 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -35,7 +35,7 @@ rsyslogd_SOURCES = \ pidfile.h \ \ ../dirty.h -rsyslogd_CPPFLAGS = $(PTHREADS_CFLAGS) $(RSRT_CFLAGS) +rsyslogd_CPPFLAGS = $(PTHREADS_CFLAGS) $(RSRT_CFLAGS) $(CNF_LIBS) rsyslogd_LDADD = $(ZLIB_LIBS) $(PTHREADS_LIBS) $(RSRT_LIBS) $(SOL_LIBS) $(LIBEE_LIBS) $(LIBLOGNORM_LIBS) rsyslogd_LDFLAGS = -export-dynamic -- cgit v1.2.3