From 87c936ab65b4381fed35689b38c98f130883d903 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 17 Apr 2008 09:07:12 +0200 Subject: modularization work cleanup + created an abstract class for global data items and moved glblGetWorkDir to it --- cfsysline.c | 990 -------------------------------------------- cfsysline.h | 75 ---- dirty.h | 2 +- doc/features.html | 6 +- plugins/im3195/Makefile.am | 8 + plugins/imfile/imfile.c | 8 +- runtime/Makefile.am | 5 +- runtime/cfsysline.c | 991 +++++++++++++++++++++++++++++++++++++++++++++ runtime/cfsysline.h | 76 ++++ runtime/glbl.h | 19 +- runtime/obj-types.h | 19 +- runtime/queue.c | 16 +- runtime/rsyslog.c | 26 +- runtime/rsyslog.h | 4 +- tools/syslogd.c | 20 +- 15 files changed, 1155 insertions(+), 1110 deletions(-) delete mode 100644 cfsysline.c delete mode 100644 cfsysline.h create mode 100644 plugins/im3195/Makefile.am create mode 100644 runtime/cfsysline.c create mode 100644 runtime/cfsysline.h diff --git a/cfsysline.c b/cfsysline.c deleted file mode 100644 index 9f2372af..00000000 --- a/cfsysline.c +++ /dev/null @@ -1,990 +0,0 @@ -/* cfsysline.c - * Implementation of the configuration system line object. - * - * File begun on 2007-07-30 by RGerhards - * - * This file is part of rsyslog. - * - * Rsyslog is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Rsyslog is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Rsyslog. If not, see . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ -#include "config.h" - -#include "rsyslog.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dirty.h" /* TODO: when the module interface & library design is done, this should be able to go away */ -#include "cfsysline.h" -#include "obj.h" -#include "errmsg.h" -#include "srUtils.h" - - -/* static data */ -DEFobjCurrIf(obj) -DEFobjCurrIf(errmsg) - -linkedList_t llCmdList; /* this is NOT a pointer - no typo here ;) */ - -/* --------------- START functions for handling canned syntaxes --------------- */ - - -/* parse a character from the config line - * added 2007-07-17 by rgerhards - * TODO: enhance this function to handle different classes of characters - * HINT: check if char is ' and, if so, use 'c' where c may also be things - * like \t etc. - */ -static rsRetVal doGetChar(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *pVal) -{ - DEFiRet; - - assert(pp != NULL); - assert(*pp != NULL); - - skipWhiteSpace(pp); /* skip over any whitespace */ - - /* if we are not at a '\0', we have our new char - no validity checks here... */ - if(**pp == '\0') { - errmsg.LogError(NO_ERRCODE, "No character available"); - iRet = RS_RET_NOT_FOUND; - } else { - if(pSetHdlr == NULL) { - /* we should set value directly to var */ - *((uchar*)pVal) = **pp; - } else { - /* we set value via a set function */ - CHKiRet(pSetHdlr(pVal, **pp)); - } - ++(*pp); /* eat processed char */ - } - -finalize_it: - RETiRet; -} - - -/* Parse a number from the configuration line. This is more or less - * a shell to call the custom handler. - * rgerhards, 2007-07-31 - */ -static rsRetVal doCustomHdlr(uchar **pp, rsRetVal (*pSetHdlr)(uchar**, void*), void *pVal) -{ - DEFiRet; - - assert(pp != NULL); - assert(*pp != NULL); - - CHKiRet(pSetHdlr(pp, pVal)); - -finalize_it: - RETiRet; -} - - -/* Parse a number from the configuration line. This functions just parses - * the number and does NOT call any handlers or set any values. It is just - * for INTERNAL USE by other parse functions! - * rgerhards, 2008-01-08 - */ -static rsRetVal parseIntVal(uchar **pp, int64 *pVal) -{ - DEFiRet; - uchar *p; - int64 i; - int bWasNegative; - - assert(pp != NULL); - assert(*pp != NULL); - assert(pVal != NULL); - - skipWhiteSpace(pp); /* skip over any whitespace */ - p = *pp; - - if(*p == '-') { - bWasNegative = 1; - ++p; /* eat it */ - } else { - bWasNegative = 0; - } - - if(!isdigit((int) *p)) { - errno = 0; - errmsg.LogError(NO_ERRCODE, "invalid number"); - ABORT_FINALIZE(RS_RET_INVALID_INT); - } - - /* pull value */ - for(i = 0 ; *p && (isdigit((int) *p) || *p == '.' || *p == ',') ; ++p) { - if(isdigit((int) *p)) { - i = i * 10 + *p - '0'; - } - } - - if(bWasNegative) - i *= -1; - - *pVal = i; - *pp = p; - -finalize_it: - RETiRet; -} - - -/* Parse a number from the configuration line. - * rgerhards, 2007-07-31 - */ -static rsRetVal doGetInt(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *pVal) -{ - uchar *p; - DEFiRet; - int64 i; - - assert(pp != NULL); - assert(*pp != NULL); - - CHKiRet(parseIntVal(pp, &i)); - p = *pp; - - if(pSetHdlr == NULL) { - /* we should set value directly to var */ - *((int*)pVal) = (int) i; - } else { - /* we set value via a set function */ - CHKiRet(pSetHdlr(pVal, (int) i)); - } - - *pp = p; - -finalize_it: - RETiRet; -} - - -/* Parse a size from the configuration line. This is basically an integer - * syntax, but modifiers may be added after the integer (e.g. 1k to mean - * 1024). The size must immediately follow the number. Note that the - * param value must be int64! - * rgerhards, 2008-01-09 - */ -static rsRetVal doGetSize(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *pVal) -{ - DEFiRet; - int64 i; - - assert(pp != NULL); - assert(*pp != NULL); - - CHKiRet(parseIntVal(pp, &i)); - - /* we now check if the next character is one of our known modifiers. - * If so, we accept it as such. If not, we leave it alone. tera and - * above does not make any sense as that is above a 32-bit int value. - */ - switch(**pp) { - /* traditional binary-based definitions */ - case 'k': i *= 1024; ++(*pp); break; - case 'm': i *= 1024 * 1024; ++(*pp); break; - case 'g': i *= 1024 * 1024 * 1024; ++(*pp); break; - case 't': i *= (int64) 1024 * 1024 * 1024 * 1024; ++(*pp); break; /* tera */ - case 'p': i *= (int64) 1024 * 1024 * 1024 * 1024 * 1024; ++(*pp); break; /* peta */ - case 'e': i *= (int64) 1024 * 1024 * 1024 * 1024 * 1024 * 1024; ++(*pp); break; /* exa */ - /* and now the "new" 1000-based definitions */ - case 'K': i *= 1000; ++(*pp); break; - case 'M': i *= 10000; ++(*pp); break; - case 'G': i *= 100000; ++(*pp); break; - case 'T': i *= 1000000; ++(*pp); break; /* tera */ - case 'P': i *= 10000000; ++(*pp); break; /* peta */ - case 'E': i *= 100000000; ++(*pp); break; /* exa */ - } - - /* done */ - if(pSetHdlr == NULL) { - /* we should set value directly to var */ - *((int64*)pVal) = i; - } else { - /* we set value via a set function */ - CHKiRet(pSetHdlr(pVal, i)); - } - -finalize_it: - RETiRet; -} - - -/* Parse and interpet a $FileCreateMode and $umask line. This function - * pulls the creation mode and, if successful, stores it - * into the global variable so that the rest of rsyslogd - * opens files with that mode. Any previous value will be - * overwritten. - * HINT: if we store the creation mode in selector_t, we - * can even specify multiple modes simply be virtue of - * being placed in the right section of rsyslog.conf - * rgerhards, 2007-07-4 (happy independence day to my US friends!) - * Parameter **pp has a pointer to the current config line. - * On exit, it will be updated to the processed position. - */ -static rsRetVal doFileCreateMode(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *pVal) -{ - uchar *p; - DEFiRet; - uchar errMsg[128]; /* for dynamic error messages */ - int iVal; - - assert(pp != NULL); - assert(*pp != NULL); - - skipWhiteSpace(pp); /* skip over any whitespace */ - p = *pp; - - /* for now, we parse and accept only octal numbers - * Sequence of tests is important, we are using boolean shortcuts - * to avoid addressing invalid memory! - */ - if(!( (*p == '0') - && (*(p+1) && *(p+1) >= '0' && *(p+1) <= '7') - && (*(p+2) && *(p+2) >= '0' && *(p+2) <= '7') - && (*(p+3) && *(p+3) >= '0' && *(p+3) <= '7') ) ) { - snprintf((char*) errMsg, sizeof(errMsg)/sizeof(uchar), - "value must be octal (e.g 0644)."); - errno = 0; - errmsg.LogError(NO_ERRCODE, "%s", errMsg); - ABORT_FINALIZE(RS_RET_INVALID_VALUE); - } - - /* we reach this code only if the octal number is ok - so we can now - * compute the value. - */ - iVal = (*(p+1)-'0') * 64 + (*(p+2)-'0') * 8 + (*(p+3)-'0'); - - if(pSetHdlr == NULL) { - /* we should set value directly to var */ - *((int*)pVal) = iVal; - } else { - /* we set value via a set function */ - CHKiRet(pSetHdlr(pVal, iVal)); - } - - p += 4; /* eat the octal number */ - *pp = p; - -finalize_it: - RETiRet; -} - - -/* Parse and interpret an on/off inside a config file line. This is most - * often used for boolean options, but of course it may also be used - * for other things. The passed-in pointer is updated to point to - * the first unparsed character on exit. Function emits error messages - * if the value is neither on or off. It returns 0 if the option is off, - * 1 if it is on and another value if there was an error. - * rgerhards, 2007-07-15 - */ -static int doParseOnOffOption(uchar **pp) -{ - uchar *pOptStart; - uchar szOpt[32]; - - assert(pp != NULL); - assert(*pp != NULL); - - pOptStart = *pp; - skipWhiteSpace(pp); /* skip over any whitespace */ - - if(getSubString(pp, (char*) szOpt, sizeof(szOpt) / sizeof(uchar), ' ') != 0) { - errmsg.LogError(NO_ERRCODE, "Invalid $-configline - could not extract on/off option"); - return -1; - } - - if(!strcmp((char*)szOpt, "on")) { - return 1; - } else if(!strcmp((char*)szOpt, "off")) { - return 0; - } else { - errmsg.LogError(NO_ERRCODE, "Option value must be on or off, but is '%s'", (char*)pOptStart); - return -1; - } -} - - -/* extract a groupname and return its gid. - * rgerhards, 2007-07-17 - */ -static rsRetVal doGetGID(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *pVal) -{ - struct group *pgBuf; - struct group gBuf; - DEFiRet; - uchar szName[256]; - char stringBuf[2048]; /* I hope this is large enough... */ - - assert(pp != NULL); - assert(*pp != NULL); - - if(getSubString(pp, (char*) szName, sizeof(szName) / sizeof(uchar), ' ') != 0) { - errmsg.LogError(NO_ERRCODE, "could not extract group name"); - ABORT_FINALIZE(RS_RET_NOT_FOUND); - } - - getgrnam_r((char*)szName, &gBuf, stringBuf, sizeof(stringBuf), &pgBuf); - - if(pgBuf == NULL) { - errmsg.LogError(NO_ERRCODE, "ID for group '%s' could not be found or error", (char*)szName); - iRet = RS_RET_NOT_FOUND; - } else { - if(pSetHdlr == NULL) { - /* we should set value directly to var */ - *((gid_t*)pVal) = pgBuf->gr_gid; - } else { - /* we set value via a set function */ - CHKiRet(pSetHdlr(pVal, pgBuf->gr_gid)); - } - dbgprintf("gid %d obtained for group '%s'\n", pgBuf->gr_gid, szName); - } - - skipWhiteSpace(pp); /* skip over any whitespace */ - -finalize_it: - RETiRet; -} - - -/* extract a username and return its uid. - * rgerhards, 2007-07-17 - */ -static rsRetVal doGetUID(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *pVal) -{ - struct passwd *ppwBuf; - struct passwd pwBuf; - DEFiRet; - uchar szName[256]; - char stringBuf[2048]; /* I hope this is large enough... */ - - assert(pp != NULL); - assert(*pp != NULL); - - if(getSubString(pp, (char*) szName, sizeof(szName) / sizeof(uchar), ' ') != 0) { - errmsg.LogError(NO_ERRCODE, "could not extract user name"); - ABORT_FINALIZE(RS_RET_NOT_FOUND); - } - - getpwnam_r((char*)szName, &pwBuf, stringBuf, sizeof(stringBuf), &ppwBuf); - - if(ppwBuf == NULL) { - errmsg.LogError(NO_ERRCODE, "ID for user '%s' could not be found or error", (char*)szName); - iRet = RS_RET_NOT_FOUND; - } else { - if(pSetHdlr == NULL) { - /* we should set value directly to var */ - *((uid_t*)pVal) = ppwBuf->pw_uid; - } else { - /* we set value via a set function */ - CHKiRet(pSetHdlr(pVal, ppwBuf->pw_uid)); - } - dbgprintf("uid %d obtained for user '%s'\n", ppwBuf->pw_uid, szName); - } - - skipWhiteSpace(pp); /* skip over any whitespace */ - -finalize_it: - RETiRet; -} - - -/* Parse and process an binary cofig option. pVal must be - * a pointer to an integer which is to receive the option - * value. - * rgerhards, 2007-07-15 - */ -static rsRetVal doBinaryOptionLine(uchar **pp, rsRetVal (*pSetHdlr)(void*, int), void *pVal) -{ - int iOption; - DEFiRet; - - assert(pp != NULL); - assert(*pp != NULL); - - if((iOption = doParseOnOffOption(pp)) == -1) - return RS_RET_ERR; /* nothing left to do */ - - if(pSetHdlr == NULL) { - /* we should set value directly to var */ - *((int*)pVal) = iOption; - } else { - /* we set value via a set function */ - CHKiRet(pSetHdlr(pVal, iOption)); - } - - skipWhiteSpace(pp); /* skip over any whitespace */ - -finalize_it: - RETiRet; -} - - -/* parse a whitespace-delimited word from the provided string. This is a - * helper function for a number of syntaxes. The parsed value is returned - * in ppStrB (which must be provided by caller). - * rgerhards, 2008-02-14 - */ -static rsRetVal -getWord(uchar **pp, cstr_t **ppStrB) -{ - DEFiRet; - uchar *p; - - ASSERT(pp != NULL); - ASSERT(*pp != NULL); - ASSERT(ppStrB != NULL); - - CHKiRet(rsCStrConstruct(ppStrB)); - - /* parse out the word */ - p = *pp; - - while(*p && !isspace((int) *p)) { - CHKiRet(rsCStrAppendChar(*ppStrB, *p++)); - } - CHKiRet(rsCStrFinish(*ppStrB)); - - *pp = p; - -finalize_it: - RETiRet; -} - - -/* Parse and a word config line option. A word is a consequtive - * sequence of non-whitespace characters. pVal must be - * a pointer to a string which is to receive the option - * value. The returned string must be freed by the caller. - * rgerhards, 2007-09-07 - * To facilitate multiple instances of the same command line - * directive, doGetWord() now checks if pVal is already a - * non-NULL pointer. If so, we assume it was created by a previous - * incarnation and is automatically freed. This happens only when - * no custom handler is defined. If it is, the customer handler - * must do the cleanup. I have checked and this was al also memory - * leak with some code. Obviously, not a large one. -- rgerhards, 2007-12-20 - * Just to clarify: if pVal is parsed to a custom handler, this handler - * is responsible for freeing pVal. -- rgerhards, 2008-03-20 - */ -static rsRetVal doGetWord(uchar **pp, rsRetVal (*pSetHdlr)(void*, uchar*), void *pVal) -{ - DEFiRet; - cstr_t *pStrB; - uchar *pNewVal; - - ASSERT(pp != NULL); - ASSERT(*pp != NULL); - - CHKiRet(getWord(pp, &pStrB)); - CHKiRet(rsCStrConvSzStrAndDestruct(pStrB, &pNewVal, 0)); - pStrB = NULL; - - /* we got the word, now set it */ - if(pSetHdlr == NULL) { - /* we should set value directly to var */ - if(*((uchar**)pVal) != NULL) - free(*((uchar**)pVal)); /* free previous entry */ - *((uchar**)pVal) = pNewVal; /* set new one */ - } else { - /* we set value via a set function */ - CHKiRet(pSetHdlr(pVal, pNewVal)); - } - - skipWhiteSpace(pp); /* skip over any whitespace */ - -finalize_it: - if(iRet != RS_RET_OK) { - if(pStrB != NULL) - rsCStrDestruct(&pStrB); - } - - RETiRet; -} - - -/* parse a syslog name from the string. This is the generic code that is - * called by the facility/severity functions. Note that we do not check the - * validity of numerical values, something that should probably change over - * time (TODO). -- rgerhards, 2008-02-14 - */ -static rsRetVal -doSyslogName(uchar **pp, rsRetVal (*pSetHdlr)(void*, int), void *pVal, syslogName_t *pNameTable) -{ - DEFiRet; - cstr_t *pStrB; - int iNewVal; - - ASSERT(pp != NULL); - ASSERT(*pp != NULL); - - CHKiRet(getWord(pp, &pStrB)); /* get word */ - iNewVal = decodeSyslogName(rsCStrGetSzStr(pStrB), pNameTable); - - if(pSetHdlr == NULL) { - /* we should set value directly to var */ - *((int*)pVal) = iNewVal; /* set new one */ - } else { - /* we set value via a set function */ - CHKiRet(pSetHdlr(pVal, iNewVal)); - } - - skipWhiteSpace(pp); /* skip over any whitespace */ - -finalize_it: - if(pStrB != NULL) - rsCStrDestruct(&pStrB); - - RETiRet; -} - - -/* Implements the facility syntax. - * rgerhards, 2008-02-14 - */ -static rsRetVal -doFacility(uchar **pp, rsRetVal (*pSetHdlr)(void*, int), void *pVal) -{ - DEFiRet; - iRet = doSyslogName(pp, pSetHdlr, pVal, syslogFacNames); - RETiRet; -} - - -/* Implements the severity syntax. - * rgerhards, 2008-02-14 - */ -static rsRetVal -doSeverity(uchar **pp, rsRetVal (*pSetHdlr)(void*, int), void *pVal) -{ - DEFiRet; - iRet = doSyslogName(pp, pSetHdlr, pVal, syslogPriNames); - RETiRet; -} - - -/* --------------- END functions for handling canned syntaxes --------------- */ - -/* destructor for cslCmdHdlr - * pThis is actually a cslCmdHdlr_t, but we do not cast it as all we currently - * need to do is free it. - */ -static rsRetVal cslchDestruct(void *pThis) -{ - ASSERT(pThis != NULL); - free(pThis); - - return RS_RET_OK; -} - - -/* constructor for cslCmdHdlr - */ -static rsRetVal cslchConstruct(cslCmdHdlr_t **ppThis) -{ - cslCmdHdlr_t *pThis; - DEFiRet; - - assert(ppThis != NULL); - if((pThis = calloc(1, sizeof(cslCmdHdlr_t))) == NULL) { - ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); - } - -finalize_it: - *ppThis = pThis; - RETiRet; -} - -/* destructor for linked list keys. As we do not use any dynamic memory, - * we simply return. However, this entry point must be defined for the - * linkedList class to make sure we have not forgotten a destructor. - * rgerhards, 2007-11-21 - */ -static rsRetVal cslchKeyDestruct(void __attribute__((unused)) *pData) -{ - return RS_RET_OK; -} - - -/* Key compare operation for linked list class. This compares two - * owner cookies (void *). - * rgerhards, 2007-11-21 - */ -static int cslchKeyCompare(void *pKey1, void *pKey2) -{ - if(pKey1 == pKey2) - return 0; - else - if(pKey1 < pKey2) - return -1; - else - return 1; -} - - -/* set data members for this object - */ -rsRetVal cslchSetEntry(cslCmdHdlr_t *pThis, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData) -{ - assert(pThis != NULL); - assert(eType != eCmdHdlrInvalid); - - pThis->eType = eType; - pThis->cslCmdHdlr = pHdlr; - pThis->pData = pData; - - return RS_RET_OK; -} - - -/* call the specified handler - */ -static rsRetVal cslchCallHdlr(cslCmdHdlr_t *pThis, uchar **ppConfLine) -{ - DEFiRet; - rsRetVal (*pHdlr)() = NULL; - assert(pThis != NULL); - assert(ppConfLine != NULL); - - switch(pThis->eType) { - case eCmdHdlrCustomHandler: - pHdlr = doCustomHdlr; - break; - case eCmdHdlrUID: - pHdlr = doGetUID; - break; - case eCmdHdlrGID: - pHdlr = doGetGID; - break; - case eCmdHdlrBinary: - pHdlr = doBinaryOptionLine; - break; - case eCmdHdlrFileCreateMode: - pHdlr = doFileCreateMode; - break; - case eCmdHdlrInt: - pHdlr = doGetInt; - break; - case eCmdHdlrSize: - pHdlr = doGetSize; - break; - case eCmdHdlrGetChar: - pHdlr = doGetChar; - break; - case eCmdHdlrFacility: - pHdlr = doFacility; - break; - case eCmdHdlrSeverity: - pHdlr = doSeverity; - break; - case eCmdHdlrGetWord: - pHdlr = doGetWord; - break; - default: - iRet = RS_RET_NOT_IMPLEMENTED; - goto finalize_it; - } - - /* we got a pointer to the handler, so let's call it */ - assert(pHdlr != NULL); - CHKiRet(pHdlr(ppConfLine, pThis->cslCmdHdlr, pThis->pData)); - -finalize_it: - RETiRet; -} - - -/* ---------------------------------------------------------------------- * - * now come the handlers for cslCmd_t - * ---------------------------------------------------------------------- */ - -/* destructor for a cslCmd list key (a string as of now) - */ -static rsRetVal cslcKeyDestruct(void *pData) -{ - free(pData); /* we do not need to cast as all we do is free it anyway... */ - return RS_RET_OK; -} - -/* destructor for cslCmd - */ -static rsRetVal cslcDestruct(void *pData) -{ - cslCmd_t *pThis = (cslCmd_t*) pData; - - assert(pThis != NULL); - - llDestroy(&pThis->llCmdHdlrs); - free(pThis); - - return RS_RET_OK; -} - - -/* constructor for cslCmd - */ -static rsRetVal cslcConstruct(cslCmd_t **ppThis, int bChainingPermitted) -{ - cslCmd_t *pThis; - DEFiRet; - - assert(ppThis != NULL); - if((pThis = calloc(1, sizeof(cslCmd_t))) == NULL) { - ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); - } - - pThis->bChainingPermitted = bChainingPermitted; - - CHKiRet(llInit(&pThis->llCmdHdlrs, cslchDestruct, cslchKeyDestruct, cslchKeyCompare)); - -finalize_it: - *ppThis = pThis; - RETiRet; -} - - -/* add a handler entry to a known command - */ -static rsRetVal cslcAddHdlr(cslCmd_t *pThis, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData, void *pOwnerCookie) -{ - DEFiRet; - cslCmdHdlr_t *pCmdHdlr = NULL; - - assert(pThis != NULL); - - CHKiRet(cslchConstruct(&pCmdHdlr)); - CHKiRet(cslchSetEntry(pCmdHdlr, eType, pHdlr, pData)); - CHKiRet(llAppend(&pThis->llCmdHdlrs, pOwnerCookie, pCmdHdlr)); - -finalize_it: - if(iRet != RS_RET_OK) { - if(pHdlr != NULL) - cslchDestruct(pCmdHdlr); - } - - RETiRet; -} - - -/* function that registers cfsysline handlers. - * The supplied pCmdName is copied and a new buffer is allocated. This - * buffer is automatically destroyed when the element is freed, the - * caller does not need to take care of that. The caller must, however, - * free pCmdName if he allocated it dynamically! -- rgerhards, 2007-08-09 - */ -rsRetVal regCfSysLineHdlr(uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData, - void *pOwnerCookie) -{ - DEFiRet; - cslCmd_t *pThis; - uchar *pMyCmdName; - - iRet = llFind(&llCmdList, (void *) pCmdName, (void*) &pThis); - if(iRet == RS_RET_NOT_FOUND) { - /* new command */ - CHKiRet(cslcConstruct(&pThis, bChainingPermitted)); - CHKiRet_Hdlr(cslcAddHdlr(pThis, eType, pHdlr, pData, pOwnerCookie)) { - cslcDestruct(pThis); - goto finalize_it; - } - /* important: add to list, AFTER everything else is OK. Else - * we mess up things in the error case. - */ - if((pMyCmdName = (uchar*) strdup((char*)pCmdName)) == NULL) { - cslcDestruct(pThis); - ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); - } - CHKiRet_Hdlr(llAppend(&llCmdList, pMyCmdName, (void*) pThis)) { - cslcDestruct(pThis); - goto finalize_it; - } - } else { - /* command already exists, are we allowed to chain? */ - if(pThis->bChainingPermitted == 0 || bChainingPermitted == 0) { - ABORT_FINALIZE(RS_RET_CHAIN_NOT_PERMITTED); - } - CHKiRet_Hdlr(cslcAddHdlr(pThis, eType, pHdlr, pData, pOwnerCookie)) { - cslcDestruct(pThis); - goto finalize_it; - } - } - -finalize_it: - RETiRet; -} - - -rsRetVal unregCfSysLineHdlrs(void) -{ - return llDestroy(&llCmdList); -} - - -/* helper function for unregCfSysLineHdlrs4Owner(). This is used to see if there is - * a handler of this owner inside the element and, if so, remove it. Please note that - * it keeps track of a pointer to the last linked list entry, as this is needed to - * remove an entry from the list. - * rgerhards, 2007-11-21 - */ -DEFFUNC_llExecFunc(unregHdlrsHeadExec) -{ - DEFiRet; - cslCmd_t *pListHdr = (cslCmd_t*) pData; - int iNumElts; - - /* first find element */ - iRet = llFindAndDelete(&(pListHdr->llCmdHdlrs), pParam); - - /* now go back and check how many elements are left */ - CHKiRet(llGetNumElts(&(pListHdr->llCmdHdlrs), &iNumElts)); - - if(iNumElts == 0) { - /* nothing left in header, so request to delete it */ - iRet = RS_RET_OK_DELETE_LISTENTRY; - } - -finalize_it: - RETiRet; -} -/* unregister and destroy cfSysLineHandlers for a specific owner. This method is - * most importantly used before unloading a loadable module providing some handlers. - * The full list of handlers is searched. If the to-be removed handler was the only - * handler for a directive name, the directive header, too, is deleted. - * rgerhards, 2007-11-21 - */ -rsRetVal unregCfSysLineHdlrs4Owner(void *pOwnerCookie) -{ - DEFiRet; - /* we need to walk through all directive names, as the linked list - * class does not provide a way to just search the lower-level handlers. - */ - iRet = llExecFunc(&llCmdList, unregHdlrsHeadExec, pOwnerCookie); - - RETiRet; -} - - -/* process a cfsysline command (based on handler structure) - * param "p" is a pointer to the command line after the command. Should be - * updated. - */ -rsRetVal processCfSysLineCommand(uchar *pCmdName, uchar **p) -{ - DEFiRet; - rsRetVal iRetLL; /* for linked list handling */ - cslCmd_t *pCmd; - cslCmdHdlr_t *pCmdHdlr; - linkedListCookie_t llCookieCmdHdlr; - uchar *pHdlrP; /* the handler's private p (else we could only call one handler) */ - int bWasOnceOK; /* was the result of an handler at least once RS_RET_OK? */ - uchar *pOKp = NULL; /* returned conf line pointer when it was OK */ - - iRet = llFind(&llCmdList, (void *) pCmdName, (void*) &pCmd); - - if(iRet == RS_RET_NOT_FOUND) { - errmsg.LogError(NO_ERRCODE, "invalid or yet-unknown config file command - have you forgotten to load a module?"); - } - - if(iRet != RS_RET_OK) - goto finalize_it; - - llCookieCmdHdlr = NULL; - bWasOnceOK = 0; - while((iRetLL = llGetNextElt(&pCmd->llCmdHdlrs, &llCookieCmdHdlr, (void*)&pCmdHdlr)) == RS_RET_OK) { - /* for the time being, we ignore errors during handlers. The - * reason is that handlers are independent. An error in one - * handler does not necessarily mean that another one will - * fail, too. Later, we might add a config variable to control - * this behaviour (but I am not sure if that is rally - * necessary). -- rgerhards, 2007-07-31 - */ - pHdlrP = *p; - if((iRet = cslchCallHdlr(pCmdHdlr, &pHdlrP)) == RS_RET_OK) { - bWasOnceOK = 1; - pOKp = pHdlrP; - } - } - - if(bWasOnceOK == 1) { - *p = pOKp; - iRet = RS_RET_OK; - } - - if(iRetLL != RS_RET_END_OF_LINKEDLIST) - iRet = iRetLL; - -finalize_it: - RETiRet; -} - - -/* debug print the command handler structure - */ -void dbgPrintCfSysLineHandlers(void) -{ - DEFiRet; - - cslCmd_t *pCmd; - cslCmdHdlr_t *pCmdHdlr; - linkedListCookie_t llCookieCmd; - linkedListCookie_t llCookieCmdHdlr; - uchar *pKey; - - dbgprintf("Sytem Line Configuration Commands:\n"); - llCookieCmd = NULL; - while((iRet = llGetNextElt(&llCmdList, &llCookieCmd, (void*)&pCmd)) == RS_RET_OK) { - llGetKey(llCookieCmd, (void*) &pKey); /* TODO: using the cookie is NOT clean! */ - dbgprintf("\tCommand '%s':\n", pKey); - llCookieCmdHdlr = NULL; - while((iRet = llGetNextElt(&pCmd->llCmdHdlrs, &llCookieCmdHdlr, (void*)&pCmdHdlr)) == RS_RET_OK) { - dbgprintf("\t\ttype : %d\n", pCmdHdlr->eType); - dbgprintf("\t\tpData: 0x%lx\n", (unsigned long) pCmdHdlr->pData); - dbgprintf("\t\tHdlr : 0x%lx\n", (unsigned long) pCmdHdlr->cslCmdHdlr); - dbgprintf("\t\tOwner: 0x%lx\n", (unsigned long) llCookieCmdHdlr->pKey); - dbgprintf("\n"); - } - } - dbgprintf("\n"); - ENDfunc -} - - -/* our init function. TODO: remove once converted to a class - */ -rsRetVal cfsyslineInit() -{ - DEFiRet; - CHKiRet(objGetObjInterface(&obj)); - CHKiRet(objUse(errmsg, CORE_COMPONENT)); - - CHKiRet(llInit(&llCmdList, cslcDestruct, cslcKeyDestruct, strcasecmp)); - -finalize_it: - RETiRet; -} - -/* vim:set ai: - */ diff --git a/cfsysline.h b/cfsysline.h deleted file mode 100644 index 2eec18ab..00000000 --- a/cfsysline.h +++ /dev/null @@ -1,75 +0,0 @@ -/* Definition of the cfsysline (config file system line) object. - * - * Copyright 2007 Rainer Gerhards and Adiscon GmbH. - * - * This file is part of rsyslog. - * - * Rsyslog is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Rsyslog is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Rsyslog. If not, see . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ - -#ifndef CFSYSLINE_H_INCLUDED -#define CFSYSLINE_H_INCLUDED - -#include "linkedlist.h" - -/* types of configuration handlers - */ -typedef enum cslCmdHdlrType { - eCmdHdlrInvalid = 0, /* invalid handler type - indicates a coding error */ - eCmdHdlrCustomHandler, /* custom handler, just call handler function */ - eCmdHdlrUID, - eCmdHdlrGID, - eCmdHdlrBinary, - eCmdHdlrFileCreateMode, - eCmdHdlrInt, - eCmdHdlrSize, - eCmdHdlrGetChar, - eCmdHdlrFacility, - eCmdHdlrSeverity, - eCmdHdlrGetWord -} ecslCmdHdrlType; - -/* this is a single entry for a parse routine. It describes exactly - * one entry point/handler. - * The short name is cslch (Configfile SysLine CommandHandler) - */ -struct cslCmdHdlr_s { /* config file sysline parse entry */ - ecslCmdHdrlType eType; /* which type of handler is this? */ - rsRetVal (*cslCmdHdlr)(); /* function pointer to use with handler (params depending on eType) */ - void *pData; /* user-supplied data pointer */ -}; -typedef struct cslCmdHdlr_s cslCmdHdlr_t; - - -/* this is the list of known configuration commands with pointers to - * their handlers. - * The short name is cslc (Configfile SysLine Command) - */ -struct cslCmd_s { /* config file sysline parse entry */ - int bChainingPermitted; /* may multiple handlers be chained for this command? */ - linkedList_t llCmdHdlrs; /* linked list of command handlers */ -}; -typedef struct cslCmd_s cslCmd_t; - -/* prototypes */ -rsRetVal regCfSysLineHdlr(uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData, void *pOwnerCookie); -rsRetVal unregCfSysLineHdlrs(void); -rsRetVal unregCfSysLineHdlrs4Owner(void *pOwnerCookie); -rsRetVal processCfSysLineCommand(uchar *pCmd, uchar **p); -rsRetVal cfsyslineInit(void); -void dbgPrintCfSysLineHandlers(void); - -#endif /* #ifndef CFSYSLINE_H_INCLUDED */ diff --git a/dirty.h b/dirty.h index f0664639..9e15b4ab 100644 --- a/dirty.h +++ b/dirty.h @@ -42,7 +42,7 @@ extern char **StripDomains; extern char *LocalDomain; extern char**LocalHosts; extern uchar *LocalHostName; -extern int family; +//extern int family; extern int bDropMalPTRMsgs; extern int option_DisallowWarning; diff --git a/doc/features.html b/doc/features.html index 13fc34c6..f9d17818 100644 --- a/doc/features.html +++ b/doc/features.html @@ -101,10 +101,8 @@ typically within reach of implementation. Users are encouraged to submit feature requests there (or via our forums). If we like them but they look quite long-lived (aka "not soon to be implemented"), they will possibly be migrated to this list here and at some time moved back -to the sourceforge tracker.

