diff options
-rw-r--r-- | ChangeLog | 1 | ||||
-rw-r--r-- | modules.c | 98 | ||||
-rw-r--r-- | modules.h | 18 | ||||
-rw-r--r-- | omfile.c | 262 | ||||
-rw-r--r-- | omfile.h | 6 | ||||
-rw-r--r-- | omfwd.c | 36 | ||||
-rw-r--r-- | omfwd.h | 2 | ||||
-rw-r--r-- | ommysql.c | 36 | ||||
-rw-r--r-- | ommysql.h | 2 | ||||
-rw-r--r-- | omshell.c | 36 | ||||
-rw-r--r-- | omshell.h | 2 | ||||
-rw-r--r-- | omusrmsg.c | 128 | ||||
-rw-r--r-- | omusrmsg.h | 4 | ||||
-rw-r--r-- | rsyslog.h | 8 | ||||
-rwxr-xr-x | srUtils.c | 4 | ||||
-rw-r--r-- | syslogd.c | 311 | ||||
-rw-r--r-- | syslogd.h | 13 |
17 files changed, 690 insertions, 277 deletions
@@ -1,5 +1,6 @@ --------------------------------------------------------------------------- Version 1.17.3 (rgerhards), 2007-07-2? +- continued working on modularization - fixed a memory leak in config file parsing thanks to varmojfekoj <varmojfekoj@gmail.com> for the patch - rsyslogd now checks on startup if it is capable to performa any work @@ -23,6 +23,7 @@ * A copy of the GPL can be found in the file "COPYING" in this distribution. */ #include "config.h" +#include "rsyslog.h" #include <stdio.h> #include <stdarg.h> #include <stdlib.h> @@ -34,7 +35,7 @@ #include <unistd.h> #include <sys/file.h> -#include "rsyslog.h" +#include "syslogd.h" #include "modules.h" static modInfo_t *pLoadedModules = NULL; /* list of currently-loaded modules */ @@ -59,6 +60,15 @@ static rsRetVal moduleConstruct(modInfo_t **pThis) } +/* Destructs a module objects. The object must not be linked to the + * linked list of modules. + */ +static void moduleDestruct(modInfo_t *pThis) +{ + free(pThis); +} + + /* Add a module to the loaded module linked list */ static inline void addModToList(modInfo_t *pThis) @@ -75,28 +85,108 @@ static inline void addModToList(modInfo_t *pThis) } +/* Get the next module pointer - this is used to traverse the list. + * The function returns the next pointer or NULL, if there is no next one. + * The last object must be provided to the function. If NULL is provided, + * it starts at the root of the list. Even in this case, NULL may be + * returned - then, the list is empty. + * rgerhards, 2007-07-23 + */ +modInfo_t *modGetNxt(modInfo_t *pThis) +{ + modInfo_t *pNew; + + if(pThis == NULL) + pNew = pLoadedModules; + else + pNew = pThis->pNext; + + return(pNew); +} + + /* Add an already-loaded module to the module linked list. This function does * anything that is needed to fully initialize the module. */ -rsRetVal doModInit(rsRetVal *doInit(), uchar *name) +rsRetVal doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)()), uchar *name) { modInfo_t *pNew; rsRetVal iRet; + assert(modInit != NULL); + + printf("Enter doModInit for module '%s'\n", (char*)name); + if((iRet = moduleConstruct(&pNew)) != RS_RET_OK) return iRet; - if((iRet = *doInit()) != RS_RET_OK) + if((iRet = (*modInit)(1, &pNew->iIFVers, &pNew->modQueryEtryPt)) != RS_RET_OK) { + moduleDestruct(pNew); return iRet; + } + + if(pNew->iIFVers != 1) { + moduleDestruct(pNew); + return RS_RET_MISSING_INTERFACE; + } /* OK, we know we can successfully work with the module. So we now fill the * rest of the data elements. */ + if((iRet = (*pNew->modQueryEtryPt)((uchar*)"doAction", &pNew->mod.om.doAction)) != RS_RET_OK) { + moduleDestruct(pNew); + return iRet; + } +/* later... + if((iRet = (*pNew->modQueryEtryPt)((uchar*)"freeInstance", &pNew->freeInstance)) != RS_RET_OK) { + moduleDestruct(pNew); + return iRet; + } +*/ + + pNew->pszName = (uchar*) strdup((char*)name); /* we do not care if strdup() fails, we can accept that */ + pNew->eType = eMOD_OUT; /* TODO: take this from module */ - pNew->pszModName = (uchar*) strdup((char*)name); /* we do not care if strdup() fails, we can accept that */ + /* we initialized the structure, now let's add it to the linked list of modules */ + addModToList(pNew); return RS_RET_OK; } + +/* Print loaded modules. This is more or less a + * debug or test aid, but anyhow I think it's worth it... + * This only works if the dprintf() subsystem is initialized. + */ +void modPrintList(void) +{ + modInfo_t *pMod; + + pMod = modGetNxt(NULL); + while(pMod != NULL) { + dprintf("Loaded Module: Name='%s', IFVersion=%d, ", + (pMod->pszName == NULL) ? "NULL" : (char*)pMod->pszName, + pMod->iIFVers); + dprintf("type="); + switch(pMod->eType) { + case eMOD_OUT: + dprintf("output"); + break; + case eMOD_IN: + dprintf("input"); + break; + case eMOD_FILTER: + dprintf("filter"); + break; + } + dprintf(" module.\n"); + dprintf("Entry points:\n"); + dprintf("\tqueryEtryPt: 0x%x\n", (unsigned) pMod->modQueryEtryPt); + dprintf("\tdoAction: 0x%x\n", (unsigned) pMod->mod.om.doAction); + dprintf("\tfreeInstance: 0x%x\n", (unsigned) pMod->freeInstance); + dprintf("\n"); + pMod = modGetNxt(pMod); /* done, go next */ + } +} /* * vi:set ai: */ @@ -45,13 +45,15 @@ typedef enum eModLinkType_ { typedef struct moduleInfo { struct moduleInfo *pNext; /* support for creating a linked module list */ - int iModVers; /* Interface version of module */ - eModType_t eModType; /* type of this module */ - eModLinkType_t eModLinkType; - uchar* pszModName; /* printable module name, e.g. for dprintf */ + int iIFVers; /* Interface version of module */ + eModType_t eType; /* type of this module */ + eModLinkType_t eLinkType; + uchar* pszName; /* printable module name, e.g. for dprintf */ /* functions supported by all types of modules */ - rsRetVal (*modInit)(); /* initialize the module */ + rsRetVal (*modInit)(int, int*, rsRetVal(**)()); /* initialize the module */ /* be sure to support version handshake! */ + rsRetVal (*modQueryEtryPt)(uchar *name, rsRetVal (**EtryPoint)()); /* query entry point addresses */ + rsRetVal (*freeInstance)(struct filed*);/* called before termination or module unload */ rsRetVal (*modExit)(); /* called before termination or module unload */ /* below: parse a configuration line - return if processed * or not. If not, must be parsed to next module. @@ -66,16 +68,18 @@ typedef struct moduleInfo { /* input modules come after output modules are finished, I am * currently not really thinking about them. rgerhards, 2007-07-19 */ - }; + } im; struct {/* data for output modules */ /* below: perform the configured action */ rsRetVal (*doAction)(); - }; + } om; } mod; } modInfo_t; /* prototypes */ +rsRetVal doModInit(rsRetVal (*modInit)(), uchar *name); +void modPrintList(void); #endif /* #ifndef MODULES_H_INCLUDED */ /* @@ -35,7 +35,7 @@ #include <time.h> #include <assert.h> #include <errno.h> - +#include <ctype.h> #include <unistd.h> #include <sys/file.h> @@ -44,9 +44,97 @@ #include "syslogd-types.h" #include "srUtils.h" #include "template.h" +#include "outchannel.h" #include "omfile.h" +/* Helper to cfline(). Parses a output channel name up until the first + * comma and then looks for the template specifier. Tries + * to find that template. Maps the output channel to the + * proper filed structure settings. Everything is stored in the + * filed struct. Over time, the dependency on filed might be + * removed. + * rgerhards 2005-06-21 + */ +static void cflineParseOutchannel(selector_t *f, uchar* p) +{ + size_t i; + struct outchannel *pOch; + char szBuf[128]; /* should be more than sufficient */ + + /* this must always be a file, because we can not set a size limit + * on a pipe... + * rgerhards 2005-06-21: later, this will be a separate type, but let's + * emulate things for the time being. When everything runs, we can + * extend it... + */ + f->f_type = F_FILE; + f->doAction = doActionFile; + + ++p; /* skip '$' */ + i = 0; + /* get outchannel name */ + while(*p && *p != ';' && *p != ' ' && + i < sizeof(szBuf) / sizeof(char)) { + szBuf[i++] = *p++; + } + szBuf[i] = '\0'; + + /* got the name, now look up the channel... */ + pOch = ochFind(szBuf, i); + + if(pOch == NULL) { + char errMsg[128]; + errno = 0; + snprintf(errMsg, sizeof(errMsg)/sizeof(char), + "outchannel '%s' not found - ignoring action line", + szBuf); + logerror(errMsg); + f->f_type = F_UNUSED; + return; + } + + /* check if there is a file name in the outchannel... */ + if(pOch->pszFileTemplate == NULL) { + char errMsg[128]; + errno = 0; + snprintf(errMsg, sizeof(errMsg)/sizeof(char), + "outchannel '%s' has no file name template - ignoring action line", + szBuf); + logerror(errMsg); + f->f_type = F_UNUSED; + return; + } + + /* OK, we finally got a correct template. So let's use it... */ + strncpy(f->f_un.f_file.f_fname, pOch->pszFileTemplate, MAXFNAME); + f->f_un.f_file.f_sizeLimit = pOch->uSizeLimit; + /* WARNING: It is dangerous "just" to pass the pointer. As we + * never rebuild the output channel description, this is acceptable here. + */ + f->f_un.f_file.f_sizeLimitCmd = pOch->cmdOnSizeLimit; + + /* back to the input string - 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 */ + + cflineParseTemplateName(&p, szBuf, + sizeof(szBuf) / sizeof(char)); + + if(szBuf[0] == '\0') /* no template? */ + strcpy(szBuf, " TradFmt"); /* use default! */ + + cflineSetTemplateAndIOV(f, szBuf); + + dprintf("[outchannel]filename: '%s', template: '%s', size: %lu\n", f->f_un.f_file.f_fname, szBuf, + f->f_un.f_file.f_sizeLimit); +} + + /* rgerhards 2005-06-21: Try to resolve a size limit * situation. This first runs the command, and then * checks if we are still above the treshold. @@ -405,7 +493,7 @@ again: /* free an instance * returns 0 if it succeeds, something else otherwise */ -int freeInstanceFile(selector_t *f) +rsRetVal freeInstanceFile(selector_t *f) { assert(f != NULL); if(f->f_un.f_file.bDynamicName) { @@ -419,12 +507,11 @@ int freeInstanceFile(selector_t *f) /* call the shell action * returns 0 if it succeeds, something else otherwise */ -int doActionFile(selector_t *f) +rsRetVal doActionFile(selector_t *f) { assert(f != NULL); dprintf(" (%s)\n", f->f_un.f_file.f_fname); -printf("iovUsed address: %x, size %d\n",&f->f_iIovUsed, sizeof(selector_t)); /* f->f_file == -1 is an indicator that the we couldn't * open the file at startup. For dynaFiles, this is ok, * all others are doomed. @@ -433,6 +520,173 @@ printf("iovUsed address: %x, size %d\n",&f->f_iIovUsed, sizeof(selector_t)); writeFile(f); return 0; } + +/* 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 + */ +rsRetVal parseSelectorActFile(uchar **pp, selector_t *f) +{ + uchar *p; + int syncfile; + rsRetVal iRet = RS_RET_CONFLINE_PROCESSED; + + assert(pp != NULL); + assert(f != NULL); + + p = *pp; + + if (*p == '-') { + syncfile = 0; + p++; + } else + syncfile = 1; + + switch (*p) + { + case '$': + /* 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 + * traditional mode lines. + */ + cflineParseOutchannel(f, p); + f->f_un.f_file.bDynamicName = 0; + f->f_un.f_file.fCreateMode = fCreateMode; /* preserve current setting */ + f->f_un.f_file.fDirCreateMode = fDirCreateMode; /* preserve current setting */ + f->f_file = open(f->f_un.f_file.f_fname, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY, + f->f_un.f_file.fCreateMode); + break; + + case '?': /* This is much like a regular file handle, but we need to obtain + * a template name. rgerhards, 2007-07-03 + */ + ++p; /* eat '?' */ + cflineParseFileName(f, p); + f->f_un.f_file.pTpl = tplFind((char*)f->f_un.f_file.f_fname, + strlen((char*) f->f_un.f_file.f_fname)); + if(f->f_un.f_file.pTpl == NULL) { + logerrorSz("Template '%s' not found - dynaFile deactivated.", f->f_un.f_file.f_fname); + f->f_type = F_UNUSED; /* that's it... :( */ + } + if(f->f_type == F_UNUSED) + /* safety measure to make sure we have a valid + * selector line before we continue down below. + * rgerhards 2005-07-29 + */ + break; + + if(syncfile) + f->f_flags |= SYNC_FILE; + f->f_un.f_file.bDynamicName = 1; + f->f_un.f_file.iCurrElt = -1; /* no current element */ + f->f_un.f_file.fCreateMode = fCreateMode; /* freeze current setting */ + f->f_un.f_file.fDirCreateMode = fDirCreateMode; /* preserve current setting */ + f->f_un.f_file.bCreateDirs = bCreateDirs; + f->f_un.f_file.bFailOnChown = bFailOnChown; + f->f_un.f_file.fileUID = fileUID; + f->f_un.f_file.fileGID = fileGID; + f->f_un.f_file.dirUID = dirUID; + f->f_un.f_file.dirGID = dirGID; + f->f_un.f_file.iDynaFileCacheSize = iDynaFileCacheSize; /* freeze current setting */ + /* we now allocate the cache table. We use calloc() intentionally, as we + * need all pointers to be initialized to NULL pointers. + */ + if((f->f_un.f_file.dynCache = (dynaFileCacheEntry**) + calloc(iDynaFileCacheSize, sizeof(dynaFileCacheEntry*))) == NULL) { + f->f_type = F_UNUSED; + dprintf("Could not allocate memory for dynaFileCache - selector disabled.\n"); + } + break; + + case '|': + case '/': + /* rgerhards 2004-11-17: from now, we need to have different + * processing, because after the first comma, the template name + * to use is specified. So we need to scan for the first coma first + * and then look at the rest of the line. + */ + cflineParseFileName(f, p); + if(f->f_type == F_UNUSED) + /* safety measure to make sure we have a valid + * selector line before we continue down below. + * rgerhards 2005-07-29 + */ + break; + + if(syncfile) + f->f_flags |= SYNC_FILE; + f->f_un.f_file.bDynamicName = 0; + f->f_un.f_file.fCreateMode = fCreateMode; /* preserve current setting */ + if(f->f_type == F_PIPE) { + f->f_file = open(f->f_un.f_file.f_fname, O_RDWR|O_NONBLOCK); + } else { + f->f_file = open(f->f_un.f_file.f_fname, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY, + f->f_un.f_file.fCreateMode); + } + + if ( f->f_file < 0 ){ + f->f_file = -1; + dprintf("Error opening log file: %s\n", f->f_un.f_file.f_fname); + logerror(f->f_un.f_file.f_fname); + break; + } + if (isatty(f->f_file)) { + f->f_type = F_TTY; + untty(); + } + if (strcmp((char*) p, ctty) == 0) + f->f_type = F_CONSOLE; + break; + default: + iRet = RS_RET_CONFLINE_UNPROCESSED; + break; + } + + if(iRet == RS_RET_CONFLINE_PROCESSED) + *pp = p; + return iRet; +} + + +/* query an entry point + */ +static rsRetVal queryEtryPt(uchar *name, rsRetVal (**pEtryPoint)()) +{ + if((name == NULL) || (pEtryPoint == NULL)) + return RS_RET_PARAM_ERROR; + + *pEtryPoint = NULL; + if(!strcmp((char*) name, "doAction")) { + *pEtryPoint = doActionFile; + } else if(!strcmp((char*) name, "freeInstance")) { + *pEtryPoint = freeInstanceFile; + } + + return(*pEtryPoint == NULL) ? RS_RET_NOT_FOUND : RS_RET_OK; +} + +/* initialize the module + * + * Later, much more must be done. So far, we only return a pointer + * to the queryEtryPt() function + * TODO: do interface version checking & handshaking + * iIfVersRequeted is the version of the interface specification that the + * caller would like to see being used. ipIFVersProvided is what we + * decide to provide. + */ +rsRetVal modInitFile(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)()) +{ + if((pQueryEtryPt == NULL) || (ipIFVersProvided == NULL)) + return RS_RET_PARAM_ERROR; + + *ipIFVersProvided = 1; /* so far, we only support the initial definition */ + + *pQueryEtryPt = queryEtryPt; + return RS_RET_OK; +} + + /* * vi:set ai: */ @@ -26,8 +26,10 @@ /* prototypes */ -int doActionFile(selector_t *f); -int freeInstanceFile(selector_t *f); +rsRetVal doActionFile(selector_t *f); +rsRetVal freeInstanceFile(selector_t *f); +rsRetVal modInitFile(int iIFVersRequested, int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)()); +rsRetVal parseSelectorActFile(uchar **pp, selector_t *f); #endif /* #ifndef OMFILE_H_INCLUDED */ /* @@ -237,6 +237,42 @@ int doActionFwd(selector_t *f) return 0; } +/* query an entry point + */ +static rsRetVal queryEtryPt(uchar *name, rsRetVal (**pEtryPoint)()) +{ + if((name == NULL) || (pEtryPoint == NULL)) + return RS_RET_PARAM_ERROR; + + *pEtryPoint = NULL; + if(!strcmp((char*) name, "doAction")) { + *pEtryPoint = doActionFwd; + } /*else if(!strcmp((char*) name, "freeInstance")) { + *pEtryPoint = freeInstanceFile; + }*/ + + return(*pEtryPoint == NULL) ? RS_RET_NOT_FOUND : RS_RET_OK; +} + +/* initialize the module + * + * Later, much more must be done. So far, we only return a pointer + * to the queryEtryPt() function + * TODO: do interface version checking & handshaking + * iIfVersRequeted is the version of the interface specification that the + * caller would like to see being used. ipIFVersProvided is what we + * decide to provide. + */ +rsRetVal modInitFwd(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)()) +{ + if((pQueryEtryPt == NULL) || (ipIFVersProvided == NULL)) + return RS_RET_PARAM_ERROR; + + *ipIFVersProvided = 1; /* so far, we only support the initial definition */ + + *pQueryEtryPt = queryEtryPt; + return RS_RET_OK; +} #endif /* #ifdef SYSLOG_INET */ /* * vi:set ai: @@ -25,8 +25,8 @@ #define OMFWD_H_INCLUDED 1 /* prototypes */ - int doActionFwd(selector_t *f); +rsRetVal modInitFwd(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)()); #endif /* #ifndef OMFWD_H_INCLUDED */ /* @@ -268,6 +268,42 @@ int doActionMySQL(selector_t *f) return 0; } +/* query an entry point + */ +static rsRetVal queryEtryPt(uchar *name, rsRetVal (**pEtryPoint)()) +{ + if((name == NULL) || (pEtryPoint == NULL)) + return RS_RET_PARAM_ERROR; + + *pEtryPoint = NULL; + if(!strcmp((char*) name, "doAction")) { + *pEtryPoint = doActionMySQL; + } /*else if(!strcmp((char*) name, "freeInstance")) { + *pEtryPoint = freeInstanceFile; + }*/ + + return(*pEtryPoint == NULL) ? RS_RET_NOT_FOUND : RS_RET_OK; +} + +/* initialize the module + * + * Later, much more must be done. So far, we only return a pointer + * to the queryEtryPt() function + * TODO: do interface version checking & handshaking + * iIfVersRequeted is the version of the interface specification that the + * caller would like to see being used. ipIFVersProvided is what we + * decide to provide. + */ +rsRetVal modInitMySQL(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)()) +{ + if((pQueryEtryPt == NULL) || (ipIFVersProvided == NULL)) + return RS_RET_PARAM_ERROR; + + *ipIFVersProvided = 1; /* so far, we only support the initial definition */ + + *pQueryEtryPt = queryEtryPt; + return RS_RET_OK; +} #endif /* #ifdef WITH_DB */ /* * vi:set ai: @@ -34,7 +34,7 @@ void writeMySQL(register selector_t *f); void closeMySQL(register selector_t *f); void reInitMySQL(register selector_t *f); int checkDBErrorState(register selector_t *f); -//void DBErrorHandler(register selector_t *f); +rsRetVal modInitMySQL(int iIFVersRequested, int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)()); int doActionMySQL(selector_t *f); @@ -61,6 +61,42 @@ int doActionShell(selector_t *f) return 0; } +/* query an entry point + */ +static rsRetVal queryEtryPt(uchar *name, rsRetVal (**pEtryPoint)()) +{ + if((name == NULL) || (pEtryPoint == NULL)) + return RS_RET_PARAM_ERROR; + + *pEtryPoint = NULL; + if(!strcmp((char*) name, "doAction")) { + *pEtryPoint = doActionShell; + } /*else if(!strcmp((char*) name, "freeInstance")) { + *pEtryPoint = freeInstanceFile; + }*/ + + return(*pEtryPoint == NULL) ? RS_RET_NOT_FOUND : RS_RET_OK; +} + +/* initialize the module + * + * Later, much more must be done. So far, we only return a pointer + * to the queryEtryPt() function + * TODO: do interface version checking & handshaking + * iIfVersRequeted is the version of the interface specification that the + * caller would like to see being used. ipIFVersProvided is what we + * decide to provide. + */ +rsRetVal modInitShell(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)()) +{ + if((pQueryEtryPt == NULL) || (ipIFVersProvided == NULL)) + return RS_RET_PARAM_ERROR; + + *ipIFVersProvided = 1; /* so far, we only support the initial definition */ + + *pQueryEtryPt = queryEtryPt; + return RS_RET_OK; +} /* * vi:set ai: */ @@ -25,8 +25,8 @@ #define ACTSHELL_H_INCLUDED 1 /* prototypes */ - int doActionShell(selector_t *f); +rsRetVal modInitShell(int iIFVersRequested, int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)()); #endif /* #ifndef ACTSHELL_H_INCLUDED */ /* @@ -205,7 +205,7 @@ static void wallmsg(selector_t *f) /* call the shell action * returns 0 if it succeeds, something else otherwise */ -int doActionUsrMsg(selector_t *f) +static int doAction(selector_t *f) { assert(f != NULL); @@ -214,6 +214,132 @@ int doActionUsrMsg(selector_t *f) return 0; } +/* query an entry point + */ +static rsRetVal queryEtryPt(uchar *name, rsRetVal (**pEtryPoint)()) +{ + if((name == NULL) || (pEtryPoint == NULL)) + return RS_RET_PARAM_ERROR; + + *pEtryPoint = NULL; + if(!strcmp((char*) name, "doAction")) { + *pEtryPoint = doAction; + } /*else if(!strcmp((char*) name, "freeInstance")) { + *pEtryPoint = freeInstanceFile; + }*/ + + return(*pEtryPoint == NULL) ? RS_RET_NOT_FOUND : RS_RET_OK; +} + +/* 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 + */ +rsRetVal parseSelectorActUsrMsg(uchar **pp, selector_t *f) +{ + uchar *p, *q; + int i; + char szTemplateName[128]; + + assert(pp != NULL); + assert(f != NULL); + +#if 0 /* TODO: think about it and activate later - see comments in else below */ + if(**pp != '*') + return RS_RET_CONFLINE_UNPROCESSED; +#endif + p = *pp; + + if(*p == '*') { /* wall */ + dprintf ("write-all"); + f->f_type = F_WALL; + f->doAction = doAction; + if(*(p+1) == ';') { + /* we have a template specifier! */ + p += 2; /* eat "*;" */ + cflineParseTemplateName(&p, szTemplateName, + sizeof(szTemplateName) / sizeof(uchar)); + } + else /* assign default format if none given! */ + szTemplateName[0] = '\0'; + if(szTemplateName[0] == '\0') + strcpy(szTemplateName, " WallFmt"); + cflineSetTemplateAndIOV(f, szTemplateName); + if(f->f_type != F_UNUSED) + /* safety measure to make sure we have a valid + * selector line before we continue down below. + * rgerhards 2005-07-29 + */ + dprintf(" template '%s'\n", szTemplateName); + } else { + /* everything else is currently treated as a user name + * TODO: we must reconsider this - see also comment in + * loadBuildInModules() in syslogd.c + */ + dprintf ("users: %s\n", p); /* ASP */ + f->f_type = F_USERS; + f->doAction = doAction; + for (i = 0; i < MAXUNAMES && *p && *p != ';'; i++) { + for (q = p; *q && *q != ',' && *q != ';'; ) + q++; + (void) strncpy((char*) f->f_un.f_uname[i], (char*) p, UNAMESZ); + if ((q - p) > UNAMESZ) + f->f_un.f_uname[i][UNAMESZ] = '\0'; + else + f->f_un.f_uname[i][q - p] = '\0'; + while (*q == ',' || *q == ' ') + q++; + p = q; + } + /* done, now check if we have a template name + * TODO: we need to handle the case where i >= MAXUNAME! + */ + szTemplateName[0] = '\0'; + if(*p == ';') { + /* we have a template specifier! */ + ++p; /* eat ";" */ + cflineParseTemplateName(&p, szTemplateName, + sizeof(szTemplateName) / sizeof(char)); + } + if(szTemplateName[0] == '\0') + strcpy(szTemplateName, " StdUsrMsgFmt"); + cflineSetTemplateAndIOV(f, szTemplateName); + /* Please note that we would need to check if the template + * was found. If not, f->f_type would be F_UNUSED and we + * can NOT carry on processing. These checks can be seen + * on all other selector line code above. However, as we + * do not have anything else to do here, we do not include + * this check. Should you add any further processing at + * this point here, you must first add a check for this + * condition! + * rgerhards 2005-07-29 + */ + } + + *pp = p; + return RS_RET_CONFLINE_PROCESSED; +} + +/* initialize the module + * + * Later, much more must be done. So far, we only return a pointer + * to the queryEtryPt() function + * TODO: do interface version checking & handshaking + * iIfVersRequeted is the version of the interface specification that the + * caller would like to see being used. ipIFVersProvided is what we + * decide to provide. + */ +rsRetVal modInitUsrMsg(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)()) +{ + if((pQueryEtryPt == NULL) || (ipIFVersProvided == NULL)) + return RS_RET_PARAM_ERROR; + + *ipIFVersProvided = 1; /* so far, we only support the initial definition */ + + *pQueryEtryPt = queryEtryPt; + return RS_RET_OK; +} + /* * vi:set ai: */ @@ -25,8 +25,8 @@ #define OMUSRMSG_H_INCLUDED 1 /* prototypes */ - -int doActionUsrMsg(selector_t *f); +rsRetVal modInitUsrMsg(int iIFVersRequested, int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)()); +rsRetVal parseSelectorActUsrMsg(uchar **pp, selector_t *f); #endif /* #ifndef OMUSRMSG_H_INCLUDED */ /* @@ -45,6 +45,11 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_NO_MORE_DATA = -3006, /**< insufficient data, e.g. end of string during parsing */ RS_RET_INVALID_IP = -3007, /**< invalid ip found where valid was expected */ RS_RET_OBJ_CREATION_FAILED = - 3008, /**< the creation of an object failed (no details available) */ + RS_RET_PARAM_ERROR = -1000, /**< invalid parameter in call to function */ + RS_RET_MISSING_INTERFACE = -1001,/**< interface version mismatch, required missing */ + /* return states for config file processing */ + RS_RET_CONFLINE_PROCESSED = -2000, /**< config line was processed, do not pass to any other module */ + RS_RET_CONFLINE_UNPROCESSED = -2001,/**< config line was not processed, pass to other module */ RS_RET_OK = 0 /**< operation successful */ }; typedef enum rsRetVal_ rsRetVal; /**< friendly type for global return value */ @@ -106,3 +111,6 @@ typedef unsigned char uchar; #endif #endif /* multi-include protection */ +/* + * vi:set ai: + */ @@ -115,7 +115,7 @@ uchar *srUtilStrDup(uchar *pOld, size_t len) * are to be created with. */ int makeFileParentDirs(uchar *szFile, size_t lenFile, mode_t mode, - uid_t uid, gid_t gid, int bFailOnChown) + uid_t uid, gid_t gid, int bFailOnChownFail) { uchar *p; uchar *pszWork; @@ -138,7 +138,7 @@ int makeFileParentDirs(uchar *szFile, size_t lenFile, mode_t mode, if(uid != (uid_t) -1 || gid != (gid_t) -1) { /* we need to set owner/group */ if(chown((char*)pszWork, uid, gid) != 0) - if(bFailOnChown) + if(bFailOnChownFail) bErr = 1; /* silently ignore if configured * to do so. @@ -213,6 +213,7 @@ #include "parse.h" #include "msg.h" +#include "modules.h" #include "tcpsyslog.h" #include "omshell.h" #include "omusrmsg.h" @@ -357,7 +358,7 @@ syslogCODE rs_facilitynames[] = static char *ConfFile = _PATH_LOGCONF; /* read-only after startup */ static char *PidFile = _PATH_LOGPID; /* read-only after startup */ -static char ctty[] = _PATH_CONSOLE; /* this is read-only */ +char ctty[] = _PATH_CONSOLE; /* this is read-only */ static int bModMySQLLoaded = 0; /* was a $ModLoad MySQL done? */ static pid_t myPid; /* our pid for use in self-generated messages, e.g. on startup */ @@ -369,9 +370,9 @@ static int bRequestDoMark = 0; /* do mark processing? (multithread safe) */ #define MAXFUNIX 20 int glblHadMemShortage = 0; /* indicates if we had memory shortage some time during the run */ -static int iDynaFileCacheSize = 10; /* max cache for dynamic files */ -static int fCreateMode = 0644; /* mode to use when creating files */ -static int fDirCreateMode = 0644; /* mode to use when creating files */ +int iDynaFileCacheSize = 10; /* max cache for dynamic files */ +int fCreateMode = 0644; /* mode to use when creating files */ +int fDirCreateMode = 0644; /* mode to use when creating files */ int nfunix = 1; /* number of Unix sockets open / read-only after startup */ int startIndexUxLocalSockets = 0; /* process funix from that index on (used to * suppress local logging. rgerhards 2005-08-01 @@ -533,13 +534,13 @@ static struct code FacNames[] = { /* global variables for config file state */ static int bDropTrailingLF = 1; /* drop trailing LF's on reception? */ int Debug; /* debug flag - read-only after startup */ -static int bFailOnChown; /* fail if chown fails? */ -static uid_t fileUID; /* UID to be used for newly created files */ -static uid_t fileGID; /* GID to be used for newly created files */ -static uid_t dirUID; /* UID to be used for newly created directories */ -static uid_t dirGID; /* GID to be used for newly created directories */ +int bFailOnChown; /* fail if chown fails? */ +uid_t fileUID; /* UID to be used for newly created files */ +uid_t fileGID; /* GID to be used for newly created files */ +uid_t dirUID; /* UID to be used for newly created directories */ +uid_t dirGID; /* GID to be used for newly created directories */ +int bCreateDirs; /* auto-create directories for dynaFiles: 0 - no, 1 - yes */ static int bDebugPrintTemplateList;/* output template list in debug mode? */ -static int bCreateDirs; /* auto-create directories for dynaFiles: 0 - no, 1 - yes */ int bDropMalPTRMsgs = 0;/* Drop messages which have malicious PTR records during DNS lookup */ static uchar cCCEscapeChar = '\\';/* character to be used to start an escape sequence for control chars */ static int bEscapeCCOnRcv; /* escape control characters on reception: 0 - no, 1 - yes */ @@ -672,7 +673,6 @@ static void sighup_handler(); static void die(int sig); static int getSubString(uchar **pSrc, char *pDst, size_t DstSize, char cSep); -static void cflineSetTemplateAndIOV(selector_t *f, char *pTemplateName); /* Access functions for the selector_t. These functions are primarily * necessary to make things thread-safe. Consequently, they are slim @@ -4462,6 +4462,7 @@ static void init() printf("\n"); if(bDebugPrintTemplateList) tplPrintList(); + modPrintList(); ochPrintList(); #ifdef SYSLOG_INET @@ -4508,7 +4509,7 @@ static void init() * to a filed entry and allocates memory for its iovec. * rgerhards 2004-11-19 */ -static void cflineSetTemplateAndIOV(selector_t *f, char *pTemplateName) +void cflineSetTemplateAndIOV(selector_t *f, char *pTemplateName) { char errMsg[512]; @@ -4553,7 +4554,7 @@ static void cflineSetTemplateAndIOV(selector_t *f, char *pTemplateName) * to be \0 in this case. * rgerhards 2004-11-19 */ -static void cflineParseTemplateName(uchar** pp, +void cflineParseTemplateName(uchar** pp, register char* pTemplateName, int iLenTemplate) { register uchar *p; @@ -4584,7 +4585,7 @@ static void cflineParseTemplateName(uchar** pp, * filed struct. * rgerhards 2004-11-18 */ -static void cflineParseFileName(selector_t *f, uchar* p) +void cflineParseFileName(selector_t *f, uchar* p) { register char *pName; int i; @@ -4627,93 +4628,6 @@ static void cflineParseFileName(selector_t *f, uchar* p) } -/* Helper to cfline(). Parses a output channel name up until the first - * comma and then looks for the template specifier. Tries - * to find that template. Maps the output channel to the - * proper filed structure settings. Everything is stored in the - * filed struct. Over time, the dependency on filed might be - * removed. - * rgerhards 2005-06-21 - */ -static void cflineParseOutchannel(selector_t *f, uchar* p) -{ - size_t i; - struct outchannel *pOch; - char szBuf[128]; /* should be more than sufficient */ - - /* this must always be a file, because we can not set a size limit - * on a pipe... - * rgerhards 2005-06-21: later, this will be a separate type, but let's - * emulate things for the time being. When everything runs, we can - * extend it... - */ - f->f_type = F_FILE; - f->doAction = doActionFile; - - ++p; /* skip '$' */ - i = 0; - /* get outchannel name */ - while(*p && *p != ';' && *p != ' ' && - i < sizeof(szBuf) / sizeof(char)) { - szBuf[i++] = *p++; - } - szBuf[i] = '\0'; - - /* got the name, now look up the channel... */ - pOch = ochFind(szBuf, i); - - if(pOch == NULL) { - char errMsg[128]; - errno = 0; - snprintf(errMsg, sizeof(errMsg)/sizeof(char), - "outchannel '%s' not found - ignoring action line", - szBuf); - logerror(errMsg); - f->f_type = F_UNUSED; - return; - } - - /* check if there is a file name in the outchannel... */ - if(pOch->pszFileTemplate == NULL) { - char errMsg[128]; - errno = 0; - snprintf(errMsg, sizeof(errMsg)/sizeof(char), - "outchannel '%s' has no file name template - ignoring action line", - szBuf); - logerror(errMsg); - f->f_type = F_UNUSED; - return; - } - - /* OK, we finally got a correct template. So let's use it... */ - strncpy(f->f_un.f_file.f_fname, pOch->pszFileTemplate, MAXFNAME); - f->f_un.f_file.f_sizeLimit = pOch->uSizeLimit; - /* WARNING: It is dangerous "just" to pass the pointer. As we - * never rebuild the output channel description, this is acceptable here. - */ - f->f_un.f_file.f_sizeLimitCmd = pOch->cmdOnSizeLimit; - - /* back to the input string - 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 */ - - cflineParseTemplateName(&p, szBuf, - sizeof(szBuf) / sizeof(char)); - - if(szBuf[0] == '\0') /* no template? */ - strcpy(szBuf, " TradFmt"); /* use default! */ - - cflineSetTemplateAndIOV(f, szBuf); - - dprintf("[outchannel]filename: '%s', template: '%s', size: %lu\n", f->f_un.f_file.f_fname, szBuf, - f->f_un.f_file.f_sizeLimit); -} - - /* * 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 @@ -5323,123 +5237,14 @@ static rsRetVal cfline(char *line, register selector_t *f) break; case '$': - /* 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 - * traditional mode lines. - */ - cflineParseOutchannel(f, p); - f->f_un.f_file.bDynamicName = 0; - f->f_un.f_file.fCreateMode = fCreateMode; /* preserve current setting */ - f->f_un.f_file.fDirCreateMode = fDirCreateMode; /* preserve current setting */ - f->f_file = open(f->f_un.f_file.f_fname, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY, - f->f_un.f_file.fCreateMode); - break; - - case '?': /* This is much like a regular file handle, but we need to obtain - * a template name. rgerhards, 2007-07-03 - */ - ++p; /* eat '?' */ - cflineParseFileName(f, p); - f->f_un.f_file.pTpl = tplFind((char*)f->f_un.f_file.f_fname, - strlen((char*) f->f_un.f_file.f_fname)); - if(f->f_un.f_file.pTpl == NULL) { - logerrorSz("Template '%s' not found - dynaFile deactivated.", f->f_un.f_file.f_fname); - f->f_type = F_UNUSED; /* that's it... :( */ - } - if(f->f_type == F_UNUSED) - /* safety measure to make sure we have a valid - * selector line before we continue down below. - * rgerhards 2005-07-29 - */ - break; - - if(syncfile) - f->f_flags |= SYNC_FILE; - f->f_un.f_file.bDynamicName = 1; - f->f_un.f_file.iCurrElt = -1; /* no current element */ - f->f_un.f_file.fCreateMode = fCreateMode; /* freeze current setting */ - f->f_un.f_file.fDirCreateMode = fDirCreateMode; /* preserve current setting */ - f->f_un.f_file.bCreateDirs = bCreateDirs; - f->f_un.f_file.bFailOnChown = bFailOnChown; - f->f_un.f_file.fileUID = fileUID; - f->f_un.f_file.fileGID = fileGID; - f->f_un.f_file.dirUID = dirUID; - f->f_un.f_file.dirGID = dirGID; - f->f_un.f_file.iDynaFileCacheSize = iDynaFileCacheSize; /* freeze current setting */ - /* we now allocate the cache table. We use calloc() intentionally, as we - * need all pointers to be initialized to NULL pointers. - */ - if((f->f_un.f_file.dynCache = (dynaFileCacheEntry**) - calloc(iDynaFileCacheSize, sizeof(dynaFileCacheEntry*))) == NULL) { - f->f_type = F_UNUSED; - dprintf("Could not allocate memory for dynaFileCache - selector disabled.\n"); - } - break; - + case '?': case '|': case '/': - /* rgerhards 2004-11-17: from now, we need to have different - * processing, because after the first comma, the template name - * to use is specified. So we need to scan for the first coma first - * and then look at the rest of the line. - */ - cflineParseFileName(f, p); - if(f->f_type == F_UNUSED) - /* safety measure to make sure we have a valid - * selector line before we continue down below. - * rgerhards 2005-07-29 - */ - break; - - if(syncfile) - f->f_flags |= SYNC_FILE; - f->f_un.f_file.bDynamicName = 0; - f->f_un.f_file.fCreateMode = fCreateMode; /* preserve current setting */ - if(f->f_type == F_PIPE) { - f->f_file = open(f->f_un.f_file.f_fname, O_RDWR|O_NONBLOCK); - } else { - f->f_file = open(f->f_un.f_file.f_fname, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY, - f->f_un.f_file.fCreateMode); - } - - if ( f->f_file < 0 ){ - f->f_file = -1; - dprintf("Error opening log file: %s\n", f->f_un.f_file.f_fname); - logerror(f->f_un.f_file.f_fname); - break; - } - if (isatty(f->f_file)) { - f->f_type = F_TTY; - untty(); - } - if (strcmp((char*) p, ctty) == 0) - f->f_type = F_CONSOLE; + parseSelectorActFile(&p, f); break; case '*': - dprintf ("write-all"); - f->f_type = F_WALL; - f->doAction = doActionUsrMsg; - if(*(p+1) == ';') { - /* we have a template specifier! */ - p += 2; /* eat "*;" */ - cflineParseTemplateName(&p, szTemplateName, - sizeof(szTemplateName) / sizeof(uchar)); - } - else /* assign default format if none given! */ - szTemplateName[0] = '\0'; - if(szTemplateName[0] == '\0') - strcpy(szTemplateName, " WallFmt"); - cflineSetTemplateAndIOV(f, szTemplateName); - if(f->f_type == F_UNUSED) - /* safety measure to make sure we have a valid - * selector line before we continue down below. - * rgerhards 2005-07-29 - */ - break; - - dprintf(" template '%s'\n", szTemplateName); + parseSelectorActUsrMsg(&p, f); break; case '~': /* rgerhards 2005-09-09: added support for discard */ @@ -5541,45 +5346,7 @@ static rsRetVal cfline(char *line, register selector_t *f) break; default: - dprintf ("users: %s\n", p); /* ASP */ - f->f_type = F_USERS; - f->doAction = doActionUsrMsg; - for (i = 0; i < MAXUNAMES && *p && *p != ';'; i++) { - for (q = p; *q && *q != ',' && *q != ';'; ) - q++; - (void) strncpy((char*) f->f_un.f_uname[i], (char*) p, UNAMESZ); - if ((q - p) > UNAMESZ) - f->f_un.f_uname[i][UNAMESZ] = '\0'; - else - f->f_un.f_uname[i][q - p] = '\0'; - while (*q == ',' || *q == ' ') - q++; - p = q; - } - /* done, now check if we have a template name - * TODO: we need to handle the case where i >= MAXUNAME! - */ - szTemplateName[0] = '\0'; - if(*p == ';') { - /* we have a template specifier! */ - ++p; /* eat ";" */ - cflineParseTemplateName(&p, szTemplateName, - sizeof(szTemplateName) / sizeof(char)); - } - if(szTemplateName[0] == '\0') - strcpy(szTemplateName, " StdUsrMsgFmt"); - cflineSetTemplateAndIOV(f, szTemplateName); - /* Please note that we would need to check if the template - * was found. If not, f->f_type would be F_UNUSED and we - * can NOT carry on processing. These checks can be seen - * on all other selector line code above. However, as we - * do not have anything else to do here, we do not include - * this check. Should you add any further processing at - * this point here, you must first add a check for this - * condition! - * rgerhards 2005-07-29 - */ - break; + parseSelectorActUsrMsg(&p, f); } return RS_RET_OK; } @@ -6157,10 +5924,44 @@ static void checkPermissions() } } + +/* load build-in modules + * very first version begun on 2007-07-23 by rgerhards + */ +static rsRetVal loadBuildInModules(void) +{ + rsRetVal iRet; + + if((iRet = doModInit(modInitFile, (uchar*) "builtin-file")) != RS_RET_OK) + return iRet; + if((iRet = doModInit(modInitFwd, (uchar*) "builtin-fwd")) != RS_RET_OK) + return iRet; + if((iRet = doModInit(modInitShell, (uchar*) "builtin-shell")) != RS_RET_OK) + return iRet; +# ifdef WITH_DB + if((iRet = doModInit(modInitMySQL, (uchar*) "builtin-mysql")) != RS_RET_OK) + return iRet; +# endif + + /* dirty, but this must be for the time being: the usrmsg module must always be + * loaded as last module. This is because it processes any time of action selector. + * If we load it before other modules, these others will never have a chance of + * working with the config file. We may change that implementation so that a user name + * must start with an alnum, that would definitely help (but would it break backwards + * compatibility?). * rgerhards, 2007-07-23 + */ + if((iRet = doModInit(modInitUsrMsg, (uchar*) "builtin-usrmsg")) != RS_RET_OK) + return iRet; + + return RS_RET_OK; +} + + int main(int argc, char **argv) { register int i; register char *p; int num_fds; + rsRetVal iRet; #ifdef MTRACE mtrace(); /* this is a debug aid for leak detection - either remove @@ -6185,6 +5986,12 @@ int main(int argc, char **argv) funix[i] = -1; } + if((iRet = loadBuildInModules()) != RS_RET_OK) { + fprintf(stderr, "fatal error: could not activate built-in modules. Error code %d.\n", + iRet); + exit(1); /* "good" exit, leaving at init for fatal error */ + } + while ((ch = getopt(argc, argv, "46Aa:dehi:f:l:m:nop:r::s:t:u:vwx")) != EOF) { switch((char)ch) { case '4': @@ -84,6 +84,9 @@ int formatTimestamp3164(struct syslogTime *ts, char* pBuf, size_t iLenBuf); void iovCreate(selector_t *f); char *iovAsString(selector_t *f); void untty(void); +void cflineSetTemplateAndIOV(selector_t *f, char *pTemplateName); +void cflineParseTemplateName(uchar** pp, register char* pTemplateName, int iLenTemplate); +void cflineParseFileName(selector_t *f, uchar* p); extern int glblHadMemShortage; /* indicates if we had memory shortage some time during the run */ extern syslogCODE rs_prioritynames[]; @@ -101,5 +104,15 @@ extern char **StripDomains; extern char *LocalDomain; extern int bDropMalPTRMsgs; extern struct AllowedSenders *pAllowedSenders_TCP; +extern int fCreateMode; +extern int fDirCreateMode; +extern int bFailOnChown; +extern uid_t fileUID; +extern uid_t fileGID; +extern uid_t dirUID; +extern uid_t dirGID; +extern int bCreateDirs; +extern int iDynaFileCacheSize; +extern char ctty[]; #endif /* #ifndef SYSLOGD_H_INCLUDED */ |