From 58d50e94455c52385595146e9fa19563b162e912 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 26 Sep 2005 13:14:03 +0000 Subject: added isequal comparison operation; ability to negat comparison operations fixed some bugs --- NEWS | 9 +++++ doc/manual.html | 7 ++-- doc/status.html | 6 +-- stringbuf.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++---------- stringbuf.h | 3 +- syslogd.c | 72 ++++++++++++++++++++++++--------- version.h | 2 +- 7 files changed, 172 insertions(+), 48 deletions(-) diff --git a/NEWS b/NEWS index 7d558f74..7e0bd578 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,13 @@ --------------------------------------------------------------------------- +Version 1.10.2 (RGer), 2005-09-2? +- added the "isequal" comparison operation in property-based filters +- added ability to negate all property-based filter comparison oprations + by adding a !-sign right in front of the operation name +- fixed a bug that caused rsyslogd to dump core when the compare value + was not quoted in property-based filters +- fixed a bug in the new CStr compare function which lead to invalid + results (fortunately, this function was not yet used widely) +--------------------------------------------------------------------------- Version 1.10.1 (RGer), 2005-09-23 - added the ability to execute a shell script as an action. Thanks to Bjoern Kalkbrenner for providing the code! diff --git a/doc/manual.html b/doc/manual.html index d1bcfd57..5025c8e8 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -5,8 +5,9 @@

RSyslog - Documentation

Rsyslog is an enhanced syslogd -supporting, among others, MySQL, syslog/tcp -and fine grain output format control. It is quite compatible to stock +supporting, among others, MySQL, syslog/tcp, +fine grain output format control, and the ability to filter on any message part. +It is quite compatible to stock sysklogd and can be used as a drop-in replacement. Its advanced features make it suitable for enterprise-class, encryption protected syslog @@ -50,4 +51,4 @@ If you are interested in the "backstage", you may find Rainer's syslog blog an interesting read.

- \ No newline at end of file + diff --git a/doc/status.html b/doc/status.html index 9d12e4d1..245e1f5f 100644 --- a/doc/status.html +++ b/doc/status.html @@ -4,10 +4,10 @@

rsyslog status page

-

This page reflects the status as of 2005-09-20.

+

This page reflects the status as of 2005-09-27.

Current Releases

-

development: 1.10.1 - change log - -download

+

development: 1.10.2 - change log - +download

stable: 1.0.1 - change log - download

 (How are versions named?)