+to the bugzilla tracker.

    -
  • implement native email-functionality in selector (probably -best done as a plug-in)
  • port it to more *nix variants (eg AIX and HP UX) - this needs volunteers with access to those machines and knowledge
  • support for native SSL enryption of plain tcp syslog @@ -124,4 +122,4 @@ future of RFC 3195 in rsyslog.
  • To see when each feature was added, see the rsyslog change log (online only).

    - \ No newline at end of file + diff --git a/plugins/im3195/Makefile.am b/plugins/im3195/Makefile.am new file mode 100644 index 00000000..57c8ab8b --- /dev/null +++ b/plugins/im3195/Makefile.am @@ -0,0 +1,8 @@ +pkglib_LTLIBRARIES = im3195.la + +im3195_la_SOURCES = im3195.c im3195.h +im3195_la_CPPFLAGS = $(rsrt_cflags) $(pthreads_cflags) $(LIBLOGGING_CFLAGS) +im3195_la_LDFLAGS = -module -avoid-version +im3195_la_LIBADD = $(LIBLOGGING_LIBS) + +EXTRA_DIST = diff --git a/plugins/imfile/imfile.c b/plugins/imfile/imfile.c index 925d0175..f95f9bc4 100644 --- a/plugins/imfile/imfile.c +++ b/plugins/imfile/imfile.c @@ -43,6 +43,7 @@ #include "msg.h" #include "stream.h" #include "errmsg.h" +#include "glbl.h" #include "datetime.h" MODULE_TYPE_INPUT /* must be present for input modules, do not remove */ @@ -52,6 +53,7 @@ MODULE_TYPE_INPUT /* must be present for input modules, do not remove */ /* Module static data */ DEF_IMOD_STATIC_DATA /* must be present, starts static data */ DEFobjCurrIf(errmsg) +DEFobjCurrIf(glbl) DEFobjCurrIf(datetime) typedef struct fileInfo_s { @@ -121,7 +123,7 @@ openFile(fileInfo_t *pThis) /* Construct file name */ lenSFNam = snprintf((char*)pszSFNam, sizeof(pszSFNam) / sizeof(uchar), "%s/%s", - (char*) glblGetWorkDir(), (char*)pThis->pszStateFile); + (char*) glbl.GetWorkDir(), (char*)pThis->pszStateFile); /* check if the file exists */ if(stat((char*) pszSFNam, &stat_buf) == -1) { @@ -334,7 +336,7 @@ persistStrmState(fileInfo_t *pInfo) /* TODO: create a function persistObj in obj.c? */ CHKiRet(strmConstruct(&psSF)); - CHKiRet(strmSetDir(psSF, glblGetWorkDir(), strlen((char*)glblGetWorkDir()))); + CHKiRet(strmSetDir(psSF, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir()))); CHKiRet(strmSettOperationsMode(psSF, STREAMMODE_WRITE)); CHKiRet(strmSetiAddtlOpenFlags(psSF, O_TRUNC)); CHKiRet(strmSetsType(psSF, STREAMTYPE_FILE_SINGLE)); @@ -381,6 +383,7 @@ BEGINmodExit CODESTARTmodExit /* release objects we used */ objRelease(datetime, CORE_COMPONENT); + objRelease(glbl, CORE_COMPONENT); objRelease(errmsg, CORE_COMPONENT); ENDmodExit @@ -488,6 +491,7 @@ CODESTARTmodInit *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(errmsg, CORE_COMPONENT)); + CHKiRet(objUse(glbl, CORE_COMPONENT)); CHKiRet(objUse(datetime, CORE_COMPONENT)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfilename", 0, eCmdHdlrGetWord, diff --git a/runtime/Makefile.am b/runtime/Makefile.am index 6cd54f91..73418fdf 100644 --- a/runtime/Makefile.am +++ b/runtime/Makefile.am @@ -12,6 +12,7 @@ librsyslog_la_SOURCES = \ module-template.h \ obj-types.h \ glbl.h \ + glbl.c \ msg.c \ msg.h \ linkedlist.c \ @@ -59,7 +60,9 @@ librsyslog_la_SOURCES = \ vmop.c \ vmop.h \ queue.c \ - queue.h + queue.h \ + cfsysline.c \ + cfsysline.h librsyslog_la_CPPFLAGS = -D_PATH_MODDIR=\"$(pkglibdir)/\" -I$(top_srcdir) $(pthreads_cflags) #librsyslog_la_LDFLAGS = -module -avoid-version diff --git a/runtime/cfsysline.c b/runtime/cfsysline.c new file mode 100644 index 00000000..cb86c7d4 --- /dev/null +++ b/runtime/cfsysline.c @@ -0,0 +1,991 @@ +/* cfsysline.c + * Implementation of the configuration system line object. + * + * File begun on 2007-07-30 by RGerhards + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#include "config.h" + +#include "rsyslog.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dirty.h" /* TODO: when the module interface & library design is done, this should be able to go away */ +#include "cfsysline.h" +#include "obj.h" +#include "errmsg.h" +#include "srUtils.h" + + +/* static data */ +DEFobjCurrIf(obj) +DEFobjCurrIf(errmsg) + +linkedList_t llCmdList; /* this is NOT a pointer - no typo here ;) */ + +/* --------------- START functions for handling canned syntaxes --------------- */ + + +/* parse a character from the config line + * added 2007-07-17 by rgerhards + * TODO: enhance this function to handle different classes of characters + * HINT: check if char is ' and, if so, use 'c' where c may also be things + * like \t etc. + */ +static rsRetVal doGetChar(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *pVal) +{ + DEFiRet; + + assert(pp != NULL); + assert(*pp != NULL); + + skipWhiteSpace(pp); /* skip over any whitespace */ + + /* if we are not at a '\0', we have our new char - no validity checks here... */ + if(**pp == '\0') { + errmsg.LogError(NO_ERRCODE, "No character available"); + iRet = RS_RET_NOT_FOUND; + } else { + if(pSetHdlr == NULL) { + /* we should set value directly to var */ + *((uchar*)pVal) = **pp; + } else { + /* we set value via a set function */ + CHKiRet(pSetHdlr(pVal, **pp)); + } + ++(*pp); /* eat processed char */ + } + +finalize_it: + RETiRet; +} + + +/* Parse a number from the configuration line. This is more or less + * a shell to call the custom handler. + * rgerhards, 2007-07-31 + */ +static rsRetVal doCustomHdlr(uchar **pp, rsRetVal (*pSetHdlr)(uchar**, void*), void *pVal) +{ + DEFiRet; + + assert(pp != NULL); + assert(*pp != NULL); + + CHKiRet(pSetHdlr(pp, pVal)); + +finalize_it: + RETiRet; +} + + +/* Parse a number from the configuration line. This functions just parses + * the number and does NOT call any handlers or set any values. It is just + * for INTERNAL USE by other parse functions! + * rgerhards, 2008-01-08 + */ +static rsRetVal parseIntVal(uchar **pp, int64 *pVal) +{ + DEFiRet; + uchar *p; + int64 i; + int bWasNegative; + + assert(pp != NULL); + assert(*pp != NULL); + assert(pVal != NULL); + + skipWhiteSpace(pp); /* skip over any whitespace */ + p = *pp; + + if(*p == '-') { + bWasNegative = 1; + ++p; /* eat it */ + } else { + bWasNegative = 0; + } + + if(!isdigit((int) *p)) { + errno = 0; + errmsg.LogError(NO_ERRCODE, "invalid number"); + ABORT_FINALIZE(RS_RET_INVALID_INT); + } + + /* pull value */ + for(i = 0 ; *p && (isdigit((int) *p) || *p == '.' || *p == ',') ; ++p) { + if(isdigit((int) *p)) { + i = i * 10 + *p - '0'; + } + } + + if(bWasNegative) + i *= -1; + + *pVal = i; + *pp = p; + +finalize_it: + RETiRet; +} + + +/* Parse a number from the configuration line. + * rgerhards, 2007-07-31 + */ +static rsRetVal doGetInt(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *pVal) +{ + uchar *p; + DEFiRet; + int64 i; + + assert(pp != NULL); + assert(*pp != NULL); + + CHKiRet(parseIntVal(pp, &i)); + p = *pp; + + if(pSetHdlr == NULL) { + /* we should set value directly to var */ + *((int*)pVal) = (int) i; + } else { + /* we set value via a set function */ + CHKiRet(pSetHdlr(pVal, (int) i)); + } + + *pp = p; + +finalize_it: + RETiRet; +} + + +/* Parse a size from the configuration line. This is basically an integer + * syntax, but modifiers may be added after the integer (e.g. 1k to mean + * 1024). The size must immediately follow the number. Note that the + * param value must be int64! + * rgerhards, 2008-01-09 + */ +static rsRetVal doGetSize(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *pVal) +{ + DEFiRet; + int64 i; + + assert(pp != NULL); + assert(*pp != NULL); + + CHKiRet(parseIntVal(pp, &i)); + + /* we now check if the next character is one of our known modifiers. + * If so, we accept it as such. If not, we leave it alone. tera and + * above does not make any sense as that is above a 32-bit int value. + */ + switch(**pp) { + /* traditional binary-based definitions */ + case 'k': i *= 1024; ++(*pp); break; + case 'm': i *= 1024 * 1024; ++(*pp); break; + case 'g': i *= 1024 * 1024 * 1024; ++(*pp); break; + case 't': i *= (int64) 1024 * 1024 * 1024 * 1024; ++(*pp); break; /* tera */ + case 'p': i *= (int64) 1024 * 1024 * 1024 * 1024 * 1024; ++(*pp); break; /* peta */ + case 'e': i *= (int64) 1024 * 1024 * 1024 * 1024 * 1024 * 1024; ++(*pp); break; /* exa */ + /* and now the "new" 1000-based definitions */ + case 'K': i *= 1000; ++(*pp); break; + case 'M': i *= 10000; ++(*pp); break; + case 'G': i *= 100000; ++(*pp); break; + case 'T': i *= 1000000; ++(*pp); break; /* tera */ + case 'P': i *= 10000000; ++(*pp); break; /* peta */ + case 'E': i *= 100000000; ++(*pp); break; /* exa */ + } + + /* done */ + if(pSetHdlr == NULL) { + /* we should set value directly to var */ + *((int64*)pVal) = i; + } else { + /* we set value via a set function */ + CHKiRet(pSetHdlr(pVal, i)); + } + +finalize_it: + RETiRet; +} + + +/* Parse and interpet a $FileCreateMode and $umask line. This function + * pulls the creation mode and, if successful, stores it + * into the global variable so that the rest of rsyslogd + * opens files with that mode. Any previous value will be + * overwritten. + * HINT: if we store the creation mode in selector_t, we + * can even specify multiple modes simply be virtue of + * being placed in the right section of rsyslog.conf + * rgerhards, 2007-07-4 (happy independence day to my US friends!) + * Parameter **pp has a pointer to the current config line. + * On exit, it will be updated to the processed position. + */ +static rsRetVal doFileCreateMode(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *pVal) +{ + uchar *p; + DEFiRet; + uchar errMsg[128]; /* for dynamic error messages */ + int iVal; + + assert(pp != NULL); + assert(*pp != NULL); + + skipWhiteSpace(pp); /* skip over any whitespace */ + p = *pp; + + /* for now, we parse and accept only octal numbers + * Sequence of tests is important, we are using boolean shortcuts + * to avoid addressing invalid memory! + */ + if(!( (*p == '0') + && (*(p+1) && *(p+1) >= '0' && *(p+1) <= '7') + && (*(p+2) && *(p+2) >= '0' && *(p+2) <= '7') + && (*(p+3) && *(p+3) >= '0' && *(p+3) <= '7') ) ) { + snprintf((char*) errMsg, sizeof(errMsg)/sizeof(uchar), + "value must be octal (e.g 0644)."); + errno = 0; + errmsg.LogError(NO_ERRCODE, "%s", errMsg); + ABORT_FINALIZE(RS_RET_INVALID_VALUE); + } + + /* we reach this code only if the octal number is ok - so we can now + * compute the value. + */ + iVal = (*(p+1)-'0') * 64 + (*(p+2)-'0') * 8 + (*(p+3)-'0'); + + if(pSetHdlr == NULL) { + /* we should set value directly to var */ + *((int*)pVal) = iVal; + } else { + /* we set value via a set function */ + CHKiRet(pSetHdlr(pVal, iVal)); + } + + p += 4; /* eat the octal number */ + *pp = p; + +finalize_it: + RETiRet; +} + + +/* Parse and interpret an on/off inside a config file line. This is most + * often used for boolean options, but of course it may also be used + * for other things. The passed-in pointer is updated to point to + * the first unparsed character on exit. Function emits error messages + * if the value is neither on or off. It returns 0 if the option is off, + * 1 if it is on and another value if there was an error. + * rgerhards, 2007-07-15 + */ +static int doParseOnOffOption(uchar **pp) +{ + uchar *pOptStart; + uchar szOpt[32]; + + assert(pp != NULL); + assert(*pp != NULL); + + pOptStart = *pp; + skipWhiteSpace(pp); /* skip over any whitespace */ + + if(getSubString(pp, (char*) szOpt, sizeof(szOpt) / sizeof(uchar), ' ') != 0) { + errmsg.LogError(NO_ERRCODE, "Invalid $-configline - could not extract on/off option"); + return -1; + } + + if(!strcmp((char*)szOpt, "on")) { + return 1; + } else if(!strcmp((char*)szOpt, "off")) { + return 0; + } else { + errmsg.LogError(NO_ERRCODE, "Option value must be on or off, but is '%s'", (char*)pOptStart); + return -1; + } +} + + +/* extract a groupname and return its gid. + * rgerhards, 2007-07-17 + */ +static rsRetVal doGetGID(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *pVal) +{ + struct group *pgBuf; + struct group gBuf; + DEFiRet; + uchar szName[256]; + char stringBuf[2048]; /* I hope this is large enough... */ + + assert(pp != NULL); + assert(*pp != NULL); + + if(getSubString(pp, (char*) szName, sizeof(szName) / sizeof(uchar), ' ') != 0) { + errmsg.LogError(NO_ERRCODE, "could not extract group name"); + ABORT_FINALIZE(RS_RET_NOT_FOUND); + } + + getgrnam_r((char*)szName, &gBuf, stringBuf, sizeof(stringBuf), &pgBuf); + + if(pgBuf == NULL) { + errmsg.LogError(NO_ERRCODE, "ID for group '%s' could not be found or error", (char*)szName); + iRet = RS_RET_NOT_FOUND; + } else { + if(pSetHdlr == NULL) { + /* we should set value directly to var */ + *((gid_t*)pVal) = pgBuf->gr_gid; + } else { + /* we set value via a set function */ + CHKiRet(pSetHdlr(pVal, pgBuf->gr_gid)); + } + dbgprintf("gid %d obtained for group '%s'\n", pgBuf->gr_gid, szName); + } + + skipWhiteSpace(pp); /* skip over any whitespace */ + +finalize_it: + RETiRet; +} + + +/* extract a username and return its uid. + * rgerhards, 2007-07-17 + */ +static rsRetVal doGetUID(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *pVal) +{ + struct passwd *ppwBuf; + struct passwd pwBuf; + DEFiRet; + uchar szName[256]; + char stringBuf[2048]; /* I hope this is large enough... */ + + assert(pp != NULL); + assert(*pp != NULL); + + if(getSubString(pp, (char*) szName, sizeof(szName) / sizeof(uchar), ' ') != 0) { + errmsg.LogError(NO_ERRCODE, "could not extract user name"); + ABORT_FINALIZE(RS_RET_NOT_FOUND); + } + + getpwnam_r((char*)szName, &pwBuf, stringBuf, sizeof(stringBuf), &ppwBuf); + + if(ppwBuf == NULL) { + errmsg.LogError(NO_ERRCODE, "ID for user '%s' could not be found or error", (char*)szName); + iRet = RS_RET_NOT_FOUND; + } else { + if(pSetHdlr == NULL) { + /* we should set value directly to var */ + *((uid_t*)pVal) = ppwBuf->pw_uid; + } else { + /* we set value via a set function */ + CHKiRet(pSetHdlr(pVal, ppwBuf->pw_uid)); + } + dbgprintf("uid %d obtained for user '%s'\n", ppwBuf->pw_uid, szName); + } + + skipWhiteSpace(pp); /* skip over any whitespace */ + +finalize_it: + RETiRet; +} + + +/* Parse and process an binary cofig option. pVal must be + * a pointer to an integer which is to receive the option + * value. + * rgerhards, 2007-07-15 + */ +static rsRetVal doBinaryOptionLine(uchar **pp, rsRetVal (*pSetHdlr)(void*, int), void *pVal) +{ + int iOption; + DEFiRet; + + assert(pp != NULL); + assert(*pp != NULL); + + if((iOption = doParseOnOffOption(pp)) == -1) + return RS_RET_ERR; /* nothing left to do */ + + if(pSetHdlr == NULL) { + /* we should set value directly to var */ + *((int*)pVal) = iOption; + } else { + /* we set value via a set function */ + CHKiRet(pSetHdlr(pVal, iOption)); + } + + skipWhiteSpace(pp); /* skip over any whitespace */ + +finalize_it: + RETiRet; +} + + +/* parse a whitespace-delimited word from the provided string. This is a + * helper function for a number of syntaxes. The parsed value is returned + * in ppStrB (which must be provided by caller). + * rgerhards, 2008-02-14 + */ +static rsRetVal +getWord(uchar **pp, cstr_t **ppStrB) +{ + DEFiRet; + uchar *p; + + ASSERT(pp != NULL); + ASSERT(*pp != NULL); + ASSERT(ppStrB != NULL); + + CHKiRet(rsCStrConstruct(ppStrB)); + + /* parse out the word */ + p = *pp; + + while(*p && !isspace((int) *p)) { + CHKiRet(rsCStrAppendChar(*ppStrB, *p++)); + } + CHKiRet(rsCStrFinish(*ppStrB)); + + *pp = p; + +finalize_it: + RETiRet; +} + + +/* Parse and a word config line option. A word is a consequtive + * sequence of non-whitespace characters. pVal must be + * a pointer to a string which is to receive the option + * value. The returned string must be freed by the caller. + * rgerhards, 2007-09-07 + * To facilitate multiple instances of the same command line + * directive, doGetWord() now checks if pVal is already a + * non-NULL pointer. If so, we assume it was created by a previous + * incarnation and is automatically freed. This happens only when + * no custom handler is defined. If it is, the customer handler + * must do the cleanup. I have checked and this was al also memory + * leak with some code. Obviously, not a large one. -- rgerhards, 2007-12-20 + * Just to clarify: if pVal is parsed to a custom handler, this handler + * is responsible for freeing pVal. -- rgerhards, 2008-03-20 + */ +static rsRetVal doGetWord(uchar **pp, rsRetVal (*pSetHdlr)(void*, uchar*), void *pVal) +{ + DEFiRet; + cstr_t *pStrB; + uchar *pNewVal; + + ASSERT(pp != NULL); + ASSERT(*pp != NULL); + + CHKiRet(getWord(pp, &pStrB)); + CHKiRet(rsCStrConvSzStrAndDestruct(pStrB, &pNewVal, 0)); + pStrB = NULL; + + /* we got the word, now set it */ + if(pSetHdlr == NULL) { + /* we should set value directly to var */ + if(*((uchar**)pVal) != NULL) + free(*((uchar**)pVal)); /* free previous entry */ + *((uchar**)pVal) = pNewVal; /* set new one */ + } else { + /* we set value via a set function */ + CHKiRet(pSetHdlr(pVal, pNewVal)); + } + + skipWhiteSpace(pp); /* skip over any whitespace */ + +finalize_it: + if(iRet != RS_RET_OK) { + if(pStrB != NULL) + rsCStrDestruct(&pStrB); + } + + RETiRet; +} + + +/* parse a syslog name from the string. This is the generic code that is + * called by the facility/severity functions. Note that we do not check the + * validity of numerical values, something that should probably change over + * time (TODO). -- rgerhards, 2008-02-14 + */ +static rsRetVal +doSyslogName(uchar **pp, rsRetVal (*pSetHdlr)(void*, int), void *pVal, syslogName_t *pNameTable) +{ + DEFiRet; + cstr_t *pStrB; + int iNewVal; + + ASSERT(pp != NULL); + ASSERT(*pp != NULL); + + CHKiRet(getWord(pp, &pStrB)); /* get word */ + iNewVal = decodeSyslogName(rsCStrGetSzStr(pStrB), pNameTable); + + if(pSetHdlr == NULL) { + /* we should set value directly to var */ + *((int*)pVal) = iNewVal; /* set new one */ + } else { + /* we set value via a set function */ + CHKiRet(pSetHdlr(pVal, iNewVal)); + } + + skipWhiteSpace(pp); /* skip over any whitespace */ + +finalize_it: + if(pStrB != NULL) + rsCStrDestruct(&pStrB); + + RETiRet; +} + + +/* Implements the facility syntax. + * rgerhards, 2008-02-14 + */ +static rsRetVal +doFacility(uchar **pp, rsRetVal (*pSetHdlr)(void*, int), void *pVal) +{ + DEFiRet; + iRet = doSyslogName(pp, pSetHdlr, pVal, syslogFacNames); + RETiRet; +} + + +/* Implements the severity syntax. + * rgerhards, 2008-02-14 + */ +static rsRetVal +doSeverity(uchar **pp, rsRetVal (*pSetHdlr)(void*, int), void *pVal) +{ + DEFiRet; + iRet = doSyslogName(pp, pSetHdlr, pVal, syslogPriNames); + RETiRet; +} + + +/* --------------- END functions for handling canned syntaxes --------------- */ + +/* destructor for cslCmdHdlr + * pThis is actually a cslCmdHdlr_t, but we do not cast it as all we currently + * need to do is free it. + */ +static rsRetVal cslchDestruct(void *pThis) +{ + ASSERT(pThis != NULL); + free(pThis); + + return RS_RET_OK; +} + + +/* constructor for cslCmdHdlr + */ +static rsRetVal cslchConstruct(cslCmdHdlr_t **ppThis) +{ + cslCmdHdlr_t *pThis; + DEFiRet; + + assert(ppThis != NULL); + if((pThis = calloc(1, sizeof(cslCmdHdlr_t))) == NULL) { + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + } + +finalize_it: + *ppThis = pThis; + RETiRet; +} + +/* destructor for linked list keys. As we do not use any dynamic memory, + * we simply return. However, this entry point must be defined for the + * linkedList class to make sure we have not forgotten a destructor. + * rgerhards, 2007-11-21 + */ +static rsRetVal cslchKeyDestruct(void __attribute__((unused)) *pData) +{ + return RS_RET_OK; +} + + +/* Key compare operation for linked list class. This compares two + * owner cookies (void *). + * rgerhards, 2007-11-21 + */ +static int cslchKeyCompare(void *pKey1, void *pKey2) +{ + if(pKey1 == pKey2) + return 0; + else + if(pKey1 < pKey2) + return -1; + else + return 1; +} + + +/* set data members for this object + */ +rsRetVal cslchSetEntry(cslCmdHdlr_t *pThis, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData) +{ + assert(pThis != NULL); + assert(eType != eCmdHdlrInvalid); + + pThis->eType = eType; + pThis->cslCmdHdlr = pHdlr; + pThis->pData = pData; + + return RS_RET_OK; +} + + +/* call the specified handler + */ +static rsRetVal cslchCallHdlr(cslCmdHdlr_t *pThis, uchar **ppConfLine) +{ + DEFiRet; + rsRetVal (*pHdlr)() = NULL; + assert(pThis != NULL); + assert(ppConfLine != NULL); + + switch(pThis->eType) { + case eCmdHdlrCustomHandler: + pHdlr = doCustomHdlr; + break; + case eCmdHdlrUID: + pHdlr = doGetUID; + break; + case eCmdHdlrGID: + pHdlr = doGetGID; + break; + case eCmdHdlrBinary: + pHdlr = doBinaryOptionLine; + break; + case eCmdHdlrFileCreateMode: + pHdlr = doFileCreateMode; + break; + case eCmdHdlrInt: + pHdlr = doGetInt; + break; + case eCmdHdlrSize: + pHdlr = doGetSize; + break; + case eCmdHdlrGetChar: + pHdlr = doGetChar; + break; + case eCmdHdlrFacility: + pHdlr = doFacility; + break; + case eCmdHdlrSeverity: + pHdlr = doSeverity; + break; + case eCmdHdlrGetWord: + pHdlr = doGetWord; + break; + default: + iRet = RS_RET_NOT_IMPLEMENTED; + goto finalize_it; + } + + /* we got a pointer to the handler, so let's call it */ + assert(pHdlr != NULL); + CHKiRet(pHdlr(ppConfLine, pThis->cslCmdHdlr, pThis->pData)); + +finalize_it: + RETiRet; +} + + +/* ---------------------------------------------------------------------- * + * now come the handlers for cslCmd_t + * ---------------------------------------------------------------------- */ + +/* destructor for a cslCmd list key (a string as of now) + */ +static rsRetVal cslcKeyDestruct(void *pData) +{ + free(pData); /* we do not need to cast as all we do is free it anyway... */ + return RS_RET_OK; +} + +/* destructor for cslCmd + */ +static rsRetVal cslcDestruct(void *pData) +{ + cslCmd_t *pThis = (cslCmd_t*) pData; + + assert(pThis != NULL); + + llDestroy(&pThis->llCmdHdlrs); + free(pThis); + + return RS_RET_OK; +} + + +/* constructor for cslCmd + */ +static rsRetVal cslcConstruct(cslCmd_t **ppThis, int bChainingPermitted) +{ + cslCmd_t *pThis; + DEFiRet; + + assert(ppThis != NULL); + if((pThis = calloc(1, sizeof(cslCmd_t))) == NULL) { + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + } + + pThis->bChainingPermitted = bChainingPermitted; + + CHKiRet(llInit(&pThis->llCmdHdlrs, cslchDestruct, cslchKeyDestruct, cslchKeyCompare)); + +finalize_it: + *ppThis = pThis; + RETiRet; +} + + +/* add a handler entry to a known command + */ +static rsRetVal cslcAddHdlr(cslCmd_t *pThis, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData, void *pOwnerCookie) +{ + DEFiRet; + cslCmdHdlr_t *pCmdHdlr = NULL; + + assert(pThis != NULL); + + CHKiRet(cslchConstruct(&pCmdHdlr)); + CHKiRet(cslchSetEntry(pCmdHdlr, eType, pHdlr, pData)); + CHKiRet(llAppend(&pThis->llCmdHdlrs, pOwnerCookie, pCmdHdlr)); + +finalize_it: + if(iRet != RS_RET_OK) { + if(pHdlr != NULL) + cslchDestruct(pCmdHdlr); + } + + RETiRet; +} + + +/* function that registers cfsysline handlers. + * The supplied pCmdName is copied and a new buffer is allocated. This + * buffer is automatically destroyed when the element is freed, the + * caller does not need to take care of that. The caller must, however, + * free pCmdName if he allocated it dynamically! -- rgerhards, 2007-08-09 + */ +rsRetVal regCfSysLineHdlr(uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData, + void *pOwnerCookie) +{ + DEFiRet; + cslCmd_t *pThis; + uchar *pMyCmdName; + + iRet = llFind(&llCmdList, (void *) pCmdName, (void*) &pThis); + if(iRet == RS_RET_NOT_FOUND) { + /* new command */ + CHKiRet(cslcConstruct(&pThis, bChainingPermitted)); + CHKiRet_Hdlr(cslcAddHdlr(pThis, eType, pHdlr, pData, pOwnerCookie)) { + cslcDestruct(pThis); + goto finalize_it; + } + /* important: add to list, AFTER everything else is OK. Else + * we mess up things in the error case. + */ + if((pMyCmdName = (uchar*) strdup((char*)pCmdName)) == NULL) { + cslcDestruct(pThis); + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + } + CHKiRet_Hdlr(llAppend(&llCmdList, pMyCmdName, (void*) pThis)) { + cslcDestruct(pThis); + goto finalize_it; + } + } else { + /* command already exists, are we allowed to chain? */ + if(pThis->bChainingPermitted == 0 || bChainingPermitted == 0) { + ABORT_FINALIZE(RS_RET_CHAIN_NOT_PERMITTED); + } + CHKiRet_Hdlr(cslcAddHdlr(pThis, eType, pHdlr, pData, pOwnerCookie)) { + cslcDestruct(pThis); + goto finalize_it; + } + } + +finalize_it: + RETiRet; +} + + +rsRetVal unregCfSysLineHdlrs(void) +{ + return llDestroy(&llCmdList); +} + + +/* helper function for unregCfSysLineHdlrs4Owner(). This is used to see if there is + * a handler of this owner inside the element and, if so, remove it. Please note that + * it keeps track of a pointer to the last linked list entry, as this is needed to + * remove an entry from the list. + * rgerhards, 2007-11-21 + */ +DEFFUNC_llExecFunc(unregHdlrsHeadExec) +{ + DEFiRet; + cslCmd_t *pListHdr = (cslCmd_t*) pData; + int iNumElts; + + /* first find element */ + iRet = llFindAndDelete(&(pListHdr->llCmdHdlrs), pParam); + + /* now go back and check how many elements are left */ + CHKiRet(llGetNumElts(&(pListHdr->llCmdHdlrs), &iNumElts)); + + if(iNumElts == 0) { + /* nothing left in header, so request to delete it */ + iRet = RS_RET_OK_DELETE_LISTENTRY; + } + +finalize_it: + RETiRet; +} +/* unregister and destroy cfSysLineHandlers for a specific owner. This method is + * most importantly used before unloading a loadable module providing some handlers. + * The full list of handlers is searched. If the to-be removed handler was the only + * handler for a directive name, the directive header, too, is deleted. + * rgerhards, 2007-11-21 + */ +rsRetVal unregCfSysLineHdlrs4Owner(void *pOwnerCookie) +{ + DEFiRet; + /* we need to walk through all directive names, as the linked list + * class does not provide a way to just search the lower-level handlers. + */ + iRet = llExecFunc(&llCmdList, unregHdlrsHeadExec, pOwnerCookie); + + RETiRet; +} + + +/* process a cfsysline command (based on handler structure) + * param "p" is a pointer to the command line after the command. Should be + * updated. + */ +rsRetVal processCfSysLineCommand(uchar *pCmdName, uchar **p) +{ + DEFiRet; + rsRetVal iRetLL; /* for linked list handling */ + cslCmd_t *pCmd; + cslCmdHdlr_t *pCmdHdlr; + linkedListCookie_t llCookieCmdHdlr; + uchar *pHdlrP; /* the handler's private p (else we could only call one handler) */ + int bWasOnceOK; /* was the result of an handler at least once RS_RET_OK? */ + uchar *pOKp = NULL; /* returned conf line pointer when it was OK */ + + iRet = llFind(&llCmdList, (void *) pCmdName, (void*) &pCmd); + + if(iRet == RS_RET_NOT_FOUND) { + errmsg.LogError(NO_ERRCODE, "invalid or yet-unknown config file command - have you forgotten to load a module?"); + } + + if(iRet != RS_RET_OK) + goto finalize_it; + + llCookieCmdHdlr = NULL; + bWasOnceOK = 0; + while((iRetLL = llGetNextElt(&pCmd->llCmdHdlrs, &llCookieCmdHdlr, (void*)&pCmdHdlr)) == RS_RET_OK) { + /* for the time being, we ignore errors during handlers. The + * reason is that handlers are independent. An error in one + * handler does not necessarily mean that another one will + * fail, too. Later, we might add a config variable to control + * this behaviour (but I am not sure if that is rally + * necessary). -- rgerhards, 2007-07-31 + */ + pHdlrP = *p; + if((iRet = cslchCallHdlr(pCmdHdlr, &pHdlrP)) == RS_RET_OK) { + bWasOnceOK = 1; + pOKp = pHdlrP; + } + } + + if(bWasOnceOK == 1) { + *p = pOKp; + iRet = RS_RET_OK; + } + + if(iRetLL != RS_RET_END_OF_LINKEDLIST) + iRet = iRetLL; + +finalize_it: + RETiRet; +} + + +/* debug print the command handler structure + */ +void dbgPrintCfSysLineHandlers(void) +{ + DEFiRet; + + cslCmd_t *pCmd; + cslCmdHdlr_t *pCmdHdlr; + linkedListCookie_t llCookieCmd; + linkedListCookie_t llCookieCmdHdlr; + uchar *pKey; + + dbgprintf("Sytem Line Configuration Commands:\n"); + llCookieCmd = NULL; + while((iRet = llGetNextElt(&llCmdList, &llCookieCmd, (void*)&pCmd)) == RS_RET_OK) { + llGetKey(llCookieCmd, (void*) &pKey); /* TODO: using the cookie is NOT clean! */ + dbgprintf("\tCommand '%s':\n", pKey); + llCookieCmdHdlr = NULL; + while((iRet = llGetNextElt(&pCmd->llCmdHdlrs, &llCookieCmdHdlr, (void*)&pCmdHdlr)) == RS_RET_OK) { + dbgprintf("\t\ttype : %d\n", pCmdHdlr->eType); + dbgprintf("\t\tpData: 0x%lx\n", (unsigned long) pCmdHdlr->pData); + dbgprintf("\t\tHdlr : 0x%lx\n", (unsigned long) pCmdHdlr->cslCmdHdlr); + dbgprintf("\t\tOwner: 0x%lx\n", (unsigned long) llCookieCmdHdlr->pKey); + dbgprintf("\n"); + } + } + dbgprintf("\n"); + ENDfunc +} + + +/* our init function. TODO: remove once converted to a class + */ +rsRetVal cfsyslineInit() +{ + DEFiRet; + CHKiRet(objGetObjInterface(&obj)); + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + + CHKiRet(llInit(&llCmdList, cslcDestruct, cslcKeyDestruct, strcasecmp)); + +finalize_it: + RETiRet; +} + +/* vim:set ai: + */ diff --git a/runtime/cfsysline.h b/runtime/cfsysline.h new file mode 100644 index 00000000..07ab5fcd --- /dev/null +++ b/runtime/cfsysline.h @@ -0,0 +1,76 @@ +/* Definition of the cfsysline (config file system line) object. + * + * Copyright 2007 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ + +#ifndef CFSYSLINE_H_INCLUDED +#define CFSYSLINE_H_INCLUDED + +#include "linkedlist.h" + +/* types of configuration handlers + */ +typedef enum cslCmdHdlrType { + eCmdHdlrInvalid = 0, /* invalid handler type - indicates a coding error */ + eCmdHdlrCustomHandler, /* custom handler, just call handler function */ + eCmdHdlrUID, + eCmdHdlrGID, + eCmdHdlrBinary, + eCmdHdlrFileCreateMode, + eCmdHdlrInt, + eCmdHdlrSize, + eCmdHdlrGetChar, + eCmdHdlrFacility, + eCmdHdlrSeverity, + eCmdHdlrGetWord +} ecslCmdHdrlType; + +/* this is a single entry for a parse routine. It describes exactly + * one entry point/handler. + * The short name is cslch (Configfile SysLine CommandHandler) + */ +struct cslCmdHdlr_s { /* config file sysline parse entry */ + ecslCmdHdrlType eType; /* which type of handler is this? */ + rsRetVal (*cslCmdHdlr)(); /* function pointer to use with handler (params depending on eType) */ + void *pData; /* user-supplied data pointer */ +}; +typedef struct cslCmdHdlr_s cslCmdHdlr_t; + + +/* this is the list of known configuration commands with pointers to + * their handlers. + * The short name is cslc (Configfile SysLine Command) + */ +struct cslCmd_s { /* config file sysline parse entry */ + int bChainingPermitted; /* may multiple handlers be chained for this command? */ + linkedList_t llCmdHdlrs; /* linked list of command handlers */ +}; +typedef struct cslCmd_s cslCmd_t; + +/* prototypes */ +rsRetVal regCfSysLineHdlr(uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData, void *pOwnerCookie); +rsRetVal unregCfSysLineHdlrs(void); +rsRetVal unregCfSysLineHdlrs4Owner(void *pOwnerCookie); +rsRetVal processCfSysLineCommand(uchar *pCmd, uchar **p); +rsRetVal cfsyslineInit(void); +void dbgPrintCfSysLineHandlers(void); + +#endif /* #ifndef CFSYSLINE_H_INCLUDED */ diff --git a/runtime/glbl.h b/runtime/glbl.h index 037c9ec4..b2a26deb 100644 --- a/runtime/glbl.h +++ b/runtime/glbl.h @@ -35,7 +35,22 @@ #define glblGetIOBufSize() 4096 /* size of the IO buffer, e.g. for strm class */ extern uchar *glblModPath; /* module load path */ -extern uchar *pszWorkDir; -#define glblGetWorkDir() (pszWorkDir == NULL ? (uchar*) "" : pszWorkDir) + +/* the glbl object + * Note: this must be defined to satisfy the interface. We do not + * actually have instance data.*/ +typedef struct glbl_s { + BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ +} glbl_t; + + +/* interfaces */ +BEGINinterface(glbl) /* name must also be changed in ENDinterface macro! */ + uchar* (*GetWorkDir)(void); +ENDinterface(glbl) +#define glblCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ + +/* the remaining prototypes */ +PROTOTYPEObj(glbl); #endif /* #ifndef GLBL_H_INCLUDED */ diff --git a/runtime/obj-types.h b/runtime/obj-types.h index 901733c5..32589646 100644 --- a/runtime/obj-types.h +++ b/runtime/obj-types.h @@ -64,13 +64,13 @@ typedef enum { /* IDs of base methods supported by all objects - used for jump t /* the base data type for interfaces * This MUST be in sync with the ifBEGIN macro */ -typedef struct interface_s { +struct interface_s { int ifVersion; /* must be set to version requested */ int ifIsLoaded; /* is the interface loaded? (0-no, 1-yes, 2-load failed; if not 1, functions can NOT be called! */ -} interface_t; +}; -typedef struct objInfo_s { +struct objInfo_s { uchar *pszID; /* the object ID as a string */ size_t lenID; /* length of the ID string */ int iObjVers; @@ -78,7 +78,7 @@ typedef struct objInfo_s { rsRetVal (*objMethods[OBJ_NUM_METHODS])(); rsRetVal (*QueryIF)(interface_t*); struct modInfo_s *pModInfo; -} objInfo_t; +}; typedef struct obj { /* the dummy struct that each derived class can be casted to */ @@ -313,8 +313,8 @@ rsRetVal objName##ClassExit(void) \ } /* ------------------------------ object loader system ------------------------------ * - * The following code is the early beginning of a dynamic object loader system. The - * root idea is that all objects will become dynamically loadable libraries over time, + * The following code builds a dynamic object loader system. The + * root idea is that all objects are dynamically loadable, * which is necessary to get a clean plug-in interface where every plugin can access * rsyslog's rich object model via simple and quite portable methods. * @@ -327,17 +327,12 @@ rsRetVal objName##ClassExit(void) \ * macros create a static variable named like the object in each calling objects * static data block. * - * To facilitate moving to this system, I begin to implement some hooks, which - * allows to use interfaces today (when the rest of the infrastructure is not yet - * there). This is in the hope that it will ease migration to the full-fledged system - * once we are ready to work on that. - * rgerhards, 2008-02-21 + * rgerhards, 2008-02-21 (initial implementation), 2008-04-17 (update of this note) */ /* this defines the QueryInterface print entry point. Over time, it should be * present in all objects. */ -//#define PROTOTYPEObjQueryInterface(obj) rsRetVal obj##QueryInterface(obj##_if_t *pThis) #define BEGINobjQueryInterface(obj) \ rsRetVal obj##QueryInterface(obj##_if_t *pIf) \ { \ diff --git a/runtime/queue.c b/runtime/queue.c index 11c073a0..c6b617a9 100644 --- a/runtime/queue.c +++ b/runtime/queue.c @@ -53,6 +53,7 @@ /* static data */ DEFobjStaticHelpers +DEFobjCurrIf(glbl) /* forward-definitions */ rsRetVal queueChkPersist(queue_t *pThis); @@ -642,7 +643,7 @@ queueLoadPersStrmInfoFixup(strm_t *pStrm, queue_t __attribute__((unused)) *pThis DEFiRet; ISOBJ_TYPE_assert(pStrm, strm); ISOBJ_TYPE_assert(pThis, queue); - CHKiRet(strmSetDir(pStrm, glblGetWorkDir(), strlen((char*)glblGetWorkDir()))); + CHKiRet(strmSetDir(pStrm, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir()))); finalize_it: RETiRet; } @@ -667,7 +668,7 @@ queueHaveQIF(queue_t *pThis) /* Construct file name */ lenQIFNam = snprintf((char*)pszQIFNam, sizeof(pszQIFNam) / sizeof(uchar), "%s/%s.qi", - (char*) glblGetWorkDir(), (char*)pThis->pszFilePrefix); + (char*) glbl.GetWorkDir(), (char*)pThis->pszFilePrefix); /* check if the file exists */ if(stat((char*) pszQIFNam, &stat_buf) == -1) { @@ -704,7 +705,7 @@ queueTryLoadPersistedInfo(queue_t *pThis) /* Construct file name */ lenQIFNam = snprintf((char*)pszQIFNam, sizeof(pszQIFNam) / sizeof(uchar), "%s/%s.qi", - (char*) glblGetWorkDir(), (char*)pThis->pszFilePrefix); + (char*) glbl.GetWorkDir(), (char*)pThis->pszFilePrefix); /* check if the file exists */ if(stat((char*) pszQIFNam, &stat_buf) == -1) { @@ -791,7 +792,7 @@ static rsRetVal qConstructDisk(queue_t *pThis) ; } else { CHKiRet(strmConstruct(&pThis->tVars.disk.pWrite)); - CHKiRet(strmSetDir(pThis->tVars.disk.pWrite, glblGetWorkDir(), strlen((char*)glblGetWorkDir()))); + CHKiRet(strmSetDir(pThis->tVars.disk.pWrite, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir()))); CHKiRet(strmSetiMaxFiles(pThis->tVars.disk.pWrite, 10000000)); CHKiRet(strmSettOperationsMode(pThis->tVars.disk.pWrite, STREAMMODE_WRITE)); CHKiRet(strmSetsType(pThis->tVars.disk.pWrite, STREAMTYPE_FILE_CIRCULAR)); @@ -799,7 +800,7 @@ static rsRetVal qConstructDisk(queue_t *pThis) CHKiRet(strmConstruct(&pThis->tVars.disk.pRead)); CHKiRet(strmSetbDeleteOnClose(pThis->tVars.disk.pRead, 1)); - CHKiRet(strmSetDir(pThis->tVars.disk.pRead, glblGetWorkDir(), strlen((char*)glblGetWorkDir()))); + CHKiRet(strmSetDir(pThis->tVars.disk.pRead, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir()))); CHKiRet(strmSetiMaxFiles(pThis->tVars.disk.pRead, 10000000)); CHKiRet(strmSettOperationsMode(pThis->tVars.disk.pRead, STREAMMODE_READ)); CHKiRet(strmSetsType(pThis->tVars.disk.pRead, STREAMTYPE_FILE_CIRCULAR)); @@ -1259,7 +1260,7 @@ rsRetVal queueConstruct(queue_t **ppThis, queueType_t qType, int iWorkerThreads, /* we have an object, so let's fill the properties */ objConstructSetObjInfo(pThis); - if((pThis->pszSpoolDir = (uchar*) strdup((char*)glblGetWorkDir())) == NULL) + if((pThis->pszSpoolDir = (uchar*) strdup((char*)glbl.GetWorkDir())) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); /* set some water marks so that we have useful defaults if none are set specifically */ @@ -1872,7 +1873,7 @@ static rsRetVal queuePersist(queue_t *pThis, int bIsCheckpoint) /* Construct file name */ lenQIFNam = snprintf((char*)pszQIFNam, sizeof(pszQIFNam) / sizeof(uchar), "%s/%s.qi", - (char*) glblGetWorkDir(), (char*)pThis->pszFilePrefix); + (char*) glbl.GetWorkDir(), (char*)pThis->pszFilePrefix); if((bIsCheckpoint != QUEUE_CHECKPOINT) && (queueGetOverallQueueSize(pThis) == 0)) { if(pThis->bNeedDelQIF) { @@ -2313,6 +2314,7 @@ rsRetVal queueQueryInterface(void) { return RS_RET_NOT_IMPLEMENTED; } */ BEGINObjClassInit(queue, 1, OBJ_IS_CORE_MODULE) /* request objects we use */ + CHKiRet(objUse(glbl, CORE_COMPONENT)); /* now set our own handlers */ OBJSetMethodHandler(objMethod_SETPROPERTY, queueSetProperty); diff --git a/runtime/rsyslog.c b/runtime/rsyslog.c index 6051cc57..b7f0c2c1 100644 --- a/runtime/rsyslog.c +++ b/runtime/rsyslog.c @@ -3,7 +3,7 @@ * This module contains all function which work on a RTL global level. It's * name is abbreviated to "rsrt" (rsyslog runtime). * - * Please note that the runtime library is plugin-safe. That is, it must be + * Please note that the runtime library tends to be plugin-safe. That is, it must be * initialized by calling a global initialization function. However, that * function checks if the library is already initialized and, if so, does * nothing except incrementing a refeence count. Similarly, the deinit @@ -11,7 +11,23 @@ * is tracked via the refcount). As such, it is safe to call init and * exit multiple times, as long as this are always matching calls. This * capability is needed for a plugin system, where one plugin never - * knows what the other did. + * knows what the other did. HOWEVER, as of this writing, not all runtime + * library objects may work cleanly without static global data (the + * debug system is a very good example of this). So while we aim at the + * ability to work well in a plugin environment, things may not really work + * out. If you intend to use the rsyslog runtime library inside plugins, + * you should investigate the situation in detail. Please note that the + * rsyslog project itself does not yet need this functionality - thus you + * can safely assume it is totally untested ;). + * + * rgerhards, 2008-04-17: I have now once again checked on the plugin-safety. + * Unfortunately, there is currently no hook at all with which we could + * abstract a global data instance class. As such, we can NOT make the + * runtime plugin-safe in the above-described sense. As the rsyslog + * project itself does not need this functionality (and it is quesationable + * if someone else ever will), we do currently do not make an effort to + * support it. So if you intend to use rsyslog runtime inside a non-rsyslog + * plugin system, be careful! * * The rsyslog runtime library is in general reentrant and thread-safe. There * are some intentional exceptions (e.g. inside the msg object). These are @@ -58,6 +74,7 @@ #include "datetime.h" #include "queue.h" #include "conf.h" +#include "glbl.h" /* static data */ static int iRefCount = 0; /* our refcount - it MUST exist only once inside a process (not thread) @@ -91,6 +108,8 @@ rsrtInit(char **ppErrObj, obj_if_t *pObjIF) * class immediately after it is initialized. And, of course, we load those classes * first that we use ourselfs... -- rgerhards, 2008-03-07 */ + if(ppErrObj != NULL) *ppErrObj = "glbl"; + CHKiRet(glblClassInit(NULL)); if(ppErrObj != NULL) *ppErrObj = "datetime"; CHKiRet(datetimeClassInit(NULL)); if(ppErrObj != NULL) *ppErrObj = "msg"; @@ -143,13 +162,14 @@ finalize_it: * rgerhards, 2008-04-16 */ rsRetVal -rsrtExit(obj_if_t *pObjIF) +rsrtExit(void) { DEFiRet; if(iRefCount == 1) { /* do actual de-init only if we are the last runtime user */ confClassExit(); + glblClassExit(); objClassExit(); /* *THIS* *MUST/SHOULD?* always be the first class initilizer being called (except debug)! */ } diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index 54373673..3841df6c 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -63,6 +63,8 @@ typedef struct thrdInfo thrdInfo_t; typedef struct filed selector_t; /* TODO: this so far resides in syslogd.c, think about modularization */ typedef struct NetAddr netAddr_t; typedef struct msg msg_t; +typedef struct interface_s interface_t; +typedef struct objInfo_s objInfo_t; /* some universal 64 bit define... */ typedef long long int64; @@ -287,7 +289,7 @@ void dbgprintf(char *, ...) __attribute__((format(printf, 1, 2))); /* some runtime prototypes */ rsRetVal rsrtInit(char **ppErrObj, obj_if_t *pObjIF); -rsRetVal rsrtExit(obj_if_t *pObjIF); +rsRetVal rsrtExit(void); int rsrtIsInit(void); #endif /* multi-include protection */ diff --git a/tools/syslogd.c b/tools/syslogd.c index 6f252b3a..4fe0071a 100644 --- a/tools/syslogd.c +++ b/tools/syslogd.c @@ -160,6 +160,7 @@ /* definitions for objects we access */ DEFobjCurrIf(obj) +DEFobjCurrIf(glbl) DEFobjCurrIf(datetime) DEFobjCurrIf(conf) DEFobjCurrIf(expr) @@ -288,14 +289,13 @@ static int bEscapeCCOnRcv = 1; /* escape control characters on reception: 0 - n int bReduceRepeatMsgs; /* reduce repeated message - 0 - no, 1 - yes */ int bActExecWhenPrevSusp; /* execute action only when previous one was suspended? */ int iActExecOnceInterval = 0; /* execute action once every nn seconds */ -uchar *pszWorkDir = NULL;/* name of rsyslog's spool directory (without trailing slash) */ uchar *glblModPath = NULL; /* module load path - only used during initial init, only settable via -M command line option */ /* end global config file state variables */ uchar *LocalHostName = NULL;/* our hostname - read-only after startup */ char *LocalDomain; /* our local domain name - read-only after startup */ int MarkInterval = 20 * 60; /* interval between marks in seconds - read-only after startup */ -int family = PF_UNSPEC; /* protocol family (IPv4, IPv6 or both), set via cmdline */ +//int family = PF_UNSPEC; /* protocol family (IPv4, IPv6 or both), set via cmdline */ int send_to_all = 0; /* send message to all IPv4/IPv6 addresses */ static int NoFork = 0; /* don't fork - don't run in daemon mode - read-only after startup */ int DisableDNS = 0; /* don't look up IP addresses of remote messages */ @@ -373,10 +373,6 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a bEscapeCCOnRcv = 1; /* default is to escape control characters */ bReduceRepeatMsgs = 0; bDropMalPTRMsgs = 0; - if(pszWorkDir != NULL) { - free(pszWorkDir); - pszWorkDir = NULL; - } if(pszMainMsgQFName != NULL) { free(pszMainMsgQFName); pszMainMsgQFName = NULL; @@ -1849,8 +1845,6 @@ static void doDie(int sig) static void freeAllDynMemForTermination(void) { - if(pszWorkDir != NULL) - free(pszWorkDir); if(pszMainMsgQFName != NULL) free(pszMainMsgQFName); if(pModDir != NULL) @@ -2143,7 +2137,7 @@ static void dbgPrintInitInfo(void) setQPROP(queueSetiMinMsgsPerWrkr, "$MainMsgQueueWorkerThreadMinimumMessages", 100); setQPROP(queueSetbSaveOnShutdown, "$MainMsgQueueSaveOnShutdown", 1); */ - dbgprintf("Work Directory: '%s'.\n", pszWorkDir); + dbgprintf("Work Directory: '%s'.\n", glbl.GetWorkDir()); } @@ -2266,7 +2260,7 @@ init(void) if(MainMsgQueType == QUEUETYPE_DISK) { errno = 0; /* for logerror! */ - if(pszWorkDir == NULL) { + if(glbl.GetWorkDir() == NULL) { errmsg.LogError(NO_ERRCODE, "No $WorkDirectory specified - can not run main message queue in 'disk' mode. " "Using 'FixedArray' instead.\n"); MainMsgQueType = QUEUETYPE_FIXED_ARRAY; @@ -2618,7 +2612,7 @@ static rsRetVal loadBuildInModules(void) * is that rsyslog will terminate if we can not register our built-in config commands. * This, I think, is the right thing to do. -- rgerhards, 2007-07-31 */ - CHKiRet(regCfSysLineHdlr((uchar *)"workdirectory", 0, eCmdHdlrGetWord, NULL, &pszWorkDir, NULL)); +// CHKiRet(regCfSysLineHdlr((uchar *)"workdirectory", 0, eCmdHdlrGetWord, NULL, &pszWorkDir, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"actionresumeretrycount", 0, eCmdHdlrInt, NULL, &glbliActionResumeRetryCount, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuefilename", 0, eCmdHdlrGetWord, NULL, &pszMainMsgQFName, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuesize", 0, eCmdHdlrInt, NULL, &iMainMsgQueueSize, NULL)); @@ -2782,6 +2776,8 @@ InitGlobalClasses(void) CHKiRet(rsrtInit(&pErrObj, &obj)); /* Now tell the system which classes we need ourselfs */ + pErrObj = "glbl"; + CHKiRet(objUse(glbl, CORE_COMPONENT)); pErrObj = "errmsg"; CHKiRet(objUse(errmsg, CORE_COMPONENT)); pErrObj = "module"; @@ -2870,7 +2866,7 @@ GlobalClassExit(void) CHKiRet(objUse(errmsg, CORE_COMPONENT)); CHKiRet(objUse(module, CORE_COMPONENT)); #endif - rsrtExit(&obj); /* *THIS* *MUST/SHOULD?* always be the first class initilizer being called (except debug)! */ + rsrtExit(); /* *THIS* *MUST/SHOULD?* always be the first class initilizer being called (except debug)! */ RETiRet; } -- cgit