diff options
author | Rainer Gerhards <rgerhards@adiscon.com> | 2012-09-20 11:26:24 +0200 |
---|---|---|
committer | Rainer Gerhards <rgerhards@adiscon.com> | 2012-09-20 11:26:24 +0200 |
commit | a5dcf8cdb7db791d3f43e8cc4f793380739807c8 (patch) | |
tree | e8dbe37ad6c2dcc8d44b5bc07bdce9da0a57a082 | |
parent | dc40e3b43fa92d06ba03c4bb434f885db0ed4352 (diff) | |
download | rsyslog-a5dcf8cdb7db791d3f43e8cc4f793380739807c8.tar.gz rsyslog-a5dcf8cdb7db791d3f43e8cc4f793380739807c8.tar.xz rsyslog-a5dcf8cdb7db791d3f43e8cc4f793380739807c8.zip |
Implement RainerScript field() function
-rw-r--r-- | ChangeLog | 3 | ||||
-rw-r--r-- | doc/rainerscript.html | 5 | ||||
-rw-r--r-- | grammar/rainerscript.c | 112 | ||||
-rw-r--r-- | grammar/rainerscript.h | 3 | ||||
-rw-r--r-- | runtime/rsyslog.h | 1 | ||||
-rw-r--r-- | tests/Makefile.am | 3 | ||||
-rwxr-xr-x | tests/rscript_field.sh | 13 | ||||
-rw-r--r-- | tests/testsuites/rscript_field.conf | 11 |
8 files changed, 143 insertions, 8 deletions
@@ -1,4 +1,7 @@ --------------------------------------------------------------------------- +Version 7.1.5 [devel] 2012-09-?? +- implemented RainerScript field() function +--------------------------------------------------------------------------- Version 7.1.4 [devel] 2012-09-19 - implemented ability for CEE-based properties to be stored in disk queues - implemented string concatenation in expressions via &-operator diff --git a/doc/rainerscript.html b/doc/rainerscript.html index fcc2674d..df31db2a 100644 --- a/doc/rainerscript.html +++ b/doc/rainerscript.html @@ -61,6 +61,11 @@ variable, if it exists. Returns an empty string if it does not exist. <li>cstr(expr) - converts expr to a string value <li>cnum(expr) - converts expr to a number (integer) <li>re_match(expr, re) - returns 1, if expr matches re, 0 otherwise +<li>field(str, delim, matchnbr) - returns a field-based substring. str is the string +to search, delim is the numerical ascii value of the field delimiter (so that +non-printable characters can by specified) and matchnbr is the match to search +for (the first match starts at 1). This works similar as the field based +property-replacer option. </ul> <p>The following example can be used to build a dynamic filter based on some environment variable: diff --git a/grammar/rainerscript.c b/grammar/rainerscript.c index e7299e62..9eeda64e 100644 --- a/grammar/rainerscript.c +++ b/grammar/rainerscript.c @@ -744,11 +744,13 @@ var2Number(struct var *r, int *bSuccess) long long n; if(r->datatype == 'S') { n = es_str2num(r->d.estr, bSuccess); - } else if(r->datatype == 'J') { - n = (r->d.json == NULL) ? 0 : json_object_get_int(r->d.json); } else { - n = r->d.n; - if(bSuccess) + if(r->datatype == 'J') { + n = (r->d.json == NULL) ? 0 : json_object_get_int(r->d.json); + } else { + n = r->d.n; + } + if(bSuccess != NULL) *bSuccess = 1; } return n; @@ -782,6 +784,67 @@ var2String(struct var *r, int *bMustFree) return estr; } +static uchar* +var2CString(struct var *r, int *bMustFree) +{ + uchar *cstr; + es_str_t *estr; + estr = var2String(r, bMustFree); + cstr = (uchar*) es_str2cstr(estr, NULL); + if(*bMustFree) + es_deleteStr(estr); + *bMustFree = 1; + return cstr; +} + +rsRetVal +doExtractField(uchar *str, uchar delim, int matchnbr, uchar **resstr) +{ + int iCurrFld; + int iLen; + uchar *pBuf; + uchar *pFld; + uchar *pFldEnd; + DEFiRet; + + /* first, skip to the field in question */ + iCurrFld = 1; + pFld = str; + while(*pFld && iCurrFld < matchnbr) { + /* skip fields until the requested field or end of string is found */ + while(*pFld && (uchar) *pFld != delim) + ++pFld; /* skip to field terminator */ + if(*pFld == delim) { + ++pFld; /* eat it */ + ++iCurrFld; + } + } + dbgprintf("field() field requested %d, field found %d\n", matchnbr, iCurrFld); + + if(iCurrFld == matchnbr) { + /* field found, now extract it */ + /* first of all, we need to find the end */ + pFldEnd = pFld; + while(*pFldEnd && *pFldEnd != delim) + ++pFldEnd; + --pFldEnd; /* we are already at the delimiter - so we need to + * step back a little not to copy it as part of the field. */ + /* we got our end pointer, now do the copy */ + iLen = pFldEnd - pFld + 1; /* the +1 is for an actual char, NOT \0! */ + CHKmalloc(pBuf = MALLOC((iLen + 1) * sizeof(char))); + /* now copy */ + memcpy(pBuf, pFld, iLen); + pBuf[iLen] = '\0'; /* terminate it */ + if(*(pFldEnd+1) != '\0') + ++pFldEnd; /* OK, skip again over delimiter char */ + *resstr = pBuf; + } else { + ABORT_FINALIZE(RS_RET_FIELD_NOT_FOUND); + } +finalize_it: + RETiRet; +} + /* Perform a function call. This has been moved out of cnfExprEval in order * to keep the code small and easier to maintain. */ @@ -793,8 +856,12 @@ doFuncCall(struct cnffunc *func, struct var *ret, void* usrptr) int bMustFree; es_str_t *estr; char *str; + uchar *resStr; int retval; struct var r[CNFFUNC_MAX_ARGS]; + int delim; + int matchnbr; + rsRetVal localRet; dbgprintf("rainerscript: executing function id %d\n", func->fID); switch(func->fID) { @@ -862,8 +929,7 @@ doFuncCall(struct cnffunc *func, struct var *ret, void* usrptr) break; case CNFFUNC_RE_MATCH: cnfexprEval(func->expr[0], &r[0], usrptr); - estr = var2String(&r[0], &bMustFree); - str = es_str2cstr(estr, NULL); + str = (char*) var2CString(&r[0], &bMustFree); retval = regexp.regexec(func->funcdata, str, 0, NULL, 0); if(retval == 0) ret->d.n = 1; @@ -874,10 +940,35 @@ doFuncCall(struct cnffunc *func, struct var *ret, void* usrptr) } } ret->datatype = 'N'; - if(bMustFree) es_deleteStr(estr); + if(bMustFree) free(str); free(str); if(r[0].datatype == 'S') es_deleteStr(r[0].d.estr); break; + case CNFFUNC_FIELD: + cnfexprEval(func->expr[0], &r[0], usrptr); + cnfexprEval(func->expr[1], &r[1], usrptr); + cnfexprEval(func->expr[2], &r[2], usrptr); + str = (char*) var2CString(&r[0], &bMustFree); + delim = var2Number(&r[1], NULL); + matchnbr = var2Number(&r[2], NULL); + localRet = doExtractField((uchar*)str, (char) delim, matchnbr, &resStr); +dbgprintf("RRRR: field() returns %d, str: '%s'\n", localRet, resStr); + if(localRet == RS_RET_OK) { + ret->d.estr = es_newStrFromCStr((char*)resStr, strlen((char*)resStr)); + free(resStr); + } else if(localRet == RS_RET_OK) { + ret->d.estr = es_newStrFromCStr("***FIELD NOT FOUND***", + sizeof("***FIELD NOT FOUND***")-1); + } else { + ret->d.estr = es_newStrFromCStr("***ERROR in field() FUNCTION***", + sizeof("***ERROR in field() FUNCTION***")-1); + } + ret->datatype = 'S'; + if(bMustFree) free(str); + if(r[0].datatype == 'S') es_deleteStr(r[0].d.estr); + if(r[1].datatype == 'S') es_deleteStr(r[1].d.estr); + if(r[2].datatype == 'S') es_deleteStr(r[2].d.estr); + break; default: if(Debug) { fname = es_str2cstr(func->fname, NULL); @@ -1805,6 +1896,13 @@ funcName2ID(es_str_t *fname, unsigned short nParams) return CNFFUNC_INVALID; } return CNFFUNC_RE_MATCH; + } else if(!es_strbufcmp(fname, (unsigned char*)"field", sizeof("field") - 1)) { + if(nParams != 3) { + parser_errmsg("number of parameters for field() must be three " + "but is %d.", nParams); + return CNFFUNC_INVALID; + } + return CNFFUNC_FIELD; } else { return CNFFUNC_INVALID; } diff --git a/grammar/rainerscript.h b/grammar/rainerscript.h index f01b1d79..a6693ef0 100644 --- a/grammar/rainerscript.h +++ b/grammar/rainerscript.h @@ -207,7 +207,8 @@ enum cnffuncid { CNFFUNC_TOLOWER, CNFFUNC_CSTR, CNFFUNC_CNUM, - CNFFUNC_RE_MATCH + CNFFUNC_RE_MATCH, + CNFFUNC_FIELD }; struct cnffunc { diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index ceb277d0..fe9f856a 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -392,6 +392,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ + RS_RET_FIELD_NOT_FOUND = 1002, /**< field() function did not find requested field */ /* some generic error/status codes */ RS_RET_OK = 0, /**< operation successful */ diff --git a/tests/Makefile.am b/tests/Makefile.am index f107a87e..fde9666c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -65,6 +65,7 @@ TESTS += \ failover-no-basic.sh \ rcvr_fail_restore.sh \ rscript_contains.sh \ + rscript_field.sh \ cee_simple.sh \ cee_diskqueue.sh \ linkedlistqueue.sh @@ -269,6 +270,8 @@ EXTRA_DIST= 1.rstest 2.rstest 3.rstest err1.rstest \ testsuites/arrayqueue.conf \ rscript_contains.sh \ testsuites/rscript_contains.conf \ + rscript_field.sh \ + testsuites/rscript_field.conf \ cee_simple.sh \ testsuites/cee_simple.conf \ cee_diskqueue.sh \ diff --git a/tests/rscript_field.sh b/tests/rscript_field.sh new file mode 100755 index 00000000..e989e666 --- /dev/null +++ b/tests/rscript_field.sh @@ -0,0 +1,13 @@ +# added 2012-09-20 by rgerhards +# This file is part of the rsyslog project, released under ASL 2.0 +echo =============================================================================== +echo \[rscript_field.sh\]: testing rainerscript field\(\) function +source $srcdir/diag.sh init +source $srcdir/diag.sh startup rscript_field.conf +source $srcdir/diag.sh injectmsg 0 5000 +echo doing shutdown +source $srcdir/diag.sh shutdown-when-empty +echo wait on shutdown +source $srcdir/diag.sh wait-shutdown +source $srcdir/diag.sh seq-check 0 4999 +source $srcdir/diag.sh exit diff --git a/tests/testsuites/rscript_field.conf b/tests/testsuites/rscript_field.conf new file mode 100644 index 00000000..d7eb9066 --- /dev/null +++ b/tests/testsuites/rscript_field.conf @@ -0,0 +1,11 @@ +$IncludeConfig diag-common.conf + +template(name="outfmt" type="list") { + property(name="$!usr!msgnum") + constant(value="\n") +} + +if $msg contains 'msgnum' then { + set $!usr!msgnum = field($msg, 58, 2); + action(type="omfile" file="./rsyslog.out.log" template="outfmt") +} |