diff --git a/stringbuf.c b/stringbuf.c index 85763f27..1f6d6316 100755 --- a/stringbuf.c +++ b/stringbuf.c @@ -343,6 +343,9 @@ rsRetVal rsCStrTrimTrailingWhiteSpace(rsCStrObj *pThis) * faster in the majority of cases, simply because it can * rely on StrLen. * rgerhards 2005-09-19 + * fixed bug, in which only the last byte was actually compared + * in equal-size strings. + * rgerhards, 2005-09-26 */ int rsCStrCStrCmp(rsCStrObj *pCS1, rsCStrObj *pCS2) { @@ -351,37 +354,113 @@ int rsCStrCStrCmp(rsCStrObj *pCS1, rsCStrObj *pCS2) if(pCS1->iStrLen == pCS2->iStrLen) if(pCS1->iStrLen == 0) return 0; /* zero-sized string are equal ;) */ - else - return pCS1->pBuf[pCS1->iStrLen - 1] - - pCS2->pBuf[pCS2->iStrLen - 1]; + else { /* we now have two non-empty strings of equal + * length, so we need to actually check if they + * are equal. + */ + register int i; + for(i = 0 ; i < pCS1->iStrLen ; ++i) { + if(pCS1->pBuf[i] != pCS2->pBuf[i]) + return pCS1->pBuf[i] - pCS2->pBuf[i]; + } + /* if we arrive here, the strings are equal */ + return 0; + } else return pCS1->iStrLen - pCS2->iStrLen; } -/* compare a string object with a classical zero-terminated C-string. - * this function is primarily meant to support comparisons with constants. - * It should not be used for variables (except with a very good reason). - * rgerhards 2005-09-19 + +/* compare a rsCStr object with a classical sz string. This function + * is almost identical to rsCStrZsStrCmp(), but it also takes an offset + * to the CStr object from where the comparison is to start. + * I have thought quite a while if it really makes sense to more or + * less duplicate the code. After all, if you call it with an offset of + * zero, the functionality is exactly the same. So it looks natural to + * just have a single function. However, supporting the offset requires + * some (few) additional integer operations. While they are few, they + * happen at places in the code that is run very frequently. All in all, + * I have opted for performance and thus duplicated the code. I hope + * this is a good, or at least acceptable, compromise. + * rgerhards, 2005-09-26 + * This function also has an offset-pointer which allows to + * specify *where* the compare operation should begin in + * the CStr. If everything is to be compared, it must be set + * to 0. If some leading bytes are to be skipped, it must be set + * to the first index that is to be compared. It must not be + * set higher than the string length (this is considered a + * program bug and will lead to unpredictable results and program aborts). + * rgerhards 2005-09-26 */ -int rsCStrSzCmp(rsCStrObj *pCStr, char *sz) +int rsCStrOffsetSzStrCmp(rsCStrObj *pCS1, int iOffset, char *psz, int iLenSz) { - int iszLen; - - rsCHECKVALIDOBJECT(pCStr, OIDrsCStr); - assert(sz != NULL); + rsCHECKVALIDOBJECT(pCS1, OIDrsCStr); + assert(iOffset >= 0); + assert(iOffset < pCS1->iStrLen); + assert(psz != NULL); + assert(iLenSz == strlen(psz)); /* just make sure during debugging! */ + if((pCS1->iStrLen - iOffset) == iLenSz) { + /* we are using iLenSz below, because the lengths + * are equal and iLenSz is faster to access + */ + if(iLenSz == 0) + return 0; /* zero-sized strings are equal ;) */ + else { /* we now have two non-empty strings of equal + * length, so we need to actually check if they + * are equal. + */ + register int i; + for(i = 0 ; i < iLenSz ; ++i) { + if(pCS1->pBuf[i+iOffset] != psz[i]) + return pCS1->pBuf[i+iOffset] - psz[i]; + } + /* if we arrive here, the strings are equal */ + return 0; + } + } + else + return pCS1->iStrLen - iOffset - iLenSz; +} - iszLen = strlen(sz); - if(pCStr->iStrLen == iszLen) - /* note: we are using iszLen below, because it doesn't matter - * and the simple integer is faster to derefence... +/* compare a rsCStr object with a classical sz string. + * Just like rsCStrCStrCmp, just for a different data type. + * There must not only the sz string but also its length be + * provided. If the caller does not know the length he can + * call with + * rsCstrSzStrCmp(pCS, psz, strlen(psz)); + * we are not doing the strlen() ourselfs as the caller might + * already know the length and in such cases we can save the + * overhead of doing it one more time (strelen() is costly!). + * The bottom line is that the provided length MUST be correct! + * The to sz string pointer must not be NULL! + * rgerhards 2005-09-26 + */ +int rsCStrSzStrCmp(rsCStrObj *pCS1, char *psz, int iLenSz) +{ + rsCHECKVALIDOBJECT(pCS1, OIDrsCStr); + assert(psz != NULL); + assert(iLenSz == strlen(psz)); /* just make sure during debugging! */ + if(pCS1->iStrLen == iLenSz) + /* we are using iLenSz below, because the lengths + * are equal and iLenSz is faster to access */ - if(iszLen == 0) - return 0; /* zero-sized string are equal ;) */ - else - return pCStr->pBuf[iszLen - 1] - sz[iszLen - 1]; + if(iLenSz == 0) + return 0; /* zero-sized strings are equal ;) */ + else { /* we now have two non-empty strings of equal + * length, so we need to actually check if they + * are equal. + */ + register int i; + for(i = 0 ; i < iLenSz ; ++i) { + if(pCS1->pBuf[i] != psz[i]) + return pCS1->pBuf[i] - psz[i]; + } + /* if we arrive here, the strings are equal */ + return 0; + } else - return pCStr->iStrLen - iszLen; + return pCS1->iStrLen - iLenSz; } diff --git a/stringbuf.h b/stringbuf.h index 053f10c7..a5e99392 100755 --- a/stringbuf.h +++ b/stringbuf.h @@ -103,7 +103,8 @@ rsRetVal rsCStrAppendInt(rsCStrObj *pThis, int i); char* rsCStrGetSzStr(rsCStrObj *pThis); char* rsCStrConvSzStrAndDestruct(rsCStrObj *pThis); int rsCStrCStrCmp(rsCStrObj *pCS1, rsCStrObj *pCS2); -int rsCStrSzCmp(rsCStrObj *pCStr, char *sz); +int rsCStrSzStrCmp(rsCStrObj *pCS1, char *psz, int iLenSz); +int rsCStrOffsetSzStrCmp(rsCStrObj *pCS1, int iOffset, char *psz, int iLenSz); int rsCStrLocateSzStr(rsCStrObj *pCStr, char *sz); int rsCStrLocateInSzStr(rsCStrObj *pThis, char *sz); diff --git a/syslogd.c b/syslogd.c index cec310f4..02cd5b54 100644 --- a/syslogd.c +++ b/syslogd.c @@ -487,8 +487,10 @@ struct filed { enum { FIOP_NOP = 0, /* do not use - No Operation */ FIOP_CONTAINS = 1, /* contains string? */ + FIOP_ISEQUAL = 2, /* is (exactly) equal? */ } operation; rsCStrObj *pCSCompValue; /* value to "compare" against */ + char isNegated; /* actually a boolean ;) */ } prop; } f_filterData; union { @@ -2799,7 +2801,7 @@ int main(argc, argv) logerror("recvfrom inet"); /* should be harmless now that we set * BSDCOMPAT on the socket */ - sleep(10); + sleep(1); } } @@ -3320,11 +3322,19 @@ int shouldProcessThisMessage(struct filed *f, struct msg *pMsg) if(f->f_filterData.prop.operation == FIOP_CONTAINS) { if(rsCStrLocateInSzStr(f->f_filterData.prop.pCSCompValue, pszPropVal) != -1) iRet = 1; + } else if(f->f_filterData.prop.operation == FIOP_ISEQUAL) { + if(rsCStrSzStrCmp(f->f_filterData.prop.pCSCompValue, + pszPropVal, strlen(pszPropVal)) == 0) + iRet = 1; /* process message! */ } else { /* here, it handles NOP (for performance reasons) */ assert(f->f_filterData.prop.operation == FIOP_NOP); iRet = 1; /* as good as any other default ;) */ } + /* now check if the value must be negated */ + if(f->f_filterData.prop.isNegated) + iRet = (iRet == 1) ? 0 : 1; + /* cleanup */ if(pbMustBeFreed) free(pszPropVal); @@ -3524,6 +3534,14 @@ void logmsg(int pri, struct msg *pMsg, int flags) } for (f = Files; f; f = f->f_next) { + /* first, we need to check if this is a disabled (F_UNUSED) + * entry. If so, we must not further process it, as the data + * structure probably contains invalid pointers and other + * such mess. + * rgerhards 2005-09-26 + */ + if(f->f_type == F_UNUSED) + continue; /* on to next */ /* This is actually the "filter logic". Looks like we need * to improve it a little for complex selector line conditions. We @@ -3534,8 +3552,10 @@ void logmsg(int pri, struct msg *pMsg, int flags) * of this, I am moving the actual decision code to outside this function. * 2005-09-19 rgerhards */ - if(!shouldProcessThisMessage(f, pMsg)) + if(!shouldProcessThisMessage(f, pMsg)) { + dprintf("message filter does not match - ignore this selector line\n"); continue; + } /* We now need to check a special case - F_DISCARD. If that * action is specified in the selector line, no futher processing @@ -3854,20 +3874,6 @@ void iovCreate(struct filed *f) } } #endif /* debug aid */ -#if 0 - /* almost done - just let's check how we need to terminate - * the message. - */ - dprintf(" %s\n", f->f_un.f_fname); - if (f->f_type == F_TTY || f->f_type == F_CONSOLE) { - v->iov_base = "\r\n"; - v->iov_len = 2; - } else { - v->iov_base = "\n"; - v->iov_len = 1; - } - -#endif return; } @@ -4148,7 +4154,7 @@ void fprintlog(register struct filed *f, int flags) case F_TTY: case F_FILE: case F_PIPE: - dprintf("\n"); + dprintf("(%s)\n", f->f_un.f_fname); /* TODO: check if we need f->f_time = now;*/ /* f->f_file == -1 is an indicator that the we couldn't open the file at startup. */ @@ -4959,10 +4965,14 @@ void init() printf("\tProperty.: '%s'\n", rsCStrGetSzStr(f->f_filterData.prop.pCSPropName)); printf("\tOperation: "); + if(f->f_filterData.prop.isNegated) + printf("NOT "); if(f->f_filterData.prop.operation == FIOP_NOP) printf("'NOP'"); else if(f->f_filterData.prop.operation == FIOP_CONTAINS) printf("'contains'"); + else if(f->f_filterData.prop.operation == FIOP_ISEQUAL) + printf("'isequal'"); else printf("'ERROR - invalid filter type!'"); printf("\n"); @@ -5410,6 +5420,7 @@ rsRetVal cflineProcessPropFilter(char **pline, register struct filed *f) rsCStrObj *pCSLine; rsCStrObj *pCSCompOp; rsRetVal iRet; + int iOffset; /* for compare operations */ assert(pline != NULL); assert(*pline != NULL); @@ -5457,8 +5468,29 @@ rsRetVal cflineProcessPropFilter(char **pline, register struct filed *f) RSFREEOBJ(pCSLine); return(iRet); } - if(!rsCStrSzCmp(pCSCompOp, "contains")) { + + /* we now first check if the condition is to be negated. To do so, we first + * must make sure we have at least one char in the param and then check the + * first one. + * rgerhards, 2005-09-26 + */ + if(rsCStrLen(pCSCompOp) > 0) { + if(*rsCStrGetBufBeg(pCSCompOp) == '!') { + f->f_filterData.prop.isNegated = 1; + iOffset = 1; /* ignore '!' */ + } else { + f->f_filterData.prop.isNegated = 0; + iOffset = 0; + } + } else { + f->f_filterData.prop.isNegated = 0; + iOffset = 0; + } + + if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, "contains", 8)) { f->f_filterData.prop.operation = FIOP_CONTAINS; + } else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, "isequal", 7)) { + f->f_filterData.prop.operation = FIOP_ISEQUAL; } else { logerrorSz("error: invalid compare operation '%s' - ignoring selector", rsCStrGetSzStr(pCSCompOp)); @@ -5534,8 +5566,10 @@ rsRetVal cfline(char *line, register struct filed *f) } /* check if that went well... */ - if(iRet != RS_RET_OK) + if(iRet != RS_RET_OK) { + f->f_type = F_UNUSED; return iRet; + } if (*p == '-') { diff --git a/version.h b/version.h index 2cbe0878..b7a684d7 100644 --- a/version.h +++ b/version.h @@ -1,2 +1,2 @@ #define VERSION "1.10" -#define PATCHLEVEL "1" +#define PATCHLEVEL "2" -- cgit