summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRainer Gerhards <rgerhards@adiscon.com>2012-09-20 11:26:24 +0200
committerRainer Gerhards <rgerhards@adiscon.com>2012-09-20 11:26:24 +0200
commita5dcf8cdb7db791d3f43e8cc4f793380739807c8 (patch)
treee8dbe37ad6c2dcc8d44b5bc07bdce9da0a57a082
parentdc40e3b43fa92d06ba03c4bb434f885db0ed4352 (diff)
downloadrsyslog-a5dcf8cdb7db791d3f43e8cc4f793380739807c8.tar.gz
rsyslog-a5dcf8cdb7db791d3f43e8cc4f793380739807c8.tar.xz
rsyslog-a5dcf8cdb7db791d3f43e8cc4f793380739807c8.zip
Implement RainerScript field() function
-rw-r--r--ChangeLog3
-rw-r--r--doc/rainerscript.html5
-rw-r--r--grammar/rainerscript.c112
-rw-r--r--grammar/rainerscript.h3
-rw-r--r--runtime/rsyslog.h1
-rw-r--r--tests/Makefile.am3
-rwxr-xr-xtests/rscript_field.sh13
-rw-r--r--tests/testsuites/rscript_field.conf11
8 files changed, 143 insertions, 8 deletions
diff --git a/ChangeLog b/ChangeLog
index 0d1e0e61..cdf4c046 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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")
+}