From 1d96a98daf4ac4c4ec9e664e328f1aac4bf6af9e Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 27 Jul 2007 16:55:40 +0000 Subject: - added omsr object (objomsr.c, objomsr.h) - template request for output modules - changed doAction() interface - templates and output string generation for doActon() is now fully --- module-template.h | 31 +++++-- modules.h | 6 +- objomsr.c | 3 +- objomsr.h | 7 +- omdiscard.c | 1 + omfile.c | 77 +++++++---------- omfwd.c | 31 +++---- ommysql.c | 44 +++------- omshell.c | 6 +- omusrmsg.c | 42 +++------- rsyslog.h | 1 + syslogd-types.h | 1 + syslogd.c | 245 +++++++++++++++++++++++++++++++++++++----------------- syslogd.h | 6 +- 14 files changed, 276 insertions(+), 225 deletions(-) diff --git a/module-template.h b/module-template.h index 2d34893e..470d81d8 100644 --- a/module-template.h +++ b/module-template.h @@ -25,6 +25,8 @@ #ifndef MODULE_TEMPLATE_H_INCLUDED #define MODULE_TEMPLATE_H_INCLUDED 1 +#include "objomsr.h" + /* to following macros are used to generate function headers and standard * functionality. It works as follows (described on the sample case of * createInstance()): @@ -89,13 +91,13 @@ static rsRetVal isCompatibleWithFeature(syslogFeature __attribute__((unused)) eF /* doAction() */ #define BEGINdoAction \ -static rsRetVal doAction(selector_t *f, uchar __attribute__((unused)) *pMsg, instanceData __attribute__((unused)) *pData)\ +static rsRetVal doAction(selector_t *f, uchar __attribute__((unused)) **ppString, unsigned __attribute__((unused)) iMsgOpts, instanceData __attribute__((unused)) *pData)\ {\ rsRetVal iRet = RS_RET_OK; #define CODESTARTdoAction \ assert(f != NULL);\ - assert(pMsg != NULL); + assert(ppString != NULL); #define ENDdoAction \ return iRet;\ @@ -191,10 +193,18 @@ static rsRetVal getWriteFDForSelect(selector_t *f, void *pModData, short *fd)\ * Extra comments: * try to process a selector action line. Checks if the action * applies to this module and, if so, processed it. If not, it - * is left untouched. The driver will then call another module + * is left untouched. The driver will then call another module. + * On exit, ppModData must point to instance data. Also, a string + * request object must be created and filled. A macro is defined + * for that. + * For the most usual case, we have defined a macro below. + * If more than one string is requested, the macro can be used together + * with own code that overwrites the entry count. In this case, the + * macro must come before the own code. It is recommended to be + * placed right after CODESTARTparseSelectorAct. */ #define BEGINparseSelectorAct \ -static rsRetVal parseSelectorAct(uchar **pp, selector_t *f, void **ppModData)\ +static rsRetVal parseSelectorAct(uchar **pp, selector_t *f, void **ppModData, omodStringRequest_t **ppOMSR)\ {\ rsRetVal iRet = RS_RET_OK;\ uchar *p;\ @@ -204,13 +214,24 @@ static rsRetVal parseSelectorAct(uchar **pp, selector_t *f, void **ppModData)\ assert(pp != NULL);\ assert(ppModData != NULL);\ assert(f != NULL);\ + assert(ppOMSR != NULL);\ p = *pp; +#define CODE_STD_STRING_REQUESTparseSelectorAct(NumStrReqEntries) \ + if((iRet = OMSRconstruct(ppOMSR, NumStrReqEntries)) != RS_RET_OK)\ + goto do_abort;\ + #define ENDparseSelectorAct \ +do_abort:\ if(iRet == RS_RET_OK) {\ *ppModData = pData;\ *pp = p;\ - }\ + } else {\ + /* cleanup, we failed */\ + if(*ppOMSR != NULL)\ + OMSRdestruct(*ppOMSR);\ + *ppOMSR = NULL;\ + }\ return iRet;\ } diff --git a/modules.h b/modules.h index 19695eba..e6398a2e 100644 --- a/modules.h +++ b/modules.h @@ -30,6 +30,8 @@ #ifndef MODULES_H_INCLUDED #define MODULES_H_INCLUDED 1 +#include "objomsr.h" + typedef enum eModType_ { eMOD_IN, /* input module */ eMOD_OUT, /* output module */ @@ -77,8 +79,8 @@ typedef struct moduleInfo { struct {/* data for output modules */ /* below: perform the configured action */ - rsRetVal (*doAction)(selector_t*, uchar*, void*); - rsRetVal (*parseSelectorAct)(uchar**, selector_t*, void**); + rsRetVal (*doAction)(selector_t*, uchar**, unsigned, void*); + rsRetVal (*parseSelectorAct)(uchar**, selector_t*, void**,omodStringRequest_t**); } om; } mod; } modInfo_t; diff --git a/objomsr.c b/objomsr.c index 85aaee95..00e5e668 100644 --- a/objomsr.c +++ b/objomsr.c @@ -101,6 +101,7 @@ rsRetVal OMSRsetEntry(omodStringRequest_t *pThis, int iEntry, uchar *pTplName, i assert(pTplName != NULL); assert(iEntry < pThis->iNumEntries); +printf("OMSRsetEntry %s: %d\n", pTplName, iTplOpts); if(pThis->ppTplName[iEntry] != NULL) free(pThis->ppTplName[iEntry]); pThis->ppTplName[iEntry] = pTplName; /* TODO: do we need to copy? */ @@ -135,7 +136,7 @@ int OMSRgetEntry(omodStringRequest_t *pThis, int iEntry, uchar **ppTplName, int *ppTplName = pThis->ppTplName[iEntry]; *piTplOpts = pThis->piTplOpts[iEntry]; - return pThis->iNumEntries; + return RS_RET_OK; } /* * vi:set ai: diff --git a/objomsr.h b/objomsr.h index c84b2870..f6eae475 100644 --- a/objomsr.h +++ b/objomsr.h @@ -22,10 +22,15 @@ #ifndef OBJOMSR_H_INCLUDED #define OBJOMSR_H_INCLUDED +/* define flags for required template options */ +#define OMSR_NO_RQD_TPL_OPTS 0 +#define OMSR_RQD_TPL_OPT_SQL 1 +/* next option is 2, 4, 8, ... */ + struct omodStringRequest_s { /* strings requested by output module for doAction() */ int iNumEntries; /* number of array entries for data elements below */ uchar **ppTplName; /* pointer to array of template names */ - int *piTplOpts; /* pointer to array of check-options when pulling template */ + int *piTplOpts;/* pointer to array of check-options when pulling template */ }; typedef struct omodStringRequest_s omodStringRequest_t; diff --git a/omdiscard.c b/omdiscard.c index 54eab33d..3a29d2d3 100644 --- a/omdiscard.c +++ b/omdiscard.c @@ -76,6 +76,7 @@ ENDfreeInstance BEGINparseSelectorAct CODESTARTparseSelectorAct +CODE_STD_STRING_REQUESTparseSelectorAct(0) pData = NULL; /* this action does not have any instance data */ p = *pp; diff --git a/omfile.c b/omfile.c index 701e96e9..13e436bf 100644 --- a/omfile.c +++ b/omfile.c @@ -96,13 +96,13 @@ ENDisCompatibleWithFeature BEGINdbgPrintInstInfo CODESTARTdbgPrintInstInfo if(pData->bDynamicName) { - printf("[dynamic]\n\ttemplate='%s'\n" + printf("[dynamic]\n\ttemplate='%s'" "\tfile cache size=%d\n" "\tcreate directories: %s\n" "\tfile owner %d, group %d\n" "\tdirectory owner %d, group %d\n" "\tfail if owner/group can not be set: %s\n", - pData->f_fname, + pData->f_fname, pData->iDynaFileCacheSize, pData->bCreateDirs ? "yes" : "no", pData->fileUID, pData->fileGID, @@ -125,7 +125,7 @@ ENDdbgPrintInstInfo * removed. * rgerhards 2005-06-21 */ -static rsRetVal cflineParseOutchannel(selector_t *f, instanceData *pData, uchar* p) +static rsRetVal cflineParseOutchannel(instanceData *pData, uchar* p, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts) { rsRetVal iRet = RS_RET_OK; size_t i; @@ -189,16 +189,7 @@ static rsRetVal cflineParseOutchannel(selector_t *f, instanceData *pData, uchar* if(*p == ';') ++p; /* eat it */ - if((iRet = cflineParseTemplateName(&p, szBuf, - sizeof(szBuf) / sizeof(char))) == RS_RET_OK) { - if(szBuf[0] == '\0') /* no template? */ - strcpy(szBuf, " TradFmt"); /* use default! */ - - iRet = cflineSetTemplateAndIOV(f, szBuf); - - dprintf("[outchannel]filename: '%s', template: '%s', size: %lu\n", pData->f_fname, szBuf, - pData->f_sizeLimit); - } + iRet = cflineParseTemplateName(&p, pOMSR, iEntry, iTplOpts, (uchar*) " TradFmt"); return(iRet); } @@ -309,33 +300,24 @@ static void dynaFileFreeCache(instanceData *pData) } -/* This function handles dynamic file names. It generates a new one - * based on the current message, checks if that file is already open - * and, if not, does everything needed to switch to the new one. +/* This function handles dynamic file names. It checks if the + * requested file name is already open and, if not, does everything + * needed to switch to the it. * Function returns 0 if all went well and non-zero otherwise. * On successful return pData->fd must point to the correct file to * be written. * This is a helper to writeFile(). rgerhards, 2007-07-03 */ -static int prepareDynFile(selector_t *f, instanceData *pData) +static int prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts) { - uchar *newFileName; time_t ttOldest; /* timestamp of oldest element */ int iOldest; int i; int iFirstFree; dynaFileCacheEntry **pCache; - assert(f != NULL); assert(pData != NULL); - if((newFileName = tplToString(pData->pTpl, f->f_pMsg)) == NULL) { - /* memory shortage - there is nothing we can do to resolve it. - * We silently ignore it, this is probably the best we can do. - */ - glblHadMemShortage = TRUE; - dprintf("prepareDynfile(): could not create file name, discarding this request\n"); - return -1; - } + assert(newFileName != NULL); pCache = pData->dynCache; @@ -343,10 +325,8 @@ static int prepareDynFile(selector_t *f, instanceData *pData) * I *hope* this will be a performance enhancement. */ if( (pData->iCurrElt != -1) - && !strcmp((char*) newFileName, - (char*) pCache[pData->iCurrElt])) { + && !strcmp((char*) newFileName, (char*) pCache[pData->iCurrElt])) { /* great, we are all set */ - free(newFileName); pCache[pData->iCurrElt]->lastUsed = time(NULL); /* update timestamp for LRU */ return 0; } @@ -366,7 +346,6 @@ static int prepareDynFile(selector_t *f, instanceData *pData) /* we found our element! */ pData->fd = pCache[i]->fd; pData->iCurrElt = i; - free(newFileName); pCache[i]->lastUsed = time(NULL); /* update timestamp for LRU */ return 0; } @@ -393,7 +372,6 @@ static int prepareDynFile(selector_t *f, instanceData *pData) if(pCache[iFirstFree] == NULL) { glblHadMemShortage = TRUE; dprintf("prepareDynfile(): could not alloc mem, discarding this request\n"); - free(newFileName); return -1; } } @@ -443,17 +421,16 @@ static int prepareDynFile(selector_t *f, instanceData *pData) * message. Otherwise, we could run into a never-ending loop. The bad * news is that we also lose errors on startup messages, but so it is. */ - if(f->f_pMsg->msgFlags & INTERNAL_MSG) + if(iMsgOpts & INTERNAL_MSG) dprintf("Could not open dynaFile, discarding message\n"); else logerrorSz("Could not open dynamic file '%s' - discarding message", (char*)newFileName); - free(newFileName); dynaFileDelCacheEntry(pCache, iFirstFree, 1); return -1; } pCache[iFirstFree]->fd = pData->fd; - pCache[iFirstFree]->pName = newFileName; + pCache[iFirstFree]->pName = (uchar*)strdup((char*)newFileName); /* TODO: check for NULL (very unlikely) */ pCache[iFirstFree]->lastUsed = time(NULL); pData->iCurrElt = iFirstFree; dprintf("Added new entry %d for file cache, file '%s'.\n", @@ -467,7 +444,7 @@ static int prepareDynFile(selector_t *f, instanceData *pData) * will be called for all outputs using file semantics, * for example also for pipes. */ -static rsRetVal writeFile(selector_t *f, uchar *pMsg, instanceData *pData) +static rsRetVal writeFile(selector_t *f, uchar **ppString, unsigned iMsgOpts, instanceData *pData) { off_t actualFileSize; rsRetVal iRet = RS_RET_OK; @@ -479,7 +456,7 @@ static rsRetVal writeFile(selector_t *f, uchar *pMsg, instanceData *pData) * check if it still is ok or a new file needs to be created */ if(pData->bDynamicName) { - if(prepareDynFile(f, pData) != 0) + if(prepareDynFile(pData, ppString[1], iMsgOpts) != 0) return RS_RET_ERR; } @@ -516,7 +493,7 @@ again: } } - if (write(pData->fd, pMsg, strlen((char*)pMsg)) < 0) { + if (write(pData->fd, ppString[0], strlen((char*)ppString[0])) < 0) { int e = errno; /* If a named pipe is full, just ignore it for now @@ -598,7 +575,7 @@ CODESTARTdoAction * all others are doomed. */ if(pData->bDynamicName || (pData->fd != -1)) - iRet = writeFile(f, pMsg, pData); + iRet = writeFile(f, ppString, iMsgOpts, pData); ENDdoAction @@ -628,6 +605,7 @@ CODESTARTparseSelectorAct switch (*p) { case '$': + CODE_STD_STRING_REQUESTparseSelectorAct(1) /* rgerhards 2005-06-21: this is a special setting for output-channel * definitions. In the long term, this setting will probably replace * anything else, but for the time being we must co-exist with the @@ -635,7 +613,7 @@ CODESTARTparseSelectorAct * rgerhards, 2007-07-24: output-channels will go away. We keep them * for compatibility reasons, but seems to have been a bad idea. */ - if((iRet = cflineParseOutchannel(f, pData, p)) == RS_RET_OK) { + if((iRet = cflineParseOutchannel(pData, p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS)) == RS_RET_OK) { pData->bDynamicName = 0; pData->fCreateMode = fCreateMode; /* preserve current setting */ pData->fDirCreateMode = fDirCreateMode; /* preserve current setting */ @@ -647,16 +625,16 @@ CODESTARTparseSelectorAct case '?': /* This is much like a regular file handle, but we need to obtain * a template name. rgerhards, 2007-07-03 */ + CODE_STD_STRING_REQUESTparseSelectorAct(2) ++p; /* eat '?' */ - if((iRet = cflineParseFileName(f, p, (uchar*) pData->f_fname)) != RS_RET_OK) + if((iRet = cflineParseFileName(p, (uchar*) pData->f_fname, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS)) + != RS_RET_OK) break; - pData->pTpl = tplFind((char*)pData->f_fname, - strlen((char*) pData->f_fname)); - if(pData->pTpl == NULL) { - logerrorSz("Template '%s' not found - dynaFile deactivated.", pData->f_fname); - iRet = RS_RET_NOT_FOUND; /* that's it... :( */ + /* "filename" is actually a template name, we need this as string 1. So let's add it + * to the pOMSR. -- rgerhards, 2007-07-27 + */ + if((iRet = OMSRsetEntry(*ppOMSR, 1, (uchar*)strdup(pData->f_fname), OMSR_NO_RQD_TPL_OPTS)) != RS_RET_OK) break; - } pData->bDynamicName = 1; pData->iCurrElt = -1; /* no current element */ @@ -681,6 +659,7 @@ CODESTARTparseSelectorAct case '|': case '/': + CODE_STD_STRING_REQUESTparseSelectorAct(1) /* rgerhards, 2007-0726: first check if file or pipe */ if(*p == '|') { pData->fileType = eTypePIPE; @@ -693,7 +672,8 @@ CODESTARTparseSelectorAct * to use is specified. So we need to scan for the first coma first * and then look at the rest of the line. */ - if((iRet = cflineParseFileName(f, p, (uchar*) pData->f_fname)) != RS_RET_OK) + if((iRet = cflineParseFileName(p, (uchar*) pData->f_fname, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS)) + != RS_RET_OK) break; pData->bDynamicName = 0; @@ -736,7 +716,6 @@ CODESTARTmodInit *ipIFVersProvided = 1; /* so far, we only support the initial definition */ ENDmodInit - /* * vi:set ai: */ diff --git a/omfwd.c b/omfwd.c index 84436381..83409201 100644 --- a/omfwd.c +++ b/omfwd.c @@ -602,7 +602,7 @@ CODESTARTdoAction dprintf("Not sending message to remote.\n"); else { pData->ttSuspend = time(NULL); - psz = (char*) pMsg; + psz = (char*) ppString[0]; l = strlen((char*) psz); if (l > MAXLINE) l = MAXLINE; @@ -710,10 +710,8 @@ BEGINparseSelectorAct int error; int bErr; struct addrinfo hints, *res; - char szTemplateName[128]; CODESTARTparseSelectorAct - p = *pp; - +CODE_STD_STRING_REQUESTparseSelectorAct(1) if(*p == '@') { if((iRet = createInstance(&pData)) != RS_RET_OK) return iRet; @@ -828,21 +826,20 @@ CODESTARTparseSelectorAct ++p; } + /* TODO: make this if go away! */ if(*p == ';') { *p = '\0'; /* trick to obtain hostname (later)! */ - ++p; - /* Now look for the template! */ - cflineParseTemplateName(&p, szTemplateName, - sizeof(szTemplateName) / sizeof(char)); + strcpy(pData->f_hname, (char*) q); + *p = ';'; } else - szTemplateName[0] = '\0'; - if(szTemplateName[0] == '\0') { - /* we do not have a template, so let's use the default */ - strcpy(szTemplateName, " StdFwdFmt"); - } + strcpy(pData->f_hname, (char*) q); + + /* process template */ + if((iRet = cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar*) " StdFwdFmt")) + != RS_RET_OK) + return iRet; /* first set the pData->eDestState */ - strcpy(pData->f_hname, (char*) q); memset(&hints, 0, sizeof(hints)); /* port must be numeric, because config file syntax requests this */ hints.ai_flags = AI_NUMERICSERV; @@ -857,12 +854,6 @@ CODESTARTparseSelectorAct pData->f_addr = res; } - /* then try to find the template */ - if((iRet = cflineSetTemplateAndIOV(f, szTemplateName)) == RS_RET_OK) { - dprintf("forwarding host: '%s:%s/%s' template '%s'\n", q, getFwdSyslogPt(pData), - pData->protocol == FORW_UDP ? "udp" : "tcp", - szTemplateName); - } /* * Otherwise the host might be unknown due to an * inaccessible nameserver (perhaps on the same diff --git a/ommysql.c b/ommysql.c index 67803b5c..d5c327c1 100644 --- a/ommysql.c +++ b/ommysql.c @@ -321,16 +321,14 @@ rsRetVal writeMySQL(register selector_t *f, uchar *psz, instanceData *pData) BEGINdoAction CODESTARTdoAction dprintf("\n"); - iRet = writeMySQL(f, pMsg, pData); + iRet = writeMySQL(f, ppString[0], pData); ENDdoAction BEGINparseSelectorAct int iMySQLPropErr = 0; - char szTemplateName[128]; CODESTARTparseSelectorAct - p = *pp; - +CODE_STD_STRING_REQUESTparseSelectorAct(1) /* yes, the if below is redundant, but I need it now. Will go away as * the code further changes. -- rgerhards, 2007-07-25 */ @@ -341,8 +339,7 @@ CODESTARTparseSelectorAct switch (*p) { - case '>': - /* rger 2004-10-28: added support for MySQL + case '>': /* rger 2004-10-28: added support for MySQL * >server,dbname,userid,password * rgerhards 2005-08-12: changed rsyslogd so that * if no DB is selected and > is used, an error @@ -376,34 +373,15 @@ CODESTARTparseSelectorAct iMySQLPropErr++; if(getSubString(&p, pData->f_dbpwd, _DB_MAXPWDLEN+1, ';')) iMySQLPropErr++; - if(*p == '\n' || *p == '\0') { - /* assign default format if none given! */ - szTemplateName[0] = '\0'; - } else { - /* we have a template specifier! */ - if((iRet = cflineParseTemplateName(&p, szTemplateName, - sizeof(szTemplateName) / sizeof(char))) != RS_RET_OK) - break; - } - - if(szTemplateName[0] == '\0') - strcpy(szTemplateName, " StdDBFmt"); - - if((iRet = cflineSetTemplateAndIOV(f, szTemplateName)) != RS_RET_OK) - break; - - dprintf(" template '%s'\n", szTemplateName); - - /* If db used, the template have to use the SQL option. - This is for your own protection (prevent sql injection). */ - if (f->f_pTpl->optFormatForSQL == 0) { - iRet = RS_RET_NO_SQL_STRING; - logerror("DB logging disabled. You have to use" - " the SQL or stdSQL option in your template!\n"); - break; - } + /* now check for template + * We specify that the SQL option must be present in the template. + * This is for your own protection (prevent sql injection). + */ + if((iRet = cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_RQD_TPL_OPT_SQL, (uchar*) " StdSQLFmt")) + != RS_RET_OK) + return iRet; - /* If we dedect invalid properties, we disable logging, + /* If we detect invalid properties, we disable logging, * because right properties are vital at this place. * Retries make no sense. */ diff --git a/omshell.c b/omshell.c index efeb7bf7..5a25cc9f 100644 --- a/omshell.c +++ b/omshell.c @@ -81,14 +81,14 @@ CODESTARTdoAction * rgerhards, 2007-07-20 */ dprintf("\n"); - if(execProg((uchar*) pData->progName, 1, pMsg) == 0) + if(execProg((uchar*) pData->progName, 1, ppString[0]) == 0) logerrorSz("Executing program '%s' failed", (char*)pData->progName); ENDdoAction BEGINparseSelectorAct CODESTARTparseSelectorAct - p = *pp; +CODE_STD_STRING_REQUESTparseSelectorAct(1) /* yes, the if below is redundant, but I need it now. Will go away as * the code further changes. -- rgerhards, 2007-07-25 */ @@ -103,7 +103,7 @@ CODESTARTparseSelectorAct case '^': /* bkalkbrenner 2005-09-20: execute shell command */ dprintf("exec\n"); ++p; - iRet = cflineParseFileName(f, p, (uchar*) pData->progName); + iRet = cflineParseFileName(p, (uchar*) pData->progName, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS); break; default: iRet = RS_RET_CONFLINE_UNPROCESSED; diff --git a/omusrmsg.c b/omusrmsg.c index 3c47fb8d..96b18375 100644 --- a/omusrmsg.c +++ b/omusrmsg.c @@ -247,15 +247,15 @@ BEGINdoAction CODESTARTdoAction dprintf("\n"); /* TODO: change wallmsg so that it returns iRet */ - wallmsg(f, pMsg, pData); + wallmsg(f, ppString[0], pData); ENDdoAction BEGINparseSelectorAct uchar *q; int i; - char szTemplateName[128]; CODESTARTparseSelectorAct +CODE_STD_STRING_REQUESTparseSelectorAct(1) #if 0 /* TODO: think about it and activate later - see comments in else below */ if(**pp != '*') @@ -267,20 +267,11 @@ CODESTARTparseSelectorAct if(*p == '*') { /* wall */ dprintf ("write-all"); + ++p; /* eat '*' */ pData->bIsWall = 1; /* write to all users */ - if(*(p+1) == ';') { - /* we have a template specifier! */ - p += 2; /* eat "*;" */ - if((iRet = cflineParseTemplateName(&p, szTemplateName, - sizeof(szTemplateName) / sizeof(uchar))) != RS_RET_OK) - return iRet; - } - else /* assign default format if none given! */ - szTemplateName[0] = '\0'; - if(szTemplateName[0] == '\0') - strcpy(szTemplateName, " WallFmt"); - if((iRet = cflineSetTemplateAndIOV(f, szTemplateName)) == RS_RET_OK) - dprintf(" template '%s'\n", szTemplateName); + if((iRet = cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar*) " WallFmt")) + != RS_RET_OK) + return iRet; } else { /* everything else is currently treated as a user name * TODO: we must reconsider this - see also comment in @@ -300,26 +291,13 @@ CODESTARTparseSelectorAct q++; p = q; } - /* done, now check if we have a template name + /* done, on to the template * TODO: we need to handle the case where i >= MAXUNAME! */ - szTemplateName[0] = '\0'; - if(*p == ';') { - /* we have a template specifier! */ - ++p; /* eat ";" */ - if((iRet = cflineParseTemplateName(&p, szTemplateName, - sizeof(szTemplateName) / sizeof(char))) != RS_RET_OK) - return iRet; - } - if(szTemplateName[0] == '\0') - strcpy(szTemplateName, " StdUsrMsgFmt"); - iRet = cflineSetTemplateAndIOV(f, szTemplateName); - /* NOTE: if you intend to do anything else here, be sure to - * chck iRet - as we currently have nothing else to do, we do not - * care (yet). - */ + if((iRet = cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar*)" StdUsrMsgFmt")) + != RS_RET_OK) + return iRet; } - ENDparseSelectorAct diff --git a/rsyslog.h b/rsyslog.h index 560e0f10..149e589c 100644 --- a/rsyslog.h +++ b/rsyslog.h @@ -58,6 +58,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_NO_SQL_STRING = -2005, /**< string is not suitable for use as SQL */ RS_RET_DISABLE_ACTION = -2006, /**< action requests that it be disabled */ RS_RET_SUSPENDED = -2007, /**< something was suspended, not neccesarily an error */ + RS_RET_RQD_TPLOPT_MISSING = -2008,/**< a required template option is missing */ RS_RET_OK = 0 /**< operation successful */ }; typedef enum rsRetVal_ rsRetVal; /**< friendly type for global return value */ diff --git a/syslogd-types.h b/syslogd-types.h index 1792f509..9aff6c88 100644 --- a/syslogd-types.h +++ b/syslogd-types.h @@ -161,6 +161,7 @@ struct filed { int iNumTpls; /* number of array entries for template element below */ struct template **ppTpl; /* array of template to use - strings must be passed to doAction * in this order. */ + uchar **ppMsgs; /* array of message pointers for doAction */ struct template __attribute__((deprecated)) *f_pTpl; /* pointer to template to use */ struct msg* f_pMsg; /* pointer to the message (this will * replace the other vars with msg diff --git a/syslogd.c b/syslogd.c index e46adffe..fb697046 100644 --- a/syslogd.c +++ b/syslogd.c @@ -3059,8 +3059,8 @@ rsRetVal fprintlog(register selector_t *f) msg_t *pMsgSave; /* to save current message pointer, necessary to restore it in case it needs to be updated (e.g. repeated msgs) */ pMsgSave = NULL; /* indicate message poiner not saved */ - uchar *pszMsg; rsRetVal iRet = RS_RET_OK; + int i; /* first check if this is a regular message or the repeation of * a previous message. If so, we need to change the message text @@ -3102,14 +3102,17 @@ rsRetVal fprintlog(register selector_t *f) /* When we reach this point, we have a valid, non-disabled action. * So let's execute it. -- rgerhards, 2007-07-24 */ - if((pszMsg = tplToString(f->f_pTpl, f->f_pMsg)) == NULL) { - dprintf("memory alloc failed while generating message string - message ignored\n"); - glblHadMemShortage = 1; - iRet = RS_RET_OUT_OF_MEMORY; - } else { - iRet = f->pMod->mod.om.doAction(f, pszMsg, f->pModData); /* call configured action */ - free(pszMsg); + /* here we must loop to process all requested strings */ + + for(i = 0 ; i < f->iNumTpls ; ++i) { + if((f->ppMsgs[i] = tplToString(f->ppTpl[i], f->f_pMsg)) == NULL) { + dprintf("memory alloc failed while generating message strings - message ignored\n"); + glblHadMemShortage = 1; + iRet = RS_RET_OUT_OF_MEMORY; + goto finalize_it; + } } + iRet = f->pMod->mod.om.doAction(f, f->ppMsgs, f->f_pMsg->msgFlags, f->pModData); /* call configured action */ if(iRet == RS_RET_DISABLE_ACTION) f->bEnabled = 0; /* that's it... */ @@ -3117,6 +3120,15 @@ rsRetVal fprintlog(register selector_t *f) if(iRet == RS_RET_OK) f->f_prevcount = 0; /* message process, so we start a new cycle */ +finalize_it: + /* cleanup */ + for(i = 0 ; i < f->iNumTpls ; ++i) { + if(f->ppMsgs[i] != NULL) { + free(f->ppMsgs[i]); + f->ppMsgs[i] = NULL; + } + } + if(pMsgSave != NULL) { /* we had saved the original message pointer. That was * done because we needed to create a temporary one @@ -3803,7 +3815,7 @@ void cfsysline(uchar *p) assert(p != NULL); errno = 0; - dprintf("cfsysline --> %s", p); + dprintf("cfsysline --> %s\n", p); if(getSubString(&p, (char*) szCmd, sizeof(szCmd) / sizeof(uchar), ' ') != 0) { logerror("Invalid $-configline - could not extract command - line ignored\n"); return; @@ -3899,6 +3911,11 @@ static void freeSelectors(void) if(f->f_pMsg != NULL) MsgDestruct(f->f_pMsg); + + if(f->ppTpl != NULL) + free(f->ppTpl); + if(f->ppMsgs != NULL) + free(f->ppMsgs); /* done with this entry, we now need to delete itself */ fPrev = f; f = f->f_next; @@ -4025,6 +4042,10 @@ static void init() #else while (fgets(cline, sizeof(cline), cf) != NULL) { #endif + /* drop LF - TODO: make it better, replace fgets(), but its clean as it is */ + if(cline[strlen(cline)-1] == '\n') { + cline[strlen(cline) -1] = '\0'; + } /* * check for end-of-section, comments, strip off trailing * spaces and newline character. @@ -4238,67 +4259,65 @@ static void init() dprintf(" restarted.\n"); } -/* helper to cfline() and its helpers. Assign the right template - * to a filed entry and allocates memory for its iovec. - * rgerhards 2004-11-19 - */ -rsRetVal cflineSetTemplateAndIOV(selector_t *f, char *pTemplateName) -{ - rsRetVal iRet = RS_RET_OK; - char errMsg[512]; - assert(f != NULL); - assert(pTemplateName != NULL); - - /* Ok, we got everything, so it now is time to look up the - * template (Hint: templates MUST be defined before they are - * used!) and initialize the pointer to it PLUS the iov - * pointer. We do the later because the template tells us - * how many elements iov must have - and this can never change. - */ - if((f->f_pTpl = tplFind(pTemplateName, strlen(pTemplateName))) == NULL) { - snprintf(errMsg, sizeof(errMsg) / sizeof(char), - " Could not find template '%s' - selector line disabled\n", - pTemplateName); - errno = 0; - logerror(errMsg); - iRet = RS_RET_NOT_FOUND; - } - return iRet; -} - /* Helper to cfline() and its helpers. Parses a template name * from an "action" line. Must be called with the Line pointer * pointing to the first character after the semicolon. - * Everything is stored in the filed struct. If there is no - * template name (it is empty), than it is ensured that the - * returned string is "\0". So you can count on the first character - * to be \0 in this case. * rgerhards 2004-11-19 + * changed function to work with OMSR. -- rgerhards, 2007-07-27 + * the default template is to be used when no template is specified. */ -rsRetVal cflineParseTemplateName(uchar** pp, - register char* pTemplateName, int iLenTemplate) +rsRetVal cflineParseTemplateName(uchar** pp, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts, uchar *dfltTplName) { - register uchar *p; + uchar *p; + uchar *tplName; rsRetVal iRet = RS_RET_OK; - int i; + rsCStrObj *pStrB; assert(pp != NULL); assert(*pp != NULL); + assert(pOMSR != NULL); +dprintf("cflineParsetTemplateName opts: %d\n", iTplOpts); p =*pp; + /* a template must follow - search it and complain, if not found + */ + skipWhiteSpace(&p); + if(*p == ';') + ++p; /* eat it */ + else if(*p != '\0' && *p != '#') { + logerror("invalid character in selector line - ';template' expected"); + iRet = RS_RET_ERR; + goto finalize_it; + } - /* Just as a general precaution, we skip whitespace. */ - while(*p && isspace((int) *p)) - ++p; + skipWhiteSpace(&p); /* go to begin of template name */ - i = 1; /* we start at 1 so that we reserve space for the '\0'! */ - while(*p && i < iLenTemplate) { - *pTemplateName++ = *p++; - ++i; + if(*p == '\0') { + /* no template specified, use the default */ + /* TODO: check NULL ptr */ + tplName = (uchar*) strdup((char*)dfltTplName); + } else { + /* template specified, pick it up */ + if((pStrB = rsCStrConstruct()) == NULL) { + glblHadMemShortage = 1; + iRet = RS_RET_OUT_OF_MEMORY; + goto finalize_it; + } + + /* now copy the string */ + while(*p && *p != '#' && !isspace((int) *p)) { + if((iRet = rsCStrAppendChar(pStrB, *p)) != RS_RET_OK) goto finalize_it; + ++p; + } + if((iRet = rsCStrFinish(pStrB)) != RS_RET_OK) goto finalize_it; + tplName = rsCStrConvSzStrAndDestruct(pStrB); } - *pTemplateName = '\0'; + iRet = OMSRsetEntry(pOMSR, iEntry, tplName, iTplOpts); + if(iRet != RS_RET_OK) goto finalize_it; + +finalize_it: *pp = p; return iRet; @@ -4306,19 +4325,20 @@ rsRetVal cflineParseTemplateName(uchar** pp, /* Helper to cfline(). Parses a file name up until the first * comma and then looks for the template specifier. Tries - * to find that template. Everything is stored in the - * filed struct. + * to find that template. * rgerhards 2004-11-18 * parameter pFileName must point to a buffer large enough * to hold the largest possible filename. * rgerhards, 2007-07-25 + * updated to include OMSR pointer -- rgerhards, 2007-07-27 */ -rsRetVal cflineParseFileName(selector_t *f, uchar* p, uchar *pFileName) +rsRetVal cflineParseFileName(uchar* p, uchar *pFileName, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts) { register uchar *pName; int i; rsRetVal iRet = RS_RET_OK; - char szTemplateName[128]; /* should be more than sufficient */ + + assert(pOMSR != NULL); pName = pFileName; i = 1; /* we start at 1 so that we reseve space for the '\0'! */ @@ -4328,23 +4348,7 @@ rsRetVal cflineParseFileName(selector_t *f, uchar* p, uchar *pFileName) } *pName = '\0'; - /* got the file name - now let's look for the template to use - * Just as a general precaution, we skip whitespace. - */ - while(*p && isspace((int) *p)) - ++p; - if(*p == ';') - ++p; /* eat it */ - - if((iRet = cflineParseTemplateName(&p, szTemplateName, - sizeof(szTemplateName) / sizeof(char))) != RS_RET_OK) - return iRet; - - if(szTemplateName[0] == '\0') /* no template? */ - strcpy(szTemplateName, " TradFmt"); /* use default! */ - - if((iRet = cflineSetTemplateAndIOV(f, szTemplateName)) == RS_RET_OK) - dprintf("filename: '%s', template: '%s'\n", pFileName, szTemplateName); + iRet = cflineParseTemplateName(&p, pOMSR, iEntry, iTplOpts, (uchar*) " TradFmt"); return iRet; } @@ -4711,8 +4715,95 @@ static rsRetVal cflineProcessTagSelector(uchar **pline) } +/* add an Action to the current selector + * The pOMSR is freed, as it is not needed after this function. + * rgerhards, 2007-07-27 + */ +rsRetVal addAction(selector_t *f, modInfo_t *pMod, void *pModData, omodStringRequest_t *pOMSR) +{ + rsRetVal iRet = RS_RET_OK; + int i; + int iTplOpts; + uchar *pTplName; + char errMsg[512]; + + assert(f != NULL); + assert(pMod != NULL); + assert(pOMSR != NULL); + dprintf("Module %s processed this config line.\n", modGetName(pMod)); + + /* check if we can obtain the template pointers - TODO: move to separat function? */ + f->iNumTpls = OMSRgetEntryCount(pOMSR); + /* we first need to create the template pointer array */ + if((f->ppTpl = calloc(1, sizeof(struct template *))) == NULL) { + iRet = RS_RET_OUT_OF_MEMORY; + glblHadMemShortage = 1; + goto finalize_it; + } + /* and now the array for doAction() message pointers */ + if((f->ppMsgs = calloc(1, sizeof(uchar *))) == NULL) { + iRet = RS_RET_OUT_OF_MEMORY; + glblHadMemShortage = 1; + goto finalize_it; + } + + for(i = 0 ; i < f->iNumTpls ; ++i) { + if((iRet = OMSRgetEntry(pOMSR, i, &pTplName, &iTplOpts)) != RS_RET_OK) goto finalize_it; + /* Ok, we got everything, so it now is time to look up the + * template (Hint: templates MUST be defined before they are + * used!) + */ + if((f->ppTpl[i] = tplFind((char*)pTplName, strlen((char*)pTplName))) == NULL) { + snprintf(errMsg, sizeof(errMsg) / sizeof(char), + " Could not find template '%s' - selector line disabled\n", + pTplName); + errno = 0; + logerror(errMsg); + iRet = RS_RET_NOT_FOUND; + goto finalize_it; + } + /* check required template options */ +dprintf("iTplOpts %d, check %d\n", iTplOpts, OMSR_RQD_TPL_OPT_SQL); + if( (iTplOpts & OMSR_RQD_TPL_OPT_SQL) + && (f->ppTpl[i]->optFormatForSQL == 0)) { + errno = 0; + logerror("Selector disabled. To use this action, you have to specify " + "the SQL or stdSQL option in your template!\n"); + iRet = RS_RET_RQD_TPLOPT_MISSING; + goto finalize_it; + } + + dprintf("template: '%s' assgined\n", pTplName); + /* TODO: generate macro CHKiRet((code));? */ + } + + f->pMod = pMod; + f->pModData = pModData; + /* now check if the module is compatible with select features */ + if(pMod->isCompatibleWithFeature(sFEATURERepeatedMsgReduction) == RS_RET_OK) + f->f_ReduceRepeated = bReduceRepeatMsgs; + else { + dprintf("module is incompatible with RepeatedMsgReduction - turned off\n"); + f->f_ReduceRepeated = 0; + } + f->bEnabled = 1; /* action is enabled */ + +finalize_it: + if(iRet == RS_RET_OK) + iRet = OMSRdestruct(pOMSR); + else { + /* do not overwrite error state! */ + OMSRdestruct(pOMSR); + /* TODO: free pMod instance data, potential mem leak */ + /* TODO: better said - where is the selector_t AND its elements destroyed? */ + } + + return iRet; +} + + /* - * Crack a configuration file line + * Cranck a configuration file line * rgerhards 2004-11-17: well, I somewhat changed this function. It now does NOT * handle config lines in general, but only lines that reflect actual filter * pairs (the original syslog message line format). Extended lines (those starting @@ -4727,6 +4818,7 @@ static rsRetVal cfline(char *line, register selector_t *f) uchar *p; rsRetVal iRet; modInfo_t *pMod; + omodStringRequest_t *pOMSR; void *pModData; dprintf("cfline(%s)", line); @@ -4776,9 +4868,10 @@ static rsRetVal cfline(char *line, register selector_t *f) /* loop through all modules and see if one picks up the line */ pMod = omodGetNxt(NULL); while(pMod != NULL) { - iRet = pMod->mod.om.parseSelectorAct(&p , f, &pModData); + iRet = pMod->mod.om.parseSelectorAct(&p , f, &pModData, &pOMSR); dprintf("trying selector action for %s: %d\n", modGetName(pMod), iRet); if(iRet == RS_RET_OK) { + addAction(f, pMod, pModData, pOMSR); dprintf("Module %s processed this config line.\n", modGetName(pMod)); f->pMod = pMod; diff --git a/syslogd.h b/syslogd.h index afe177fd..b553d5fc 100644 --- a/syslogd.h +++ b/syslogd.h @@ -21,6 +21,7 @@ #define SYSLOGD_H_INCLUDED 1 #include "syslogd-types.h" +#include "objomsr.h" #ifdef USE_NETZIP #include @@ -83,9 +84,8 @@ int formatTimestamp3164(struct syslogTime *ts, char* pBuf, size_t iLenBuf); void iovCreate(selector_t *f); char *iovAsString(selector_t *f); void untty(void); -rsRetVal cflineSetTemplateAndIOV(selector_t *f, char *pTemplateName); -rsRetVal cflineParseTemplateName(uchar** pp, register char* pTemplateName, int iLenTemplate); -rsRetVal cflineParseFileName(selector_t *f, uchar* p, uchar *pFileName); +rsRetVal cflineParseTemplateName(uchar** pp, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts, uchar *dfltTplName); +rsRetVal cflineParseFileName(uchar* p, uchar *pFileName, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts); int getSubString(uchar **ppSrc, char *pDst, size_t DstSize, char cSep); extern int glblHadMemShortage; /* indicates if we had memory shortage some time during the run */ -- cgit