From 37fd063042821dc328c88e1f7366543decdcb76f Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 7 Jul 2008 09:41:31 +0200 Subject: added capability to create a printable string of a vmprg This is needed so that we can create simple testbenches which will check the result of a test (a generated program) via a simple strcmp. --- runtime/var.c | 40 ++++++++++++++++++++++++++++++++++++++++ runtime/var.h | 1 + runtime/vmop.c | 37 +++++++++++++++++++++++++++++++++++-- runtime/vmop.h | 2 ++ runtime/vmprg.c | 40 ++++++++++++++++++++++++++++++++++++++-- runtime/vmprg.h | 3 ++- 6 files changed, 118 insertions(+), 5 deletions(-) (limited to 'runtime') diff --git a/runtime/var.c b/runtime/var.c index 7e51fc6d..559bc56c 100644 --- a/runtime/var.c +++ b/runtime/var.c @@ -29,6 +29,7 @@ */ #include "config.h" +#include #include #include @@ -89,6 +90,44 @@ CODESTARTobjDebugPrint(var) ENDobjDebugPrint(var) +/* This function is similar to DebugPrint, but does not send its output to + * the debug log but instead to a caller-provided string. The idea here is that + * we can use this string to get a textual representation of a variable. + * Among others, this is useful for creating testbenches, our first use case for + * it. Here, it enables simple comparison of the resulting program to a + * reference program by simple string compare. + * Note that the caller must initialize the string object. We always add + * data to it. So, it can be easily combined into a chain of methods + * to generate the final string. + * rgerhards, 2008-07-07 + */ +static rsRetVal +Obj2Str(var_t *pThis, cstr_t *pstrPrg) +{ + DEFiRet; + size_t lenBuf; + uchar szBuf[2048]; + + ISOBJ_TYPE_assert(pThis, var); + assert(pstrPrg != NULL); + switch(pThis->varType) { + case VARTYPE_STR: + lenBuf = snprintf((char*) szBuf, sizeof(szBuf), "%s[cstr]", rsCStrGetSzStr(pThis->val.pStr)); + break; + case VARTYPE_NUMBER: + lenBuf = snprintf((char*) szBuf, sizeof(szBuf), "%lld[nbr]", pThis->val.num); + break; + default: + lenBuf = snprintf((char*) szBuf, sizeof(szBuf), "**UNKNOWN**[%d]", pThis->varType); + break; + } + CHKiRet(rsCStrAppendStrWithLen(pstrPrg, szBuf, lenBuf)); + +finalize_it: + RETiRet; +} + + /* duplicates a var instance * rgerhards, 2008-02-25 */ @@ -387,6 +426,7 @@ CODESTARTobjQueryInterface(var) pIf->ConstructFinalize = varConstructFinalize; pIf->Destruct = varDestruct; pIf->DebugPrint = varDebugPrint; + pIf->Obj2Str = Obj2Str; pIf->SetNumber = varSetNumber; pIf->SetString = varSetString; pIf->ConvForOperation = ConvForOperation; diff --git a/runtime/var.h b/runtime/var.h index bbe7ba33..6d890ec9 100644 --- a/runtime/var.h +++ b/runtime/var.h @@ -59,6 +59,7 @@ BEGINinterface(var) /* name must also be changed in ENDinterface macro! */ rsRetVal (*ConvToNumber)(var_t *pThis); rsRetVal (*ConvToBool)(var_t *pThis); rsRetVal (*ConvToString)(var_t *pThis); + rsRetVal (*Obj2Str)(var_t *pThis, cstr_t*); rsRetVal (*Duplicate)(var_t *pThis, var_t **ppNew); ENDinterface(var) #define varCURR_IF_VERSION 1 /* increment whenever you change the interface above! */ diff --git a/runtime/vmop.c b/runtime/vmop.c index 219315c4..fcacb15b 100644 --- a/runtime/vmop.c +++ b/runtime/vmop.c @@ -25,6 +25,7 @@ */ #include "config.h" +#include #include #include @@ -81,6 +82,39 @@ CODESTARTobjDebugPrint(vmop) ENDobjDebugPrint(vmop) +/* This function is similar to DebugPrint, but does not send its output to + * the debug log but instead to a caller-provided string. The idea here is that + * we can use this string to get a textual representation of an operation. + * Among others, this is useful for creating testbenches, our first use case for + * it. Here, it enables simple comparison of the resulting program to a + * reference program by simple string compare. + * Note that the caller must initialize the string object. We always add + * data to it. So, it can be easily combined into a chain of methods + * to generate the final string. + * rgerhards, 2008-07-04 + */ +static rsRetVal +Obj2Str(vmop_t *pThis, cstr_t *pstrPrg) +{ + uchar *pOpcodeName; + uchar szBuf[2048]; + size_t lenBuf; + DEFiRet; + + ISOBJ_TYPE_assert(pThis, vmop); + assert(pstrPrg != NULL); + vmopOpcode2Str(pThis, &pOpcodeName); + lenBuf = snprintf((char*) szBuf, sizeof(szBuf), "%s\t", pOpcodeName); + CHKiRet(rsCStrAppendStrWithLen(pstrPrg, szBuf, lenBuf)); + if(pThis->operand.pVar != NULL) + CHKiRet(var.Obj2Str(pThis->operand.pVar, pstrPrg)); + CHKiRet(rsCStrAppendChar(pstrPrg, '\n')); + +finalize_it: + RETiRet; +} + + /* set operand (variant case) * rgerhards, 2008-02-20 */ @@ -206,8 +240,6 @@ CODESTARTobjQueryInterface(vmop) * work here (if we can support an older interface version - that, * of course, also affects the "if" above). */ - //xxxpIf->oID = OBJvmop; - pIf->Construct = vmopConstruct; pIf->ConstructFinalize = vmopConstructFinalize; pIf->Destruct = vmopDestruct; @@ -215,6 +247,7 @@ CODESTARTobjQueryInterface(vmop) pIf->SetOpcode = vmopSetOpcode; pIf->SetVar = vmopSetVar; pIf->Opcode2Str = vmopOpcode2Str; + pIf->Obj2Str = Obj2Str; finalize_it: ENDobjQueryInterface(vmop) diff --git a/runtime/vmop.h b/runtime/vmop.h index 97f924d7..c3d5d5f4 100644 --- a/runtime/vmop.h +++ b/runtime/vmop.h @@ -26,6 +26,7 @@ #define INCLUDED_VMOP_H #include "ctok_token.h" +#include "stringbuf.h" /* machine instructions types */ typedef enum { /* do NOT start at 0 to detect uninitialized types after calloc() */ @@ -83,6 +84,7 @@ BEGINinterface(vmop) /* name must also be changed in ENDinterface macro! */ rsRetVal (*SetOpcode)(vmop_t *pThis, opcode_t opcode); rsRetVal (*SetVar)(vmop_t *pThis, var_t *pVar); rsRetVal (*Opcode2Str)(vmop_t *pThis, uchar **ppName); + rsRetVal (*Obj2Str)(vmop_t *pThis, cstr_t *pstr); ENDinterface(vmop) #define vmopCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ diff --git a/runtime/vmprg.c b/runtime/vmprg.c index a2b744d7..c7354fe4 100644 --- a/runtime/vmprg.c +++ b/runtime/vmprg.c @@ -24,12 +24,14 @@ */ #include "config.h" +#include #include #include #include "rsyslog.h" #include "obj.h" #include "vmprg.h" +#include "stringbuf.h" /* static data */ DEFobjStaticHelpers @@ -79,6 +81,41 @@ CODESTARTobjDebugPrint(vmprg) ENDobjDebugPrint(vmprg) +/* This function is similar to DebugPrint, but does not send its output to + * the debug log but instead to a caller-provided string. The idea here is that + * we can use this string to get a textual representation of a bytecode program. + * Among others, this is useful for creating testbenches, our first use case for + * it. Here, it enables simple comparison of the resulting program to a + * reference program by simple string compare. + * Note that the caller must initialize the string object. We always add + * data to it. So, it can be easily combined into a chain of methods + * to generate the final string. + * rgerhards, 2008-07-04 + */ +static rsRetVal +Obj2Str(vmprg_t *pThis, cstr_t *pstrPrg) +{ + uchar szAddr[12]; + vmop_t *pOp; + int i; + int lenAddr; + DEFiRet; + + ISOBJ_TYPE_assert(pThis, vmprg); + assert(pstrPrg != NULL); + CHKiRet(rsCStrAppendStr(pstrPrg, (uchar*)"program contents:\n")); + i = 0; /* "program counter" */ + for(pOp = pThis->vmopRoot ; pOp != NULL ; pOp = pOp->pNext) { + lenAddr = snprintf((char*)szAddr, sizeof(szAddr), "%8.8d: ", i++); + CHKiRet(rsCStrAppendStrWithLen(pstrPrg, szAddr, lenAddr)); + vmop.Obj2Str(pOp, pstrPrg); + } + +finalize_it: + RETiRet; +} + + /* add an operation (instruction) to the end of the current program. This * function is expected to be called while creating the program, but never * again after this is done and it is being executed. Results are undefined if @@ -146,12 +183,11 @@ CODESTARTobjQueryInterface(vmprg) * work here (if we can support an older interface version - that, * of course, also affects the "if" above). */ - //xxxpIf->oID = OBJvmprg; - pIf->Construct = vmprgConstruct; pIf->ConstructFinalize = vmprgConstructFinalize; pIf->Destruct = vmprgDestruct; pIf->DebugPrint = vmprgDebugPrint; + pIf->Obj2Str = Obj2Str; pIf->AddOperation = vmprgAddOperation; pIf->AddVarOperation = vmprgAddVarOperation; finalize_it: diff --git a/runtime/vmprg.h b/runtime/vmprg.h index db1f62f0..c1042f7d 100644 --- a/runtime/vmprg.h +++ b/runtime/vmprg.h @@ -38,7 +38,7 @@ #define INCLUDED_VMPRG_H #include "vmop.h" - +#include "stringbuf.h" /* the vmprg object */ typedef struct vmprg_s { @@ -56,6 +56,7 @@ BEGINinterface(vmprg) /* name must also be changed in ENDinterface macro! */ rsRetVal (*Destruct)(vmprg_t **ppThis); rsRetVal (*AddOperation)(vmprg_t *pThis, vmop_t *pOp); rsRetVal (*AddVarOperation)(vmprg_t *pThis, opcode_t opcode, var_t *pVar); + rsRetVal (*Obj2Str)(vmprg_t *pThis, cstr_t *pstr); ENDinterface(vmprg) #define vmprgCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ -- cgit From 493d6a799be82a2df652b666d541a5e004ebcc76 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 7 Jul 2008 12:04:33 +0200 Subject: changed RainerScript testbench to a full-blow test suite it now works based on test case files, which enable to quickly generate test cases. --- runtime/expr.c | 2 +- runtime/vmprg.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/expr.c b/runtime/expr.c index 9a314855..ee5b9e2c 100644 --- a/runtime/expr.c +++ b/runtime/expr.c @@ -114,7 +114,7 @@ terminal(expr_t *pThis, ctok_t *tok) finalize_it: if(pToken != NULL) { - CHKiRet(ctok_token.Destruct(&pToken)); /* "eat" processed token */ + ctok_token.Destruct(&pToken); /* "eat" processed token */ } RETiRet; diff --git a/runtime/vmprg.c b/runtime/vmprg.c index c7354fe4..705e6948 100644 --- a/runtime/vmprg.c +++ b/runtime/vmprg.c @@ -103,7 +103,6 @@ Obj2Str(vmprg_t *pThis, cstr_t *pstrPrg) ISOBJ_TYPE_assert(pThis, vmprg); assert(pstrPrg != NULL); - CHKiRet(rsCStrAppendStr(pstrPrg, (uchar*)"program contents:\n")); i = 0; /* "program counter" */ for(pOp = pThis->vmopRoot ; pOp != NULL ; pOp = pOp->pNext) { lenAddr = snprintf((char*)szAddr, sizeof(szAddr), "%8.8d: ", i++); -- cgit From d2feb7063e73938c05b76862ea2e211cc09b30fe Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 29 Jul 2008 10:07:50 +0200 Subject: enhanced configuration file error reporting and verification - enhanced config file checking - no active actions are detected - added -N rsyslogd command line option for a config validation run (which does not execute actual syslogd code and does not interfere with a running instance) - somewhat improved emergency configuration. It is now also selected if the config contains no active actions - rsyslogd error messages are now reported to stderr by default. can be turned off by the new "$ErrorMessagesToStderr off" directive Thanks to HKS for suggesting these new features. --- runtime/conf.c | 33 +++++++++++++++++++++++++++++++++ runtime/conf.h | 4 +++- runtime/rsyslog.h | 2 ++ 3 files changed, 38 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/conf.c b/runtime/conf.c index 71b2b2da..6a7caa41 100644 --- a/runtime/conf.c +++ b/runtime/conf.c @@ -83,6 +83,8 @@ DEFobjCurrIf(module) DEFobjCurrIf(errmsg) DEFobjCurrIf(net) +static int iNbrActions; /* number of actions the running config has. Needs to be init on ReInitConf() */ + /* The following global variables are used for building * tag and host selector lines during startup and config reload. * This is stored as a global variable pool because of its ease. It is @@ -1060,6 +1062,7 @@ static rsRetVal cflineDoAction(uchar **p, action_t **ppAction) pAction->f_ReduceRepeated = 0; } pAction->bEnabled = 1; /* action is enabled */ + iNbrActions++; /* one more active action! */ } break; } @@ -1159,6 +1162,34 @@ cfline(uchar *line, selector_t **pfCurr) } +/* Reinitialize the configuration subsystem. This is a "work-around" to the fact + * that we do not yet have actual config objects. This method is to be called + * whenever a totally new config is started (which means on startup and HUP). + * Note that it MUST NOT be called for an included config file. + * rgerhards, 2008-07-28 + */ +static rsRetVal +ReInitConf(void) +{ + DEFiRet; + iNbrActions = 0; /* this is what we created the function for ;) - action count is reset */ + RETiRet; +} + + +/* return the current number of active actions + * rgerhards, 2008-07-28 + */ +static rsRetVal +GetNbrActActions(int *piNbrActions) +{ + DEFiRet; + assert(piNbrActions != NULL); + *piNbrActions = iNbrActions; + RETiRet; +} + + /* queryInterface function * rgerhards, 2008-02-29 */ @@ -1179,6 +1210,8 @@ CODESTARTobjQueryInterface(conf) pIf->doIncludeLine = doIncludeLine; pIf->cfline = cfline; pIf->processConfFile = processConfFile; + pIf->ReInitConf = ReInitConf; + pIf->GetNbrActActions = GetNbrActActions; finalize_it: ENDobjQueryInterface(conf) diff --git a/runtime/conf.h b/runtime/conf.h index 31ca27b3..2494d4dc 100644 --- a/runtime/conf.h +++ b/runtime/conf.h @@ -37,8 +37,10 @@ BEGINinterface(conf) /* name must also be changed in ENDinterface macro! */ rsRetVal (*doIncludeLine)(uchar **pp, __attribute__((unused)) void* pVal); rsRetVal (*cfline)(uchar *line, selector_t **pfCurr); rsRetVal (*processConfFile)(uchar *pConfFile); + rsRetVal (*ReInitConf)(void); + rsRetVal (*GetNbrActActions)(int *); ENDinterface(conf) -#define confCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ +#define confCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */ /* prototypes */ diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index 95b2c756..61d81f90 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -123,6 +123,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_TRUE = -3, /**< to indicate a true state (can be used as TRUE, legacy) */ RS_RET_FALSE = -2, /**< to indicate a false state (can be used as FALSE, legacy) */ RS_RET_NO_IRET = -8, /**< This is a trick for the debuging system - it means no iRet is provided */ + RS_RET_VALIDATION_RUN = -9, /**< indicates a (config) validation run, processing not carried out */ RS_RET_ERR = -3000, /**< generic failure */ RS_TRUNCAT_TOO_LARGE = -3001, /**< truncation operation where too many chars should be truncated */ RS_RET_FOUND_AT_STRING_END = -3002, /**< some value found, but at the last pos of string */ @@ -248,6 +249,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_RETRY = -2100, /**< call should be retried (e.g. EGAIN on recv) */ RS_RET_GSS_ERR = -2101, /**< generic error occured in GSSAPI subsystem */ RS_RET_CERTLESS = -2102, /**< state: we run without machine cert (this may be OK) */ + RS_RET_NO_ACTIONS = -2103, /**< no active actions are configured (no output will be created) */ /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ -- cgit From c3c385c63b627d559bdd7a7a710c543e9c16a20a Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 29 Jul 2008 14:55:44 +0200 Subject: added testbed for config errors and fixed a bug - bugfix: no error was reported if the target of a $IncludeConfig could not be accessed. - added testbed for common config errors --- runtime/conf.c | 12 ++++++++++-- runtime/rsyslog.h | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/conf.c b/runtime/conf.c index 6a7caa41..ffe67dbe 100644 --- a/runtime/conf.c +++ b/runtime/conf.c @@ -190,6 +190,7 @@ doIncludeLine(uchar **pp, __attribute__((unused)) void* pVal) char pattern[MAXFNAME]; uchar *cfgFile; glob_t cfgFiles; + int result; size_t i = 0; struct stat fileInfo; @@ -197,14 +198,21 @@ doIncludeLine(uchar **pp, __attribute__((unused)) void* pVal) ASSERT(*pp != NULL); if(getSubString(pp, (char*) pattern, sizeof(pattern) / sizeof(char), ' ') != 0) { - errmsg.LogError(0, RS_RET_NOT_FOUND, "could not extract group name"); + errmsg.LogError(0, RS_RET_NOT_FOUND, "could not parse config file name"); ABORT_FINALIZE(RS_RET_NOT_FOUND); } /* Use GLOB_MARK to append a trailing slash for directories. * Required by doIncludeDirectory(). */ - glob(pattern, GLOB_MARK, NULL, &cfgFiles); + result = glob(pattern, GLOB_MARK, NULL, &cfgFiles); + if(result != 0) { + char errStr[1024]; + rs_strerror_r(errno, errStr, sizeof(errStr)); + errmsg.LogError(0, RS_RET_FILE_NOT_FOUND, "error accessing config file or directory '%s': %s", + pattern, errStr); + ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND); + } for(i = 0; i < cfgFiles.gl_pathc; i++) { cfgFile = (uchar*) cfgFiles.gl_pathv[i]; diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index 61d81f90..f75a5663 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -250,6 +250,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_GSS_ERR = -2101, /**< generic error occured in GSSAPI subsystem */ RS_RET_CERTLESS = -2102, /**< state: we run without machine cert (this may be OK) */ RS_RET_NO_ACTIONS = -2103, /**< no active actions are configured (no output will be created) */ + RS_RET_CONF_FILE_NOT_FOUND = -2104, /**< config file or directory not found */ /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ -- cgit From d7bc0f1f1e81ca0fa678b945962574112df949bc Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 12 Aug 2008 10:46:58 +0200 Subject: some very minor cleanup --- runtime/queue.c | 1 - 1 file changed, 1 deletion(-) (limited to 'runtime') diff --git a/runtime/queue.c b/runtime/queue.c index 00f811a0..7e7d4152 100644 --- a/runtime/queue.c +++ b/runtime/queue.c @@ -2115,7 +2115,6 @@ queueEnqObj(queue_t *pThis, flowControl_t flowCtlType, void *pUsr) if(pThis->bIsDA) CHKiRet(queueChkStrtDA(pThis)); - /* handle flow control * There are two different flow control mechanisms: basic and advanced flow control. * Basic flow control has always been implemented and protects the queue structures -- cgit From 1a9ac0ced72dd163228438af4f31f233fab20529 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 2 Sep 2008 11:38:31 +0200 Subject: removed compile time fixed message size limit (was 2K) The limit can now be set via $MaxMessageSize global config directive (finally gotten rid of MAXLINE ;)) --- runtime/conf.c | 13 +++++++------ runtime/glbl.c | 3 +++ runtime/glbl.h | 1 + 3 files changed, 11 insertions(+), 6 deletions(-) (limited to 'runtime') diff --git a/runtime/conf.c b/runtime/conf.c index ffe67dbe..e55b8d18 100644 --- a/runtime/conf.c +++ b/runtime/conf.c @@ -570,8 +570,7 @@ cflineParseFileName(uchar* p, uchar *pFileName, omodStringRequest_t *pOMSR, int } -/* - * Helper to cfline(). This function takes the filter part of a traditional, PRI +/* Helper to cfline(). This function takes the filter part of a traditional, PRI * based line and decodes the PRIs given in the selector line. It processed the * line up to the beginning of the action part. A pointer to that beginnig is * passed back to the caller. @@ -587,8 +586,9 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register selector_t *f int pri; int singlpri = 0; int ignorepri = 0; - uchar buf[MAXLINE]; + uchar buf[2048]; /* buffer for facility and priority names */ uchar xbuf[200]; + DEFiRet; ASSERT(pline != NULL); ASSERT(*pline != NULL); @@ -613,7 +613,7 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register selector_t *f continue; /* collect priority name */ - for (bp = buf; *q && !strchr("\t ,;", *q); ) + for (bp = buf; *q && !strchr("\t ,;", *q) && bp < buf+sizeof(buf)-1 ; ) *bp++ = *q++; *bp = '\0'; @@ -624,6 +624,7 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register selector_t *f /* decode priority name */ if ( *buf == '!' ) { ignorepri = 1; + /* copy below is ok, we can NOT go off the allocated area */ for (bp=buf; *(bp+1); bp++) *bp=*(bp+1); *bp='\0'; @@ -649,7 +650,7 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register selector_t *f /* scan facilities */ while (*p && !strchr("\t .;", *p)) { - for (bp = buf; *p && !strchr("\t ,;.", *p); ) + for (bp = buf; *p && !strchr("\t ,;.", *p) && bp < buf+sizeof(buf)-1 ; ) *bp++ = *p++; *bp = '\0'; if (*buf == '*') { @@ -732,7 +733,7 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register selector_t *f p++; *pline = p; - return RS_RET_OK; + RETiRet; } diff --git a/runtime/glbl.c b/runtime/glbl.c index 11a664f8..1114fcd3 100644 --- a/runtime/glbl.c +++ b/runtime/glbl.c @@ -51,6 +51,7 @@ DEFobjStaticHelpers * class... */ static uchar *pszWorkDir = NULL; +static int iMaxLine = 2048; /* maximum length of a syslog message */ static int iDefPFFamily = PF_UNSPEC; /* protocol family (IPv4, IPv6 or both) */ static int bDropMalPTRMsgs = 0;/* Drop messages which have malicious PTR records during DNS lookup */ static int option_DisallowWarning = 1; /* complain if message from disallowed sender is received */ @@ -84,6 +85,7 @@ static dataType Get##nameFunc(void) \ return(nameVar); \ } +SIMP_PROP(MaxLine, iMaxLine, int) SIMP_PROP(DefPFFamily, iDefPFFamily, int) /* note that in the future we may check the family argument */ SIMP_PROP(DropMalPTRMsgs, bDropMalPTRMsgs, int) SIMP_PROP(Option_DisallowWarning, option_DisallowWarning, int) @@ -170,6 +172,7 @@ CODESTARTobjQueryInterface(glbl) #define SIMP_PROP(name) \ pIf->Get##name = Get##name; \ pIf->Set##name = Set##name; + SIMP_PROP(MaxLine); SIMP_PROP(DefPFFamily); SIMP_PROP(DropMalPTRMsgs); SIMP_PROP(Option_DisallowWarning); diff --git a/runtime/glbl.h b/runtime/glbl.h index 90436319..0c83bdd5 100644 --- a/runtime/glbl.h +++ b/runtime/glbl.h @@ -40,6 +40,7 @@ BEGINinterface(glbl) /* name must also be changed in ENDinterface macro! */ #define SIMP_PROP(name, dataType) \ dataType (*Get##name)(void); \ rsRetVal (*Set##name)(dataType); + SIMP_PROP(MaxLine, int) SIMP_PROP(DefPFFamily, int) SIMP_PROP(DropMalPTRMsgs, int) SIMP_PROP(Option_DisallowWarning, int) -- cgit From d65470842d1455fd335fcd62e54aed01628a2af6 Mon Sep 17 00:00:00 2001 From: Zhuang Yuyao Date: Wed, 10 Sep 2008 12:18:34 +0200 Subject: added new capability to property replacer. Multiple immediately successive field delimiters are treated as a single one. Signed-off-by: Rainer Gerhards --- runtime/msg.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'runtime') diff --git a/runtime/msg.c b/runtime/msg.c index fdeae077..28cb235a 100644 --- a/runtime/msg.c +++ b/runtime/msg.c @@ -1809,6 +1809,11 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, ++pFld; /* skip to field terminator */ if(*pFld == pTpe->data.field.field_delim) { ++pFld; /* eat it */ + if (pTpe->data.field.field_expand != 0) { + while (*pFld == pTpe->data.field.field_delim) { + ++pFld; + } + } ++iCurrFld; } } -- cgit From 262f61d869b95f63b393ae48d683b13e70322478 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Wed, 10 Sep 2008 15:59:53 +0200 Subject: added properties "inputname" and "$myhostname" - added message property "inputname", which contains the name of the input (module) that generated it. Presence is depending on suport in each input module (else it is blank). - added system property "$myhostname", which contains the name of the local host as it knows itself. --- runtime/msg.c | 37 +++++++++++++++++++++++++++++++++++++ runtime/msg.h | 5 ++++- runtime/obj.c | 2 +- 3 files changed, 42 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/msg.c b/runtime/msg.c index 28cb235a..f4eb9414 100644 --- a/runtime/msg.c +++ b/runtime/msg.c @@ -42,6 +42,7 @@ #include "msg.h" #include "var.h" #include "datetime.h" +#include "glbl.h" #include "regexp.h" #include "atomic.h" @@ -49,6 +50,7 @@ DEFobjStaticHelpers DEFobjCurrIf(var) DEFobjCurrIf(datetime) +DEFobjCurrIf(glbl) DEFobjCurrIf(regexp) static syslogCODE rs_prioritynames[] = @@ -287,6 +289,8 @@ CODESTARTobjDestruct(msg) free(pThis->pszTAG); if(pThis->pszHOSTNAME != NULL) free(pThis->pszHOSTNAME); + if(pThis->pszInputName != NULL) + free(pThis->pszInputName); if(pThis->pszRcvFrom != NULL) free(pThis->pszRcvFrom); if(pThis->pszRcvFromIP != NULL) @@ -453,6 +457,7 @@ static rsRetVal MsgSerialize(msg_t *pThis, strm_t *pStrm) objSerializePTR(pStrm, pszUxTradMsg, PSZ); objSerializePTR(pStrm, pszTAG, PSZ); objSerializePTR(pStrm, pszHOSTNAME, PSZ); + objSerializePTR(pStrm, pszInputName, PSZ); objSerializePTR(pStrm, pszRcvFrom, PSZ); objSerializePTR(pStrm, pszRcvFromIP, PSZ); @@ -1219,6 +1224,18 @@ char *getHOSTNAME(msg_t *pM) } +uchar *getInputName(msg_t *pM) +{ + if(pM == NULL) + return (uchar*) ""; + else + if(pM->pszInputName == NULL) + return (uchar*) ""; + else + return pM->pszInputName; +} + + char *getRcvFrom(msg_t *pM) { if(pM == NULL) @@ -1399,6 +1416,19 @@ static int getAPPNAMELen(msg_t *pM) return (pM->pCSAPPNAME == NULL) ? 0 : rsCStrLen(pM->pCSAPPNAME); } +/* rgerhards 2008-09-10: set pszInputName in msg object + */ +void MsgSetInputName(msg_t *pMsg, char* pszInputName) +{ + assert(pMsg != NULL); + if(pMsg->pszInputName != NULL) + free(pMsg->pszInputName); + + pMsg->iLenInputName = strlen(pszInputName); + if((pMsg->pszInputName = malloc(pMsg->iLenInputName + 1)) != NULL) { + memcpy(pMsg->pszInputName, pszInputName, pMsg->iLenInputName + 1); + } +} /* rgerhards 2004-11-16: set pszRcvFrom in msg object */ @@ -1685,6 +1715,8 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, pRes = getRawMsg(pMsg); } else if(!strcmp((char*) pName, "uxtradmsg")) { pRes = getUxTradMsg(pMsg); + } else if(!strcmp((char*) pName, "inputname")) { + pRes = (char*) getInputName(pMsg); } else if(!strcmp((char*) pName, "fromhost")) { pRes = getRcvFrom(pMsg); } else if(!strcmp((char*) pName, "fromhost-ip")) { @@ -1772,6 +1804,8 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, return "***OUT OF MEMORY***"; } else *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ + } else if(!strcmp((char*) pName, "$myhostname")) { + pRes = (char*) glbl.GetLocalHostName(); } else { /* there is no point in continuing, we may even otherwise render the * error message unreadable. rgerhards, 2007-07-10 @@ -2368,6 +2402,8 @@ rsRetVal MsgSetProperty(msg_t *pThis, var_t *pProp) MsgSetUxTradMsg(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); } else if(isProp("pszTAG")) { MsgSetTAG(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); + } else if(isProp("pszInputName")) { + MsgSetInputName(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); } else if(isProp("pszRcvFromIP")) { MsgSetRcvFromIP(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr)); } else if(isProp("pszRcvFrom")) { @@ -2430,6 +2466,7 @@ BEGINObjClassInit(msg, 1, OBJ_IS_CORE_MODULE) /* request objects we use */ CHKiRet(objUse(var, CORE_COMPONENT)); CHKiRet(objUse(datetime, CORE_COMPONENT)); + CHKiRet(objUse(glbl, CORE_COMPONENT)); /* set our own handlers */ OBJSetMethodHandler(objMethod_SERIALIZE, MsgSerialize); diff --git a/runtime/msg.h b/runtime/msg.h index c428237a..21cb2c64 100644 --- a/runtime/msg.h +++ b/runtime/msg.h @@ -91,9 +91,11 @@ struct msg { int iLenRcvFrom; /* Length of pszRcvFrom */ uchar *pszRcvFromIP; /* IP of system message was received from */ int iLenRcvFromIP; /* Length of pszRcvFromIP */ + uchar *pszInputName; /* name of the input module that submitted this message */ + int iLenInputName; /* Length of pszInputName */ short iProtocolVersion;/* protocol version of message received 0 - legacy, 1 syslog-protocol) */ cstr_t *pCSProgName; /* the (BSD) program name */ - cstr_t *pCSStrucData;/* STRUCTURED-DATA */ + cstr_t *pCSStrucData; /* STRUCTURED-DATA */ cstr_t *pCSAPPNAME; /* APP-NAME */ cstr_t *pCSPROCID; /* PROCID */ cstr_t *pCSMSGID; /* MSGID */ @@ -135,6 +137,7 @@ char *getSeverity(msg_t *pM); char *getSeverityStr(msg_t *pM); char *getFacility(msg_t *pM); char *getFacilityStr(msg_t *pM); +void MsgSetInputName(msg_t *pMsg, char*); rsRetVal MsgSetAPPNAME(msg_t *pMsg, char* pszAPPNAME); char *getAPPNAME(msg_t *pM); rsRetVal MsgSetPROCID(msg_t *pMsg, char* pszPROCID); diff --git a/runtime/obj.c b/runtime/obj.c index 082aa691..2a9df9ed 100644 --- a/runtime/obj.c +++ b/runtime/obj.c @@ -780,7 +780,7 @@ Deserialize(void *ppObj, uchar *pszTypeExpected, strm_t *pStrm, rsRetVal (*fFixu DEFiRet; rsRetVal iRetLocal; obj_t *pObj = NULL; - int oVers = 0; /* after all, it is totally useless but takes up some execution time... */ + int oVers = 0; /* keep compiler happy, but it is totally useless but takes up some execution time... */ cstr_t *pstrID = NULL; objInfo_t *pObjInfo; -- cgit From 760e5f0d3986aa93a07e207dde3206741ec3ad74 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 16 Sep 2008 11:56:07 +0200 Subject: performance optimization: unnecessary time() calls during message parsing removed thanks to David Lang for his excellent performance analysis --- runtime/datetime.c | 268 +++++++++++++++++++++++++++++++---------------------- runtime/datetime.h | 4 +- runtime/rsyslog.h | 1 + 3 files changed, 161 insertions(+), 112 deletions(-) (limited to 'runtime') diff --git a/runtime/datetime.c b/runtime/datetime.c index 6d5652ff..47cde8c2 100644 --- a/runtime/datetime.c +++ b/runtime/datetime.c @@ -159,18 +159,34 @@ static int srSLMGParseInt32(char** ppsz) /** * Parse a TIMESTAMP-3339. - * updates the parse pointer position. + * updates the parse pointer position. The pTime parameter + * is guranteed to be updated only if a new valid timestamp + * could be obtained (restriction added 2008-09-16 by rgerhards). */ -static int +static rsRetVal ParseTIMESTAMP3339(struct syslogTime *pTime, char** ppszTS) { char *pszTS = *ppszTS; + /* variables to temporarily hold time information while we parse */ + int year; + int month; + int day; + int hour; /* 24 hour clock */ + int minute; + int second; + int secfrac; /* fractional seconds (must be 32 bit!) */ + int secfracPrecision; + char OffsetMode; /* UTC offset + or - */ + char OffsetHour; /* UTC offset in hours */ + int OffsetMinute; /* UTC offset in minutes */ + /* end variables to temporarily hold time information while we parse */ + DEFiRet; assert(pTime != NULL); assert(ppszTS != NULL); assert(pszTS != NULL); - pTime->year = srSLMGParseInt32(&pszTS); + year = srSLMGParseInt32(&pszTS); /* We take the liberty to accept slightly malformed timestamps e.g. in * the format of 2003-9-1T1:0:0. This doesn't hurt on receiving. Of course, @@ -178,105 +194,128 @@ ParseTIMESTAMP3339(struct syslogTime *pTime, char** ppszTS) * here because at postion 11, there is no "T" in such cases ;) */ if(*pszTS++ != '-') - return FALSE; - pTime->month = srSLMGParseInt32(&pszTS); - if(pTime->month < 1 || pTime->month > 12) - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); + month = srSLMGParseInt32(&pszTS); + if(month < 1 || month > 12) + ABORT_FINALIZE(RS_RET_INVLD_TIME); if(*pszTS++ != '-') - return FALSE; - pTime->day = srSLMGParseInt32(&pszTS); - if(pTime->day < 1 || pTime->day > 31) - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); + day = srSLMGParseInt32(&pszTS); + if(day < 1 || day > 31) + ABORT_FINALIZE(RS_RET_INVLD_TIME); if(*pszTS++ != 'T') - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); - pTime->hour = srSLMGParseInt32(&pszTS); - if(pTime->hour < 0 || pTime->hour > 23) - return FALSE; + hour = srSLMGParseInt32(&pszTS); + if(hour < 0 || hour > 23) + ABORT_FINALIZE(RS_RET_INVLD_TIME); if(*pszTS++ != ':') - return FALSE; - pTime->minute = srSLMGParseInt32(&pszTS); - if(pTime->minute < 0 || pTime->minute > 59) - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); + minute = srSLMGParseInt32(&pszTS); + if(minute < 0 || minute > 59) + ABORT_FINALIZE(RS_RET_INVLD_TIME); if(*pszTS++ != ':') - return FALSE; - pTime->second = srSLMGParseInt32(&pszTS); - if(pTime->second < 0 || pTime->second > 60) - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); + second = srSLMGParseInt32(&pszTS); + if(second < 0 || second > 60) + ABORT_FINALIZE(RS_RET_INVLD_TIME); /* Now let's see if we have secfrac */ - if(*pszTS == '.') - { + if(*pszTS == '.') { char *pszStart = ++pszTS; - pTime->secfrac = srSLMGParseInt32(&pszTS); - pTime->secfracPrecision = (int) (pszTS - pszStart); - } - else - { - pTime->secfracPrecision = 0; - pTime->secfrac = 0; + secfrac = srSLMGParseInt32(&pszTS); + secfracPrecision = (int) (pszTS - pszStart); + } else { + secfracPrecision = 0; + secfrac = 0; } /* check the timezone */ if(*pszTS == 'Z') { pszTS++; /* eat Z */ - pTime->OffsetMode = 'Z'; - pTime->OffsetHour = 0; - pTime->OffsetMinute = 0; - } - else if((*pszTS == '+') || (*pszTS == '-')) - { - pTime->OffsetMode = *pszTS; + OffsetMode = 'Z'; + OffsetHour = 0; + OffsetMinute = 0; + } else if((*pszTS == '+') || (*pszTS == '-')) { + OffsetMode = *pszTS; pszTS++; - pTime->OffsetHour = srSLMGParseInt32(&pszTS); - if(pTime->OffsetHour < 0 || pTime->OffsetHour > 23) - return FALSE; + OffsetHour = srSLMGParseInt32(&pszTS); + if(OffsetHour < 0 || OffsetHour > 23) + ABORT_FINALIZE(RS_RET_INVLD_TIME); if(*pszTS++ != ':') - return FALSE; - pTime->OffsetMinute = srSLMGParseInt32(&pszTS); - if(pTime->OffsetMinute < 0 || pTime->OffsetMinute > 59) - return FALSE; - } - else + ABORT_FINALIZE(RS_RET_INVLD_TIME); + OffsetMinute = srSLMGParseInt32(&pszTS); + if(OffsetMinute < 0 || OffsetMinute > 59) + ABORT_FINALIZE(RS_RET_INVLD_TIME); + } else { /* there MUST be TZ information */ - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); + } /* OK, we actually have a 3339 timestamp, so let's indicated this */ if(*pszTS == ' ') ++pszTS; else - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); - /* update parse pointer */ + /* we had success, so update parse pointer and caller-provided timestamp */ *ppszTS = pszTS; + pTime->timeType = 2; + pTime->year = year; + pTime->month = month; + pTime->day = day; + pTime->hour = hour; + pTime->minute = minute; + pTime->second = second; + pTime->secfrac = secfrac; + pTime->secfracPrecision = secfracPrecision; + pTime->OffsetMode = OffsetMode; + pTime->OffsetHour = OffsetHour; + pTime->OffsetMinute = OffsetMinute; - return TRUE; +finalize_it: + RETiRet; } /** - * Parse a TIMESTAMP-3164. - * Returns TRUE on parse OK, FALSE on parse error. + * Parse a TIMESTAMP-3164. The pTime parameter + * is guranteed to be updated only if a new valid timestamp + * could be obtained (restriction added 2008-09-16 by rgerhards). This + * also means the caller *must* provide a valid (probably current) + * timstamp in pTime when calling this function. a 3164 timestamp contains + * only partial information and only that partial information is updated. + * So the "output timestamp" is a valid timestamp only if the "input + * timestamp" was valid, too. The is actually an optimization, as it + * permits us to use a pre-aquired timestamp and thus avoids to do + * a (costly) time() call. Thanks to David Lang for insisting on + * time() call reduction ;). */ -static int +static rsRetVal ParseTIMESTAMP3164(struct syslogTime *pTime, char** ppszTS) { + /* variables to temporarily hold time information while we parse */ + int month; + int day; + int hour; /* 24 hour clock */ + int minute; + int second; + /* end variables to temporarily hold time information while we parse */ char *pszTS; + DEFiRet; assert(ppszTS != NULL); pszTS = *ppszTS; assert(pszTS != NULL); assert(pTime != NULL); - getCurrTime(pTime); /* obtain the current year and UTC offsets! */ - /* If we look at the month (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec), * we may see the following character sequences occur: * @@ -301,117 +340,117 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, char** ppszTS) ++pszTS; if(*pszTS == 'n') { ++pszTS; - pTime->month = 1; + month = 1; } else - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); } else if(*pszTS == 'u') { ++pszTS; if(*pszTS == 'n') { ++pszTS; - pTime->month = 6; + month = 6; } else if(*pszTS == 'l') { ++pszTS; - pTime->month = 7; + month = 7; } else - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); } else - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); break; case 'F': if(*pszTS == 'e') { ++pszTS; if(*pszTS == 'b') { ++pszTS; - pTime->month = 2; + month = 2; } else - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); } else - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); break; case 'M': if(*pszTS == 'a') { ++pszTS; if(*pszTS == 'r') { ++pszTS; - pTime->month = 3; + month = 3; } else if(*pszTS == 'y') { ++pszTS; - pTime->month = 5; + month = 5; } else - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); } else - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); break; case 'A': if(*pszTS == 'p') { ++pszTS; if(*pszTS == 'r') { ++pszTS; - pTime->month = 4; + month = 4; } else - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); } else if(*pszTS == 'u') { ++pszTS; if(*pszTS == 'g') { ++pszTS; - pTime->month = 8; + month = 8; } else - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); } else - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); break; case 'S': if(*pszTS == 'e') { ++pszTS; if(*pszTS == 'p') { ++pszTS; - pTime->month = 9; + month = 9; } else - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); } else - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); break; case 'O': if(*pszTS == 'c') { ++pszTS; if(*pszTS == 't') { ++pszTS; - pTime->month = 10; + month = 10; } else - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); } else - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); break; case 'N': if(*pszTS == 'o') { ++pszTS; if(*pszTS == 'v') { ++pszTS; - pTime->month = 11; + month = 11; } else - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); } else - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); break; case 'D': if(*pszTS == 'e') { ++pszTS; if(*pszTS == 'c') { ++pszTS; - pTime->month = 12; + month = 12; } else - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); } else - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); break; default: - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); } /* done month */ if(*pszTS++ != ' ') - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); /* we accept a slightly malformed timestamp when receiving. This is * we accept one-digit days @@ -419,42 +458,51 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, char** ppszTS) if(*pszTS == ' ') ++pszTS; - pTime->day = srSLMGParseInt32(&pszTS); - if(pTime->day < 1 || pTime->day > 31) - return FALSE; + day = srSLMGParseInt32(&pszTS); + if(day < 1 || day > 31) + ABORT_FINALIZE(RS_RET_INVLD_TIME); if(*pszTS++ != ' ') - return FALSE; - pTime->hour = srSLMGParseInt32(&pszTS); - if(pTime->hour < 0 || pTime->hour > 23) - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); + hour = srSLMGParseInt32(&pszTS); + if(hour < 0 || hour > 23) + ABORT_FINALIZE(RS_RET_INVLD_TIME); if(*pszTS++ != ':') - return FALSE; - pTime->minute = srSLMGParseInt32(&pszTS); - if(pTime->minute < 0 || pTime->minute > 59) - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); + minute = srSLMGParseInt32(&pszTS); + if(minute < 0 || minute > 59) + ABORT_FINALIZE(RS_RET_INVLD_TIME); if(*pszTS++ != ':') - return FALSE; - pTime->second = srSLMGParseInt32(&pszTS); - if(pTime->second < 0 || pTime->second > 60) - return FALSE; + ABORT_FINALIZE(RS_RET_INVLD_TIME); + second = srSLMGParseInt32(&pszTS); + if(second < 0 || second > 60) + ABORT_FINALIZE(RS_RET_INVLD_TIME); - /* we provide support for an exter ":" after the date. While this is an + /* we provide support for an extra ":" after the date. While this is an * invalid format, it occurs frequently enough (e.g. with Cisco devices) * to permit it as a valid case. -- rgerhards, 2008-09-12 */ if(*pszTS++ == ':') - ++pszTS; + ++pszTS; /* just skip past it */ - /* OK, we actually have a 3164 timestamp, so let's indicate this - * and fill the rest of the properties. */ + /* we had success, so update parse pointer and caller-provided timestamp + * fields we do not have are not updated in the caller's timestamp. This + * is the reason why the caller must pass in a correct timestamp. + */ + *ppszTS = pszTS; /* provide updated parse position back to caller */ pTime->timeType = 1; + pTime->month = month; + pTime->day = day; + pTime->hour = hour; + pTime->minute = minute; + pTime->second = second; pTime->secfracPrecision = 0; pTime->secfrac = 0; - *ppszTS = pszTS; /* provide updated parse position back to caller */ - return TRUE; + +finalize_it: + RETiRet; } /******************************************************************* diff --git a/runtime/datetime.h b/runtime/datetime.h index 755cc0ed..2210af02 100644 --- a/runtime/datetime.h +++ b/runtime/datetime.h @@ -36,8 +36,8 @@ typedef struct datetime_s { /* interfaces */ BEGINinterface(datetime) /* name must also be changed in ENDinterface macro! */ void (*getCurrTime)(struct syslogTime *t); - int (*ParseTIMESTAMP3339)(struct syslogTime *pTime, char** ppszTS); - int (*ParseTIMESTAMP3164)(struct syslogTime *pTime, char** pszTS); + rsRetVal (*ParseTIMESTAMP3339)(struct syslogTime *pTime, char** ppszTS); + rsRetVal (*ParseTIMESTAMP3164)(struct syslogTime *pTime, char** pszTS); int (*formatTimestampToMySQL)(struct syslogTime *ts, char* pDst, size_t iLenDst); int (*formatTimestampToPgSQL)(struct syslogTime *ts, char *pDst, size_t iLenDst); int (*formatTimestamp3339)(struct syslogTime *ts, char* pBuf, size_t iLenBuf); diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index a1d1d9fc..361bfb47 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -251,6 +251,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_CONF_FILE_NOT_FOUND = -2104, /**< config file or directory not found */ RS_RET_QUEUE_FULL = -2105, /**< queue is full, operation could not be completed */ RS_RET_ACCEPT_ERR = -2106, /**< error during accept() system call */ + RS_RET_INVLD_TIME = -2107, /**< invalid timestamp (e.g. could not be parsed) */ /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ -- cgit From cf8b0717e12330695ce2e4b16966fda2a64b053e Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 18 Sep 2008 10:48:19 +0200 Subject: ignoring an (acceptable) race in debug system --- runtime/debug.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'runtime') diff --git a/runtime/debug.c b/runtime/debug.c index 1450d029..7ed1442b 100644 --- a/runtime/debug.c +++ b/runtime/debug.c @@ -480,7 +480,23 @@ static inline void dbgMutexUnlockLog(pthread_mutex_t *pmut, dbgFuncDB_t *pFuncDB pthread_mutex_lock(&mutMutLog); pLog = dbgMutLogFindSpecific(pmut, MUTOP_LOCK, NULL); +#if 0 /* toggle for testing */ assert(pLog != NULL); +#else +/* the change below seems not to work - the problem seems to be a real race... I keep this code in just in case + * I need to re-use it. It should be removed once we are finished analyzing this problem. -- rgerhards, 2008-09-17 + */ +if(pLog == NULL) { + /* this may happen due to some races. We do not try to avoid + * this, as it would complicate the "real" code. This is not justified + * just to keep the debug info system up. -- rgerhards, 2008-09-17 + */ + pthread_mutex_unlock(&mutMutLog); + dbgprintf("%s:%d:%s: mutex %p UNlocked [but we did not yet know this mutex!]\n", + pFuncDB->file, unlockLn, pFuncDB->func, (void*)pmut); + return; /* if we don't know it yet, we can not clean up... */ +} +#endif /* we found the last lock entry. We now need to see from which FuncDB we need to * remove it. This is recorded inside the mutex log entry. -- cgit From 988989e49ef8639123c83383ba256c4e67679c8d Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 18 Sep 2008 10:49:16 +0200 Subject: re-enabled gcc builtin atomic operations and added a proper ./configure check --- runtime/atomic.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'runtime') diff --git a/runtime/atomic.h b/runtime/atomic.h index 430ae7f0..d6811628 100644 --- a/runtime/atomic.h +++ b/runtime/atomic.h @@ -1,6 +1,6 @@ /* This header supplies atomic operations. So far, we rely on GCC's - * atomic builtins. I have no idea if we can check them via autotools, - * but I am making the necessary provisioning to live without them if + * atomic builtins. During configure, we check if atomic operatons are + * available. If they are not, I am making the necessary provisioning to live without them if * they are not available. Please note that you should only use the macros * here if you think you can actually live WITHOUT an explicit atomic operation, * because in the non-presence of them, we simply do it without atomicitiy. @@ -36,16 +36,16 @@ #ifndef INCLUDED_ATOMIC_H #define INCLUDED_ATOMIC_H -/* set the following to 1 if we have atomic operations (and #undef it otherwise) */ -/* #define DO_HAVE_ATOMICS 1 */ /* for this release, we disable atomic calls because there seem to be some * portability problems and we can not fix that without destabilizing the build. * They simply came in too late. -- rgerhards, 2008-04-02 */ -/* make sure they are not used! -#define ATOMIC_INC(data) ((void) __sync_fetch_and_add(&data, 1)) -#define ATOMIC_DEC_AND_FETCH(data) __sync_sub_and_fetch(&data, 1) -*/ -#define ATOMIC_INC(data) (++(data)) +#ifdef HAVE_ATOMIC_BUILTINS +# define ATOMIC_INC(data) ((void) __sync_fetch_and_add(&data, 1)) +# define ATOMIC_DEC_AND_FETCH(data) __sync_sub_and_fetch(&data, 1) +#else +# warning "atomic builtins not available, using nul operations" +# define ATOMIC_INC(data) (++(data)) +#endif #endif /* #ifndef INCLUDED_ATOMIC_H */ -- cgit From 4c96ebdcfe075e80810b01257cf21ea1c9b3ec0e Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 18 Sep 2008 12:19:33 +0200 Subject: bugfix: potential race condition when adding messages to queue There was a wrong order of mutex lock operations. It is hard to believe that really caused problems, but in theory it could and with threading we often see that theory becomes practice if something is only used long enough on a fast enough machine with enough CPUs ;) --- runtime/atomic.h | 8 ++++++-- runtime/debug.h | 3 ++- runtime/queue.c | 12 ++++++------ 3 files changed, 14 insertions(+), 9 deletions(-) (limited to 'runtime') diff --git a/runtime/atomic.h b/runtime/atomic.h index d6811628..d15f78ee 100644 --- a/runtime/atomic.h +++ b/runtime/atomic.h @@ -41,11 +41,15 @@ * They simply came in too late. -- rgerhards, 2008-04-02 */ #ifdef HAVE_ATOMIC_BUILTINS -# define ATOMIC_INC(data) ((void) __sync_fetch_and_add(&data, 1)) -# define ATOMIC_DEC_AND_FETCH(data) __sync_sub_and_fetch(&data, 1) +# define ATOMIC_INC(data) ((void) __sync_fetch_and_add(&(data), 1)) +# define ATOMIC_DEC_AND_FETCH(data) __sync_sub_and_fetch(&(data), 1) +# define ATOMIC_FETCH_32BIT(data) ((unsigned) __sync_fetch_and_and(&(data), 0xffffffff)) +# define ATOMIC_STORE_1_TO_32BIT(data) __sync_lock_test_and_set(&(data), 1) #else # warning "atomic builtins not available, using nul operations" # define ATOMIC_INC(data) (++(data)) +# define ATOMIC_FETCH_32BIT(data) (data) +# define ATOMIC_STORE_1_TO_32BIT(data) (data) = 1 #endif #endif /* #ifndef INCLUDED_ATOMIC_H */ diff --git a/runtime/debug.h b/runtime/debug.h index 214b7c05..d9d576b5 100644 --- a/runtime/debug.h +++ b/runtime/debug.h @@ -130,7 +130,8 @@ void dbgPrintAllDebugInfo(void); /* debug aides */ -#ifdef RTINST +//#ifdef RTINST +#if 0 // temporarily removed for helgrind #define d_pthread_mutex_lock(x) dbgMutexLock(x, pdbgFuncDB, __LINE__, dbgCALLStaCK_POP_POINT ) #define d_pthread_mutex_unlock(x) dbgMutexUnlock(x, pdbgFuncDB, __LINE__, dbgCALLStaCK_POP_POINT ) #define d_pthread_cond_wait(cond, mut) dbgCondWait(cond, mut, pdbgFuncDB, __LINE__, dbgCALLStaCK_POP_POINT ) diff --git a/runtime/queue.c b/runtime/queue.c index 7e7d4152..c0a37019 100644 --- a/runtime/queue.c +++ b/runtime/queue.c @@ -2171,17 +2171,17 @@ queueEnqObj(queue_t *pThis, flowControl_t flowCtlType, void *pUsr) finalize_it: if(pThis->qType != QUEUETYPE_DIRECT) { - d_pthread_mutex_unlock(pThis->mut); + /* make sure at least one worker is running. */ + if(pThis->qType != QUEUETYPE_DIRECT) { + queueAdviseMaxWorkers(pThis); + } + /* and release the mutex */ i = pthread_cond_signal(&pThis->notEmpty); + d_pthread_mutex_unlock(pThis->mut); dbgoprint((obj_t*) pThis, "EnqueueMsg signaled condition (%d)\n", i); pthread_setcancelstate(iCancelStateSave, NULL); } - /* make sure at least one worker is running. */ - if(pThis->qType != QUEUETYPE_DIRECT) { - queueAdviseMaxWorkers(pThis); - } - RETiRet; } -- cgit From bc70a730194759e85f9c3641573c46b4a8476198 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 19 Sep 2008 17:41:11 +0200 Subject: bugfix: proper synchronization on message destruction The code was potentially race, at least on systems where a memory barrier was needed. Fix not fully tested yet. --- runtime/atomic.h | 1 + runtime/msg.c | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'runtime') diff --git a/runtime/atomic.h b/runtime/atomic.h index d15f78ee..2dbe7f52 100644 --- a/runtime/atomic.h +++ b/runtime/atomic.h @@ -48,6 +48,7 @@ #else # warning "atomic builtins not available, using nul operations" # define ATOMIC_INC(data) (++(data)) +# define ATOMIC_DEC_AND_FETCH(data) (--(data)) # define ATOMIC_FETCH_32BIT(data) (data) # define ATOMIC_STORE_1_TO_32BIT(data) (data) = 1 #endif diff --git a/runtime/msg.c b/runtime/msg.c index f4eb9414..346bbc5f 100644 --- a/runtime/msg.c +++ b/runtime/msg.c @@ -276,8 +276,10 @@ CODESTARTobjDestruct(msg) # ifdef DO_HAVE_ATOMICS currRefCount = ATOMIC_DEC_AND_FETCH(pThis->iRefCount); # else + MsgLock(pThis); currRefCount = --pThis->iRefCount; # endif +// we need a mutex, because we may be suspended after getting the refcount but before if(currRefCount == 0) { /* DEV Debugging Only! dbgprintf("msgDestruct\t0x%lx, RefCount now 0, doing DESTROY\n", (unsigned long)pThis); */ @@ -337,9 +339,11 @@ CODESTARTobjDestruct(msg) rsCStrDestruct(&pThis->pCSPROCID); if(pThis->pCSMSGID != NULL) rsCStrDestruct(&pThis->pCSMSGID); + MsgUnlock(pThis); funcDeleteMutex(pThis); } else { pThis = NULL; /* tell framework not to destructing the object! */ + MsgUnlock(pThis); } ENDobjDestruct(msg) -- cgit From 47a2593ae4e2d4077b4a9e8c77a2b686abfa8d2c Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 23 Sep 2008 14:41:02 +0200 Subject: atomic memory access calls re-enabled in msg.c destructor not yet permitted because verification is missing that a atomic opration is sufficient for the job required --- runtime/msg.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'runtime') diff --git a/runtime/msg.c b/runtime/msg.c index 346bbc5f..d5e1fde9 100644 --- a/runtime/msg.c +++ b/runtime/msg.c @@ -273,12 +273,12 @@ BEGINobjDestruct(msg) /* be sure to specify the object type also in END and CODE int currRefCount; CODESTARTobjDestruct(msg) /* DEV Debugging only ! dbgprintf("msgDestruct\t0x%lx, Ref now: %d\n", (unsigned long)pM, pM->iRefCount - 1); */ -# ifdef DO_HAVE_ATOMICS - currRefCount = ATOMIC_DEC_AND_FETCH(pThis->iRefCount); -# else +//# ifdef DO_HAVE_ATOMICS +// currRefCount = ATOMIC_DEC_AND_FETCH(pThis->iRefCount); +//# else MsgLock(pThis); currRefCount = --pThis->iRefCount; -# endif +//# endif // we need a mutex, because we may be suspended after getting the refcount but before if(currRefCount == 0) { @@ -487,7 +487,7 @@ finalize_it: msg_t *MsgAddRef(msg_t *pM) { assert(pM != NULL); -# ifdef DO_HAVE_ATOMICS +# ifdef HAVE_ATOMIC_BUILTINS ATOMIC_INC(pM->iRefCount); # else MsgLock(pM); -- cgit From dc44f5a475101aa18568a0eaceacc2a4eb718391 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 26 Sep 2008 13:40:41 +0200 Subject: cleaned up internal debug system code and made it behave better in regard to multi-threading --- runtime/debug.c | 208 +++++++++++++++++++++----------------------------------- 1 file changed, 79 insertions(+), 129 deletions(-) (limited to 'runtime') diff --git a/runtime/debug.c b/runtime/debug.c index 7ed1442b..15e9201b 100644 --- a/runtime/debug.c +++ b/runtime/debug.c @@ -43,6 +43,9 @@ #include #include #include +#include +#include +#include #include "rsyslog.h" #include "debug.h" @@ -62,8 +65,8 @@ static int bPrintTime = 1; /* print a timestamp together with debug message */ static int bPrintAllDebugOnExit = 0; static int bAbortTrace = 1; /* print a trace after SIGABRT or SIGSEGV */ static char *pszAltDbgFileName = NULL; /* if set, debug output is *also* sent to here */ -static FILE *altdbg = NULL; /* and the handle for alternate debug output */ -static FILE *stddbg; +static int altdbg = -1; /* and the handle for alternate debug output */ +static int stddbg; /* list of files/objects that should be printed */ typedef struct dbgPrintName_s { @@ -113,8 +116,7 @@ static dbgThrdInfo_t *dbgCallStackListRoot = NULL; static dbgThrdInfo_t *dbgCallStackListLast = NULL; static pthread_mutex_t mutCallStack; -static pthread_mutex_t mutdbgprintf; -static pthread_mutex_t mutdbgoprint; +static pthread_mutex_t mutdbgprint; static pthread_key_t keyCallStack; @@ -748,8 +750,6 @@ sigsegvHdlr(int signum) } dbgprintf("\n\nTo submit bug reports, visit http://www.rsyslog.com/bugs\n\n"); - if(stddbg != NULL) fflush(stddbg); - if(altdbg != NULL) fflush(altdbg); /* and finally abort... */ /* TODO: think about restarting rsyslog in this case: may be a good idea, @@ -758,56 +758,36 @@ sigsegvHdlr(int signum) abort(); } - +#if 1 #pragma GCC diagnostic ignored "-Wempty-body" -/* print some debug output when an object is given - * This is mostly a copy of dbgprintf, but I do not know how to combine it - * into a single function as we have variable arguments and I don't know how to call - * from one vararg function into another. I don't dig in this, it is OK for the - * time being. -- rgerhards, 2008-01-29 +/* write the debug message. This is a helper to dbgprintf and dbgoprint which + * contains common code. added 2008-09-26 rgerhards */ -void -dbgoprint(obj_t *pObj, char *fmt, ...) +static void +dbgprint(obj_t *pObj, char *pszMsg, size_t lenMsg) { static pthread_t ptLastThrdID = 0; static int bWasNL = 0; - va_list ap; - static char pszThrdName[64]; /* 64 is to be on the safe side, anything over 20 is bad... */ - static char pszWriteBuf[1024]; + char pszThrdName[64]; /* 64 is to be on the safe side, anything over 20 is bad... */ + char pszWriteBuf[1024]; size_t lenWriteBuf; struct timespec t; - if(!(Debug && debugging_on)) - return; - - /* a quick and very dirty hack to enable us to display just from those objects - * that we are interested in. So far, this must be changed at compile time (and - * chances are great it is commented out while you read it. Later, this shall - * be selectable via the environment. -- rgerhards, 2008-02-20 - */ -#if 0 - if(objGetObjID(pObj) != OBJexpr) - return; -#endif - - - pthread_mutex_lock(&mutdbgoprint); - pthread_cleanup_push(dbgMutexCancelCleanupHdlr, &mutdbgoprint); + pthread_mutex_lock(&mutdbgprint); + pthread_cleanup_push(dbgMutexCancelCleanupHdlr, &mutdbgprint); /* The bWasNL handler does not really work. It works if no thread * switching occurs during non-NL messages. Else, things are messed * up. Anyhow, it works well enough to provide useful help during * getting this up and running. It is questionable if the extra effort - * is worth fixing it, giving the limited appliability. - * rgerhards, 2005-10-25 + * is worth fixing it, giving the limited appliability. -- rgerhards, 2005-10-25 * I have decided that it is not worth fixing it - especially as it works - * pretty well. - * rgerhards, 2007-06-15 + * pretty well. -- rgerhards, 2007-06-15 */ if(ptLastThrdID != pthread_self()) { if(!bWasNL) { - if(stddbg != NULL) fprintf(stddbg, "\n"); - if(altdbg != NULL) fprintf(altdbg, "\n"); + if(stddbg != -1) write(stddbg, "\n", 1); + if(altdbg != -1) write(altdbg, "\n", 1); bWasNL = 1; } ptLastThrdID = pthread_self(); @@ -821,113 +801,82 @@ dbgoprint(obj_t *pObj, char *fmt, ...) if(bWasNL) { if(bPrintTime) { clock_gettime(CLOCK_REALTIME, &t); - if(stddbg != NULL) fprintf(stddbg, "%4.4ld.%9.9ld:", (long) (t.tv_sec % 10000), t.tv_nsec); - if(altdbg != NULL) fprintf(altdbg, "%4.4ld.%9.9ld:", (long) (t.tv_sec % 10000), t.tv_nsec); + lenWriteBuf = snprintf(pszWriteBuf, sizeof(pszWriteBuf), + "%4.4ld.%9.9ld:", (long) (t.tv_sec % 10000), t.tv_nsec); + if(stddbg != -1) write(stddbg, pszWriteBuf, lenWriteBuf); + if(altdbg != -1) write(altdbg, pszWriteBuf, lenWriteBuf); } - if(stddbg != NULL) fprintf(stddbg, "%s: ", pszThrdName); - if(altdbg != NULL) fprintf(altdbg, "%s: ", pszThrdName); + lenWriteBuf = snprintf(pszWriteBuf, sizeof(pszWriteBuf), "%s: ", pszThrdName); + if(stddbg != -1) write(stddbg, pszWriteBuf, lenWriteBuf); + if(altdbg != -1) write(altdbg, pszWriteBuf, lenWriteBuf); /* print object name header if we have an object */ if(pObj != NULL) { - if(stddbg != NULL) fprintf(stddbg, "%s: ", obj.GetName(pObj)); - if(altdbg != NULL) fprintf(altdbg, "%s: ", obj.GetName(pObj)); + lenWriteBuf = snprintf(pszWriteBuf, sizeof(pszWriteBuf), "%s: ", obj.GetName(pObj)); + if(stddbg != -1) write(stddbg, pszWriteBuf, lenWriteBuf); + if(altdbg != -1) write(altdbg, pszWriteBuf, lenWriteBuf); } } - bWasNL = (*(fmt + strlen(fmt) - 1) == '\n') ? 1 : 0; + if(stddbg != -1) write(stddbg, pszMsg, lenMsg); + if(altdbg != -1) write(altdbg, pszMsg, lenMsg); + + bWasNL = (pszMsg[lenMsg - 1] == '\n') ? 1 : 0; + + pthread_cleanup_pop(1); +} +#pragma GCC diagnostic warning "-Wempty-body" +#endif + +/* print some debug output when an object is given + * This is mostly a copy of dbgprintf, but I do not know how to combine it + * into a single function as we have variable arguments and I don't know how to call + * from one vararg function into another. I don't dig in this, it is OK for the + * time being. -- rgerhards, 2008-01-29 + */ +void +dbgoprint(obj_t *pObj, char *fmt, ...) +{ + va_list ap; + char pszWriteBuf[1024]; + size_t lenWriteBuf; + + if(!(Debug && debugging_on)) + return; + + /* a quick and very dirty hack to enable us to display just from those objects + * that we are interested in. So far, this must be changed at compile time (and + * chances are great it is commented out while you read it. Later, this shall + * be selectable via the environment. -- rgerhards, 2008-02-20 + */ +#if 0 + if(objGetObjID(pObj) != OBJexpr) + return; +#endif + va_start(ap, fmt); lenWriteBuf = vsnprintf(pszWriteBuf, sizeof(pszWriteBuf), fmt, ap); - if(lenWriteBuf >= sizeof(pszWriteBuf)) { - /* if our buffer was too small, we simply truncate. TODO: maybe something better? */ - lenWriteBuf = sizeof(pszWriteBuf) - 1; - } va_end(ap); - /* - if(stddbg != NULL) fprintf(stddbg, "%s", pszWriteBuf); - if(altdbg != NULL) fprintf(altdbg, "%s", pszWriteBuf); - */ - if(stddbg != NULL) fwrite(pszWriteBuf, lenWriteBuf, 1, stddbg); - if(altdbg != NULL) fwrite(pszWriteBuf, lenWriteBuf, 1, altdbg); - - if(stddbg != NULL) fflush(stddbg); - if(altdbg != NULL) fflush(altdbg); - pthread_cleanup_pop(1); + dbgprint(pObj, pszWriteBuf, lenWriteBuf); } -#pragma GCC diagnostic warning "-Wempty-body" -#pragma GCC diagnostic ignored "-Wempty-body" /* print some debug output when no object is given * WARNING: duplicate code, see dbgoprin above! */ void dbgprintf(char *fmt, ...) { - static pthread_t ptLastThrdID = 0; - static int bWasNL = 0; va_list ap; - static char pszThrdName[64]; /* 64 is to be on the safe side, anything over 20 is bad... */ - static char pszWriteBuf[1024]; + char pszWriteBuf[1024]; size_t lenWriteBuf; - struct timespec t; if(!(Debug && debugging_on)) return; - pthread_mutex_lock(&mutdbgprintf); - pthread_cleanup_push(dbgMutexCancelCleanupHdlr, &mutdbgprintf); - - /* The bWasNL handler does not really work. It works if no thread - * switching occurs during non-NL messages. Else, things are messed - * up. Anyhow, it works well enough to provide useful help during - * getting this up and running. It is questionable if the extra effort - * is worth fixing it, giving the limited appliability. - * rgerhards, 2005-10-25 - * I have decided that it is not worth fixing it - especially as it works - * pretty well. - * rgerhards, 2007-06-15 - */ - if(ptLastThrdID != pthread_self()) { - if(!bWasNL) { - if(stddbg != NULL) fprintf(stddbg, "\n"); - if(altdbg != NULL) fprintf(altdbg, "\n"); - bWasNL = 1; - } - ptLastThrdID = pthread_self(); - } - - /* do not cache the thread name, as the caller might have changed it - * TODO: optimized, invalidate cache when new name is set - */ - dbgGetThrdName(pszThrdName, sizeof(pszThrdName), ptLastThrdID, 0); - - if(bWasNL) { - if(bPrintTime) { - clock_gettime(CLOCK_REALTIME, &t); - if(stddbg != NULL) fprintf(stddbg, "%4.4ld.%9.9ld:", (long) (t.tv_sec % 10000), t.tv_nsec); - if(altdbg != NULL) fprintf(altdbg, "%4.4ld.%9.9ld:", (long) (t.tv_sec % 10000), t.tv_nsec); - } - if(stddbg != NULL) fprintf(stddbg, "%s: ", pszThrdName); - if(altdbg != NULL) fprintf(altdbg, "%s: ", pszThrdName); - } - bWasNL = (*(fmt + strlen(fmt) - 1) == '\n') ? 1 : 0; va_start(ap, fmt); lenWriteBuf = vsnprintf(pszWriteBuf, sizeof(pszWriteBuf), fmt, ap); - if(lenWriteBuf >= sizeof(pszWriteBuf)) { - /* if our buffer was too small, we simply truncate. TODO: maybe something better? */ - lenWriteBuf = sizeof(pszWriteBuf) - 1; - } va_end(ap); - /* - if(stddbg != NULL) fprintf(stddbg, "%s", pszWriteBuf); - if(altdbg != NULL) fprintf(altdbg, "%s", pszWriteBuf); - */ - if(stddbg != NULL) fwrite(pszWriteBuf, lenWriteBuf, 1, stddbg); - if(altdbg != NULL) fwrite(pszWriteBuf, lenWriteBuf, 1, altdbg); - - if(stddbg != NULL) fflush(stddbg); - if(altdbg != NULL) fflush(altdbg); - pthread_cleanup_pop(1); + dbgprint(NULL, pszWriteBuf, lenWriteBuf); } -#pragma GCC diagnostic warning "-Wempty-body" void tester(void) { @@ -941,7 +890,7 @@ ENDfunc int dbgEntrFunc(dbgFuncDB_t **ppFuncDB, const char *file, const char *func, int line) { int iStackPtr = 0; /* TODO: find some better default, this one hurts the least, but it is not clean */ - dbgThrdInfo_t *pThrd = dbgGetThrdInfo(); + dbgThrdInfo_t *pThrd; dbgFuncDBListEntry_t *pFuncDBListEntry; unsigned int i; dbgFuncDB_t *pFuncDB; @@ -952,6 +901,8 @@ int dbgEntrFunc(dbgFuncDB_t **ppFuncDB, const char *file, const char *func, int pFuncDB = *ppFuncDB; assert((pFuncDB == NULL) || (pFuncDB->magic == dbgFUNCDB_MAGIC)); + pThrd = dbgGetThrdInfo(); /* we must do this AFTER the mutexes are initialized! */ + if(pFuncDB == NULL) { /* we do not yet have a funcDB and need to create a new one. We also add it * to the linked list of funcDBs. Please note that when a module is unloaded and @@ -1206,7 +1157,7 @@ dbgGetRuntimeOptions(void) uchar *optname; /* set some defaults */ - stddbg = stdout; + stddbg = 1; if((pszOpts = (uchar*) getenv("RSYSLOG_DEBUG")) != NULL) { /* we have options set, so let's process them */ @@ -1248,7 +1199,7 @@ dbgGetRuntimeOptions(void) } else if(!strcasecmp((char*)optname, "nologtimestamp")) { bPrintTime = 0; } else if(!strcasecmp((char*)optname, "nostdout")) { - stddbg = NULL; + stddbg = -1; } else if(!strcasecmp((char*)optname, "noaborttrace")) { bAbortTrace = 0; } else if(!strcasecmp((char*)optname, "filetrace")) { @@ -1273,7 +1224,7 @@ dbgGetRuntimeOptions(void) rsRetVal dbgClassInit(void) { - DEFiRet; + rsRetVal iRet; /* do not use DEFiRet, as this makes calls into the debug system! */ struct sigaction sigAct; sigset_t sigSet; @@ -1287,8 +1238,7 @@ rsRetVal dbgClassInit(void) pthread_mutex_init(&mutFuncDBList, NULL); pthread_mutex_init(&mutMutLog, NULL); pthread_mutex_init(&mutCallStack, NULL); - pthread_mutex_init(&mutdbgprintf, NULL); - pthread_mutex_init(&mutdbgoprint, NULL); + pthread_mutex_init(&mutdbgprint, NULL); /* while we try not to use any of the real rsyslog code (to avoid infinite loops), we * need to have the ability to query object names. Thus, we need to obtain a pointer to @@ -1310,7 +1260,7 @@ rsRetVal dbgClassInit(void) if(pszAltDbgFileName != NULL) { /* we have a secondary file, so let's open it) */ - if((altdbg = fopen(pszAltDbgFileName, "w")) == NULL) { + if((altdbg = open(pszAltDbgFileName, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY, S_IRUSR|S_IWUSR)) == -1) { fprintf(stderr, "alternate debug file could not be opened, ignoring. Error: %s\n", strerror(errno)); } } @@ -1318,7 +1268,7 @@ rsRetVal dbgClassInit(void) dbgSetThrdName((uchar*)"main thread"); finalize_it: - RETiRet; + return(iRet); } @@ -1330,8 +1280,8 @@ rsRetVal dbgClassExit(void) if(bPrintAllDebugOnExit) dbgPrintAllDebugInfo(); - if(altdbg != NULL) - fclose(altdbg); + if(altdbg != -1) + close(altdbg); /* now free all of our memory to make the memory debugger happy... */ pFuncDBListEtry = pFuncDBListRoot; -- cgit From b8e82d59b7aba7da65e4244a29b92274aa50d08d Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 26 Sep 2008 16:10:05 +0200 Subject: fixed potential race condition on HUP and termination --- runtime/wtp.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime') diff --git a/runtime/wtp.c b/runtime/wtp.c index 8b041ea2..54f12b2b 100644 --- a/runtime/wtp.c +++ b/runtime/wtp.c @@ -169,7 +169,9 @@ wtpWakeupAllWrkr(wtp_t *pThis) DEFiRet; ISOBJ_TYPE_assert(pThis, wtp); + d_pthread_mutex_lock(pThis->pmutUsr); pthread_cond_broadcast(pThis->pcondBusy); + d_pthread_mutex_unlock(pThis->pmutUsr); RETiRet; } -- cgit From 92c2e09d19bef9dd10d2e85a663925124d6e00e4 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 26 Sep 2008 18:40:28 +0200 Subject: some more threading cleanup - removed newly-introduced potential deadlock in debug system - removed unnecessary pthread_cond_signal - a bit general cleanup --- runtime/debug.c | 15 +++++++++++++-- runtime/queue.c | 8 ++------ 2 files changed, 15 insertions(+), 8 deletions(-) (limited to 'runtime') diff --git a/runtime/debug.c b/runtime/debug.c index 15e9201b..b0bf76ea 100644 --- a/runtime/debug.c +++ b/runtime/debug.c @@ -772,6 +772,17 @@ dbgprint(obj_t *pObj, char *pszMsg, size_t lenMsg) char pszWriteBuf[1024]; size_t lenWriteBuf; struct timespec t; + uchar *pszObjName = NULL; + + /* we must get the object name before we lock the mutex, because the object + * potentially calls back into us. If we locked the mutex, we would deadlock + * ourselfs. On the other hand, the GetName call needs not to be protected, as + * this thread has a valid reference. If such an object is deleted by another + * thread, we are in much more trouble than just for dbgprint(). -- rgerhards, 2008-09-26 + */ + if(pObj != NULL) { + pszObjName = obj.GetName(pObj); + } pthread_mutex_lock(&mutdbgprint); pthread_cleanup_push(dbgMutexCancelCleanupHdlr, &mutdbgprint); @@ -810,8 +821,8 @@ dbgprint(obj_t *pObj, char *pszMsg, size_t lenMsg) if(stddbg != -1) write(stddbg, pszWriteBuf, lenWriteBuf); if(altdbg != -1) write(altdbg, pszWriteBuf, lenWriteBuf); /* print object name header if we have an object */ - if(pObj != NULL) { - lenWriteBuf = snprintf(pszWriteBuf, sizeof(pszWriteBuf), "%s: ", obj.GetName(pObj)); + if(pszObjName != NULL) { + lenWriteBuf = snprintf(pszWriteBuf, sizeof(pszWriteBuf), "%s: ", pszObjName); if(stddbg != -1) write(stddbg, pszWriteBuf, lenWriteBuf); if(altdbg != -1) write(altdbg, pszWriteBuf, lenWriteBuf); } diff --git a/runtime/queue.c b/runtime/queue.c index c0a37019..3fae4aa7 100644 --- a/runtime/queue.c +++ b/runtime/queue.c @@ -2092,7 +2092,6 @@ queueEnqObj(queue_t *pThis, flowControl_t flowCtlType, void *pUsr) { DEFiRet; int iCancelStateSave; - int i; struct timespec t; ISOBJ_TYPE_assert(pThis, queue); @@ -2172,13 +2171,10 @@ queueEnqObj(queue_t *pThis, flowControl_t flowCtlType, void *pUsr) finalize_it: if(pThis->qType != QUEUETYPE_DIRECT) { /* make sure at least one worker is running. */ - if(pThis->qType != QUEUETYPE_DIRECT) { - queueAdviseMaxWorkers(pThis); - } + queueAdviseMaxWorkers(pThis); + dbgoprint((obj_t*) pThis, "EnqueueMsg advised worker start\n"); /* and release the mutex */ - i = pthread_cond_signal(&pThis->notEmpty); d_pthread_mutex_unlock(pThis->mut); - dbgoprint((obj_t*) pThis, "EnqueueMsg signaled condition (%d)\n", i); pthread_setcancelstate(iCancelStateSave, NULL); } -- cgit From 5a1a73b4326d97789b5640225be0e25cb8dd8de7 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 30 Sep 2008 14:20:01 +0200 Subject: improved threading - changed sequence when awakening thread - removed no longer needed condition variable - EXPERIMENTALLY added mutex guarding to hostname lookups this is to be removed if it does not have any verifyable useful effect --- runtime/net.c | 26 ++++++++++++++++++++++---- runtime/queue.c | 7 ++++++- runtime/wti.c | 9 ++++----- runtime/wti.h | 1 - runtime/wtp.c | 5 +++-- 5 files changed, 35 insertions(+), 13 deletions(-) (limited to 'runtime') diff --git a/runtime/net.c b/runtime/net.c index f5b8f46a..892edf4a 100644 --- a/runtime/net.c +++ b/runtime/net.c @@ -674,6 +674,25 @@ finalize_it: } + +/* This is a synchronized getnameinfo() version, because we learned + * (via drd/valgrind) that getnameinfo() seems to have some multi-threading + * issues. -- rgerhards, 2008-09-30 + */ +static int +mygetnameinfo(const struct sockaddr *sa, socklen_t salen, + char *host, size_t hostlen, + char *serv, size_t servlen, int flags) +{ + static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; + int i; + + pthread_mutex_lock(&mut); + i = getnameinfo(sa, salen, host, hostlen, serv, servlen, flags); + pthread_mutex_unlock(&mut); + return i; +} + /* Print an allowed sender list. The caller must tell us which one. * iListToPrint = 1 means UDP, 2 means TCP * rgerhards, 2005-09-27 @@ -708,7 +727,7 @@ void PrintAllowedSenders(int iListToPrint) if (F_ISSET(pSender->allowedSender.flags, ADDR_NAME)) dbgprintf ("\t%s\n", pSender->allowedSender.addr.HostWildcard); else { - if(getnameinfo (pSender->allowedSender.addr.NetAddr, + if(mygetnameinfo (pSender->allowedSender.addr.NetAddr, SALEN(pSender->allowedSender.addr.NetAddr), (char*)szIP, 64, NULL, 0, NI_NUMERICHOST) == 0) { dbgprintf ("\t%s/%u\n", szIP, pSender->SignificantBits); @@ -956,7 +975,6 @@ should_use_so_bsdcompat(void) #define SO_BSDCOMPAT 0 #endif - /* get the hostname of the message source. This was originally in cvthname() * but has been moved out of it because of clarity and fuctional separation. * It must be provided by the socket we received the message on as well as @@ -982,7 +1000,7 @@ gethname(struct sockaddr_storage *f, uchar *pszHostFQDN, uchar *ip) assert(f != NULL); assert(pszHostFQDN != NULL); - error = getnameinfo((struct sockaddr *)f, SALEN((struct sockaddr *)f), + error = mygetnameinfo((struct sockaddr *)f, SALEN((struct sockaddr *)f), (char*) ip, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); if (error) { @@ -997,7 +1015,7 @@ gethname(struct sockaddr_storage *f, uchar *pszHostFQDN, uchar *ip) sigaddset(&nmask, SIGHUP); pthread_sigmask(SIG_BLOCK, &nmask, &omask); - error = getnameinfo((struct sockaddr *)f, SALEN((struct sockaddr *) f), + error = mygetnameinfo((struct sockaddr *)f, SALEN((struct sockaddr *) f), (char*)pszHostFQDN, NI_MAXHOST, NULL, 0, NI_NAMEREQD); if (error == 0) { diff --git a/runtime/queue.c b/runtime/queue.c index 3fae4aa7..76c2f10f 100644 --- a/runtime/queue.c +++ b/runtime/queue.c @@ -1430,8 +1430,13 @@ queueDequeueConsumable(queue_t *pThis, wti_t *pWti, int iCancelStateSave) pthread_cond_broadcast(&pThis->belowLightDlyWtrMrk); } - d_pthread_mutex_unlock(pThis->mut); + /* rgerhards, 2008-09-30: I reversed the order of cond_signal und mutex_unlock + * as of the pthreads recommendation on predictable scheduling behaviour. I don't see + * any problems caused by this, but I add this comment in case some will be seen + * in the next time. + */ pthread_cond_signal(&pThis->notFull); + d_pthread_mutex_unlock(pThis->mut); pthread_setcancelstate(iCancelStateSave, NULL); /* WE ARE NO LONGER PROTECTED BY THE MUTEX */ diff --git a/runtime/wti.c b/runtime/wti.c index 13554232..365b25d5 100644 --- a/runtime/wti.c +++ b/runtime/wti.c @@ -113,6 +113,9 @@ wtiSetState(wti_t *pThis, qWrkCmd_t tCmd, int bActiveOnly, int bLockMutex) wtiGetDbgHdr(pThis), tCmd, pThis->tCurrCmd); } else { dbgprintf("%s: receiving command %d\n", wtiGetDbgHdr(pThis), tCmd); + /* we could replace this with a simple if, but we leave the switch in in case we need + * to add something at a later stage. -- rgerhards, 2008-09-30 + */ switch(tCmd) { case eWRKTHRD_TERMINATING: /* TODO: re-enable meaningful debug msg! (via function callback?) @@ -123,10 +126,8 @@ wtiSetState(wti_t *pThis, qWrkCmd_t tCmd, int bActiveOnly, int bLockMutex) pthread_cond_signal(&pThis->condExitDone); dbgprintf("%s: worker terminating\n", wtiGetDbgHdr(pThis)); break; - case eWRKTHRD_RUNNING: - pthread_cond_signal(&pThis->condInitDone); - break; /* these cases just to satisfy the compiler, we do (yet) not act an them: */ + case eWRKTHRD_RUNNING: case eWRKTHRD_STOPPED: case eWRKTHRD_RUN_CREATED: case eWRKTHRD_RUN_INIT: @@ -190,7 +191,6 @@ CODESTARTobjDestruct(wti) d_pthread_mutex_unlock(&pThis->mut); /* actual destruction */ - pthread_cond_destroy(&pThis->condInitDone); pthread_cond_destroy(&pThis->condExitDone); pthread_mutex_destroy(&pThis->mut); @@ -202,7 +202,6 @@ ENDobjDestruct(wti) /* Standard-Constructor for the wti object */ BEGINobjConstruct(wti) /* be sure to specify the object type also in END macro! */ - pthread_cond_init(&pThis->condInitDone, NULL); pthread_cond_init(&pThis->condExitDone, NULL); pthread_mutex_init(&pThis->mut, NULL); ENDobjConstruct(wti) diff --git a/runtime/wti.h b/runtime/wti.h index b3d92473..0cd6744e 100644 --- a/runtime/wti.h +++ b/runtime/wti.h @@ -35,7 +35,6 @@ typedef struct wti_s { qWrkCmd_t tCurrCmd; /* current command to be carried out by worker */ obj_t *pUsrp; /* pointer to an object meaningful for current user pointer (e.g. queue pUsr data elemt) */ wtp_t *pWtp; /* my worker thread pool (important if only the work thread instance is passed! */ - pthread_cond_t condInitDone; /* signaled when the thread startup is done (once per thread existance) */ pthread_cond_t condExitDone; /* signaled when the thread exit is done (once per thread existance) */ pthread_mutex_t mut; int bShutdownRqtd; /* shutdown for this thread requested? 0 - no , 1 - yes */ diff --git a/runtime/wtp.c b/runtime/wtp.c index 54f12b2b..734c8d57 100644 --- a/runtime/wtp.c +++ b/runtime/wtp.c @@ -304,11 +304,12 @@ wtpShutdownAll(wtp_t *pThis, wtpState_t tShutdownCmd, struct timespec *ptTimeout rsRetVal wtpSignalWrkrTermination(wtp_t *pThis) { DEFiRet; - /* I leave the mutex code here out as it give as deadlocks. I think it is not really + /* I leave the mutex code here out as it gives us deadlocks. I think it is not really * needed and we are on the safe side. I leave this comment in if practice proves us - * wrong. The whole thing should be removed after half a your or year if we see there + * wrong. The whole thing should be removed after half a year or year if we see there * actually is no issue (or revisit it from a theoretical POV). * rgerhards, 2008-01-28 + * revisited 2008-09-30, still a bit unclear, leave in */ /*TODO: mutex or not mutex, that's the question ;)DEFVARS_mutexProtection;*/ -- cgit From e30fe2842e3ce9f06098c18f0f962596059684ca Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 30 Sep 2008 15:57:10 +0200 Subject: prevent getnameinfo() from being cancelled ... but removed the mutex, as the problem seems to be in cancel processing, so the mutex is no longer necessary --- runtime/net.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/net.c b/runtime/net.c index 892edf4a..31b02f1a 100644 --- a/runtime/net.c +++ b/runtime/net.c @@ -685,11 +685,14 @@ mygetnameinfo(const struct sockaddr *sa, socklen_t salen, char *serv, size_t servlen, int flags) { static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; + int iCancelStateSave; int i; - pthread_mutex_lock(&mut); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave); + //pthread_mutex_lock(&mut); i = getnameinfo(sa, salen, host, hostlen, serv, servlen, flags); - pthread_mutex_unlock(&mut); + //pthread_mutex_unlock(&mut); + pthread_setcancelstate(iCancelStateSave, NULL); return i; } -- cgit From 925bea71772a23bc184a1da554fe310f39103b0c Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Wed, 1 Oct 2008 19:01:53 +0200 Subject: bugfix: segfault when main queue was in direct mode --- runtime/msg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/msg.c b/runtime/msg.c index d5e1fde9..6c1b9ff3 100644 --- a/runtime/msg.c +++ b/runtime/msg.c @@ -342,8 +342,8 @@ CODESTARTobjDestruct(msg) MsgUnlock(pThis); funcDeleteMutex(pThis); } else { - pThis = NULL; /* tell framework not to destructing the object! */ MsgUnlock(pThis); + pThis = NULL; /* tell framework not to destructing the object! */ } ENDobjDestruct(msg) -- cgit From 86f76c1299b7827549a0cd754960347af627e18d Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 2 Oct 2008 10:44:44 +0200 Subject: removed no longer needed mutex calls problem source is that getnameinfo() is not cancel-safe, not that it is not thread-safe. It is now guarded against cancellation. --- runtime/net.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'runtime') diff --git a/runtime/net.c b/runtime/net.c index 31b02f1a..85d6813b 100644 --- a/runtime/net.c +++ b/runtime/net.c @@ -684,14 +684,11 @@ mygetnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags) { - static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; int iCancelStateSave; int i; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave); - //pthread_mutex_lock(&mut); i = getnameinfo(sa, salen, host, hostlen, serv, servlen, flags); - //pthread_mutex_unlock(&mut); pthread_setcancelstate(iCancelStateSave, NULL); return i; } -- cgit From 1908bae50491624d270c73609ea45634c15246fd Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 2 Oct 2008 11:01:08 +0200 Subject: fixed git merge bug (duplicate code could not be detected) --- runtime/net.c | 19 ------------------- 1 file changed, 19 deletions(-) (limited to 'runtime') diff --git a/runtime/net.c b/runtime/net.c index 38b41389..44c9008a 100644 --- a/runtime/net.c +++ b/runtime/net.c @@ -695,25 +695,6 @@ finalize_it: } - -/* This is a synchronized getnameinfo() version, because we learned - * (via drd/valgrind) that getnameinfo() seems to have some multi-threading - * issues. -- rgerhards, 2008-09-30 - */ -static int -mygetnameinfo(const struct sockaddr *sa, socklen_t salen, - char *host, size_t hostlen, - char *serv, size_t servlen, int flags) -{ - int iCancelStateSave; - int i; - - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave); - i = getnameinfo(sa, salen, host, hostlen, serv, servlen, flags); - pthread_setcancelstate(iCancelStateSave, NULL); - return i; -} - /* Print an allowed sender list. The caller must tell us which one. * iListToPrint = 1 means UDP, 2 means TCP * rgerhards, 2005-09-27 -- cgit From 02bb3c901d9b14e8a08b111dde151f1ed4d2d1da Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 2 Oct 2008 15:52:24 +0200 Subject: consolidated time calls during msg object creation ...this improves performance and consistency and also fixes a bug where subsecond time properties generated by imfile, imklog and internal messages could be slightly inconsistent. --- runtime/msg.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'runtime') diff --git a/runtime/msg.c b/runtime/msg.c index 6c1b9ff3..9c2e3f17 100644 --- a/runtime/msg.c +++ b/runtime/msg.c @@ -257,7 +257,16 @@ rsRetVal msgConstruct(msg_t **ppThis) pM->iRefCount = 1; pM->iSeverity = -1; pM->iFacility = -1; + + /* we initialize both timestamps to contain the current time, so that they + * are consistent. Also, this saves us from doing any further time calls just + * to obtain a timestamp. The memcpy() should not really make a difference, + * especially as I think there is no codepath currently where it would not be + * required (after I have cleaned up the pathes ;)). -- rgerhards, 2008-10-02 + */ datetime.getCurrTime(&(pM->tRcvdAt)); + memcpy(&pM->tTIMESTAMP, &pM->tRcvdAt, sizeof(struct syslogTime)); + objConstructSetObjInfo(pM); /* DEV debugging only! dbgprintf("msgConstruct\t0x%x, ref 1\n", (int)pM);*/ -- cgit From 19ccebbf4c49c5f9954c7a1a092399303156a1f3 Mon Sep 17 00:00:00 2001 From: Marius Tomaschewski Date: Mon, 20 Oct 2008 17:36:31 +0200 Subject: added capability to support multiple module search pathes. Signed-off-by: Rainer Gerhards --- runtime/Makefile.am | 4 ++ runtime/modules.c | 108 +++++++++++++++++++++++++++++++++++----------------- 2 files changed, 77 insertions(+), 35 deletions(-) (limited to 'runtime') diff --git a/runtime/Makefile.am b/runtime/Makefile.am index 81a9d5bd..8dd8ad12 100644 --- a/runtime/Makefile.am +++ b/runtime/Makefile.am @@ -83,7 +83,11 @@ librsyslog_la_SOURCES = \ # the files with ../ we need to work on - so that they either become part of the # runtime or will no longer be needed. -- rgerhards, 2008-06-13 +if WITH_MODDIRS +librsyslog_la_CPPFLAGS = -D_PATH_MODDIR=\"$(pkglibdir)/:$(moddirs)\" $(pthreads_cflags) +else librsyslog_la_CPPFLAGS = -D_PATH_MODDIR=\"$(pkglibdir)/\" -I$(top_srcdir) $(pthreads_cflags) +endif #librsyslog_la_LDFLAGS = -module -avoid-version librsyslog_la_LIBADD = $(dl_libs) $(rt_libs) diff --git a/runtime/modules.c b/runtime/modules.c index ceb4768c..d5730ede 100644 --- a/runtime/modules.c +++ b/runtime/modules.c @@ -570,6 +570,8 @@ Load(uchar *pModName) int bHasExtension; void *pModHdlr, *pModInit; modInfo_t *pModInfo; + uchar *pModDirCurr, *pModDirNext; + int iLoadCnt; assert(pModName != NULL); dbgprintf("Requested to load module '%s'\n", pModName); @@ -591,48 +593,84 @@ Load(uchar *pModName) pModInfo = GetNxt(pModInfo); } - /* now build our load module name */ - if(*pModName == '/') { - *szPath = '\0'; /* we do not need to append the path - its already in the module name */ - iPathLen = 0; - } else { - *szPath = '\0'; - strncat((char *) szPath, (pModDir == NULL) ? _PATH_MODDIR : (char*) pModDir, sizeof(szPath) - 1); - iPathLen = strlen((char*) szPath); - if((szPath[iPathLen - 1] != '/')) { - if((iPathLen <= sizeof(szPath) - 2)) { - szPath[iPathLen++] = '/'; - szPath[iPathLen] = '\0'; - } else { - errmsg.LogError(0, RS_RET_MODULE_LOAD_ERR_PATHLEN, "could not load module '%s', path too long\n", pModName); + pModDirCurr = (uchar *)((pModDir == NULL) ? _PATH_MODDIR : (char *)pModDir); + pModDirNext = NULL; + pModHdlr = NULL; + iLoadCnt = 0; + do { + /* now build our load module name */ + if(*pModName == '/') { + *szPath = '\0'; /* we do not need to append the path - its already in the module name */ + iPathLen = 0; + } else { + *szPath = '\0'; + + iPathLen = strlen((char *)pModDirCurr); + pModDirNext = (uchar *)strchr((char *)pModDirCurr, ':'); + if(pModDirNext) + iPathLen = (size_t)(pModDirNext - pModDirCurr); + + if(iPathLen == 0) { + if(pModDirNext) { + pModDirCurr = pModDirNext + 1; + continue; + } + break; + } else if(iPathLen > sizeof(szPath) - 1) { + errmsg.LogError(0, NO_ERRCODE, "could not load module '%s', module path too long\n", pModName); ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_PATHLEN); } + + strncat((char *) szPath, (char *)pModDirCurr, iPathLen); + iPathLen = strlen((char*) szPath); + + if(pModDirNext) + pModDirCurr = pModDirNext + 1; + + if((szPath[iPathLen - 1] != '/')) { + if((iPathLen <= sizeof(szPath) - 2)) { + szPath[iPathLen++] = '/'; + szPath[iPathLen] = '\0'; + } else { + errmsg.LogError(0, RS_RET_MODULE_LOAD_ERR_PATHLEN, "could not load module '%s', path too long\n", pModName); + ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_PATHLEN); + } + } } - } - /* ... add actual name ... */ - strncat((char *) szPath, (char *) pModName, sizeof(szPath) - iPathLen - 1); + /* ... add actual name ... */ + strncat((char *) szPath, (char *) pModName, sizeof(szPath) - iPathLen - 1); + + /* now see if we have an extension and, if not, append ".so" */ + if(!bHasExtension) { + /* we do not have an extension and so need to add ".so" + * TODO: I guess this is highly importable, so we should change the + * algo over time... -- rgerhards, 2008-03-05 + */ + /* ... so now add the extension */ + strncat((char *) szPath, ".so", sizeof(szPath) - strlen((char*) szPath) - 1); + iPathLen += 3; + } - /* now see if we have an extension and, if not, append ".so" */ - if(!bHasExtension) { - /* we do not have an extension and so need to add ".so" - * TODO: I guess this is highly importable, so we should change the - * algo over time... -- rgerhards, 2008-03-05 - */ - /* ... so now add the extension */ - strncat((char *) szPath, ".so", sizeof(szPath) - strlen((char*) szPath) - 1); - iPathLen += 3; - } + if(iPathLen + strlen((char*) pModName) >= sizeof(szPath)) { + errmsg.LogError(0, RS_RET_MODULE_LOAD_ERR_PATHLEN, "could not load module '%s', path too long\n", pModName); + ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_PATHLEN); + } - if(iPathLen + strlen((char*) pModName) >= sizeof(szPath)) { - errmsg.LogError(0, RS_RET_MODULE_LOAD_ERR_PATHLEN, "could not load module '%s', path too long\n", pModName); - ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_PATHLEN); - } + /* complete load path constructed, so ... GO! */ + dbgprintf("loading module '%s'\n", szPath); + pModHdlr = dlopen((char *) szPath, RTLD_NOW); + iLoadCnt++; + + } while(pModHdlr == NULL && *pModName != '/' && pModDirNext); - /* complete load path constructed, so ... GO! */ - dbgprintf("loading module '%s'\n", szPath); - if(!(pModHdlr = dlopen((char *) szPath, RTLD_NOW))) { - errmsg.LogError(0, RS_RET_MODULE_LOAD_ERR_DLOPEN, "could not load module '%s', dlopen: %s\n", szPath, dlerror()); + if(!pModHdlr) { + if(iLoadCnt) { + errmsg.LogError(0, RS_RET_MODULE_LOAD_ERR_DLOPEN, "could not load module '%s', dlopen: %s\n", szPath, dlerror()); + } else { + errmsg.LogError(0, NO_ERRCODE, "could not load module '%s', ModDir was '%s'\n", szPath, + ((pModDir == NULL) ? _PATH_MODDIR : (char *)pModDir)); + } ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_DLOPEN); } if(!(pModInit = dlsym(pModHdlr, "modInit"))) { -- cgit