diff options
Diffstat (limited to 'runtime')
70 files changed, 3891 insertions, 5625 deletions
diff --git a/runtime/Makefile.am b/runtime/Makefile.am index 09cb6b41..67e235a0 100644 --- a/runtime/Makefile.am +++ b/runtime/Makefile.am @@ -8,11 +8,14 @@ librsyslog_la_SOURCES = \ rsyslog.c \ rsyslog.h \ typedefs.h \ + dnscache.c \ + dnscache.h \ unicode-helper.h \ atomic.h \ batch.h \ syslogd-types.h \ module-template.h \ + im-helper.h \ obj-types.h \ nsd.h \ glbl.h \ @@ -20,6 +23,8 @@ librsyslog_la_SOURCES = \ unlimited_select.h \ conf.c \ conf.h \ + rsconf.c \ + rsconf.h \ parser.h \ parser.c \ strgen.h \ @@ -44,18 +49,8 @@ librsyslog_la_SOURCES = \ obj.h \ modules.c \ modules.h \ - apc.c \ - apc.h \ statsobj.c \ statsobj.h \ - sync.c \ - sync.h \ - expr.c \ - expr.h \ - ctok.c \ - ctok.h \ - ctok_token.c \ - ctok_token.h \ stream.c \ stream.h \ var.c \ @@ -64,16 +59,6 @@ librsyslog_la_SOURCES = \ wtp.h \ wti.c \ wti.h \ - sysvar.c \ - sysvar.h \ - vm.c \ - vm.h \ - vmstk.c \ - vmstk.h \ - vmprg.c \ - vmprg.h \ - vmop.c \ - vmop.h \ queue.c \ queue.h \ ruleset.c \ @@ -110,12 +95,12 @@ librsyslog_la_SOURCES = \ # runtime or will no longer be needed. -- rgerhards, 2008-06-13 if WITH_MODDIRS -librsyslog_la_CPPFLAGS = -DSD_EXPORT_SYMBOLS -D_PATH_MODDIR=\"$(pkglibdir)/:$(moddirs)\" $(PTHREADS_CFLAGS) +librsyslog_la_CPPFLAGS = -DSD_EXPORT_SYMBOLS -D_PATH_MODDIR=\"$(pkglibdir)/:$(moddirs)\" $(PTHREADS_CFLAGS) $(LIBEE_CFLAGS) -I\$(top_srcdir)/tools else -librsyslog_la_CPPFLAGS = -DSD_EXPORT_SYMBOLS -D_PATH_MODDIR=\"$(pkglibdir)/\" -I$(top_srcdir) $(PTHREADS_CFLAGS) +librsyslog_la_CPPFLAGS = -DSD_EXPORT_SYMBOLS -D_PATH_MODDIR=\"$(pkglibdir)/\" -I$(top_srcdir) $(PTHREADS_CFLAGS) $(LIBEE_CFLAGS) -I\$(top_srcdir)/tools -I\$(top_srcdir)/grammar endif #librsyslog_la_LDFLAGS = -module -avoid-version -librsyslog_la_LIBADD = $(DL_LIBS) $(RT_LIBS) +librsyslog_la_LIBADD = $(DL_LIBS) $(RT_LIBS) $(LIBEE_LIBS) # # regular expression support diff --git a/runtime/apc.c b/runtime/apc.c deleted file mode 100644 index 3c6b7ec4..00000000 --- a/runtime/apc.c +++ /dev/null @@ -1,402 +0,0 @@ -/* apc.c - asynchronous procedure call support - * - * An asynchronous procedure call (APC) is a procedure call (guess what) that is potentially run - * asynchronously to its main thread. It can be scheduled to occur at a caller-provided time. - * As long as the procedure has not been called, the APC entry may be modified by the caller - * or deleted. It is the caller's purpose to make sure proper synchronization is in place. - * The APC object only case about APC's own control structures (which *are* properly - * guarded by synchronization primitives). - * - * Module begun 2009-06-15 by Rainer Gerhards - * - * Copyright 2009 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 <http://www.gnu.org/licenses/>. - * - * 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 <stdlib.h> -#include <string.h> -#include <assert.h> -#include <pthread.h> - -#include "rsyslog.h" -#include "obj.h" -#include "apc.h" -#include "srUtils.h" -#include "datetime.h" - -/* static data */ -DEFobjStaticHelpers -DEFobjCurrIf(datetime) - -/* following is a used to implement a monotonically increasing id for the apcs. That - * ID can be used to cancel an apc request. Note that the ID is generated with modulo - * arithmetic, so at some point, it will wrap. Howerver, this happens at 2^32-1 at - * earliest, so this is not considered a problem. - */ -apc_id_t apcID = 0; - -/* private data structures */ - -/* the apc list and its entries - * This is a doubly-linked list as we need to be able to do inserts - * and deletes right in the middle of the list. It is inspired by the - * Unix callout mechanism. - * Note that we support two generic caller-provided parameters as - * experience shows that at most two are often used. This causes very - * little overhead, but simplifies caller code in cases where exactly - * two parameters are needed. We hope this is a useful optimizaton. - * rgerhards, 2009-06-15 - */ -typedef struct apc_list_s { - struct apc_list_s *pNext; - struct apc_list_s *pPrev; - apc_id_t id; - apc_t *pApc; /* pointer to the APC object to be scheduled */ -} apc_list_t; - -apc_list_t *apcListRoot = NULL; -apc_list_t *apcListTail = NULL; -pthread_mutex_t listMutex; /* needs to be locked for all list operations */ - - -/* destructor for the apc object */ -BEGINobjDestruct(apc) /* be sure to specify the object type also in END and CODESTART macros! */ -CODESTARTobjDestruct(apc) -ENDobjDestruct(apc) - - -/* ------------------------------ APC list handling functions ------------------------------ */ - -/* Function that handles changes to the list root. Most importantly, this function - * needs to schedule a new timer. It is OK to call this function with an empty list. - */ -static rsRetVal -listRootChanged(void) -{ - DEFiRet; - - if(apcListRoot == NULL) - FINALIZE; - - // TODO: implement! - -finalize_it: - RETiRet; -} - - -/* insert an apc entry into the APC list. The same entry MUST NOT already be present! - */ -static rsRetVal -insertApc(apc_t *pThis, apc_id_t *pID) -{ - apc_list_t *pCurr; - apc_list_t *pNew; - DEFiRet; - - CHKmalloc(pNew = (apc_list_t*) calloc(1, sizeof(apc_list_t))); - pNew->pApc = pThis; - pNew->id = *pID = apcID++; -dbgprintf("insert apc %p, id %ld\n", pThis, pNew->id); - - /* find right list location */ - if(apcListRoot == NULL) { - /* no need to search, list is empty */ - apcListRoot = pNew; - apcListTail = pNew; - CHKiRet(listRootChanged()); - } else { - for(pCurr = apcListRoot ; pCurr != NULL ; pCurr = pCurr->pNext) { - if(pCurr->pApc->ttExec > pThis->ttExec) - break; - } - - if(pCurr == NULL) { - /* insert at tail */ - pNew->pPrev = apcListTail; - apcListTail->pNext = pNew; - apcListTail = pNew; - } else { - if(pCurr == apcListRoot) { - /* new first entry */ - pCurr->pPrev = pNew; - pNew->pNext = pCurr; - apcListRoot = pNew; - CHKiRet(listRootChanged()); - } else { - /* in the middle of the list */ - pCurr->pPrev = pNew; - pNew->pNext = pCurr; - } - } - } - - -finalize_it: - RETiRet; -} - - -/* Delete an apc entry from the APC list. It is OK if the entry is not found, - * in this case we assume it already has been processed. - */ -static rsRetVal -deleteApc(apc_id_t id) -{ - apc_list_t *pCurr; - DEFiRet; - -dbgprintf("trying to delete apc %ld\n", id); - for(pCurr = apcListRoot ; pCurr != NULL ; pCurr = pCurr->pNext) { - if(pCurr->id == id) { -RUNLOG_STR("apc id found, now deleting!\n"); - if(pCurr == apcListRoot) { - apcListRoot = pCurr->pNext; - CHKiRet(listRootChanged()); - } else { - pCurr->pPrev->pNext = pCurr->pNext; - } - if(pCurr->pNext == NULL) { - apcListTail = pCurr->pPrev; - } else { - pCurr->pNext->pPrev = pCurr->pPrev; - } - free(pCurr); - pCurr = NULL; - break; - } - } - -finalize_it: - RETiRet; -} - - -/* unlist all elements up to the current timestamp. Return this as a seperate list - * to the caller. Returns an empty (NULL ptr) list if there are no such elements. - * The caller must handle that gracefully. The list is returned in the parameter. - */ -static rsRetVal -unlistCurrent(apc_list_t **ppList) -{ - apc_list_t *pCurr; - time_t tCurr; - DEFiRet; - assert(ppList != NULL); - - datetime.GetTime(&tCurr); - - if(apcListRoot == NULL || apcListRoot->pApc->ttExec > tCurr) { - *ppList = NULL; - FINALIZE; - } - - *ppList = apcListRoot; - /* now search up to which entry we need to execute */ - for(pCurr = apcListRoot ; pCurr != NULL && pCurr->pApc->ttExec <= tCurr ; pCurr = pCurr->pNext) { - /*JUST SKIP TO LAST ELEMENT*/; - } - - if(pCurr == NULL) { - /* all elements can be unlisted */ - apcListRoot = NULL; - apcListTail = NULL; - } else { - /* need to set a new root */ - pCurr->pPrev->pNext = NULL; /* terminate newly unlisted list */ - pCurr->pPrev = NULL; /* we are the new root */ - apcListRoot = pCurr; - } - -finalize_it: - RETiRet; -} - - -/* ------------------------------ END APC list handling functions ------------------------------ */ - - -/* execute all list elements that are currently scheduled for execution. We do this in two phases. - * In the first phase, we look the list mutex and move everything from the head of the queue to - * the current timestamp to a new to-be-executed list. Then we unlock the mutex and do the actual - * exec (which may take some time). - * Note that the caller is responsible for proper - * caller-level synchronization. The caller may schedule another Apc, this module must - * ensure that (and it does so by not locking the list mutex while we call the Apc). - * Note: this function "consumes" the apc_t, so it is no longer existing after this - * function returns. - */ -// TODO make static and associated with our own pthread-based timer -rsRetVal -execScheduled(void) -{ - apc_list_t *pExecList; - apc_list_t *pCurr; - apc_list_t *pNext; - DEFiRet; - - d_pthread_mutex_lock(&listMutex); - iRet = unlistCurrent(&pExecList); - d_pthread_mutex_unlock(&listMutex); - CHKiRet(iRet); - - if(pExecList != NULL) { - DBGPRINTF("running apc scheduler - we have %s to execute\n", - pExecList == NULL ? "nothing" : "something"); - } - - for(pCurr = pExecList ; pCurr != NULL ; pCurr = pNext) { -dbgprintf("executing apc list entry %p, apc %p\n", pCurr, pCurr->pApc); - pNext = pCurr->pNext; - pCurr->pApc->pProc(pCurr->pApc->param1, pCurr->pApc->param2); - apcDestruct(&pCurr->pApc); - free(pCurr); - } - -finalize_it: - RETiRet; -} - - -/* Standard-Constructor - */ -BEGINobjConstruct(apc) /* be sure to specify the object type also in END macro! */ -ENDobjConstruct(apc) - - -/* ConstructionFinalizer - * Note that we use a non-standard calling interface: pID returns the current APC - * id. This is the only way to handle the situation without the need for extra - * locking. - * rgerhards, 2008-01-09 - */ -static rsRetVal -apcConstructFinalize(apc_t *pThis, apc_id_t *pID) -{ - DEFiRet; - ISOBJ_TYPE_assert(pThis, apc); - assert(pID != NULL); - d_pthread_mutex_lock(&listMutex); - insertApc(pThis, pID); - d_pthread_mutex_unlock(&listMutex); - RETiRet; -} - - -/* some set methods */ -static rsRetVal -SetProcedure(apc_t *pThis, void (*pProc)(void*, void*)) -{ - ISOBJ_TYPE_assert(pThis, apc); - pThis->pProc = pProc; - return RS_RET_OK; -} -static rsRetVal -SetParam1(apc_t *pThis, void *param1) -{ - ISOBJ_TYPE_assert(pThis, apc); - pThis->param1 = param1; - return RS_RET_OK; -} -static rsRetVal -SetParam2(apc_t *pThis, void *param2) -{ - ISOBJ_TYPE_assert(pThis, apc); - pThis->param1 = param2; - return RS_RET_OK; -} - - -/* cancel an Apc request, ID is provided. It is OK if the ID can not be found, this may - * happen if the Apc was executed in the mean time. So it is safe to call CancelApc() at - * any time. - */ -static rsRetVal -CancelApc(apc_id_t id) -{ - BEGINfunc - d_pthread_mutex_lock(&listMutex); - deleteApc(id); - d_pthread_mutex_unlock(&listMutex); - ENDfunc - return RS_RET_OK; -} - - -/* debugprint for the apc object */ -BEGINobjDebugPrint(apc) /* be sure to specify the object type also in END and CODESTART macros! */ -CODESTARTobjDebugPrint(apc) - dbgoprint((obj_t*) pThis, "APC module, currently no state info available\n"); -ENDobjDebugPrint(apc) - - -/* queryInterface function - */ -BEGINobjQueryInterface(apc) -CODESTARTobjQueryInterface(apc) - if(pIf->ifVersion != apcCURR_IF_VERSION) { /* check for current version, increment on each change */ - ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); - } - - /* ok, we have the right interface, so let's fill it - * Please note that we may also do some backwards-compatibility - * work here (if we can support an older interface version - that, - * of course, also affects the "if" above). - */ - pIf->Construct = apcConstruct; - pIf->ConstructFinalize = apcConstructFinalize; - pIf->Destruct = apcDestruct; - pIf->DebugPrint = apcDebugPrint; - pIf->CancelApc = CancelApc; - pIf->SetProcedure = SetProcedure; - pIf->SetParam1 = SetParam1; - pIf->SetParam2 = SetParam2; -finalize_it: -ENDobjQueryInterface(apc) - - -/* Exit the apc class. - * rgerhards, 2009-04-06 - */ -BEGINObjClassExit(apc, OBJ_IS_CORE_MODULE) /* class, version */ - objRelease(datetime, CORE_COMPONENT); - pthread_mutex_destroy(&listMutex); -ENDObjClassExit(apc) - - -/* Initialize the apc class. Must be called as the very first method - * before anything else is called inside this class. - * rgerhards, 2008-02-19 - */ -BEGINObjClassInit(apc, 1, OBJ_IS_CORE_MODULE) /* class, version */ - /* request objects we use */ - CHKiRet(objUse(datetime, CORE_COMPONENT)); - - /* set our own handlers */ - OBJSetMethodHandler(objMethod_DEBUGPRINT, apcDebugPrint); - OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, apcConstructFinalize); - - /* do other initializations */ - pthread_mutex_init(&listMutex, NULL); -ENDObjClassInit(apc) - -/* vi:set ai: - */ diff --git a/runtime/apc.h b/runtime/apc.h deleted file mode 100644 index 7c679b97..00000000 --- a/runtime/apc.h +++ /dev/null @@ -1,56 +0,0 @@ -/* The apc object. - * - * See apc.c for more information. - * - * Copyright 2009 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 <http://www.gnu.org/licenses/>. - * - * 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 INCLUDED_APC_H -#define INCLUDED_APC_H - -/* the apc object */ -typedef struct apc_s { - BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ - time_t ttExec; /* when to call procedure (so far seconds...) */ - void (*pProc)(void*, void*); /* which procedure to call */ - void *param1; /* user-supplied parameters */ - void *param2; /* user-supplied parameters */ -} apc_t; - -typedef unsigned long apc_id_t; /* monotonically incrementing apc ID */ - -/* interfaces */ -BEGINinterface(apc) /* name must also be changed in ENDinterface macro! */ - INTERFACEObjDebugPrint(apc); - rsRetVal (*Construct)(apc_t **ppThis); - rsRetVal (*ConstructFinalize)(apc_t *pThis, apc_id_t *); - rsRetVal (*Destruct)(apc_t **ppThis); - rsRetVal (*SetProcedure)(apc_t *pThis, void (*pProc)(void*, void*)); - rsRetVal (*SetParam1)(apc_t *pThis, void *); - rsRetVal (*SetParam2)(apc_t *pThis, void *); - rsRetVal (*CancelApc)(apc_id_t); -ENDinterface(apc) -#define apcCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ - - -/* prototypes */ -PROTOTYPEObj(apc); - -#endif /* #ifndef INCLUDED_APC_H */ diff --git a/runtime/cfsysline.c b/runtime/cfsysline.c index 36e586c1..af88b3de 100644 --- a/runtime/cfsysline.c +++ b/runtime/cfsysline.c @@ -37,6 +37,7 @@ #include "cfsysline.h" #include "obj.h" +#include "conf.h" #include "errmsg.h" #include "srUtils.h" #include "unicode-helper.h" @@ -548,7 +549,8 @@ finalize_it: * time (TODO). -- rgerhards, 2008-02-14 */ static rsRetVal -doSyslogName(uchar **pp, rsRetVal (*pSetHdlr)(void*, int), void *pVal, syslogName_t *pNameTable) +doSyslogName(uchar **pp, rsRetVal (*pSetHdlr)(void*, int), + void *pVal, syslogName_t *pNameTable) { DEFiRet; cstr_t *pStrB; @@ -590,6 +592,15 @@ doFacility(uchar **pp, rsRetVal (*pSetHdlr)(void*, int), void *pVal) } +static rsRetVal +doGoneAway(__attribute__((unused)) uchar **pp, + __attribute__((unused)) rsRetVal (*pSetHdlr)(void*, int), + __attribute__((unused)) void *pVal) +{ + errmsg.LogError(0, RS_RET_CMD_GONE_AWAY, "config directive is no longer supported -- ignored"); + return RS_RET_CMD_GONE_AWAY; +} + /* Implements the severity syntax. * rgerhards, 2008-02-14 */ @@ -719,6 +730,9 @@ static rsRetVal cslchCallHdlr(cslCmdHdlr_t *pThis, uchar **ppConfLine) case eCmdHdlrGetWord: pHdlr = doGetWord; break; + case eCmdHdlrGoneAway: + pHdlr = doGoneAway; + break; default: iRet = RS_RET_NOT_IMPLEMENTED; goto finalize_it; @@ -811,8 +825,7 @@ finalize_it: * 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) +rsRetVal regCfSysLineHdlr(uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData, void *pOwnerCookie) { DEFiRet; cslCmd_t *pThis; @@ -917,6 +930,7 @@ rsRetVal processCfSysLineCommand(uchar *pCmdName, uchar **p) 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 */ + int bHadScopingErr = 0; /* set if a scoping error occured */ iRet = llFind(&llCmdList, (void *) pCmdName, (void*) &pCmd); @@ -934,7 +948,7 @@ rsRetVal processCfSysLineCommand(uchar *pCmdName, uchar **p) * 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 + * this behaviour (but I am not sure if that is really * necessary). -- rgerhards, 2007-07-31 */ pHdlrP = *p; @@ -952,6 +966,10 @@ rsRetVal processCfSysLineCommand(uchar *pCmdName, uchar **p) if(iRetLL != RS_RET_END_OF_LINKEDLIST) iRet = iRetLL; + if(bHadScopingErr) { + iRet = RS_RET_CONF_INVLD_SCOPE; + } + finalize_it: RETiRet; } diff --git a/runtime/cfsysline.h b/runtime/cfsysline.h index 53f35f6a..2768243f 100644 --- a/runtime/cfsysline.h +++ b/runtime/cfsysline.h @@ -24,28 +24,12 @@ #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 */ + ecslConfObjType __attribute__((deprecated)) eConfObjType; /* which config object is this for? */ 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 */ diff --git a/runtime/conf.c b/runtime/conf.c index 46a89281..eec04df8 100644 --- a/runtime/conf.c +++ b/runtime/conf.c @@ -48,7 +48,6 @@ #endif #include "rsyslog.h" -#include "../tools/syslogd.h" /* TODO: this must be removed! */ #include "dirty.h" #include "parse.h" #include "action.h" @@ -62,11 +61,9 @@ #include "srUtils.h" #include "errmsg.h" #include "net.h" -#include "expr.h" -#include "ctok.h" -#include "ctok_token.h" #include "rule.h" #include "ruleset.h" +#include "rsconf.h" #include "unicode-helper.h" #ifdef OS_SOLARIS @@ -74,22 +71,19 @@ #endif /* forward definitions */ -static rsRetVal cfline(uchar *line, rule_t **pfCurr); -static rsRetVal processConfFile(uchar *pConfFile); +//static rsRetVal cfline(rsconf_t *conf, uchar *line, rule_t **pfCurr); /* static data */ DEFobjStaticHelpers -DEFobjCurrIf(expr) -DEFobjCurrIf(ctok) -DEFobjCurrIf(ctok_token) DEFobjCurrIf(module) DEFobjCurrIf(errmsg) DEFobjCurrIf(net) DEFobjCurrIf(rule) DEFobjCurrIf(ruleset) -static int iNbrActions = 0; /* number of currently defined actions */ +int bConfStrictScoping = 0; /* force strict scoping during config processing? */ + /* The following module-global variables are used for building * tag and host selector lines during startup and config reload. @@ -98,153 +92,12 @@ static int iNbrActions = 0; /* number of currently defined actions */ * be run in a single thread anyways. So there can be no race conditions. * rgerhards 2005-10-18 */ -static EHostnameCmpMode eDfltHostnameCmpMode = HN_NO_COMP; -static cstr_t *pDfltHostnameCmp = NULL; -static cstr_t *pDfltProgNameCmp = NULL; - +EHostnameCmpMode eDfltHostnameCmpMode = HN_NO_COMP; +cstr_t *pDfltHostnameCmp = NULL; +cstr_t *pDfltProgNameCmp = NULL; -/* process a directory and include all of its files into - * the current config file. There is no specific order of inclusion, - * files are included in the order they are read from the directory. - * The caller must have make sure that the provided parameter is - * indeed a directory. - * rgerhards, 2007-08-01 - */ -static rsRetVal doIncludeDirectory(uchar *pDirName) -{ - DEFiRet; - int iEntriesDone = 0; - DIR *pDir; - union { - struct dirent d; - char b[offsetof(struct dirent, d_name) + NAME_MAX + 1]; - } u; - struct dirent *res; - size_t iDirNameLen; - size_t iFileNameLen; - uchar szFullFileName[MAXFNAME]; - - ASSERT(pDirName != NULL); - - if((pDir = opendir((char*) pDirName)) == NULL) { - errmsg.LogError(errno, RS_RET_FOPEN_FAILURE, "error opening include directory"); - ABORT_FINALIZE(RS_RET_FOPEN_FAILURE); - } - - /* prepare file name buffer */ - iDirNameLen = strlen((char*) pDirName); - memcpy(szFullFileName, pDirName, iDirNameLen); - - /* now read the directory */ - iEntriesDone = 0; - while(readdir_r(pDir, &u.d, &res) == 0) { - if(res == NULL) - break; /* this also indicates end of directory */ -# ifdef DT_REG - /* TODO: find an alternate way to checking for special files if this is - * not defined. This is currently a known problem on HP UX, but the work- - * around is simple: do not create special files in that directory. So - * fixing this is actually not the most important thing on earth... - * rgerhards, 2008-03-04 - */ - if(res->d_type != DT_REG) - continue; /* we are not interested in special files */ -# endif - if(res->d_name[0] == '.') - continue; /* these files we are also not interested in */ - ++iEntriesDone; - /* construct filename */ - iFileNameLen = strlen(res->d_name); - if (iFileNameLen > NAME_MAX) - iFileNameLen = NAME_MAX; - memcpy(szFullFileName + iDirNameLen, res->d_name, iFileNameLen); - *(szFullFileName + iDirNameLen + iFileNameLen) = '\0'; - dbgprintf("including file '%s'\n", szFullFileName); - processConfFile(szFullFileName); - /* we deliberately ignore the iRet of processConfFile() - this is because - * failure to process one file does not mean all files will fail. By ignoring, - * we retry with the next file, which is the best thing we can do. -- rgerhards, 2007-08-01 - */ - } - - if(iEntriesDone == 0) { - /* I just make it a debug output, because I can think of a lot of cases where it - * makes sense not to have any files. E.g. a system maintainer may place a $Include - * into the config file just in case, when additional modules be installed. When none - * are installed, the directory will be empty, which is fine. -- rgerhards 2007-08-01 - */ - dbgprintf("warning: the include directory contained no files - this may be ok.\n"); - } - -finalize_it: - if(pDir != NULL) - closedir(pDir); - RETiRet; -} - - -/* process a $include config line. That type of line requires - * inclusion of another file. - * rgerhards, 2007-08-01 - */ -rsRetVal -doIncludeLine(uchar **pp, __attribute__((unused)) void* pVal) -{ - DEFiRet; - char pattern[MAXFNAME]; - uchar *cfgFile; - glob_t cfgFiles; - int result; - size_t i = 0; - struct stat fileInfo; - - ASSERT(pp != NULL); - ASSERT(*pp != NULL); - - if(getSubString(pp, (char*) pattern, sizeof(pattern) / sizeof(char), ' ') != 0) { - errmsg.LogError(0, RS_RET_NOT_FOUND, "could not parse config file name"); - ABORT_FINALIZE(RS_RET_NOT_FOUND); - } - - /* Use GLOB_MARK to append a trailing slash for directories. - * Required by doIncludeDirectory(). - */ - result = glob(pattern, GLOB_MARK, NULL, &cfgFiles); - if(result == GLOB_NOSPACE || result == GLOB_ABORTED) { - char errStr[1024]; - rs_strerror_r(errno, errStr, sizeof(errStr)); - errmsg.LogError(0, RS_RET_FILE_NOT_FOUND, "error accessing config file or directory '%s': %s", - pattern, errStr); - ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND); - } - - for(i = 0; i < cfgFiles.gl_pathc; i++) { - cfgFile = (uchar*) cfgFiles.gl_pathv[i]; - - if(stat((char*) cfgFile, &fileInfo) != 0) - continue; /* continue with the next file if we can't stat() the file */ - - if(S_ISREG(fileInfo.st_mode)) { /* config file */ - dbgprintf("requested to include config file '%s'\n", cfgFile); - iRet = processConfFile(cfgFile); - } else if(S_ISDIR(fileInfo.st_mode)) { /* config directory */ - dbgprintf("requested to include directory '%s'\n", cfgFile); - iRet = doIncludeDirectory(cfgFile); - } else { /* TODO: shall we handle symlinks or not? */ - dbgprintf("warning: unable to process IncludeConfig directive '%s'\n", cfgFile); - } - } - - globfree(&cfgFiles); - -finalize_it: - RETiRet; -} - - -/* process a $ModLoad config line. - */ +/* process a $ModLoad config line. */ rsRetVal doModLoad(uchar **pp, __attribute__((unused)) void* pVal) { @@ -274,7 +127,7 @@ doModLoad(uchar **pp, __attribute__((unused)) void* pVal) else pModName = szName; - CHKiRet(module.Load(pModName)); + CHKiRet(module.Load(pModName, 1)); finalize_it: RETiRet; @@ -318,7 +171,7 @@ doNameLine(uchar **pp, void* pVal) switch(eDir) { case DIR_TEMPLATE: - tplAddLine(szName, &p); + tplAddLine(loadConf, szName, &p); break; case DIR_OUTCHANNEL: ochAddLine(szName, &p); @@ -384,128 +237,6 @@ finalize_it: } - - -/* process a configuration file - * started with code from init() by rgerhards on 2007-07-31 - */ -static rsRetVal -processConfFile(uchar *pConfFile) -{ - int iLnNbr = 0; - FILE *cf; - rule_t *pCurrRule = NULL; - uchar *p; - uchar cbuf[CFGLNSIZ]; - uchar *cline; - int i; - rsRetVal localRet; - int bHadAnError = 0; - uchar *pszOrgLine = NULL; - size_t lenLine; - DEFiRet; - ASSERT(pConfFile != NULL); - - if((cf = fopen((char*)pConfFile, "r")) == NULL) { - ABORT_FINALIZE(RS_RET_FOPEN_FAILURE); - } - - /* Now process the file. - */ - cline = cbuf; - while (fgets((char*)cline, sizeof(cbuf) - (cline - cbuf), cf) != NULL) { - ++iLnNbr; - /* drop LF - TODO: make it better, replace fgets(), but its clean as it is */ - lenLine = ustrlen(cline); - if(cline[lenLine-1] == '\n') { - cline[lenLine-1] = '\0'; - } - free(pszOrgLine); - pszOrgLine = ustrdup(cline); /* save if needed for errmsg, NULL ptr is OK */ - /* check for end-of-section, comments, strip off trailing - * spaces and newline character. - */ - p = cline; - skipWhiteSpace(&p); - if (*p == '\0' || *p == '#') - continue; - - /* we now need to copy the characters to the begin of line. As this overlaps, - * we can not use strcpy(). -- rgerhards, 2008-03-20 - * TODO: review the code at whole - this is highly suspect (but will go away - * once we do the rest of RainerScript). - */ - for( i = 0 ; p[i] != '\0' ; ++i) { - cline[i] = p[i]; - } - cline[i] = '\0'; - - for (p = (uchar*) strchr((char*)cline, '\0'); isspace((int) *--p);) - /*EMPTY*/; - if (*p == '\\') { - if ((p - cbuf) > CFGLNSIZ - 30) { - /* Oops the buffer is full - what now? */ - cline = cbuf; - dbgprintf("buffer overflow extending config file\n"); - errmsg.LogError(0, RS_RET_CONFIG_ERROR, - "error: config file line %d too long", iLnNbr); - } else { - *p = 0; - cline = p; - continue; - } - } else - cline = cbuf; - *++p = '\0'; /* TODO: check this */ - - /* we now have the complete line, and are positioned at the first non-whitespace - * character. So let's process it - */ - if((localRet = cfline(cbuf, &pCurrRule)) != RS_RET_OK) { - /* we log a message, but otherwise ignore the error. After all, the next - * line can be correct. -- rgerhards, 2007-08-02 - */ - uchar szErrLoc[MAXFNAME + 64]; - if(localRet != RS_RET_OK_WARN) { - dbgprintf("config line NOT successfully processed\n"); - bHadAnError = 1; - } - snprintf((char*)szErrLoc, sizeof(szErrLoc) / sizeof(uchar), - "%s, line %d", pConfFile, iLnNbr); - errmsg.LogError(0, NO_ERRCODE, "the last %s occured in %s:\"%s\"", - (localRet == RS_RET_OK_WARN) ? "warning" : "error", - (char*)szErrLoc, (char*)pszOrgLine); - } - } - - /* we probably have one selector left to be added - so let's do that now */ - if(pCurrRule != NULL) { - CHKiRet(ruleset.AddRule(rule.GetAssRuleset(pCurrRule), &pCurrRule)); - } - - /* close the configuration file */ - fclose(cf); - -finalize_it: - if(iRet != RS_RET_OK) { - char errStr[1024]; - if(pCurrRule != NULL) - rule.Destruct(&pCurrRule); - - rs_strerror_r(errno, errStr, sizeof(errStr)); - dbgprintf("error %d processing config file '%s'; os error (if any): %s\n", - iRet, pConfFile, errStr); - } - - free(pszOrgLine); - - if(bHadAnError && (iRet == RS_RET_OK)) { /* a bit dirty, enhance in future releases */ - iRet = RS_RET_NONFATAL_CONFIG_ERR; - } - RETiRet; -} - - /* Helper to cfline() and its helpers. Parses a template name * from an "action" line. Must be called with the Line pointer * pointing to the first character after the semicolon. @@ -605,7 +336,7 @@ cflineParseFileName(uchar* p, uchar *pFileName, omodStringRequest_t *pOMSR, int * rgerhards 2005-09-15 */ /* GPLv3 - stems back to sysklogd */ -static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register rule_t *pRule) +rsRetVal cflineProcessTradPRIFilter(uchar **pline, register rule_t *pRule) { uchar *p; register uchar *q; @@ -622,7 +353,7 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register rule_t *pRule ASSERT(*pline != NULL); ISOBJ_TYPE_assert(pRule, rule); - dbgprintf(" - traditional PRI filter\n"); + dbgprintf(" - traditional PRI filter '%s'\n", *pline); errno = 0; /* keep strerror_r() stuff out of logerror messages */ pRule->f_filter_type = FILTER_PRI; @@ -635,7 +366,6 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register rule_t *pRule /* scan through the list of selectors */ for (p = *pline; *p && *p != '\t' && *p != ' ';) { - /* find the end of this facility name list */ for (q = p; *q && *q != '\t' && *q++ != '.'; ) continue; @@ -646,8 +376,10 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register rule_t *pRule *bp = '\0'; /* skip cruft */ - while (strchr(",;", *q)) - q++; + if(*q) { + while (strchr(",;", *q)) + q++; + } /* decode priority name */ if ( *buf == '!' ) { @@ -758,81 +490,12 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register rule_t *pRule } -/* Helper to cfline(). This function processes an "if" type of filter, - * what essentially means it parses an expression. As usual, - * It processes the line up to the beginning of the action part. - * A pointer to that beginnig is passed back to the caller. - * rgerhards 2008-01-19 - */ -static rsRetVal cflineProcessIfFilter(uchar **pline, register rule_t *f) -{ - DEFiRet; - ctok_t *tok; - ctok_token_t *pToken; - - ASSERT(pline != NULL); - ASSERT(*pline != NULL); - ASSERT(f != NULL); - - dbgprintf(" - general expression-based filter\n"); - errno = 0; /* keep strerror_r() stuff out of logerror messages */ - - f->f_filter_type = FILTER_EXPR; - - /* if we come to over here, pline starts with "if ". We just skip that part. */ - (*pline) += 3; - - /* we first need a tokenizer... */ - CHKiRet(ctok.Construct(&tok)); - CHKiRet(ctok.Setpp(tok, *pline)); - CHKiRet(ctok.ConstructFinalize(tok)); - - /* now construct our expression */ - CHKiRet(expr.Construct(&f->f_filterData.f_expr)); - CHKiRet(expr.ConstructFinalize(f->f_filterData.f_expr)); - - /* ready to go... */ - CHKiRet(expr.Parse(f->f_filterData.f_expr, tok)); - - /* we now need to parse off the "then" - and note an error if it is - * missing... - */ - CHKiRet(ctok.GetToken(tok, &pToken)); - if(pToken->tok != ctok_THEN) { - ctok_token.Destruct(&pToken); - ABORT_FINALIZE(RS_RET_SYNTAX_ERROR); - } - - ctok_token.Destruct(&pToken); /* no longer needed */ - - /* we are done, so we now need to restore things */ - CHKiRet(ctok.Getpp(tok, pline)); - CHKiRet(ctok.Destruct(&tok)); - - /* debug support - print vmprg after construction (uncomment to use) */ - /* vmprgDebugPrint(f->f_filterData.f_expr->pVmprg); */ - - /* we now need to skip whitespace to the action part, else we confuse - * the legacy rsyslog conf parser. -- rgerhards, 2008-02-25 - */ - while(isspace(**pline)) - ++(*pline); - -finalize_it: - if(iRet == RS_RET_SYNTAX_ERROR) { - errmsg.LogError(0, RS_RET_SYNTAX_ERROR, "syntax error in expression"); - } - - RETiRet; -} - - /* Helper to cfline(). This function takes the filter part of a property * based filter and decodes it. It processes the line up to the beginning * of the action part. A pointer to that beginnig is passed back to the caller. * rgerhards 2005-09-15 */ -static rsRetVal cflineProcessPropFilter(uchar **pline, register rule_t *f) +rsRetVal cflineProcessPropFilter(uchar **pline, register rule_t *f) { rsParsObj *pPars; cstr_t *pCSCompOp; @@ -844,7 +507,7 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register rule_t *f) ASSERT(*pline != NULL); ASSERT(f != NULL); - dbgprintf(" - property-based filter\n"); + dbgprintf(" - property-based filter '%s'\n", *pline); errno = 0; /* keep strerror_r() stuff out of logerror messages */ f->f_filter_type = FILTER_PROP; @@ -868,6 +531,14 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register rule_t *f) rsParsDestruct(pPars); return(iRet); } + if(f->f_filterData.prop.propID == PROP_CEE) { + /* in CEE case, we need to preserve the actual property name */ + if((f->f_filterData.prop.propName = + es_newStrFromBuf((char*)cstrGetSzStrNoNULL(pCSPropName)+2, cstrLen(pCSPropName)-2)) == NULL) { + cstrDestruct(&pCSPropName); + return(RS_RET_ERR); + } + } cstrDestruct(&pCSPropName); /* read operation */ @@ -900,6 +571,8 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register rule_t *f) f->f_filterData.prop.operation = FIOP_CONTAINS; } else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (uchar*) "isequal", 7)) { f->f_filterData.prop.operation = FIOP_ISEQUAL; + } else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (uchar*) "isempty", 7)) { + f->f_filterData.prop.operation = FIOP_ISEMPTY; } else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (uchar*) "startswith", 10)) { f->f_filterData.prop.operation = FIOP_STARTSWITH; } else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (unsigned char*) "regex", 5)) { @@ -912,16 +585,18 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register rule_t *f) } rsCStrDestruct(&pCSCompOp); /* no longer needed */ - /* read compare value */ - iRet = parsQuotedCStr(pPars, &f->f_filterData.prop.pCSCompValue); - if(iRet != RS_RET_OK) { - errmsg.LogError(0, iRet, "error %d compare value property - ignoring selector", iRet); - rsParsDestruct(pPars); - return(iRet); + if(f->f_filterData.prop.operation != FIOP_ISEMPTY) { + /* read compare value */ + iRet = parsQuotedCStr(pPars, &f->f_filterData.prop.pCSCompValue); + if(iRet != RS_RET_OK) { + errmsg.LogError(0, iRet, "error %d compare value property - ignoring selector", iRet); + rsParsDestruct(pPars); + return(iRet); + } } /* skip to action part */ - if((iRet = parsSkipWhitespace(pPars, 1)) != RS_RET_OK) { + if((iRet = parsSkipWhitespace(pPars)) != RS_RET_OK) { errmsg.LogError(0, iRet, "error %d skipping to action part - ignoring selector", iRet); rsParsDestruct(pPars); return(iRet); @@ -940,7 +615,7 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register rule_t *f) * from the config file ("+/-hostname"). It stores it for further reference. * rgerhards 2005-10-19 */ -static rsRetVal cflineProcessHostSelector(uchar **pline) +rsRetVal cflineProcessHostSelector(uchar **pline) { DEFiRet; @@ -990,7 +665,7 @@ finalize_it: * from the config file ("!tagname"). It stores it for further reference. * rgerhards 2005-10-18 */ -static rsRetVal cflineProcessTagSelector(uchar **pline) +rsRetVal cflineProcessTagSelector(uchar **pline) { DEFiRet; @@ -1028,75 +703,32 @@ finalize_it: } -/* read the filter part of a configuration line and store the filter - * in the supplied rule_t - * rgerhards, 2007-08-01 - */ -static rsRetVal cflineDoFilter(uchar **pp, rule_t *f) -{ - DEFiRet; - - ASSERT(pp != NULL); - ISOBJ_TYPE_assert(f, rule); - - /* check which filter we need to pull... */ - switch(**pp) { - case ':': - CHKiRet(cflineProcessPropFilter(pp, f)); - break; - case 'i': /* "if" filter? */ - if(*(*pp+1) && (*(*pp+1) == 'f') && isspace(*(*pp+2))) { - CHKiRet(cflineProcessIfFilter(pp, f)); - break; - } - /*FALLTHROUGH*/ - default: - CHKiRet(cflineProcessTradPRIFilter(pp, f)); - break; - } - - /* we now check if there are some global (BSD-style) filter conditions - * and, if so, we copy them over. rgerhards, 2005-10-18 - */ - if(pDfltProgNameCmp != NULL) { -RUNLOG_STR("dflt ProgNameCmp != NULL, setting opCSProgNameComp"); - CHKiRet(rsCStrConstructFromCStr(&(f->pCSProgNameComp), pDfltProgNameCmp)); - } - - if(eDfltHostnameCmpMode != HN_NO_COMP) { - f->eHostnameCmpMode = eDfltHostnameCmpMode; - CHKiRet(rsCStrConstructFromCStr(&(f->pCSHostnameComp), pDfltHostnameCmp)); - } - -finalize_it: - RETiRet; -} - - /* process the action part of a selector line * rgerhards, 2007-08-01 */ -static rsRetVal cflineDoAction(uchar **p, action_t **ppAction) +rsRetVal cflineDoAction(rsconf_t *conf, uchar **p, action_t **ppAction) { - DEFiRet; modInfo_t *pMod; + cfgmodules_etry_t *node; omodStringRequest_t *pOMSR; + int bHadWarning = 0; action_t *pAction = NULL; void *pModData; - int bHadWarning = 0; + DEFiRet; ASSERT(p != NULL); ASSERT(ppAction != NULL); /* loop through all modules and see if one picks up the line */ - pMod = module.GetNxtType(NULL, eMOD_OUT); - /* Note: clang static analyzer reports that pMod mybe == NULL. However, this is + node = module.GetNxtCnfType(conf, NULL, eMOD_OUT); + /* Note: clang static analyzer reports that node maybe == NULL. However, this is * not possible, because we have the built-in output modules which are always * present. Anyhow, we guard this by an assert. -- rgerhards, 2010-12-16 */ - assert(pMod != NULL); - while(pMod != NULL) { + assert(node != NULL); + while(node != NULL) { pOMSR = NULL; + pMod = node->pMod; iRet = pMod->mod.om.parseSelectorAct(p, &pModData, &pOMSR); dbgprintf("tried selector action for %s: %d\n", module.GetName(pMod), iRet); if(iRet == RS_RET_OK_WARN) { @@ -1104,20 +736,20 @@ static rsRetVal cflineDoAction(uchar **p, action_t **ppAction) iRet = RS_RET_OK; } if(iRet == RS_RET_OK || iRet == RS_RET_SUSPENDED) { - if((iRet = addAction(&pAction, pMod, pModData, pOMSR, (iRet == RS_RET_SUSPENDED)? 1 : 0)) == RS_RET_OK) { + if((iRet = addAction(&pAction, pMod, pModData, pOMSR, NULL, NULL, + (iRet == RS_RET_SUSPENDED)? 1 : 0)) == RS_RET_OK) { /* now check if the module is compatible with select features */ if(pMod->isCompatibleWithFeature(sFEATURERepeatedMsgReduction) == RS_RET_OK) - pAction->f_ReduceRepeated = bReduceRepeatMsgs; + pAction->f_ReduceRepeated = loadConf->globals.bReduceRepeatMsgs; else { dbgprintf("module is incompatible with RepeatedMsgReduction - turned off\n"); pAction->f_ReduceRepeated = 0; } pAction->eState = ACT_STATE_RDY; /* action is enabled */ - iNbrActions++; /* one more active action! */ + conf->actions.nbrActions++; /* one more active action! */ } break; - } - else if(iRet != RS_RET_CONFLINE_UNPROCESSED) { + } else if(iRet != RS_RET_CONFLINE_UNPROCESSED) { /* In this case, the module would have handled the config * line, but some error occured while doing so. This error should * already by reported by the module. We do not try any other @@ -1127,7 +759,7 @@ static rsRetVal cflineDoAction(uchar **p, action_t **ppAction) dbgprintf("error %d parsing config line\n", (int) iRet); break; } - pMod = module.GetNxtType(pMod, eMOD_OUT); + node = module.GetNxtCnfType(conf, node, eMOD_OUT); } *ppAction = pAction; @@ -1137,103 +769,15 @@ static rsRetVal cflineDoAction(uchar **p, action_t **ppAction) } -/* Process a configuration file line in traditional "filter selector" format - * or one that builds upon this format. Note that ppRule may be a NULL pointer, - * which is valid and happens if there is no previous line (right at the start - * of the master config file!). - */ -static rsRetVal -cflineClassic(uchar *p, rule_t **ppRule) -{ - DEFiRet; - action_t *pAction; - rsRetVal localRet; - int bHadWarning = 0; - - /* lines starting with '&' have no new filters and just add - * new actions to the currently processed selector. - */ - if(*p == '&') { - ++p; /* eat '&' */ - skipWhiteSpace(&p); /* on to command */ - } else { - /* we are finished with the current selector (on previous line). - * So we now need to check - * if it has any actions associated and, if so, link it to the linked - * list. If it has nothing associated with it, we can simply discard - * it. In any case, we create a fresh selector for our new filter. - * We have one special case during initialization: then, the current - * selector is NULL, which means we do not need to care about it at - * all. -- rgerhards, 2007-08-01 - */ - if(*ppRule != NULL) { - CHKiRet(ruleset.AddRule(rule.GetAssRuleset(*ppRule), ppRule)); - } - CHKiRet(rule.Construct(ppRule)); /* create "fresh" selector */ - CHKiRet(rule.SetAssRuleset(*ppRule, ruleset.GetCurrent())); /* create "fresh" selector */ - CHKiRet(rule.ConstructFinalize(*ppRule)); /* create "fresh" selector */ - CHKiRet(cflineDoFilter(&p, *ppRule)); /* pull filters */ - } - - localRet = cflineDoAction(&p, &pAction); - if(localRet == RS_RET_OK_WARN) { - bHadWarning = 1; - } else { - CHKiRet(localRet); - } - CHKiRet(llAppend(&(*ppRule)->llActList, NULL, (void*) pAction)); - -finalize_it: - if(iRet == RS_RET_OK && bHadWarning) - iRet = RS_RET_OK_WARN; - RETiRet; -} - - -/* process a configuration line - * I re-did this functon because it was desperately time to do so - * rgerhards, 2007-08-01 - */ -static rsRetVal -cfline(uchar *line, rule_t **pfCurr) -{ - DEFiRet; - - ASSERT(line != NULL); - - dbgprintf("cfline: '%s'\n", line); - - /* check type of line and call respective processing */ - switch(*line) { - case '!': - iRet = cflineProcessTagSelector(&line); - break; - case '+': - case '-': - iRet = cflineProcessHostSelector(&line); - break; - case '$': - ++line; /* eat '$' */ - iRet = cfsysline(line); - break; - default: - iRet = cflineClassic(line, pfCurr); - break; - } - - RETiRet; -} - - /* return the current number of active actions * rgerhards, 2008-07-28 */ static rsRetVal -GetNbrActActions(int *piNbrActions) +GetNbrActActions(rsconf_t *conf, int *piNbrActions) { DEFiRet; assert(piNbrActions != NULL); - *piNbrActions = iNbrActions; + *piNbrActions = conf->actions.nbrActions; RETiRet; } @@ -1255,15 +799,23 @@ CODESTARTobjQueryInterface(conf) pIf->doNameLine = doNameLine; pIf->cfsysline = cfsysline; pIf->doModLoad = doModLoad; - pIf->doIncludeLine = doIncludeLine; - pIf->cfline = cfline; - pIf->processConfFile = processConfFile; pIf->GetNbrActActions = GetNbrActActions; finalize_it: ENDobjQueryInterface(conf) +/* Reset config variables to default values. + * rgerhards, 2010-07-23 + */ +static rsRetVal +resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal) +{ + bConfStrictScoping = 0; + return RS_RET_OK; +} + + /* exit our class * rgerhards, 2008-03-11 */ @@ -1279,9 +831,6 @@ CODESTARTObjClassExit(conf) } /* release objects we no longer need */ - objRelease(expr, CORE_COMPONENT); - objRelease(ctok, CORE_COMPONENT); - objRelease(ctok_token, CORE_COMPONENT); objRelease(module, CORE_COMPONENT); objRelease(errmsg, CORE_COMPONENT); objRelease(net, LM_NET_FILENAME); @@ -1296,14 +845,16 @@ ENDObjClassExit(conf) */ BEGINAbstractObjClassInit(conf, 1, OBJ_IS_CORE_MODULE) /* class, version - CHANGE class also in END MACRO! */ /* request objects we use */ - CHKiRet(objUse(expr, CORE_COMPONENT)); - CHKiRet(objUse(ctok, CORE_COMPONENT)); - CHKiRet(objUse(ctok_token, CORE_COMPONENT)); CHKiRet(objUse(module, CORE_COMPONENT)); CHKiRet(objUse(errmsg, CORE_COMPONENT)); CHKiRet(objUse(net, LM_NET_FILENAME)); /* TODO: make this dependcy go away! */ CHKiRet(objUse(rule, CORE_COMPONENT)); CHKiRet(objUse(ruleset, CORE_COMPONENT)); + + /* These commands will NOT be supported -- the new v6.3 config system provides + * far better methods. We will remove the related code soon. -- rgerhards, 2012-01-09 + */ + CHKiRet(regCfSysLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, NULL)); ENDObjClassInit(conf) /* vi:set ai: diff --git a/runtime/conf.h b/runtime/conf.h index 93683975..018d9111 100644 --- a/runtime/conf.h +++ b/runtime/conf.h @@ -20,6 +20,7 @@ */ #ifndef INCLUDED_CONF_H #define INCLUDED_CONF_H +#include "action.h" /* definitions used for doNameLine to differentiate between different command types * (with otherwise identical code). This is a left-over from the previous config @@ -27,18 +28,27 @@ * somewhat strange (at least its name). -- rgerhards, 2007-08-01 */ enum eDirective { DIR_TEMPLATE = 0, DIR_OUTCHANNEL = 1, DIR_ALLOWEDSENDER = 2}; +extern ecslConfObjType currConfObj; +extern int bConfStrictScoping; /* force strict scoping during config processing? */ /* interfaces */ BEGINinterface(conf) /* name must also be changed in ENDinterface macro! */ rsRetVal (*doNameLine)(uchar **pp, void* pVal); rsRetVal (*cfsysline)(uchar *p); rsRetVal (*doModLoad)(uchar **pp, __attribute__((unused)) void* pVal); - rsRetVal (*doIncludeLine)(uchar **pp, __attribute__((unused)) void* pVal); - rsRetVal (*cfline)(uchar *line, rule_t **pfCurr); - rsRetVal (*processConfFile)(uchar *pConfFile); - rsRetVal (*GetNbrActActions)(int *); + rsRetVal (*GetNbrActActions)(rsconf_t *conf, int *); + /* version 4 -- 2010-07-23 rgerhards */ + /* "just" added global variables + * FYI: we reconsider repacking as a non-object, as only the core currently + * accesses this module. The current object structure complicates things without + * any real benefit. + */ + /* version 5 -- 2011-04-19 rgerhards */ + /* complete revamp, we now use the rsconf object */ + /* version 6 -- 2011-07-06 rgerhards */ + /* again a complete revamp, using flex/bison based parser now */ ENDinterface(conf) -#define confCURR_IF_VERSION 3 /* increment whenever you change the interface structure! */ +#define confCURR_IF_VERSION 6 /* increment whenever you change the interface structure! */ /* in Version 3, entry point "ReInitConf()" was removed, as we do not longer need * to support restart-type HUP -- rgerhards, 2009-07-15 */ @@ -52,5 +62,14 @@ PROTOTYPEObj(conf); rsRetVal cflineParseTemplateName(uchar** pp, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts, uchar *dfltTplName); rsRetVal cflineParseFileName(uchar* p, uchar *pFileName, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts, uchar *pszTpl); +/* more dirt to cover the new config interface (will go away...) */ +rsRetVal cflineProcessTagSelector(uchar **pline); +rsRetVal cflineProcessHostSelector(uchar **pline); +rsRetVal cflineProcessTradPRIFilter(uchar **pline, rule_t *pRule); +rsRetVal cflineProcessPropFilter(uchar **pline, rule_t *f); +rsRetVal cflineDoAction(rsconf_t *conf, uchar **p, action_t **ppAction); +extern EHostnameCmpMode eDfltHostnameCmpMode; +extern cstr_t *pDfltHostnameCmp; +extern cstr_t *pDfltProgNameCmp; #endif /* #ifndef INCLUDED_CONF_H */ diff --git a/runtime/ctok.c b/runtime/ctok.c deleted file mode 100644 index 1da4f4d6..00000000 --- a/runtime/ctok.c +++ /dev/null @@ -1,624 +0,0 @@ -/* ctok.c - helper class to tokenize an input stream - which surprisingly - * currently does not work with streams but with string. But that will - * probably change over time ;) This class was originally written to support - * the expression module but may evolve when (if) the expression module is - * expanded (or aggregated) by a full-fledged ctoken based config parser. - * Obviously, this class is used together with config files and not any other - * parse function. - * - * Module begun 2008-02-19 by Rainer Gerhards - * - * Copyright (C) 2008-2012 Adiscon GmbH. - * - * This file is part of the rsyslog runtime library. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * -or- - * see COPYING.ASL20 in the source distribution - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "config.h" -#include <stdlib.h> -#include <ctype.h> -#include <strings.h> -#include <assert.h> - -#include "rsyslog.h" -#include "template.h" -#include "ctok.h" - -/* static data */ -DEFobjStaticHelpers -DEFobjCurrIf(ctok_token) -DEFobjCurrIf(var) - - -/* Standard-Constructor - */ -BEGINobjConstruct(ctok) /* be sure to specify the object type also in END macro! */ -ENDobjConstruct(ctok) - - -/* ConstructionFinalizer - * rgerhards, 2008-01-09 - */ -rsRetVal ctokConstructFinalize(ctok_t __attribute__((unused)) *pThis) -{ - DEFiRet; - RETiRet; -} - - -/* destructor for the ctok object */ -BEGINobjDestruct(ctok) /* be sure to specify the object type also in END and CODESTART macros! */ -CODESTARTobjDestruct(ctok) - /* ... then free resources */ -ENDobjDestruct(ctok) - - -/* unget character from input stream. At most one character can be ungotten. - * This funtion is only permitted to be called after at least one character - * has been read from the stream. Right now, we handle the situation simply by - * moving the string "stream" pointer one position backwards. If we work with - * real streams (some time), the strm object will handle the functionality - * itself. -- rgerhards, 2008-02-19 - */ -static rsRetVal -ctokUngetCharFromStream(ctok_t *pThis, uchar __attribute__((unused)) c) -{ - DEFiRet; - - ISOBJ_TYPE_assert(pThis, ctok); - --pThis->pp; - - RETiRet; -} - - -/* get the next character from the input "stream". Note that this version - * does NOT look for comment characters as end-of-stream, so it is suitable - * when building constant strings! -- rgerhards, 2010-03-01 - */ -static inline rsRetVal -ctokGetCharFromStreamNoComment(ctok_t *pThis, uchar *pc) -{ - DEFiRet; - - ISOBJ_TYPE_assert(pThis, ctok); - ASSERT(pc != NULL); - - /* end of string or begin of comment terminates the "stream" */ - if(*pThis->pp == '\0') { - ABORT_FINALIZE(RS_RET_EOS); - } else { - *pc = *pThis->pp; - ++pThis->pp; - } - -finalize_it: - RETiRet; -} - - -/* get the next character from the input "stream" (currently just a in-memory - * string...) -- rgerhards, 2008-02-19 - */ -static rsRetVal -ctokGetCharFromStream(ctok_t *pThis, uchar *pc) -{ - DEFiRet; - - ISOBJ_TYPE_assert(pThis, ctok); - ASSERT(pc != NULL); - - CHKiRet(ctokGetCharFromStreamNoComment(pThis, pc)); - /* begin of comment terminates the "stream"! */ - if(*pc == '#') { - ABORT_FINALIZE(RS_RET_EOS); - } - -finalize_it: - RETiRet; -} - - -/* skip whitespace in the input "stream". - * rgerhards, 2008-02-19 - */ -static rsRetVal -ctokSkipWhitespaceFromStream(ctok_t *pThis) -{ - DEFiRet; - uchar c; - - ISOBJ_TYPE_assert(pThis, ctok); - - CHKiRet(ctokGetCharFromStream(pThis, &c)); - while(isspace(c)) { - CHKiRet(ctokGetCharFromStream(pThis, &c)); - } - - /* we must unget the one non-whitespace we found */ - CHKiRet(ctokUngetCharFromStream(pThis, c)); - -dbgprintf("skipped whitespace, stream now '%s'\n", pThis->pp); -finalize_it: - RETiRet; -} - - -/* get the next word from the input "stream" (currently just a in-memory - * string...). A word is anything from the current location until the - * first non-alphanumeric character. If the word is longer - * than the provided memory buffer, parsing terminates when buffer length - * has been reached. A buffer of 128 bytes or more should always be by - * far sufficient. -- rgerhards, 2008-02-19 - */ -static rsRetVal -ctokGetWordFromStream(ctok_t *pThis, uchar *pWordBuf, size_t lenWordBuf) -{ - DEFiRet; - uchar c; - - ISOBJ_TYPE_assert(pThis, ctok); - ASSERT(pWordBuf != NULL); - ASSERT(lenWordBuf > 0); - - CHKiRet(ctokSkipWhitespaceFromStream(pThis)); - - CHKiRet(ctokGetCharFromStream(pThis, &c)); - while((isalnum(c) || c == '_' || c == '-') && lenWordBuf > 1) { - *pWordBuf++ = c; - --lenWordBuf; - CHKiRet(ctokGetCharFromStream(pThis, &c)); - } - *pWordBuf = '\0'; /* there is always space for this - see while() */ - - /* push back the char that we have read too much */ - CHKiRet(ctokUngetCharFromStream(pThis, c)); - -finalize_it: - RETiRet; -} - - -/* read in a constant number - * This is the "number" ABNF element - * rgerhards, 2008-02-19 - */ -static rsRetVal -ctokGetNumber(ctok_t *pThis, ctok_token_t *pToken) -{ - DEFiRet; - number_t n; /* the parsed number */ - uchar c; - int valC; - int iBase; - - ISOBJ_TYPE_assert(pThis, ctok); - ASSERT(pToken != NULL); - - pToken->tok = ctok_NUMBER; - - CHKiRet(ctokGetCharFromStream(pThis, &c)); - if(c == '0') { /* octal? */ - CHKiRet(ctokGetCharFromStream(pThis, &c)); - if(c == 'x') { /* nope, hex! */ - CHKiRet(ctokGetCharFromStream(pThis, &c)); - c = tolower(c); - iBase = 16; - } else { - iBase = 8; - } - } else { - iBase = 10; - } - - n = 0; - /* this loop is quite simple, a variable name is terminated by whitespace. */ - while(isdigit(c) || (c >= 'a' && c <= 'f')) { - if(isdigit(c)) { - valC = c - '0'; - } else { - valC = c - 'a' + 10; - } - - if(valC >= iBase) { - if(iBase == 8) { - ABORT_FINALIZE(RS_RET_INVALID_OCTAL_DIGIT); - } else { - ABORT_FINALIZE(RS_RET_INVALID_HEX_DIGIT); - } - } - /* we now have the next value and know it is right */ - n = n * iBase + valC; - CHKiRet(ctokGetCharFromStream(pThis, &c)); - c = tolower(c); - } - - /* we need to unget the character that made the loop terminate */ - CHKiRet(ctokUngetCharFromStream(pThis, c)); - - CHKiRet(var.SetNumber(pToken->pVar, n)); - -finalize_it: - RETiRet; -} - - -/* read in a variable - * This covers both msgvar and sysvar from the ABNF. - * rgerhards, 2008-02-19 - */ -static rsRetVal -ctokGetVar(ctok_t *pThis, ctok_token_t *pToken) -{ - DEFiRet; - uchar c; - cstr_t *pstrVal = NULL; - - ISOBJ_TYPE_assert(pThis, ctok); - ASSERT(pToken != NULL); - - CHKiRet(ctokGetCharFromStream(pThis, &c)); - - if(c == '$') { /* second dollar, we have a system variable */ - pToken->tok = ctok_SYSVAR; - CHKiRet(ctokGetCharFromStream(pThis, &c)); /* "eat" it... */ - } else { - pToken->tok = ctok_MSGVAR; - } - - CHKiRet(cstrConstruct(&pstrVal)); - /* this loop is quite simple, a variable name is terminated when a non-supported - * character is detected. Note that we currently permit a numerical digit as the - * first char, which is not permitted by ABNF. -- rgerhards, 2009-03-10 - */ - while(isalpha(c) || isdigit(c) || (c == '_') || (c == '-')) { - CHKiRet(cstrAppendChar(pstrVal, tolower(c))); - CHKiRet(ctokGetCharFromStream(pThis, &c)); - } - CHKiRet(ctokUngetCharFromStream(pThis, c)); /* put not processed char back */ - - CHKiRet(cstrFinalize(pstrVal)); - - CHKiRet(var.SetString(pToken->pVar, pstrVal)); - pstrVal = NULL; - -finalize_it: - if(iRet != RS_RET_OK) { - if(pstrVal != NULL) { - cstrDestruct(&pstrVal); - } - } - - RETiRet; -} - - -/* read in a simple string (simpstr in ABNF) - * rgerhards, 2008-02-19 - */ -static rsRetVal -ctokGetSimpStr(ctok_t *pThis, ctok_token_t *pToken) -{ - DEFiRet; - uchar c; - int bInEsc = 0; - cstr_t *pstrVal; - - ISOBJ_TYPE_assert(pThis, ctok); - ASSERT(pToken != NULL); - - pToken->tok = ctok_SIMPSTR; - - CHKiRet(cstrConstruct(&pstrVal)); - CHKiRet(ctokGetCharFromStreamNoComment(pThis, &c)); - /* while we are in escape mode (had a backslash), no sequence - * terminates the loop. If outside, it is terminated by a single quote. - */ - while(bInEsc || c != '\'') { - if(bInEsc) { - CHKiRet(cstrAppendChar(pstrVal, c)); - bInEsc = 0; - } else { - if(c == '\\') { - bInEsc = 1; - } else { - CHKiRet(cstrAppendChar(pstrVal, c)); - } - } - CHKiRet(ctokGetCharFromStreamNoComment(pThis, &c)); - } - CHKiRet(cstrFinalize(pstrVal)); - - CHKiRet(var.SetString(pToken->pVar, pstrVal)); - pstrVal = NULL; - -finalize_it: - if(iRet != RS_RET_OK) { - if(pstrVal != NULL) { - cstrDestruct(&pstrVal); - } - } - - RETiRet; -} - - -/* Unget a token. The token ungotten will be returned the next time - * ctokGetToken() is called. Only one token can be ungotten at a time. - * If a second token is ungotten, the first is lost. This is considered - * a programming error. - * rgerhards, 2008-02-20 - */ -static rsRetVal -ctokUngetToken(ctok_t *pThis, ctok_token_t *pToken) -{ - DEFiRet; - - ISOBJ_TYPE_assert(pThis, ctok); - ASSERT(pToken != NULL); - ASSERT(pThis->pUngotToken == NULL); - - pThis->pUngotToken = pToken; - - RETiRet; -} - - -/* skip an inine comment (just like a C-comment) - * rgerhards, 2008-02-20 - */ -static rsRetVal -ctokSkipInlineComment(ctok_t *pThis) -{ - DEFiRet; - uchar c; - int bHadAsterisk = 0; - - ISOBJ_TYPE_assert(pThis, ctok); - - CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a charater */ - while(!(bHadAsterisk && c == '/')) { - bHadAsterisk = (c == '*') ? 1 : 0; - CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read next */ - } - -finalize_it: - RETiRet; -} - - - -/* Get the *next* token from the input stream. This parses the next token and - * ignores any whitespace in between. End of stream is communicated via iRet. - * The returned token must either be destructed by the caller OR being passed - * back to ctokUngetToken(). - * rgerhards, 2008-02-19 - */ -static rsRetVal -ctokGetToken(ctok_t *pThis, ctok_token_t **ppToken) -{ - DEFiRet; - ctok_token_t *pToken; - uchar c; - uchar szWord[128]; - int bRetry = 0; /* retry parse? Only needed for inline comments... */ - cstr_t *pstrVal; - - ISOBJ_TYPE_assert(pThis, ctok); - ASSERT(ppToken != NULL); - - /* first check if we have an ungotten token and, if so, provide that - * one back (without any parsing). -- rgerhards, 2008-02-20 - */ - if(pThis->pUngotToken != NULL) { - *ppToken = pThis->pUngotToken; - pThis->pUngotToken = NULL; - FINALIZE; - } - - /* setup the stage - create our token */ - CHKiRet(ctok_token.Construct(&pToken)); - CHKiRet(ctok_token.ConstructFinalize(pToken)); - - /* find the next token. We may loop when we have inline comments */ - do { - bRetry = 0; - CHKiRet(ctokSkipWhitespaceFromStream(pThis)); - CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a charater */ - switch(c) { - case '=': /* == */ - CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a character */ - pToken->tok = (c == '=')? ctok_CMP_EQ : ctok_INVALID; - break; - case '!': /* != */ - CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a character */ - pToken->tok = (c == '=')? ctok_CMP_NEQ : ctok_INVALID; - break; - case '<': /* <, <=, <> */ - CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a character */ - if(c == '=') { - pToken->tok = ctok_CMP_LTEQ; - } else if(c == '>') { - pToken->tok = ctok_CMP_NEQ; - } else { - pToken->tok = ctok_CMP_LT; - } - break; - case '>': /* >, >= */ - CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a character */ - if(c == '=') { - pToken->tok = ctok_CMP_GTEQ; - } else { - pToken->tok = ctok_CMP_GT; - } - break; - case '+': - pToken->tok = ctok_PLUS; - break; - case '-': - pToken->tok = ctok_MINUS; - break; - case '*': - pToken->tok = ctok_TIMES; - break; - case '/': /* /, /.* ... *./ (comments, mungled here for obvious reasons...) */ - CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a charater */ - if(c == '*') { - /* we have a comment and need to skip it */ - ctokSkipInlineComment(pThis); - bRetry = 1; - } else { - CHKiRet(ctokUngetCharFromStream(pThis, c)); /* put back, not processed */ - } - pToken->tok = ctok_DIV; - break; - case '%': - pToken->tok = ctok_MOD; - break; - case '(': - pToken->tok = ctok_LPAREN; - break; - case ')': - pToken->tok = ctok_RPAREN; - break; - case ',': - pToken->tok = ctok_COMMA; - break; - case '&': - pToken->tok = ctok_STRADD; - break; - case '$': - CHKiRet(ctokGetVar(pThis, pToken)); - break; - case '\'': /* simple string, this is somewhat more elaborate */ - CHKiRet(ctokGetSimpStr(pThis, pToken)); - break; - case '"': - /* TODO: template string parser */ - ABORT_FINALIZE(RS_RET_NOT_IMPLEMENTED); - break; - default: - CHKiRet(ctokUngetCharFromStream(pThis, c)); /* push back, we need it in any case */ - if(isdigit(c)) { - CHKiRet(ctokGetNumber(pThis, pToken)); - } else { /* now we check if we have a multi-char sequence */ - CHKiRet(ctokGetWordFromStream(pThis, szWord, sizeof(szWord)/sizeof(uchar))); - if(!strcasecmp((char*)szWord, "and")) { - pToken->tok = ctok_AND; - } else if(!strcasecmp((char*)szWord, "or")) { - pToken->tok = ctok_OR; - } else if(!strcasecmp((char*)szWord, "not")) { - pToken->tok = ctok_NOT; - } else if(!strcasecmp((char*)szWord, "contains")) { - pToken->tok = ctok_CMP_CONTAINS; - } else if(!strcasecmp((char*)szWord, "contains_i")) { - pToken->tok = ctok_CMP_CONTAINSI; - } else if(!strcasecmp((char*)szWord, "startswith")) { - pToken->tok = ctok_CMP_STARTSWITH; - } else if(!strcasecmp((char*)szWord, "startswith_i")) { - pToken->tok = ctok_CMP_STARTSWITHI; - } else if(!strcasecmp((char*)szWord, "then")) { - pToken->tok = ctok_THEN; - } else { - /* finally, we check if it is a function */ - CHKiRet(ctokGetCharFromStream(pThis, &c)); /* read a charater */ - if(c == '(') { - /* push c back, higher level parser needs it */ - CHKiRet(ctokUngetCharFromStream(pThis, c)); - pToken->tok = ctok_FUNCTION; - /* fill function name */ - CHKiRet(cstrConstruct(&pstrVal)); - CHKiRet(rsCStrSetSzStr(pstrVal, szWord)); - CHKiRet(cstrFinalize(pstrVal)); - CHKiRet(var.SetString(pToken->pVar, pstrVal)); - } else { /* give up... */ - dbgprintf("parser has an invalid word (token) '%s'\n", szWord); - pToken->tok = ctok_INVALID; - } - } - } - break; - } - } while(bRetry); /* warning: do ... while()! */ - - *ppToken = pToken; - dbgoprint((obj_t*) pToken, "token: %d\n", pToken->tok); - -finalize_it: -/*dbgprintf("ctokGetToken, returns %d, returns token %d, addr %p\n", iRet, (*ppToken)->tok, &((*ppToken)->tok));*/ - if(iRet != RS_RET_OK) { - if(pToken != NULL) - ctok_token.Destruct(&pToken); - } - - RETiRet; -} - - -/* property set methods */ -/* simple ones first */ -DEFpropSetMeth(ctok, pp, uchar*) - -/* return the current position of pp - most important as currently we do only - * partial parsing, so the rest must know where to start from... - * rgerhards, 2008-02-19 - */ -static rsRetVal -ctokGetpp(ctok_t *pThis, uchar **pp) -{ - DEFiRet; - ASSERT(pp != NULL); - *pp = pThis->pp; - RETiRet; -} - - -/* queryInterface function - * rgerhards, 2008-02-21 - */ -BEGINobjQueryInterface(ctok) -CODESTARTobjQueryInterface(ctok) - if(pIf->ifVersion != ctokCURR_IF_VERSION) { /* check for current version, increment on each change */ - ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); - } - - /* ok, we have the right interface, so let's fill it - * Please note that we may also do some backwards-compatibility - * work here (if we can support an older interface version - that, - * of course, also affects the "if" above). - */ - pIf->Construct = ctokConstruct; - pIf->ConstructFinalize = ctokConstructFinalize; - pIf->Destruct = ctokDestruct; - pIf->Getpp = ctokGetpp; - pIf->GetToken = ctokGetToken; - pIf->UngetToken = ctokUngetToken; - pIf->Setpp = ctokSetpp; -finalize_it: -ENDobjQueryInterface(ctok) - - - -BEGINObjClassInit(ctok, 1, OBJ_IS_CORE_MODULE) /* class, version */ - /* request objects we use */ - CHKiRet(objUse(ctok_token, CORE_COMPONENT)); - CHKiRet(objUse(var, CORE_COMPONENT)); - - OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, ctokConstructFinalize); -ENDObjClassInit(ctok) - -/* vi:set ai: - */ diff --git a/runtime/ctok.h b/runtime/ctok.h deleted file mode 100644 index 32ade045..00000000 --- a/runtime/ctok.h +++ /dev/null @@ -1,54 +0,0 @@ -/* The ctok object (implements a config file tokenizer). - * - * Copyright 2008-2012 Adiscon GmbH. - * - * This file is part of the rsyslog runtime library. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * -or- - * see COPYING.ASL20 in the source distribution - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef INCLUDED_CTOK_H -#define INCLUDED_CTOK_H - -#include "obj.h" -#include "stringbuf.h" -#include "ctok_token.h" - -/* the ctokession object */ -typedef struct ctok_s { - BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ - uchar *pp; /* this points to the next unread character, it is a reminescent of pp in - the config parser code ;) */ - ctok_token_t *pUngotToken; /* buffer for ctokUngetToken(), NULL if not set */ -} ctok_t; - - -/* interfaces */ -BEGINinterface(ctok) /* name must also be changed in ENDinterface macro! */ - INTERFACEObjDebugPrint(ctok); - INTERFACEpropSetMeth(ctok, pp, uchar*); - rsRetVal (*Construct)(ctok_t **ppThis); - rsRetVal (*ConstructFinalize)(ctok_t __attribute__((unused)) *pThis); - rsRetVal (*Destruct)(ctok_t **ppThis); - rsRetVal (*Getpp)(ctok_t *pThis, uchar **pp); - rsRetVal (*GetToken)(ctok_t *pThis, ctok_token_t **ppToken); - rsRetVal (*UngetToken)(ctok_t *pThis, ctok_token_t *pToken); -ENDinterface(ctok) -#define ctokCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ - - -/* prototypes */ -PROTOTYPEObj(ctok); - -#endif /* #ifndef INCLUDED_CTOK_H */ diff --git a/runtime/ctok_token.c b/runtime/ctok_token.c deleted file mode 100644 index e74c275b..00000000 --- a/runtime/ctok_token.c +++ /dev/null @@ -1,127 +0,0 @@ -/* ctok_token - implements the token_t class. - * - * Module begun 2008-02-20 by Rainer Gerhards - * - * Copyright 2008-2012 Adiscon GmbH. - * - * This file is part of the rsyslog runtime library. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * -or- - * see COPYING.ASL20 in the source distribution - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "config.h" -#include <stdlib.h> -#include <ctype.h> -#include <strings.h> -#include <assert.h> - -#include "rsyslog.h" -#include "template.h" -#include "ctok_token.h" - -/* static data */ -DEFobjStaticHelpers -DEFobjCurrIf(var) - - -/* Standard-Constructor - */ -BEGINobjConstruct(ctok_token) /* be sure to specify the object type also in END macro! */ - /* TODO: we may optimize the code below and alloc var only if actually - * needed (but we need it quite often) - */ - CHKiRet(var.Construct(&pThis->pVar)); - CHKiRet(var.ConstructFinalize(pThis->pVar)); -finalize_it: -ENDobjConstruct(ctok_token) - - -/* ConstructionFinalizer - * rgerhards, 2008-01-09 - */ -rsRetVal ctok_tokenConstructFinalize(ctok_token_t __attribute__((unused)) *pThis) -{ - DEFiRet; - RETiRet; -} - - -/* destructor for the ctok object */ -BEGINobjDestruct(ctok_token) /* be sure to specify the object type also in END and CODESTART macros! */ -CODESTARTobjDestruct(ctok_token) - if(pThis->pVar != NULL) { - var.Destruct(&pThis->pVar); - } -ENDobjDestruct(ctok_token) - - -/* get the cstr_t from the token, but do not destruct it. This is meant to - * be used by a caller who passes on the string to some other function. The - * caller is responsible for destructing it. - * rgerhards, 2008-02-20 - */ -static rsRetVal -ctok_tokenUnlinkVar(ctok_token_t *pThis, var_t **ppVar) -{ - DEFiRet; - - ISOBJ_TYPE_assert(pThis, ctok_token); - ASSERT(ppVar != NULL); - - *ppVar = pThis->pVar; - pThis->pVar = NULL; - - RETiRet; -} - - -/* tell the caller if the supplied token is a compare operation */ -static int ctok_tokenIsCmpOp(ctok_token_t *pThis) -{ - return(pThis->tok >= ctok_CMP_EQ && pThis->tok <= ctok_CMP_GTEQ); -} - -/* queryInterface function - * rgerhards, 2008-02-21 - */ -BEGINobjQueryInterface(ctok_token) -CODESTARTobjQueryInterface(ctok_token) - if(pIf->ifVersion != ctok_tokenCURR_IF_VERSION) { /* check for current version, increment on each change */ - ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); - } - - /* ok, we have the right interface, so let's fill it - * Please note that we may also do some backwards-compatibility - * work here (if we can support an older interface version - that, - * of course, also affects the "if" above). - */ - pIf->Construct = ctok_tokenConstruct; - pIf->ConstructFinalize = ctok_tokenConstructFinalize; - pIf->Destruct = ctok_tokenDestruct; - pIf->UnlinkVar = ctok_tokenUnlinkVar; - pIf->IsCmpOp = ctok_tokenIsCmpOp; -finalize_it: -ENDobjQueryInterface(ctok_token) - - -BEGINObjClassInit(ctok_token, 1, OBJ_IS_CORE_MODULE) /* class, version */ - /* request objects we use */ - CHKiRet(objUse(var, CORE_COMPONENT)); - - OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, ctok_tokenConstructFinalize); -ENDObjClassInit(ctok_token) - -/* vi:set ai: - */ diff --git a/runtime/ctok_token.h b/runtime/ctok_token.h deleted file mode 100644 index 578fcaa6..00000000 --- a/runtime/ctok_token.h +++ /dev/null @@ -1,85 +0,0 @@ -/* The ctok_token object - * - * Copyright 2008-2012 Adiscon GmbH. - * - * This file is part of the rsyslog runtime library. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * -or- - * see COPYING.ASL20 in the source distribution - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef INCLUDED_CTOK_TOKEN_H -#define INCLUDED_CTOK_TOKEN_H - -#include "obj.h" -#include "var.h" - -/* the tokens... I use numbers below so that the tokens can be easier - * identified in debug output. These ID's are also partly resused as opcodes. - * As such, they should be kept below 1,000 so that they do not interfer - * with the rest of the opcodes. - */ -typedef struct { - BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ - enum { - ctok_INVALID = 0, - ctok_OR = 1, - ctok_AND = 2, - ctok_PLUS = 3, - ctok_MINUS = 4, - ctok_TIMES = 5, /* "*" */ - ctok_DIV = 6, - ctok_MOD = 7, - ctok_NOT = 8, - ctok_RPAREN = 9, - ctok_LPAREN = 10, - ctok_COMMA = 11, - ctok_SYSVAR = 12, - ctok_MSGVAR = 13, - ctok_SIMPSTR = 14, - ctok_TPLSTR = 15, - ctok_NUMBER = 16, - ctok_FUNCTION = 17, - ctok_THEN = 18, - ctok_STRADD = 19, - ctok_CMP_EQ = 100, /* all compare operations must be in a row */ - ctok_CMP_NEQ = 101, - ctok_CMP_LT = 102, - ctok_CMP_GT = 103, - ctok_CMP_LTEQ = 104, - ctok_CMP_CONTAINS = 105, - ctok_CMP_STARTSWITH = 106, - ctok_CMP_CONTAINSI = 107, - ctok_CMP_STARTSWITHI = 108, - ctok_CMP_GTEQ = 109 /* end compare operations */ - } tok; - var_t *pVar; -} ctok_token_t; - - -/* interfaces */ -BEGINinterface(ctok_token) /* name must also be changed in ENDinterface macro! */ - INTERFACEObjDebugPrint(ctok_token); - rsRetVal (*Construct)(ctok_token_t **ppThis); - rsRetVal (*ConstructFinalize)(ctok_token_t __attribute__((unused)) *pThis); - rsRetVal (*Destruct)(ctok_token_t **ppThis); - rsRetVal (*UnlinkVar)(ctok_token_t *pThis, var_t **ppVar); - int (*IsCmpOp)(ctok_token_t *pThis); -ENDinterface(ctok_token) -#define ctok_tokenCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ - - -/* prototypes */ -PROTOTYPEObj(ctok_token); - -#endif /* #ifndef INCLUDED_CTOK_TOKEN_H */ diff --git a/runtime/datetime.c b/runtime/datetime.c index 85cbab84..10ab3c64 100644 --- a/runtime/datetime.c +++ b/runtime/datetime.c @@ -38,7 +38,6 @@ #include "obj.h" #include "modules.h" #include "datetime.h" -#include "sysvar.h" #include "srUtils.h" #include "stringbuf.h" #include "errmsg.h" @@ -851,6 +850,126 @@ int formatTimestamp3164(struct syslogTime *ts, char* pBuf, int bBuggyDay) } +/** + * convert syslog timestamp to time_t + */ +time_t syslogTime2time_t(struct syslogTime *ts) +{ + long MonthInDays, NumberOfYears, NumberOfDays, i; + int utcOffset; + time_t TimeInUnixFormat; + + /* Counting how many Days have passed since the 01.01 of the + * selected Year (Month level), according to the selected Month*/ + + switch(ts->month) + { + case 1: + MonthInDays = 0; //until 01 of January + break; + case 2: + MonthInDays = 31; //until 01 of February - leap year handling down below! + break; + case 3: + MonthInDays = 59; //until 01 of March + break; + case 4: + MonthInDays = 90; //until 01 of April + break; + case 5: + MonthInDays = 120; //until 01 of Mai + break; + case 6: + MonthInDays = 151; //until 01 of June + break; + case 7: + MonthInDays = 181; //until 01 of July + break; + case 8: + MonthInDays = 212; //until 01 of August + break; + case 9: + MonthInDays = 243; //until 01 of September + break; + case 10: + MonthInDays = 273; //until 01 of Oktober + break; + case 11: + MonthInDays = 304; //until 01 of November + break; + case 12: + MonthInDays = 334; //until 01 of December + break; + } + + + /* 1) Counting how many Years have passed since 1970 + 2) Counting how many Days have passed since the 01.01 of the selected Year + (Day level) according to the Selected Month and Day. Last day doesn't count, + it should be until last day + 3) Calculating this period (NumberOfDays) in seconds*/ + + NumberOfYears = ts->year - 1970; + NumberOfDays = MonthInDays + ts->day - 1; + TimeInUnixFormat = NumberOfYears * 31536000 + NumberOfDays * 86400; + + /* Now we need to adjust the number of years for leap + * year processing. If we are in Jan or Feb, this year + * will never be considered - because we haven't arrived + * at then end of Feb right now. [Feb, 29th in a leap year + * is handled correctly, because the day (29) is correctly + * added to the date serial] + */ + if(ts->month < 3) + NumberOfYears--; + + /*...AND ADDING ONE DAY FOR EACH YEAR WITH 366 DAYS + * note that we do not handle 2000 any special, as it was a + * leap year. The current code works OK until 2100, when it will + * break. As we do not process future dates, we accept that fate... + * the whole thing could be refactored by a table-based approach. + */ + for(i = 1;i <= NumberOfYears; i++) + { + /* If i = 2 we have 1972, which was a Year with 366 Days + and if (i + 2) Mod (4) = 0 we have a Year after 1972 + which is also a Year with 366 Days (repeated every 4 Years) */ + if ((i == 2) || (((i + 2) % 4) == 0)) + { /*Year with 366 Days!!!*/ + TimeInUnixFormat += 86400; + } + } + + /*Add Hours, minutes and seconds */ + TimeInUnixFormat += ts->hour*60*60; + TimeInUnixFormat += ts->minute*60; + TimeInUnixFormat += ts->second; + /* do UTC offset */ + utcOffset = ts->OffsetHour*3600 + ts->OffsetMinute*60; + if(ts->OffsetMode == '+') + utcOffset *= -1; /* if timestamp is ahead, we need to "go back" to UTC */ + TimeInUnixFormat += utcOffset; + return TimeInUnixFormat; +} + + +/** + * format a timestamp as a UNIX timestamp; subsecond resolution is + * discarded. + * Note that this code can use some refactoring. I decided to use it + * because mktime() requires an upfront TZ update as it works on local + * time. In any case, it is worth reconsidering to move to mktime() or + * some other method. + * Important: pBuf must point to a buffer of at least 11 bytes. + * rgerhards, 2012-03-29 + */ +int formatTimestampUnix(struct syslogTime *ts, char *pBuf) +{ + snprintf(pBuf, 11, "%u", (unsigned) syslogTime2time_t(ts)); + return 11; +} + + /* queryInterface function * rgerhards, 2008-03-05 */ @@ -875,6 +994,8 @@ CODESTARTobjQueryInterface(datetime) pIf->formatTimestampSecFrac = formatTimestampSecFrac; pIf->formatTimestamp3339 = formatTimestamp3339; pIf->formatTimestamp3164 = formatTimestamp3164; + pIf->formatTimestampUnix = formatTimestampUnix; + pIf->syslogTime2time_t = syslogTime2time_t; finalize_it: ENDobjQueryInterface(datetime) diff --git a/runtime/datetime.h b/runtime/datetime.h index acf54df5..9f3611e1 100644 --- a/runtime/datetime.h +++ b/runtime/datetime.h @@ -44,8 +44,11 @@ BEGINinterface(datetime) /* name must also be changed in ENDinterface macro! */ time_t (*GetTime)(time_t *ttSeconds); /* v6, 2011-06-20 */ void (*timeval2syslogTime)(struct timeval *tp, struct syslogTime *t); + /* v7, 2012-03-29 */ + int (*formatTimestampUnix)(struct syslogTime *ts, char*pBuf); + time_t (*syslogTime2time_t)(struct syslogTime *ts); ENDinterface(datetime) -#define datetimeCURR_IF_VERSION 6 /* increment whenever you change the interface structure! */ +#define datetimeCURR_IF_VERSION 7 /* increment whenever you change the interface structure! */ /* interface changes: * 1 - initial version * 2 - not compatible to 1 - bugfix required ParseTIMESTAMP3164 to accept char ** as diff --git a/runtime/debug.c b/runtime/debug.c index d02bd516..955076e2 100644 --- a/runtime/debug.c +++ b/runtime/debug.c @@ -841,12 +841,15 @@ do_dbgprint(uchar *pszObjName, char *pszMsg, size_t lenMsg) static int bWasNL = 0; char pszThrdName[64]; /* 64 is to be on the safe side, anything over 20 is bad... */ char pszWriteBuf[32*1024]; + size_t lenCopy; + size_t offsWriteBuf = 0; size_t lenWriteBuf; struct timespec t; # if _POSIX_TIMERS <= 0 struct timeval tv; # endif +#if 1 /* The bWasNL handler does not really work. It works if no thread * switching occurs during non-NL messages. Else, things are messed * up. Anyhow, it works well enough to provide useful help during @@ -857,8 +860,8 @@ do_dbgprint(uchar *pszObjName, char *pszMsg, size_t lenMsg) */ if(ptLastThrdID != pthread_self()) { if(!bWasNL) { - if(stddbg != -1) write(stddbg, "\n", 1); - if(altdbg != -1) write(altdbg, "\n", 1); + pszWriteBuf[0] = '\n'; + offsWriteBuf = 1; bWasNL = 1; } ptLastThrdID = pthread_self(); @@ -879,25 +882,28 @@ do_dbgprint(uchar *pszObjName, char *pszMsg, size_t lenMsg) t.tv_sec = tv.tv_sec; t.tv_nsec = tv.tv_usec * 1000; # endif - lenWriteBuf = snprintf(pszWriteBuf, sizeof(pszWriteBuf), + lenWriteBuf = snprintf(pszWriteBuf+offsWriteBuf, sizeof(pszWriteBuf) - offsWriteBuf, "%4.4ld.%9.9ld:", (long) (t.tv_sec % 10000), t.tv_nsec); - if(stddbg != -1) write(stddbg, pszWriteBuf, lenWriteBuf); - if(altdbg != -1) write(altdbg, pszWriteBuf, lenWriteBuf); + offsWriteBuf += lenWriteBuf; } - lenWriteBuf = snprintf(pszWriteBuf, sizeof(pszWriteBuf), "%s: ", pszThrdName); - // use for testing: lenWriteBuf = snprintf(pszWriteBuf, sizeof(pszWriteBuf), "{%ld}%s: ", (long) syscall(SYS_gettid), pszThrdName); - if(stddbg != -1) write(stddbg, pszWriteBuf, lenWriteBuf); - if(altdbg != -1) write(altdbg, pszWriteBuf, lenWriteBuf); + lenWriteBuf = snprintf(pszWriteBuf + offsWriteBuf, sizeof(pszWriteBuf) - offsWriteBuf, "%s: ", pszThrdName); + offsWriteBuf += lenWriteBuf; /* print object name header if we have an object */ if(pszObjName != NULL) { - lenWriteBuf = snprintf(pszWriteBuf, sizeof(pszWriteBuf), "%s: ", pszObjName); - if(stddbg != -1) write(stddbg, pszWriteBuf, lenWriteBuf); - if(altdbg != -1) write(altdbg, pszWriteBuf, lenWriteBuf); + lenWriteBuf = snprintf(pszWriteBuf + offsWriteBuf, sizeof(pszWriteBuf) - offsWriteBuf, "%s: ", pszObjName); + offsWriteBuf += lenWriteBuf; } } - if(stddbg != -1) write(stddbg, pszMsg, lenMsg); - if(altdbg != -1) write(altdbg, pszMsg, lenMsg); +#endif + if(lenMsg > sizeof(pszWriteBuf) - offsWriteBuf) + lenCopy = sizeof(pszWriteBuf) - offsWriteBuf; + else + lenCopy = lenMsg; + memcpy(pszWriteBuf + offsWriteBuf, pszMsg, lenCopy); + offsWriteBuf += lenCopy; + if(stddbg != -1) write(stddbg, pszWriteBuf, offsWriteBuf); + if(altdbg != -1) write(altdbg, pszWriteBuf, offsWriteBuf); bWasNL = (pszMsg[lenMsg - 1] == '\n') ? 1 : 0; } @@ -921,12 +927,12 @@ dbgprint(obj_t *pObj, char *pszMsg, size_t lenMsg) pszObjName = obj.GetName(pObj); } - pthread_mutex_lock(&mutdbgprint); - pthread_cleanup_push(dbgMutexCancelCleanupHdlr, &mutdbgprint); +// pthread_mutex_lock(&mutdbgprint); +// pthread_cleanup_push(dbgMutexCancelCleanupHdlr, &mutdbgprint); do_dbgprint(pszObjName, pszMsg, lenMsg); - pthread_cleanup_pop(1); +// pthread_cleanup_pop(1); } #pragma GCC diagnostic warning "-Wempty-body" diff --git a/runtime/dnscache.c b/runtime/dnscache.c new file mode 100644 index 00000000..32d6e425 --- /dev/null +++ b/runtime/dnscache.c @@ -0,0 +1,355 @@ +/* dnscache.c + * Implementation of a real DNS cache + * + * File begun on 2011-06-06 by RGerhards + * The initial implementation is far from being optimal. The idea is to + * first get somethting that'S functionally OK, and then evolve the algorithm. + * In any case, even the initial implementaton is far faster than what we had + * before. -- rgerhards, 2011-06-06 + * + * Copyright 2011 by Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * -or- + * see COPYING.ASL20 in the source distribution + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" + +#include "rsyslog.h" +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <netdb.h> +#include <unistd.h> + +#include "syslogd-types.h" +#include "glbl.h" +#include "errmsg.h" +#include "obj.h" +#include "unicode-helper.h" +#include "net.h" + +/* in this initial implementation, we use a simple, non-optimized at all + * linear list. + */ +/* module data structures */ +struct dnscache_entry_s { + struct sockaddr_storage addr; + uchar *pszHostFQDN; + uchar *ip; + struct dnscache_entry_s *next; + unsigned nUsed; +}; +typedef struct dnscache_entry_s dnscache_entry_t; +struct dnscache_s { + pthread_rwlock_t rwlock; + dnscache_entry_t *root; + unsigned nEntries; +}; +typedef struct dnscache_s dnscache_t; +#define MAX_CACHE_ENTRIES 1000 + + +/* static data */ +DEFobjStaticHelpers +DEFobjCurrIf(glbl) +DEFobjCurrIf(errmsg) +static dnscache_t dnsCache; + + +/* init function (must be called once) */ +rsRetVal +dnscacheInit(void) +{ + DEFiRet; + dnsCache.root = NULL; + dnsCache.nEntries = 0; + pthread_rwlock_init(&dnsCache.rwlock, NULL); + CHKiRet(objGetObjInterface(&obj)); /* this provides the root pointer for all other queries */ + CHKiRet(objUse(glbl, CORE_COMPONENT)); + CHKiRet(objUse(errmsg, CORE_COMPONENT)); +finalize_it: + RETiRet; +} + +/* deinit function (must be called once) */ +rsRetVal +dnscacheDeinit(void) +{ + DEFiRet; + //TODO: free cache elements dnsCache.root = NULL; + pthread_rwlock_destroy(&dnsCache.rwlock); + objRelease(glbl, CORE_COMPONENT); + objRelease(errmsg, CORE_COMPONENT); + RETiRet; +} + + +/* destruct a cache entry. + * Precondition: entry must already be unlinked from list + */ +static inline void +entryDestruct(dnscache_entry_t *etry) +{ + free(etry->pszHostFQDN); + free(etry->ip); + free(etry); +} + + +static inline dnscache_entry_t* +findEntry(struct sockaddr_storage *addr) +{ + dnscache_entry_t *etry; + for(etry = dnsCache.root ; etry != NULL ; etry = etry->next) { + if(SALEN((struct sockaddr*)addr) == SALEN((struct sockaddr*) &etry->addr) + && !memcmp(addr, &etry->addr, SALEN((struct sockaddr*) addr))) + break; /* in this case, we found our entry */ + } + if(etry != NULL) + ++etry->nUsed; /* this is *not* atomic, but we can live with an occasional loss! */ + return etry; +} + + +/* This is a cancel-safe getnameinfo() version, because we learned + * (via drd/valgrind) that getnameinfo() seems to have some issues + * when being cancelled, at least if the module was dlloaded. + * rgerhards, 2008-09-30 + */ +static inline int +mygetnameinfo(const struct sockaddr *sa, socklen_t salen, + char *host, size_t hostlen, + char *serv, size_t servlen, int flags) +{ + int iCancelStateSave; + int i; + + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave); + i = getnameinfo(sa, salen, host, hostlen, serv, servlen, flags); + pthread_setcancelstate(iCancelStateSave, NULL); + return i; +} + + +/* resolve an address. + * + * Please see http://www.hmug.org/man/3/getnameinfo.php (under Caveats) + * for some explanation of the code found below. We do by default not + * discard message where we detected malicouos DNS PTR records. However, + * there is a user-configurabel option that will tell us if + * we should abort. For this, the return value tells the caller if the + * message should be processed (1) or discarded (0). + */ +static rsRetVal +resolveAddr(struct sockaddr_storage *addr, uchar *pszHostFQDN, uchar *ip) +{ + DEFiRet; + int error; + sigset_t omask, nmask; + struct addrinfo hints, *res; + + assert(addr != NULL); + assert(pszHostFQDN != NULL); + + error = mygetnameinfo((struct sockaddr *)addr, SALEN((struct sockaddr *)addr), + (char*) ip, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + if(error) { + dbgprintf("Malformed from address %s\n", gai_strerror(error)); + ABORT_FINALIZE(RS_RET_INVALID_SOURCE); + } + + if(!glbl.GetDisableDNS()) { + sigemptyset(&nmask); + sigaddset(&nmask, SIGHUP); + pthread_sigmask(SIG_BLOCK, &nmask, &omask); + + error = mygetnameinfo((struct sockaddr *)addr, SALEN((struct sockaddr *) addr), + (char*)pszHostFQDN, NI_MAXHOST, NULL, 0, NI_NAMEREQD); + +dbgprintf("dnscache: error %d after 2nd mygetnameinfo\n", error); + if(error == 0) { + memset (&hints, 0, sizeof (struct addrinfo)); + hints.ai_flags = AI_NUMERICHOST; + + /* we now do a lookup once again. This one should fail, + * because we should not have obtained a non-numeric address. If + * we got a numeric one, someone messed with DNS! + */ + if(getaddrinfo ((char*)pszHostFQDN, NULL, &hints, &res) == 0) { + uchar szErrMsg[1024]; + freeaddrinfo (res); + /* OK, we know we have evil. The question now is what to do about + * it. One the one hand, the message might probably be intended + * to harm us. On the other hand, losing the message may also harm us. + * Thus, the behaviour is controlled by the $DropMsgsWithMaliciousDnsPTRRecords + * option. If it tells us we should discard, we do so, else we proceed, + * but log an error message together with it. + * time being, we simply drop the name we obtained and use the IP - that one + * is OK in any way. We do also log the error message. rgerhards, 2007-07-16 + */ + if(glbl.GetDropMalPTRMsgs() == 1) { + snprintf((char*)szErrMsg, sizeof(szErrMsg) / sizeof(uchar), + "Malicious PTR record, message dropped " + "IP = \"%s\" HOST = \"%s\"", + ip, pszHostFQDN); + errmsg.LogError(0, RS_RET_MALICIOUS_ENTITY, "%s", szErrMsg); + pthread_sigmask(SIG_SETMASK, &omask, NULL); + ABORT_FINALIZE(RS_RET_MALICIOUS_ENTITY); + } + + /* Please note: we deal with a malicous entry. Thus, we have crafted + * the snprintf() below so that all text is in front of the entry - maybe + * it contains characters that make the message unreadable + * (OK, I admit this is more or less impossible, but I am paranoid...) + * rgerhards, 2007-07-16 + */ + snprintf((char*)szErrMsg, sizeof(szErrMsg) / sizeof(uchar), + "Malicious PTR record (message accepted, but used IP " + "instead of PTR name: IP = \"%s\" HOST = \"%s\"", + ip, pszHostFQDN); + errmsg.LogError(0, NO_ERRCODE, "%s", szErrMsg); + + error = 1; /* that will trigger using IP address below. */ + } + } + pthread_sigmask(SIG_SETMASK, &omask, NULL); + } + +dbgprintf("dnscache: error %d, DisableDNS %d\n", error, glbl.GetDisableDNS()); + if(error || glbl.GetDisableDNS()) { + dbgprintf("Host name for your address (%s) unknown\n", ip); + strcpy((char*) pszHostFQDN, (char*)ip); + } + +finalize_it: + RETiRet; +} + + +/* evict an entry from the cache. We should try to evict one that does + * not decrease the hit rate that much, but we do not try to hard currently + * (as the base cache data structure may change). + * This MUST NOT be called when the cache is empty! + * rgerhards, 2011-06-06 + */ +static inline void +evictEntry(void) +{ + dnscache_entry_t *prev, *evict, *prevEvict, *etry; + unsigned lowest; + + prev = prevEvict = NULL; + evict = dnsCache.root; + lowest = evict->nUsed; + for(etry = dnsCache.root->next ; etry != NULL ; etry = etry->next) { + if(etry->nUsed < lowest) { + evict = etry; + lowest = etry->nUsed; + prevEvict = prev; + } + prev = etry; + } + + /* found lowest, unlink */ + if(prevEvict == NULL) { /* remove root? */ + dnsCache.root = evict->next; + } else { + prevEvict = evict->next; + } + entryDestruct(evict); +} + + +/* add a new entry to the cache. This means the address is resolved and + * then added to the cache. + */ +static inline rsRetVal +addEntry(struct sockaddr_storage *addr, dnscache_entry_t **pEtry) +{ + uchar pszHostFQDN[NI_MAXHOST]; + uchar ip[80]; /* 80 is safe for larges IPv6 addr */ + dnscache_entry_t *etry; + DEFiRet; + CHKiRet(resolveAddr(addr, pszHostFQDN, ip)); + CHKmalloc(etry = MALLOC(sizeof(dnscache_entry_t))); + CHKmalloc(etry->pszHostFQDN = ustrdup(pszHostFQDN)); + CHKmalloc(etry->ip = ustrdup(ip)); + memcpy(&etry->addr, addr, SALEN((struct sockaddr*) addr)); + etry->nUsed = 0; + *pEtry = etry; + + /* add to list. Currently, we place the new element always at + * the root node. This needs to be optimized later. 2011-06-06 + */ + pthread_rwlock_unlock(&dnsCache.rwlock); /* release read lock */ + pthread_rwlock_wrlock(&dnsCache.rwlock); /* and re-aquire for writing */ + if(dnsCache.nEntries >= MAX_CACHE_ENTRIES) { + evictEntry(); + } + etry->next = dnsCache.root; + dnsCache.root = etry; + pthread_rwlock_unlock(&dnsCache.rwlock); + pthread_rwlock_rdlock(&dnsCache.rwlock); /* TODO: optimize this! */ + +finalize_it: + RETiRet; +} + + +/* validate if an entry is still valid and, if not, re-query it. + * In the initial implementation, this is a dummy! + * TODO: implement! + */ +static inline rsRetVal +validateEntry(dnscache_entry_t *etry, struct sockaddr_storage *addr) +{ + return RS_RET_OK; +} + + +/* This is the main function: it looks up an entry and returns it's name + * and IP address. If the entry is not yet inside the cache, it is added. + * If the entry can not be resolved, an error is reported back. + */ +rsRetVal +dnscacheLookup(struct sockaddr_storage *addr, uchar *pszHostFQDN, uchar *ip) +{ + dnscache_entry_t *etry; + DEFiRet; + + pthread_rwlock_rdlock(&dnsCache.rwlock); /* TODO: optimize this! */ + etry = findEntry(addr); + dbgprintf("dnscache: entry %p found\n", etry); + if(etry == NULL) { + CHKiRet(addEntry(addr, &etry)); + } else { + CHKiRet(validateEntry(etry, addr)); + } + // TODO/QUESTION: can we get rid of the strcpy? +dbgprintf("XXXX: hostn '%s', ip '%s'\n", etry->pszHostFQDN, etry->ip); + strcpy((char*)pszHostFQDN, (char*)etry->pszHostFQDN); + strcpy((char*)ip, (char*)etry->ip); + +finalize_it: + pthread_rwlock_unlock(&dnsCache.rwlock); +dbgprintf("XXXX: dnscacheLookup finished, iRet=%d\n", iRet); + if(iRet != RS_RET_OK && iRet != RS_RET_ADDRESS_UNKNOWN) { + DBGPRINTF("dnscacheLookup failed with iRet %d\n", iRet); + strcpy((char*) pszHostFQDN, "???"); + strcpy((char*) ip, "???"); + } + RETiRet; +} diff --git a/runtime/dnscache.h b/runtime/dnscache.h new file mode 100644 index 00000000..69f038ee --- /dev/null +++ b/runtime/dnscache.h @@ -0,0 +1,29 @@ +/* Definitions for dnscache module. + * + * Copyright 2011-2012 Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * -or- + * see COPYING.ASL20 in the source distribution + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef INCLUDED_DNSCACHE_H +#define INCLUDED_DNSCACHE_H + +rsRetVal dnscacheInit(void); +rsRetVal dnscacheDeinit(void); +rsRetVal dnscacheLookup(struct sockaddr_storage *addr, uchar *pszHostFQDN, uchar *ip); + +#endif /* #ifndef INCLUDED_DNSCACHE_H */ diff --git a/runtime/errmsg.c b/runtime/errmsg.c index e3555c1f..dcb5b185 100644 --- a/runtime/errmsg.c +++ b/runtime/errmsg.c @@ -36,7 +36,6 @@ #include "rsyslog.h" #include "obj.h" #include "errmsg.h" -#include "sysvar.h" #include "srUtils.h" #include "stringbuf.h" diff --git a/runtime/expr.c b/runtime/expr.c deleted file mode 100644 index 6d376ad3..00000000 --- a/runtime/expr.c +++ /dev/null @@ -1,475 +0,0 @@ -/* expr.c - an expression class. - * This module contains all code needed to represent expressions. Most - * importantly, that means code to parse and execute them. Expressions - * heavily depend on (loadable) functions, so it works in conjunction - * with the function manager. - * - * Module begun 2007-11-30 by Rainer Gerhards - * - * Copyright 2007-2012 Adiscon GmbH. - * - * This file is part of the rsyslog runtime library. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * -or- - * see COPYING.ASL20 in the source distribution - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "config.h" -#include <stdlib.h> -#include <assert.h> - -#include "rsyslog.h" -#include "template.h" -#include "expr.h" - -/* static data */ -DEFobjStaticHelpers -DEFobjCurrIf(vmprg) -DEFobjCurrIf(var) -DEFobjCurrIf(ctok_token) -DEFobjCurrIf(ctok) - - -/* ------------------------------ parser functions ------------------------------ */ -/* the following functions implement the parser. They are all static. For - * simplicity, the function names match their ABNF definition. The ABNF is defined - * in the doc set. See file expression.html for details. I do *not* reproduce it - * here in an effort to keep both files in sync. - * - * All functions receive the current expression object as parameter as well as the - * current tokenizer. - * - * rgerhards, 2008-02-19 - */ - -/* forward definition - thanks to recursive ABNF, we can not avoid at least one ;) */ -static rsRetVal expr(expr_t *pThis, ctok_t *tok); - - -static rsRetVal -function(expr_t *pThis, ctok_t *tok) -{ - DEFiRet; - ctok_token_t *pToken = NULL; - int iNumArgs = 0; - var_t *pVar; - - ISOBJ_TYPE_assert(pThis, expr); - ISOBJ_TYPE_assert(tok, ctok); - - CHKiRet(ctok.GetToken(tok, &pToken)); - /* note: pToken is destructed in finalize_it */ - - if(pToken->tok == ctok_LPAREN) { - CHKiRet(ctok_token.Destruct(&pToken)); /* token processed, "eat" it */ - CHKiRet(ctok.GetToken(tok, &pToken)); /* get next one */ - } else - ABORT_FINALIZE(RS_RET_FUNC_NO_LPAREN); - - /* we first push all the params on the stack. Then we call the function */ - while(pToken->tok != ctok_RPAREN) { - ++iNumArgs; - CHKiRet(ctok.UngetToken(tok, pToken)); /* not for us, so let others process it */ - CHKiRet(expr(pThis, tok)); - CHKiRet(ctok.GetToken(tok, &pToken)); /* get next one, needed for while() check */ - if(pToken->tok == ctok_COMMA) { - CHKiRet(ctok_token.Destruct(&pToken)); /* token processed, "eat" it */ - CHKiRet(ctok.GetToken(tok, &pToken)); /* get next one */ - if(pToken->tok == ctok_RPAREN) { - ABORT_FINALIZE(RS_RET_FUNC_MISSING_EXPR); - } - } - } - - - /* now push number of arguments - this must be on top of the stack */ - CHKiRet(var.Construct(&pVar)); - CHKiRet(var.ConstructFinalize(pVar)); - CHKiRet(var.SetNumber(pVar, iNumArgs)); - CHKiRet(vmprg.AddVarOperation(pThis->pVmprg, opcode_PUSHCONSTANT, pVar)); /* add to program */ - - -finalize_it: - if(pToken != NULL) { - ctok_token.Destruct(&pToken); /* "eat" processed token */ - } - - RETiRet; -} - - -static rsRetVal -terminal(expr_t *pThis, ctok_t *tok) -{ - DEFiRet; - ctok_token_t *pToken = NULL; - var_t *pVar; - - ISOBJ_TYPE_assert(pThis, expr); - ISOBJ_TYPE_assert(tok, ctok); - - CHKiRet(ctok.GetToken(tok, &pToken)); - /* note: pToken is destructed in finalize_it */ - - switch(pToken->tok) { - case ctok_SIMPSTR: - dbgoprint((obj_t*) pThis, "simpstr\n"); - CHKiRet(ctok_token.UnlinkVar(pToken, &pVar)); - CHKiRet(vmprg.AddVarOperation(pThis->pVmprg, opcode_PUSHCONSTANT, pVar)); /* add to program */ - break; - case ctok_NUMBER: - dbgoprint((obj_t*) pThis, "number\n"); - CHKiRet(ctok_token.UnlinkVar(pToken, &pVar)); - CHKiRet(vmprg.AddVarOperation(pThis->pVmprg, opcode_PUSHCONSTANT, pVar)); /* add to program */ - break; - case ctok_FUNCTION: - dbgoprint((obj_t*) pThis, "function\n"); - CHKiRet(function(pThis, tok)); /* this creates the stack call frame */ - /* ... but we place the call instruction onto the stack ourselfs (because - * we have all relevant information) - */ - CHKiRet(ctok_token.UnlinkVar(pToken, &pVar)); - CHKiRet(var.ConvToString(pVar)); /* make sure we have a string */ - CHKiRet(vmprg.AddCallOperation(pThis->pVmprg, pVar->val.pStr)); /* add to program */ - CHKiRet(var.Destruct(&pVar)); - break; - case ctok_MSGVAR: - dbgoprint((obj_t*) pThis, "MSGVAR\n"); - CHKiRet(ctok_token.UnlinkVar(pToken, &pVar)); - CHKiRet(vmprg.AddVarOperation(pThis->pVmprg, opcode_PUSHMSGVAR, pVar)); /* add to program */ - break; - case ctok_SYSVAR: - dbgoprint((obj_t*) pThis, "SYSVAR\n"); - CHKiRet(ctok_token.UnlinkVar(pToken, &pVar)); - CHKiRet(vmprg.AddVarOperation(pThis->pVmprg, opcode_PUSHSYSVAR, pVar)); /* add to program */ - break; - case ctok_LPAREN: - dbgoprint((obj_t*) pThis, "expr\n"); - CHKiRet(ctok_token.Destruct(&pToken)); /* "eat" processed token */ - CHKiRet(expr(pThis, tok)); - CHKiRet(ctok.GetToken(tok, &pToken)); /* get next one */ - if(pToken->tok != ctok_RPAREN) - ABORT_FINALIZE(RS_RET_SYNTAX_ERROR); - break; - default: - dbgoprint((obj_t*) pThis, "invalid token %d\n", pToken->tok); - ABORT_FINALIZE(RS_RET_SYNTAX_ERROR); - break; - } - -finalize_it: - if(pToken != NULL) { - ctok_token.Destruct(&pToken); /* "eat" processed token */ - } - - RETiRet; -} - -static rsRetVal -factor(expr_t *pThis, ctok_t *tok) -{ - DEFiRet; - ctok_token_t *pToken; - int bWasNot; - int bWasUnaryMinus; - - ISOBJ_TYPE_assert(pThis, expr); - ISOBJ_TYPE_assert(tok, ctok); - - CHKiRet(ctok.GetToken(tok, &pToken)); - if(pToken->tok == ctok_NOT) { - dbgprintf("not\n"); - bWasNot = 1; - CHKiRet(ctok_token.Destruct(&pToken)); /* no longer needed */ - CHKiRet(ctok.GetToken(tok, &pToken)); /* get new one for next check */ - } else { - bWasNot = 0; - } - - if(pToken->tok == ctok_MINUS) { - dbgprintf("unary minus\n"); - bWasUnaryMinus = 1; - CHKiRet(ctok_token.Destruct(&pToken)); /* no longer needed */ - } else { - bWasUnaryMinus = 0; - /* we could not process the token, so push it back */ - CHKiRet(ctok.UngetToken(tok, pToken)); - } - - CHKiRet(terminal(pThis, tok)); - - /* warning: the order if the two following ifs is important. Do not change them, this - * would change the semantics of the expression! - */ - if(bWasUnaryMinus) { - CHKiRet(vmprg.AddVarOperation(pThis->pVmprg, opcode_UNARY_MINUS, NULL)); /* add to program */ - } - - if(bWasNot == 1) { - CHKiRet(vmprg.AddVarOperation(pThis->pVmprg, opcode_NOT, NULL)); /* add to program */ - } - -finalize_it: - RETiRet; -} - - -static rsRetVal -term(expr_t *pThis, ctok_t *tok) -{ - DEFiRet; - ctok_token_t *pToken; - - ISOBJ_TYPE_assert(pThis, expr); - ISOBJ_TYPE_assert(tok, ctok); - - CHKiRet(factor(pThis, tok)); - - /* *(("*" / "/" / "%") factor) part */ - CHKiRet(ctok.GetToken(tok, &pToken)); - while(pToken->tok == ctok_TIMES || pToken->tok == ctok_DIV || pToken->tok == ctok_MOD) { - dbgoprint((obj_t*) pThis, "/,*,%%\n"); - CHKiRet(factor(pThis, tok)); - CHKiRet(vmprg.AddVarOperation(pThis->pVmprg, (opcode_t) pToken->tok, NULL)); /* add to program */ - CHKiRet(ctok_token.Destruct(&pToken)); /* no longer needed */ - CHKiRet(ctok.GetToken(tok, &pToken)); - } - - /* unget the token that made us exit the loop - it's obviously not one - * we can process. - */ - CHKiRet(ctok.UngetToken(tok, pToken)); - -finalize_it: - RETiRet; -} - -static rsRetVal -val(expr_t *pThis, ctok_t *tok) -{ - DEFiRet; - ctok_token_t *pToken; - - ISOBJ_TYPE_assert(pThis, expr); - ISOBJ_TYPE_assert(tok, ctok); - - CHKiRet(term(pThis, tok)); - - /* *(("+" / "-") term) part */ - CHKiRet(ctok.GetToken(tok, &pToken)); - while(pToken->tok == ctok_PLUS || pToken->tok == ctok_MINUS || pToken->tok == ctok_STRADD) { - dbgoprint((obj_t*) pThis, "+/-/&\n"); - CHKiRet(term(pThis, tok)); - CHKiRet(vmprg.AddVarOperation(pThis->pVmprg, (opcode_t) pToken->tok, NULL)); /* add to program */ - CHKiRet(ctok_token.Destruct(&pToken)); /* no longer needed */ - CHKiRet(ctok.GetToken(tok, &pToken)); - } - - /* unget the token that made us exit the loop - it's obviously not one - * we can process. - */ - CHKiRet(ctok.UngetToken(tok, pToken)); - -finalize_it: - RETiRet; -} - - -static rsRetVal -e_cmp(expr_t *pThis, ctok_t *tok) -{ - DEFiRet; - ctok_token_t *pToken; - - ISOBJ_TYPE_assert(pThis, expr); - ISOBJ_TYPE_assert(tok, ctok); - - CHKiRet(val(pThis, tok)); - - /* 0*1(cmp_op val) part */ - CHKiRet(ctok.GetToken(tok, &pToken)); - if(ctok_token.IsCmpOp(pToken)) { - dbgoprint((obj_t*) pThis, "cmp\n"); - CHKiRet(val(pThis, tok)); - CHKiRet(vmprg.AddVarOperation(pThis->pVmprg, (opcode_t) pToken->tok, NULL)); /* add to program */ - CHKiRet(ctok_token.Destruct(&pToken)); /* no longer needed */ - } else { - /* we could not process the token, so push it back */ - CHKiRet(ctok.UngetToken(tok, pToken)); - } - - -finalize_it: - RETiRet; -} - - -static rsRetVal -e_and(expr_t *pThis, ctok_t *tok) -{ - DEFiRet; - ctok_token_t *pToken; - - ISOBJ_TYPE_assert(pThis, expr); - ISOBJ_TYPE_assert(tok, ctok); - - CHKiRet(e_cmp(pThis, tok)); - - /* *("and" e_cmp) part */ - CHKiRet(ctok.GetToken(tok, &pToken)); - while(pToken->tok == ctok_AND) { - dbgoprint((obj_t*) pThis, "and\n"); - CHKiRet(e_cmp(pThis, tok)); - CHKiRet(vmprg.AddVarOperation(pThis->pVmprg, opcode_AND, NULL)); /* add to program */ - CHKiRet(ctok_token.Destruct(&pToken)); /* no longer needed */ - CHKiRet(ctok.GetToken(tok, &pToken)); - } - - /* unget the token that made us exit the loop - it's obviously not one - * we can process. - */ - CHKiRet(ctok.UngetToken(tok, pToken)); - -finalize_it: - RETiRet; -} - - -static rsRetVal -expr(expr_t *pThis, ctok_t *tok) -{ - DEFiRet; - ctok_token_t *pToken; - - ISOBJ_TYPE_assert(pThis, expr); - ISOBJ_TYPE_assert(tok, ctok); - - CHKiRet(e_and(pThis, tok)); - - /* *("or" e_and) part */ - CHKiRet(ctok.GetToken(tok, &pToken)); - while(pToken->tok == ctok_OR) { - dbgoprint((obj_t*) pThis, "found OR\n"); - CHKiRet(e_and(pThis, tok)); - CHKiRet(vmprg.AddVarOperation(pThis->pVmprg, opcode_OR, NULL)); /* add to program */ - CHKiRet(ctok_token.Destruct(&pToken)); /* no longer needed */ - CHKiRet(ctok.GetToken(tok, &pToken)); - } - - /* unget the token that made us exit the loop - it's obviously not one - * we can process. - */ - CHKiRet(ctok.UngetToken(tok, pToken)); - -finalize_it: - RETiRet; -} - - -/* ------------------------------ end parser functions ------------------------------ */ - - -/* ------------------------------ actual expr object functions ------------------------------ */ - -/* Standard-Constructor - * rgerhards, 2008-02-09 (a rainy Tenerife return flight day ;)) - */ -BEGINobjConstruct(expr) /* be sure to specify the object type also in END macro! */ -ENDobjConstruct(expr) - - -/* ConstructionFinalizer - * rgerhards, 2008-01-09 - */ -rsRetVal exprConstructFinalize(expr_t __attribute__((unused)) *pThis) -{ - DEFiRet; - - ISOBJ_TYPE_assert(pThis, expr); - - RETiRet; -} - - -/* destructor for the expr object */ -BEGINobjDestruct(expr) /* be sure to specify the object type also in END and CODESTART macros! */ -CODESTARTobjDestruct(expr) - if(pThis->pVmprg != NULL) - vmprg.Destruct(&pThis->pVmprg); -ENDobjDestruct(expr) - - -/* parse an expression object based on a given tokenizer - * rgerhards, 2008-02-19 - */ -rsRetVal -exprParse(expr_t *pThis, ctok_t *tok) -{ - DEFiRet; - - ISOBJ_TYPE_assert(pThis, expr); - ISOBJ_TYPE_assert(tok, ctok); - - /* first, we need to make sure we have a program where we can add to what we parse... */ - CHKiRet(vmprg.Construct(&pThis->pVmprg)); - CHKiRet(vmprg.ConstructFinalize(pThis->pVmprg)); - - /* happy parsing... */ - CHKiRet(expr(pThis, tok)); - dbgoprint((obj_t*) pThis, "successfully parsed/created expression\n"); - -finalize_it: - RETiRet; -} - - -/* queryInterface function - * rgerhards, 2008-02-21 - */ -BEGINobjQueryInterface(expr) -CODESTARTobjQueryInterface(expr) - if(pIf->ifVersion != exprCURR_IF_VERSION) { /* check for current version, increment on each change */ - ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); - } - - /* ok, we have the right interface, so let's fill it - * Please note that we may also do some backwards-compatibility - * work here (if we can support an older interface version - that, - * of course, also affects the "if" above). - */ - pIf->Construct = exprConstruct; - pIf->ConstructFinalize = exprConstructFinalize; - pIf->Destruct = exprDestruct; - pIf->Parse = exprParse; -finalize_it: -ENDobjQueryInterface(expr) - - -/* Initialize the expr class. Must be called as the very first method - * before anything else is called inside this class. - * rgerhards, 2008-02-19 - */ -BEGINObjClassInit(expr, 1, OBJ_IS_CORE_MODULE) /* class, version */ - /* request objects we use */ - CHKiRet(objUse(var, CORE_COMPONENT)); - CHKiRet(objUse(vmprg, CORE_COMPONENT)); - CHKiRet(objUse(var, CORE_COMPONENT)); - CHKiRet(objUse(ctok_token, CORE_COMPONENT)); - CHKiRet(objUse(ctok, CORE_COMPONENT)); - - OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, exprConstructFinalize); -ENDObjClassInit(expr) - -/* vi:set ai: - */ diff --git a/runtime/expr.h b/runtime/expr.h deleted file mode 100644 index eaccb67e..00000000 --- a/runtime/expr.h +++ /dev/null @@ -1,55 +0,0 @@ -/* The expr object. - * - * Copyright 2008-2012 Rainer Gerhards and Adiscon GmbH. - * - * This file is part of the rsyslog runtime library. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * -or- - * see COPYING.ASL20 in the source distribution - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef INCLUDED_EXPR_H -#define INCLUDED_EXPR_H - -#include "obj.h" -#include "ctok.h" -#include "vmprg.h" -#include "stringbuf.h" - -/* a node inside an expression tree */ -typedef struct exprNode_s { - char dummy; -} exprNode_t; - - -/* the expression object */ -typedef struct expr_s { - BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ - vmprg_t *pVmprg; /* the expression in vmprg format - ready to execute */ -} expr_t; - - -/* interfaces */ -BEGINinterface(expr) /* name must also be changed in ENDinterface macro! */ - INTERFACEObjDebugPrint(expr); - rsRetVal (*Construct)(expr_t **ppThis); - rsRetVal (*ConstructFinalize)(expr_t __attribute__((unused)) *pThis); - rsRetVal (*Destruct)(expr_t **ppThis); - rsRetVal (*Parse)(expr_t *pThis, ctok_t *ctok); -ENDinterface(expr) -#define exprCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ - -/* prototypes */ -PROTOTYPEObj(expr); - -#endif /* #ifndef INCLUDED_EXPR_H */ diff --git a/runtime/glbl.c b/runtime/glbl.c index ff4358c4..537b7b4f 100644 --- a/runtime/glbl.c +++ b/runtime/glbl.c @@ -7,7 +7,7 @@ * * Module begun 2008-04-16 by Rainer Gerhards * - * Copyright 2008-2012 Rainer Gerhards and Adiscon GmbH. + * Copyright 2008-2011 Rainer Gerhards and Adiscon GmbH. * * This file is part of the rsyslog runtime library. * @@ -42,6 +42,7 @@ #include "prop.h" #include "atomic.h" #include "errmsg.h" +#include "rainerscript.h" #include "net.h" /* some defaults */ @@ -63,7 +64,7 @@ static uchar *pszWorkDir = NULL; static int bOptimizeUniProc = 1; /* enable uniprocessor optimizations */ static int bParseHOSTNAMEandTAG = 1; /* parser modification (based on startup params!) */ static int bPreserveFQDN = 0; /* should FQDNs always be preserved? */ -static int iMaxLine = 2048; /* maximum length of a syslog message */ +static int iMaxLine = 8096; /* maximum length of a syslog message */ static int iDefPFFamily = PF_UNSPEC; /* protocol family (IPv4, IPv6 or both) */ static int bDropMalPTRMsgs = 0;/* Drop messages which have malicious PTR records during DNS lookup */ static int option_DisallowWarning = 1; /* complain if message from disallowed sender is received */ @@ -89,6 +90,29 @@ static int iFdSetSize = howmany(FD_SETSIZE, __NFDBITS) * sizeof (fd_mask); /* si #endif +/* tables for interfacing with the v6 config system */ +static struct cnfparamdescr cnfparamdescr[] = { + { "workdirectory", eCmdHdlrString, 0 }, + { "dropmsgswithmaliciousdnsptrrecords", eCmdHdlrBinary, 0 }, + { "localhostname", eCmdHdlrGetWord, 0 }, + { "preservefqdn", eCmdHdlrBinary, 0 }, + { "defaultnetstreamdrivercafile", eCmdHdlrString, 0 }, + { "defaultnetstreamdriverkeyfile", eCmdHdlrString, 0 }, + { "defaultnetstreamdriver", eCmdHdlrString, 0 }, + { "maxmessagesize", eCmdHdlrSize, 0 }, +}; +static struct cnfparamblk paramblk = + { CNFPARAMBLK_VERSION, + sizeof(cnfparamdescr)/sizeof(struct cnfparamdescr), + cnfparamdescr + }; + +static struct cnfparamvals *cnfparamvals = NULL; +/* we need to support multiple calls into our param block, so we need + * to persist the current settings. Note that this must be re-set + * each time a new config load begins (TODO: create interface?) + */ + /* define a macro for the simple properties' set and get functions * (which are always the same). This is only suitable for pretty * simple cases which require neither checks nor memory allocation. @@ -141,7 +165,7 @@ static int GetGlobalInputTermState(void) } -/* set global termiantion state to "terminate". Note that this is a +/* set global termination state to "terminate". Note that this is a * "once in a lifetime" action which can not be undone. -- gerhards, 2009-07-20 */ static void SetGlobalInputTermination(void) @@ -499,6 +523,7 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a bDropMalPTRMsgs = 0; bOptimizeUniProc = 1; bPreserveFQDN = 0; + iMaxLine = 8192; #ifdef USE_UNLIMITED_SELECT iFdSetSize = howmany(FD_SETSIZE, __NFDBITS) * sizeof (fd_mask); #endif @@ -506,6 +531,79 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a } +/* Prepare for new config + */ +void +glblPrepCnf(void) +{ + free(cnfparamvals); + cnfparamvals = NULL; +} + +/* handle a global config object. Note that multiple global config statements + * are permitted (because of plugin support), so once we got a param block, + * we need to hold to it. + * rgerhards, 2011-07-19 + */ +void +glblProcessCnf(struct cnfobj *o) +{ + cnfparamvals = nvlstGetParams(o->nvlst, ¶mblk, cnfparamvals); + dbgprintf("glbl param blk after glblProcessCnf:\n"); + cnfparamsPrint(¶mblk, cnfparamvals); +} + +rsRetVal +glblCheckCnf() +{ +} + +void +glblDoneLoadCnf(void) +{ + int i; + unsigned char *cstr; + + if(cnfparamvals == NULL) + goto finalize_it; + + for(i = 0 ; i < paramblk.nParams ; ++i) { + if(!cnfparamvals[i].bUsed) + continue; + if(!strcmp(paramblk.descr[i].name, "workdirectory")) { + cstr = (uchar*) es_str2cstr(cnfparamvals[i].val.d.estr, NULL); + setWorkDir(NULL, cstr); + } else if(!strcmp(paramblk.descr[i].name, "localhostname")) { + free(LocalHostNameOverride); + LocalHostNameOverride = (uchar*) + es_str2cstr(cnfparamvals[i].val.d.estr, NULL); + } else if(!strcmp(paramblk.descr[i].name, "defaultnetstreamdriverkeyfile")) { + free(pszDfltNetstrmDrvrKeyFile); + pszDfltNetstrmDrvrKeyFile = (uchar*) + es_str2cstr(cnfparamvals[i].val.d.estr, NULL); + } else if(!strcmp(paramblk.descr[i].name, "defaultnetstreamdrivercafile")) { + free(pszDfltNetstrmDrvrCAF); + pszDfltNetstrmDrvrCAF = (uchar*) + es_str2cstr(cnfparamvals[i].val.d.estr, NULL); + } else if(!strcmp(paramblk.descr[i].name, "defaultnetstreamdriver")) { + free(pszDfltNetstrmDrvr); + pszDfltNetstrmDrvr = (uchar*) + es_str2cstr(cnfparamvals[i].val.d.estr, NULL); + } else if(!strcmp(paramblk.descr[i].name, "preservefqdn")) { + bPreserveFQDN = (int) cnfparamvals[i].val.d.n; + } else if(!strcmp(paramblk.descr[i].name, + "dropmsgswithmaliciousdnsptrrecords")) { + bDropMalPTRMsgs = (int) cnfparamvals[i].val.d.n; + } else if(!strcmp(paramblk.descr[i].name, "maxmessagesize")) { + iMaxLine = (int) cnfparamvals[i].val.d.n; + } else { + dbgprintf("glblDoneLoadCnf: program error, non-handled " + "param '%s'\n", paramblk.descr[i].name); + } + } +finalize_it: ; +} + /* Initialize the glbl class. Must be called as the very first method * before anything else is called inside this class. @@ -527,6 +625,8 @@ BEGINAbstractObjClassInit(glbl, 1, OBJ_IS_CORE_MODULE) /* class, version */ CHKiRet(regCfSysLineHdlr((uchar *)"localhostipif", 0, eCmdHdlrGetWord, setLocalHostIPIF, NULL, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"optimizeforuniprocessor", 0, eCmdHdlrBinary, NULL, &bOptimizeUniProc, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"preservefqdn", 0, eCmdHdlrBinary, NULL, &bPreserveFQDN, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"maxmessagesize", 0, eCmdHdlrSize, + NULL, &iMaxLine, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, NULL)); INIT_ATOMIC_HELPER_MUT(mutTerminateInputs); @@ -550,5 +650,7 @@ BEGINObjClassExit(glbl, OBJ_IS_CORE_MODULE) /* class, version */ DESTROY_ATOMIC_HELPER_MUT(mutTerminateInputs); ENDObjClassExit(glbl) +void glblProcessCnf(struct cnfobj *o); + /* vi:set ai: */ diff --git a/runtime/glbl.h b/runtime/glbl.h index 3c5e8501..d2d1e66a 100644 --- a/runtime/glbl.h +++ b/runtime/glbl.h @@ -30,6 +30,7 @@ #ifndef GLBL_H_INCLUDED #define GLBL_H_INCLUDED +#include "rainerscript.h" #include "prop.h" #define glblGetIOBufSize() 4096 /* size of the IO buffer, e.g. for strm class */ @@ -74,7 +75,7 @@ BEGINinterface(glbl) /* name must also be changed in ENDinterface macro! */ */ SIMP_PROP(FdSetSize, int) /* v7: was neeeded to mean v5+v6 - do NOT add anything else for that version! */ - /* next change is v8! */ + /* next change is v9! */ /* v8 - 2012-03-21 */ prop_t* (*GetLocalHostIP)(void); #undef SIMP_PROP @@ -85,4 +86,8 @@ ENDinterface(glbl) /* the remaining prototypes */ PROTOTYPEObj(glbl); +void glblPrepCnf(void); +void glblProcessCnf(struct cnfobj *o); +void glblDoneLoadCnf(void); + #endif /* #ifndef GLBL_H_INCLUDED */ diff --git a/runtime/im-helper.h b/runtime/im-helper.h new file mode 100644 index 00000000..6bbd6d70 --- /dev/null +++ b/runtime/im-helper.h @@ -0,0 +1,66 @@ +/* im-helper.h + * This file contains helper constructs that save time writing input modules. It + * assumes some common field names and plumbing. It is intended to be used together + * with module-template.h + * + * File begun on 2011-05-04 by RGerhards + * + * Copyright 2011 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 <http://www.gnu.org/licenses/>. + * + * 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 IM_HELPER_H_INCLUDED +#define IM_HELPER_H_INCLUDED 1 + + +/* The following function provides a complete implementation to check a + * ruleset and set the actual ruleset pointer. The macro assumes that + * standard field names are used. A functon std_checkRuleset_genErrMsg() + * must be defined to generate error messages in case the ruleset cannot + * be found. + */ +static inline void std_checkRuleset_genErrMsg(modConfData_t *modConf, instanceConf_t *inst); +static inline rsRetVal +std_checkRuleset(modConfData_t *modConf, instanceConf_t *inst) +{ + ruleset_t *pRuleset; + rsRetVal localRet; + DEFiRet; + + inst->pBindRuleset = NULL; /* assume default ruleset */ + + if(inst->pszBindRuleset == NULL) + FINALIZE; +dbgprintf("ZZZZZ: inst->pszBindRuleset %s\n", inst->pszBindRuleset); + + localRet = ruleset.GetRuleset(modConf->pConf, &pRuleset, inst->pszBindRuleset); + if(localRet == RS_RET_NOT_FOUND) { + std_checkRuleset_genErrMsg(modConf, inst); + } + CHKiRet(localRet); + inst->pBindRuleset = pRuleset; + +finalize_it: + RETiRet; +} + +#endif /* #ifndef IM_HELPER_H_INCLUDED */ + +/* vim:set ai: + */ diff --git a/runtime/module-template.h b/runtime/module-template.h index 63bf9a10..75bf7312 100644 --- a/runtime/module-template.h +++ b/runtime/module-template.h @@ -35,7 +35,7 @@ /* macro to define standard output-module static data members */ #define DEF_MOD_STATIC_DATA \ - static __attribute__((unused)) rsRetVal (*omsdRegCFSLineHdlr)(); + static __attribute__((unused)) rsRetVal (*omsdRegCFSLineHdlr)(uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData, void *pOwnerCookie); #define DEF_OMOD_STATIC_DATA \ DEF_MOD_STATIC_DATA \ @@ -110,6 +110,16 @@ static rsRetVal modGetID(void **pID) \ return RS_RET_OK;\ } +/* macro to provide the v6 config system module name + */ +#define MODULE_CNFNAME(name) \ +static __attribute__((unused)) rsRetVal modGetCnfName(uchar **cnfName) \ + { \ + *cnfName = (uchar*) name; \ + return RS_RET_OK;\ + } + + /* to following macros are used to generate function headers and standard * functionality. It works as follows (described on the sample case of * createInstance()): @@ -294,6 +304,55 @@ finalize_it:\ } +/* newActInst() + * Extra comments: + * This creates a new instance of a the action that implements the call. + * This is part of the conf2 (rsyslog v6) config system. It is called by + * the core when an action object has been obtained. The output module + * must then verify parameters and create a new action instance (if + * parameters are acceptable) or return an error code. + * On exit, ppModData must point to instance data. Also, a string + * request object must be created and filled. A macro is defined + * for that. + * For the most usual case, we have defined a macro below. + * If more than one string is requested, the macro can be used together + * with own code that overwrites the entry count. In this case, the + * macro must come before the own code. It is recommended to be + * placed right after CODESTARTnewActInst. + */ +#define BEGINnewActInst \ +static rsRetVal newActInst(uchar __attribute__((unused)) *modName, \ + struct nvlst *lst, void **ppModData, omodStringRequest_t **ppOMSR)\ +{\ + DEFiRet;\ + instanceData *pData = NULL; \ + *ppOMSR = NULL; + +#define CODESTARTnewActInst \ + +#define CODE_STD_STRING_REQUESTnewActInst(NumStrReqEntries) \ + CHKiRet(OMSRconstruct(ppOMSR, NumStrReqEntries)); + +#define CODE_STD_FINALIZERnewActInst \ +finalize_it:\ + if(iRet == RS_RET_OK || iRet == RS_RET_SUSPENDED) {\ + *ppModData = pData;\ + } else {\ + /* cleanup, we failed */\ + if(*ppOMSR != NULL) {\ + OMSRdestruct(*ppOMSR);\ + *ppOMSR = NULL;\ + }\ + if(pData != NULL) {\ + freeInstance(pData);\ + } \ + } + +#define ENDnewActInst \ + RETiRet;\ +} + + /* tryResume() * This entry point is called to check if a module can resume operations. This * happens when a module requested that it be suspended. In suspended state, @@ -316,6 +375,19 @@ static rsRetVal tryResume(instanceData __attribute__((unused)) *pData)\ } +/* initConfVars() - initialize pre-v6.3-config variables + */ +#define BEGINinitConfVars \ +static rsRetVal initConfVars(void)\ +{\ + DEFiRet; + +#define CODESTARTinitConfVars + +#define ENDinitConfVars \ + RETiRet;\ +} + /* queryEtryPt() */ @@ -413,6 +485,52 @@ static rsRetVal queryEtryPt(uchar *name, rsRetVal (**pEtryPoint)())\ *pEtryPoint = afterRun;\ } + +/* the following block is to be added for modules that support the v2 + * config system. The config name is also provided. + */ +#define CODEqueryEtryPt_STD_CONF2_QUERIES \ + else if(!strcmp((char*) name, "beginCnfLoad")) {\ + *pEtryPoint = beginCnfLoad;\ + } else if(!strcmp((char*) name, "endCnfLoad")) {\ + *pEtryPoint = endCnfLoad;\ + } else if(!strcmp((char*) name, "checkCnf")) {\ + *pEtryPoint = checkCnf;\ + } else if(!strcmp((char*) name, "activateCnf")) {\ + *pEtryPoint = activateCnf;\ + } else if(!strcmp((char*) name, "freeCnf")) {\ + *pEtryPoint = freeCnf;\ + } \ + CODEqueryEtryPt_STD_CONF2_CNFNAME_QUERIES + +/* the following block is to be added for output modules that support the v2 + * config system. The config name is also provided. + */ +#define CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES \ + else if(!strcmp((char*) name, "newActInst")) {\ + *pEtryPoint = newActInst;\ + } \ + CODEqueryEtryPt_STD_CONF2_CNFNAME_QUERIES + + +/* the following block is to be added for modules that require + * pre priv drop activation support. + */ +#define CODEqueryEtryPt_STD_CONF2_PREPRIVDROP_QUERIES \ + else if(!strcmp((char*) name, "activateCnfPrePrivDrop")) {\ + *pEtryPoint = activateCnfPrePrivDrop;\ + } + +/* the following block is to be added for modules that support + * their config name. This is required for the rsyslog v6 config + * system, especially for outout modules which do not require + * the new set of begin/end config settings. + */ +#define CODEqueryEtryPt_STD_CONF2_CNFNAME_QUERIES \ + else if(!strcmp((char*) name, "getModCnfName")) {\ + *pEtryPoint = modGetCnfName;\ + } + /* the following definition is the standard block for queryEtryPt for LIBRARY * modules. This can be used if no specific handling (e.g. to cover version * differences) is needed. @@ -485,6 +603,10 @@ rsRetVal modInit##uniqName(int iIFVersRequested __attribute__((unused)), int *ip /* now get the obj interface so that we can access other objects */ \ CHKiRet(pObjGetObjInterface(&obj)); +/* do those initializations necessary for legacy config variables */ +#define INITLegCnfVars \ + initConfVars(); + #define ENDmodInit \ finalize_it:\ *pQueryEtryPt = queryEtryPt;\ @@ -522,7 +644,6 @@ finalize_it:\ #define CODEmodInit_QueryRegCFSLineHdlr \ CHKiRet(pHostQueryEtryPt((uchar*)"regCfSysLineHdlr", &omsdRegCFSLineHdlr)); -#endif /* #ifndef MODULE_TEMPLATE_H_INCLUDED */ /* modExit() * This is the counterpart to modInit(). It destroys a module and makes it ready for @@ -548,6 +669,130 @@ static rsRetVal modExit(void)\ } +/* beginCnfLoad() + * This is a function tells an input module that a new config load begins. + * The core passes in a handle to the new module-specific module conf to + * the module. -- rgerards, 2011-05-03 + */ +#define BEGINbeginCnfLoad \ +static rsRetVal beginCnfLoad(modConfData_t **ptr, __attribute__((unused)) rsconf_t *pConf)\ +{\ + modConfData_t *pModConf; \ + DEFiRet; + +#define CODESTARTbeginCnfLoad \ + if((pModConf = calloc(1, sizeof(modConfData_t))) == NULL) {\ + *ptr = NULL;\ + ENDfunc \ + return RS_RET_OUT_OF_MEMORY;\ + } + +#define ENDbeginCnfLoad \ + *ptr = pModConf;\ + RETiRet;\ +} + + +/* endCnfLoad() + * This is a function tells an input module that the current config load ended. + * It gets a last chance to make changes to its in-memory config object. After + * this call, the config object must no longer be changed. + * The pModConf pointer passed into the module must no longer be used. + * rgerards, 2011-05-03 + */ +#define BEGINendCnfLoad \ +static rsRetVal endCnfLoad(modConfData_t *ptr)\ +{\ + modConfData_t __attribute__((unused)) *pModConf = (modConfData_t*) ptr; \ + DEFiRet; + +#define CODESTARTendCnfLoad + +#define ENDendCnfLoad \ + RETiRet;\ +} + + +/* checkCnf() + * Check the provided config object for errors, inconsistencies and other things + * that do not work out. + * NOTE: no part of the config must be activated, so some checks that require + * activation can not be done in this entry point. They must be done in the + * activateConf() stage, where the caller must also be prepared for error + * returns. + * rgerhards, 2011-05-03 + */ +#define BEGINcheckCnf \ +static rsRetVal checkCnf(modConfData_t *ptr)\ +{\ + modConfData_t __attribute__((unused)) *pModConf = (modConfData_t*) ptr; \ + DEFiRet; + +#define CODESTARTcheckCnf + +#define ENDcheckCnf \ + RETiRet;\ +} + + +/* activateCnfPrePrivDrop() + * Initial config activation, before dropping privileges. This is an optional + * entry points that should only be implemented by those module that really need + * it. Processing should be limited to the minimum possible. Main activation + * should happen in the normal activateCnf() call. + * rgerhards, 2011-05-06 + */ +#define BEGINactivateCnfPrePrivDrop \ +static rsRetVal activateCnfPrePrivDrop(modConfData_t *ptr)\ +{\ + modConfData_t *pModConf = (modConfData_t*) ptr; \ + DEFiRet; + +#define CODESTARTactivateCnfPrePrivDrop + +#define ENDactivateCnfPrePrivDrop \ + RETiRet;\ +} + + +/* activateCnf() + * This activates the provided config, and may report errors if they are detected + * during activation. + * rgerhards, 2011-05-03 + */ +#define BEGINactivateCnf \ +static rsRetVal activateCnf(modConfData_t *ptr)\ +{\ + modConfData_t __attribute__((unused)) *pModConf = (modConfData_t*) ptr; \ + DEFiRet; + +#define CODESTARTactivateCnf + +#define ENDactivateCnf \ + RETiRet;\ +} + + +/* freeCnf() + * This is a function tells an input module that it must free all data + * associated with the passed-in module config. + * rgerhards, 2011-05-03 + */ +#define BEGINfreeCnf \ +static rsRetVal freeCnf(void *ptr)\ +{\ + modConfData_t *pModConf = (modConfData_t*) ptr; \ + DEFiRet; + +#define CODESTARTfreeCnf + +#define ENDfreeCnf \ + if(pModConf != NULL)\ + free(pModConf); /* we need to free this in any case */\ + RETiRet;\ +} + + /* runInput() * This is the main function for input modules. It is used to gather data from the * input source and submit it to the message queue. Each runInput() instance has its own @@ -687,5 +932,7 @@ static rsRetVal GetStrgenName(uchar **ppSz)\ } +#endif /* #ifndef MODULE_TEMPLATE_H_INCLUDED */ + /* vim:set ai: */ diff --git a/runtime/modules.c b/runtime/modules.c index 6a32b2e8..5a87c8be 100644 --- a/runtime/modules.c +++ b/runtime/modules.c @@ -11,7 +11,7 @@ * * File begun on 2007-07-22 by RGerhards * - * Copyright 2007, 2009 Rainer Gerhards and Adiscon GmbH. + * Copyright 2007-2011 Rainer Gerhards and Adiscon GmbH. * * This file is part of the rsyslog runtime library. * @@ -55,6 +55,7 @@ #endif #include "cfsysline.h" +#include "rsconf.h" #include "modules.h" #include "errmsg.h" #include "parser.h" @@ -80,9 +81,7 @@ static modInfo_t *pLoadedModulesLast = NULL; /* tail-pointer */ /* already dlopen()-ed libs */ static struct dlhandle_s *pHandles = NULL; -/* config settings */ -uchar *pModDir = NULL; /* read-only after startup */ - +static uchar *pModDir; /* directory where loadable modules are found */ /* we provide a set of dummy functions for modules that do not support the * some interfaces. @@ -91,18 +90,30 @@ uchar *pModDir = NULL; /* read-only after startup */ * harm. This simplifies things as in action processing we do not need to check * if the transactional entry points exist. */ -static rsRetVal dummyBeginTransaction() +static rsRetVal +dummyBeginTransaction() { return RS_RET_OK; } -static rsRetVal dummyEndTransaction() +static rsRetVal +dummyEndTransaction() { return RS_RET_OK; } -static rsRetVal dummyIsCompatibleWithFeature() +static rsRetVal +dummyIsCompatibleWithFeature() { return RS_RET_INCOMPATIBLE; } +static rsRetVal +dummynewActInst(uchar *modName, struct nvlst __attribute__((unused)) *dummy1, + void __attribute__((unused)) **dummy2, omodStringRequest_t __attribute__((unused)) **dummy3) +{ + errmsg.LogError(0, RS_RET_CONFOBJ_UNSUPPORTED, "config objects are not " + "supported by module '%s' -- legacy config options " + "MUST be used instead", modName); + return RS_RET_CONFOBJ_UNSUPPORTED; +} #ifdef DEBUG /* we add some home-grown support to track our users (and detect who does not free us). In @@ -229,8 +240,8 @@ static rsRetVal moduleConstruct(modInfo_t **pThis) static void moduleDestruct(modInfo_t *pThis) { assert(pThis != NULL); - if(pThis->pszName != NULL) - free(pThis->pszName); + free(pThis->pszName); + free(pThis->cnfName); if(pThis->pModHdlr != NULL) { # ifdef VALGRIND # warning "dlclose disabled for valgrind" @@ -319,7 +330,7 @@ static uchar *modGetStateName(modInfo_t *pThis) /* Add a module to the loaded module linked list */ static inline void -addModToList(modInfo_t *pThis) +addModToGlblList(modInfo_t *pThis) { assert(pThis != NULL); @@ -334,6 +345,61 @@ addModToList(modInfo_t *pThis) } +/* Add a module to the config module list for current loadConf + */ +rsRetVal +addModToCnfList(modInfo_t *pThis) +{ + cfgmodules_etry_t *pNew; + cfgmodules_etry_t *pLast; + DEFiRet; + assert(pThis != NULL); + + if(loadConf == NULL) { + /* we are in an early init state */ + FINALIZE; + } + + /* check for duplicates and, as a side-activity, identify last node */ + pLast = loadConf->modules.root; + if(pLast != NULL) { + while(1) { /* loop broken inside */ + if(pLast->pMod == pThis) { + DBGPRINTF("module '%s' already in this config\n", modGetName(pThis)); + FINALIZE; + } + if(pLast->next == NULL) + break; + pLast = pLast -> next; + } + } + + /* if we reach this point, pLast is the tail pointer and this module is new + * inside the currently loaded config. So, iff it is an input module, let's + * pass it a pointer which it can populate with a pointer to its module conf. + */ + + CHKmalloc(pNew = MALLOC(sizeof(cfgmodules_etry_t))); + pNew->canActivate = 1; + pNew->next = NULL; + pNew->pMod = pThis; + + if(pThis->beginCnfLoad != NULL) { + CHKiRet(pThis->beginCnfLoad(&pNew->modCnf, loadConf)); + } + + if(pLast == NULL) { + loadConf->modules.root = pNew; + } else { + /* there already exist entries */ + pLast->next = pNew; + } + +finalize_it: + RETiRet; +} + + /* 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, @@ -355,19 +421,51 @@ static modInfo_t *GetNxt(modInfo_t *pThis) /* this function is like GetNxt(), but it returns pointers to - * modules of specific type only. As we currently deal just with output modules, - * it is a dummy, to be filled with real code later. - * rgerhards, 2007-07-24 + * the configmodules entry, which than can be used to obtain the + * actual module pointer. Note that it returns those for + * modules of specific type only. Only modules from the provided + * config are returned. Note that processing speed could be improved, + * but this is really not relevant, as config file loading is not really + * something we are concerned about in regard to runtime. */ -static modInfo_t *GetNxtType(modInfo_t *pThis, eModType_t rqtdType) +static cfgmodules_etry_t +*GetNxtCnfType(rsconf_t *cnf, cfgmodules_etry_t *node, eModType_t rqtdType) { - modInfo_t *pMod = pThis; + if(node == NULL) { /* start at beginning of module list */ + node = cnf->modules.root; + } else { + node = node->next; + } - do { - pMod = GetNxt(pMod); - } while(!(pMod == NULL || pMod->eType == rqtdType)); /* warning: do ... while() */ + if(rqtdType != eMOD_ANY) { /* if any, we already have the right one! */ + while(node != NULL && node->pMod->eType != rqtdType) { + node = node->next; + } + } - return pMod; + return node; +} + + +/* Find a module with the given conf name and type. Returns NULL if none + * can be found, otherwise module found. + */ +static modInfo_t * +FindWithCnfName(rsconf_t *cnf, uchar *name, eModType_t rqtdType) +{ + cfgmodules_etry_t *node; + + ; + for( node = cnf->modules.root + ; node != NULL + ; node = node->next) { + if(node->pMod->eType != rqtdType || node->pMod->cnfName == NULL) + continue; + if(!strcasecmp((char*)node->pMod->cnfName, (char*)name)) + break; + } + + return node == NULL ? NULL : node->pMod; } @@ -409,7 +507,8 @@ finalize_it: * everything needed to fully initialize the module. */ static rsRetVal -doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)(), modInfo_t*), uchar *name, void *pModHdlr) +doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)(), modInfo_t*), + uchar *name, void *pModHdlr, modInfo_t **pNewModule) { rsRetVal localRet; modInfo_t *pNew = NULL; @@ -420,6 +519,8 @@ doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)(), modInfo_ rsRetVal (*modGetType)(eModType_t *pType); rsRetVal (*modGetKeepType)(eModKeepType_t *pKeepType); struct dlhandle_s *pHandle = NULL; + rsRetVal (*getModCnfName)(uchar **cnfName); + uchar *cnfName; DEFiRet; assert(modInit != NULL); @@ -442,7 +543,7 @@ doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)(), modInfo_ CHKiRet((*modGetType)(&pNew->eType)); CHKiRet((*pNew->modQueryEtryPt)((uchar*)"getKeepType", &modGetKeepType)); CHKiRet((*modGetKeepType)(&pNew->eKeepType)); - dbgprintf("module of type %d being loaded.\n", pNew->eType); + dbgprintf("module %s of type %d being loaded.\n", name, pNew->eType); /* OK, we know we can successfully work with the module. So we now fill the * rest of the data elements. First we load the interfaces common to all @@ -456,6 +557,34 @@ doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)(), modInfo_ else if(localRet != RS_RET_OK) ABORT_FINALIZE(localRet); + /* optional calls for new config system */ + localRet = (*pNew->modQueryEtryPt)((uchar*)"getModCnfName", &getModCnfName); + if(localRet == RS_RET_OK) { + if(getModCnfName(&cnfName) == RS_RET_OK) + pNew->cnfName = (uchar*) strdup((char*)cnfName); + /**< we do not care if strdup() fails, we can accept that */ + else + pNew->cnfName = NULL; + dbgprintf("module config name is '%s'\n", cnfName); + } + localRet = (*pNew->modQueryEtryPt)((uchar*)"beginCnfLoad", &pNew->beginCnfLoad); + if(localRet == RS_RET_OK) { + dbgprintf("module %s supports rsyslog v6 config interface\n", name); + CHKiRet((*pNew->modQueryEtryPt)((uchar*)"endCnfLoad", &pNew->endCnfLoad)); + CHKiRet((*pNew->modQueryEtryPt)((uchar*)"freeCnf", &pNew->freeCnf)); + CHKiRet((*pNew->modQueryEtryPt)((uchar*)"checkCnf", &pNew->checkCnf)); + CHKiRet((*pNew->modQueryEtryPt)((uchar*)"activateCnf", &pNew->activateCnf)); + localRet = (*pNew->modQueryEtryPt)((uchar*)"activateCnfPrePrivDrop", &pNew->activateCnfPrePrivDrop); + if(localRet == RS_RET_MODULE_ENTRY_POINT_NOT_FOUND) { + pNew->activateCnfPrePrivDrop = NULL; + } else { + CHKiRet(localRet); + } + } else if(localRet == RS_RET_MODULE_ENTRY_POINT_NOT_FOUND) { + pNew->beginCnfLoad = NULL; /* flag as non-present */ + } else { + ABORT_FINALIZE(localRet); + } /* ... and now the module-specific interfaces */ switch(pNew->eType) { case eMOD_IN: @@ -487,6 +616,13 @@ doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)(), modInfo_ } else if(localRet != RS_RET_OK) { ABORT_FINALIZE(localRet); } + + localRet = (*pNew->modQueryEtryPt)((uchar*)"newActInst", &pNew->mod.om.newActInst); + if(localRet == RS_RET_MODULE_ENTRY_POINT_NOT_FOUND) { + pNew->mod.om.newActInst = dummynewActInst; + } else if(localRet != RS_RET_OK) { + ABORT_FINALIZE(localRet); + } break; case eMOD_LIB: break; @@ -533,11 +669,14 @@ doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)(), modInfo_ CHKiRet(strgen.SetModPtr(pStrgen, pNew)); CHKiRet(strgen.ConstructFinalize(pStrgen)); break; + case eMOD_ANY: /* this is mostly to keep the compiler happy! */ + DBGPRINTF("PROGRAM ERROR: eMOD_ANY set as module type\n"); + assert(0); + break; } pNew->pszName = (uchar*) strdup((char*)name); /* we do not care if strdup() fails, we can accept that */ pNew->pModHdlr = pModHdlr; - /* TODO: take this from module */ if(pModHdlr == NULL) { pNew->eLinkType = eMOD_LINK_STATIC; } else { @@ -569,12 +708,14 @@ doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)(), modInfo_ } /* we initialized the structure, now let's add it to the linked list of modules */ - addModToList(pNew); + addModToGlblList(pNew); + *pNewModule = pNew; finalize_it: if(iRet != RS_RET_OK) { if(pNew != NULL) moduleDestruct(pNew); + *pNewModule = NULL; } RETiRet; @@ -610,25 +751,34 @@ static void modPrintList(void) case eMOD_STRGEN: dbgprintf("strgen"); break; + case eMOD_ANY: /* this is mostly to keep the compiler happy! */ + DBGPRINTF("PROGRAM ERROR: eMOD_ANY set as module type\n"); + assert(0); + break; } dbgprintf(" module.\n"); dbgprintf("Entry points:\n"); dbgprintf("\tqueryEtryPt: 0x%lx\n", (unsigned long) pMod->modQueryEtryPt); dbgprintf("\tdbgPrintInstInfo: 0x%lx\n", (unsigned long) pMod->dbgPrintInstInfo); dbgprintf("\tfreeInstance: 0x%lx\n", (unsigned long) pMod->freeInstance); + dbgprintf("\tbeginCnfLoad: 0x%lx\n", (unsigned long) pMod->beginCnfLoad); + dbgprintf("\tcheckCnf: 0x%lx\n", (unsigned long) pMod->checkCnf); + dbgprintf("\tactivateCnfPrePrivDrop: 0x%lx\n", (unsigned long) pMod->activateCnfPrePrivDrop); + dbgprintf("\tactivateCnf: 0x%lx\n", (unsigned long) pMod->activateCnf); + dbgprintf("\tfreeCnf: 0x%lx\n", (unsigned long) pMod->freeCnf); switch(pMod->eType) { case eMOD_OUT: dbgprintf("Output Module Entry Points:\n"); - dbgprintf("\tdoAction: 0x%lx\n", (unsigned long) pMod->mod.om.doAction); - dbgprintf("\tparseSelectorAct: 0x%lx\n", (unsigned long) pMod->mod.om.parseSelectorAct); - dbgprintf("\ttryResume: 0x%lx\n", (unsigned long) pMod->tryResume); - dbgprintf("\tdoHUP: 0x%lx\n", (unsigned long) pMod->doHUP); - dbgprintf("\tBeginTransaction: 0x%lx\n", (unsigned long) - ((pMod->mod.om.beginTransaction == dummyBeginTransaction) ? - 0 : pMod->mod.om.beginTransaction)); - dbgprintf("\tEndTransaction: 0x%lx\n", (unsigned long) - ((pMod->mod.om.endTransaction == dummyEndTransaction) ? - 0 : pMod->mod.om.endTransaction)); + dbgprintf("\tdoAction: %p\n", pMod->mod.om.doAction); + dbgprintf("\tparseSelectorAct: %p\n", pMod->mod.om.parseSelectorAct); + dbgprintf("\tnewActInst: %p\n", (pMod->mod.om.newActInst == dummynewActInst) ? + NULL : pMod->mod.om.newActInst); + dbgprintf("\ttryResume: %p\n", pMod->tryResume); + dbgprintf("\tdoHUP: %p\n", pMod->doHUP); + dbgprintf("\tBeginTransaction: %p\n", ((pMod->mod.om.beginTransaction == dummyBeginTransaction) ? + NULL : pMod->mod.om.beginTransaction)); + dbgprintf("\tEndTransaction: %p\n", ((pMod->mod.om.endTransaction == dummyEndTransaction) ? + NULL : pMod->mod.om.endTransaction)); break; case eMOD_IN: dbgprintf("Input Module Entry Points\n"); @@ -646,6 +796,8 @@ static void modPrintList(void) dbgprintf("Strgen Module Entry Points\n"); dbgprintf("\tstrgen: 0x%lx\n", (unsigned long) pMod->mod.sm.strgen); break; + case eMOD_ANY: /* this is mostly to keep the compiler happy! */ + break; } dbgprintf("\n"); pMod = GetNxt(pMod); /* done, go next */ @@ -751,6 +903,27 @@ modUnloadAndDestructAll(eModLinkType_t modLinkTypesToUnload) RETiRet; } +/* find module with given name in global list */ +static inline rsRetVal +findModule(uchar *pModName, int iModNameLen, modInfo_t **pMod) +{ + modInfo_t *pModInfo; + uchar *pModNameCmp; + DEFiRet; + + pModInfo = GetNxt(NULL); + while(pModInfo != NULL) { + if(!strncmp((char *) pModName, (char *) (pModNameCmp = modGetName(pModInfo)), iModNameLen) && + (!*(pModNameCmp + iModNameLen) || !strcmp((char *) pModNameCmp + iModNameLen, ".so"))) { + dbgprintf("Module '%s' found\n", pModName); + break; + } + pModInfo = GetNxt(pModInfo); + } + *pMod = pModInfo; + RETiRet; +} + /* load a module and initialize it, based on doModLoad() from conf.c * rgerhards, 2008-03-05 @@ -760,9 +933,15 @@ modUnloadAndDestructAll(eModLinkType_t modLinkTypesToUnload) * configuration file processing, which is executed on a single thread. Should we * change that design at any stage (what is unlikely), we need to find a * replacement. + * rgerhards, 2011-04-27: + * Parameter "bConfLoad" tells us if the load was triggered by a config handler, in + * which case we need to tie the loaded module to the current config. If bConfLoad == 0, + * the system loads a module for internal reasons, this is not directly tied to a + * configuration. We could also think if it would be useful to add only certain types + * of modules, but the current implementation at least looks simpler. */ static rsRetVal -Load(uchar *pModName) +Load(uchar *pModName, sbool bConfLoad) { DEFiRet; @@ -799,17 +978,16 @@ Load(uchar *pModName) } else bHasExtension = FALSE; - pModInfo = GetNxt(NULL); - while(pModInfo != NULL) { - if(!strncmp((char *) pModName, (char *) (pModNameCmp = modGetName(pModInfo)), iModNameLen) && - (!*(pModNameCmp + iModNameLen) || !strcmp((char *) pModNameCmp + iModNameLen, ".so"))) { - dbgprintf("Module '%s' already loaded\n", pModName); - ABORT_FINALIZE(RS_RET_OK); - } - pModInfo = GetNxt(pModInfo); + CHKiRet(findModule(pModName, iModNameLen, &pModInfo)); + if(pModInfo != NULL) { + if(bConfLoad) + addModToCnfList(pModInfo); + dbgprintf("Module '%s' already loaded\n", pModName); + FINALIZE; } - pModDirCurr = (uchar *)((pModDir == NULL) ? _PATH_MODDIR : (char *)pModDir); + pModDirCurr = (uchar *)((pModDir == NULL) ? + _PATH_MODDIR : (char *)pModDir); pModDirNext = NULL; pModHdlr = NULL; iLoadCnt = 0; @@ -866,7 +1044,6 @@ Load(uchar *pModName) * TODO: I guess this is highly importable, so we should change the * algo over time... -- rgerhards, 2008-03-05 */ - /* ... so now add the extension */ strncat((char *) pPathBuf, ".so", lenPathBuf - strlen((char*) pPathBuf) - 1); iPathLen += 3; } @@ -902,15 +1079,19 @@ Load(uchar *pModName) ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_DLOPEN); } if(!(pModInit = dlsym(pModHdlr, "modInit"))) { - errmsg.LogError(0, RS_RET_MODULE_LOAD_ERR_NO_INIT, "could not load module '%s', dlsym: %s\n", pPathBuf, dlerror()); + errmsg.LogError(0, RS_RET_MODULE_LOAD_ERR_NO_INIT, + "could not load module '%s', dlsym: %s\n", pPathBuf, dlerror()); dlclose(pModHdlr); ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_NO_INIT); } - if((iRet = doModInit(pModInit, (uchar*) pModName, pModHdlr)) != RS_RET_OK) { - errmsg.LogError(0, RS_RET_MODULE_LOAD_ERR_INIT_FAILED, "could not load module '%s', rsyslog error %d\n", pPathBuf, iRet); + if((iRet = doModInit(pModInit, (uchar*) pModName, pModHdlr, &pModInfo)) != RS_RET_OK) { + errmsg.LogError(0, RS_RET_MODULE_LOAD_ERR_INIT_FAILED, + "could not load module '%s', rsyslog error %d\n", pPathBuf, iRet); dlclose(pModHdlr); ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_INIT_FAILED); } + if(bConfLoad) + addModToCnfList(pModInfo); finalize_it: if(pPathBuf != pathBuf) /* used malloc()ed memory? */ @@ -1022,6 +1203,7 @@ CODESTARTObjClassExit(module) * TODO: add again: pthread_mutex_destroy(&mutLoadUnload); */ + free(pModDir); # ifdef DEBUG modUsrPrintAll(); /* debug aid - TODO: integrate with debug.c, at least the settings! */ # endif @@ -1043,10 +1225,11 @@ CODESTARTobjQueryInterface(module) * of course, also affects the "if" above). */ pIf->GetNxt = GetNxt; - pIf->GetNxtType = GetNxtType; + pIf->GetNxtCnfType = GetNxtCnfType; pIf->GetName = modGetName; pIf->GetStateName = modGetStateName; pIf->PrintList = modPrintList; + pIf->FindWithCnfName = FindWithCnfName; pIf->UnloadAndDestructAll = modUnloadAndDestructAll; pIf->doModInit = doModInit; pIf->SetModDir = SetModDir; diff --git a/runtime/modules.h b/runtime/modules.h index 4daaf1f9..6c5a2cba 100644 --- a/runtime/modules.h +++ b/runtime/modules.h @@ -12,7 +12,7 @@ * * File begun on 2007-07-22 by RGerhards * - * Copyright 2007-2009 Rainer Gerhards and Adiscon GmbH. + * Copyright 2007-2011 Rainer Gerhards and Adiscon GmbH. * * This file is part of the rsyslog runtime library. * @@ -36,7 +36,7 @@ #define MODULES_H_INCLUDED 1 #include "objomsr.h" - +#include "rainerscript.h" /* the following define defines the current version of the module interface. * It can be used by any module which want's to simply prevent version conflicts @@ -47,15 +47,18 @@ * version 5 changes the way parsing works for input modules. This is * an important change, parseAndSubmitMessage() goes away. Other * module types are not affected. -- rgerhards, 2008-10-09 + * version 6 introduces scoping support (starting with the output + * modules) -- rgerhards, 2010-07-27 */ -#define CURR_MOD_IF_VERSION 5 +#define CURR_MOD_IF_VERSION 6 typedef enum eModType_ { eMOD_IN = 0, /* input module */ eMOD_OUT = 1, /* output module */ eMOD_LIB = 2, /* library module */ eMOD_PARSER = 3,/* parser module */ - eMOD_STRGEN = 4 /* strgen module */ + eMOD_STRGEN = 4,/* strgen module */ + eMOD_ANY = 5 /* meta-name for "any type of module" -- to be used in function calls */ } eModType_t; @@ -96,6 +99,7 @@ struct modInfo_s { eModLinkType_t eLinkType; eModKeepType_t eKeepType; /* keep the module dynamically linked on unload */ uchar* pszName; /* printable module name, e.g. for dbgprintf */ + uchar* cnfName; /* name to be used in config statements (e.g. 'name="omusrmsg"') */ unsigned uRefCnt; /* reference count for this module; 0 -> may be unloaded */ /* functions supported by all types of modules */ rsRetVal (*modInit)(int, int*, rsRetVal(**)()); /* initialize the module */ @@ -108,19 +112,22 @@ struct modInfo_s { rsRetVal (*modExit)(void); /* called before termination or module unload */ rsRetVal (*modGetID)(void **); /* get its unique ID from module */ rsRetVal (*doHUP)(void *); /* non-restart type HUP handler */ - /* below: parse a configuration line - return if processed - * or not. If not, must be parsed to next module. - */ - rsRetVal (*parseConfigLine)(uchar **pConfLine); + /* v2 config system specific */ + rsRetVal (*beginCnfLoad)(void*newCnf, rsconf_t *pConf); + rsRetVal (*endCnfLoad)(void*Cnf); + rsRetVal (*checkCnf)(void*Cnf); + rsRetVal (*activateCnfPrePrivDrop)(void*Cnf); + rsRetVal (*activateCnf)(void*Cnf); /* make provided config the running conf */ + rsRetVal (*freeCnf)(void*Cnf); + /* end v2 config system specific */ /* below: create an instance of this module. Most importantly the module * can allocate instance memory in this call. */ rsRetVal (*createInstance)(); - /* TODO: pass pointer to msg submit function to IM rger, 2007-12-14 */ union { struct {/* data for input modules */ +/* TODO: remove? */rsRetVal (*willRun)(void); /* check if the current config will be able to run*/ rsRetVal (*runInput)(thrdInfo_t*); /* function to gather input and submit to queue */ - rsRetVal (*willRun)(void); /* function to gather input and submit to queue */ rsRetVal (*afterRun)(thrdInfo_t*); /* function to gather input and submit to queue */ int bCanRun; /* cached value of whether willRun() succeeded */ } im; @@ -131,6 +138,7 @@ struct modInfo_s { rsRetVal (*doAction)(uchar**, unsigned, void*); rsRetVal (*endTransaction)(void*); rsRetVal (*parseSelectorAct)(uchar**, void**,omodStringRequest_t**); + rsRetVal (*newActInst)(uchar *modName, struct nvlst *lst, void **, omodStringRequest_t **); } om; struct { /* data for library modules */ char dummy; @@ -155,23 +163,28 @@ struct modInfo_s { /* interfaces */ BEGINinterface(module) /* name must also be changed in ENDinterface macro! */ modInfo_t *(*GetNxt)(modInfo_t *pThis); - modInfo_t *(*GetNxtType)(modInfo_t *pThis, eModType_t rqtdType); + cfgmodules_etry_t *(*GetNxtCnfType)(rsconf_t *cnf, cfgmodules_etry_t *pThis, eModType_t rqtdType); uchar *(*GetName)(modInfo_t *pThis); uchar *(*GetStateName)(modInfo_t *pThis); rsRetVal (*Use)(char *srcFile, modInfo_t *pThis); /**< must be called before a module is used (ref counting) */ rsRetVal (*Release)(char *srcFile, modInfo_t **ppThis); /**< release a module (ref counting) */ void (*PrintList)(void); rsRetVal (*UnloadAndDestructAll)(eModLinkType_t modLinkTypesToUnload); - rsRetVal (*doModInit)(rsRetVal (*modInit)(), uchar *name, void *pModHdlr); - rsRetVal (*Load)(uchar *name); + rsRetVal (*doModInit)(rsRetVal (*modInit)(), uchar *name, void *pModHdlr, modInfo_t **pNew); + rsRetVal (*Load)(uchar *name, sbool bConfLoad); rsRetVal (*SetModDir)(uchar *name); + modInfo_t *(*FindWithCnfName)(rsconf_t *cnf, uchar *name, eModType_t rqtdType); /* added v3, 2011-07-19 */ ENDinterface(module) -#define moduleCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ +#define moduleCURR_IF_VERSION 3 /* increment whenever you change the interface structure! */ +/* Changes: + * v2 + * - added param bCondLoad to Load call - 2011-04-27 + * - removed GetNxtType, added GetNxtCnfType - 2011-04-27 + */ /* prototypes */ PROTOTYPEObj(module); -/* TODO: remove them below (means move the config init code) -- rgerhards, 2008-02-19 */ -extern uchar *pModDir; /* read-only after startup */ - +/* TODO: remove "dirty" calls! */ +rsRetVal addModToCnfList(modInfo_t *pThis); #endif /* #ifndef MODULES_H_INCLUDED */ diff --git a/runtime/msg.c b/runtime/msg.c index 21de8e43..4ad2b89e 100644 --- a/runtime/msg.c +++ b/runtime/msg.c @@ -36,7 +36,10 @@ #include <assert.h> #include <ctype.h> #include <sys/socket.h> +#include <sys/sysinfo.h> #include <netdb.h> +#include <libestr.h> +#include <libee/libee.h> #if HAVE_MALLOC_H # include <malloc.h> #endif @@ -45,7 +48,6 @@ #include "stringbuf.h" #include "template.h" #include "msg.h" -#include "var.h" #include "datetime.h" #include "glbl.h" #include "regexp.h" @@ -54,10 +56,10 @@ #include "ruleset.h" #include "prop.h" #include "net.h" +#include "rsconf.h" /* static data */ DEFobjStaticHelpers -DEFobjCurrIf(var) DEFobjCurrIf(datetime) DEFobjCurrIf(glbl) DEFobjCurrIf(regexp) @@ -261,6 +263,9 @@ static struct { { UCHAR_CONSTANT("190"), 5}, { UCHAR_CONSTANT("191"), 5} }; +static char hexdigit[16] = + {'0', '1', '2', '3', '4', '5', '6', '7', '8', + '9', 'A', 'B', 'C', 'D', 'E', 'F' }; /*syslog facility names (as of RFC5424) */ static char *syslog_fac_names[24] = { "kern", "user", "mail", "daemon", "auth", "syslog", "lpr", @@ -431,12 +436,12 @@ resolveDNS(msg_t *pMsg) { } } finalize_it: - MsgUnlock(pMsg); if(iRet != RS_RET_OK) { /* best we can do: remove property */ MsgSetRcvFromStr(pMsg, UCHAR_CONSTANT(""), 0, &propFromHost); prop.Destruct(&propFromHost); } + MsgUnlock(pMsg); if(propFromHost != NULL) prop.Destruct(&propFromHost); if(propFromHostIP != NULL) @@ -479,16 +484,13 @@ getRcvFromIP(msg_t *pM) } - -/* map a property name (string) to a property ID */ -rsRetVal propNameToID(cstr_t *pCSPropName, propid_t *pPropID) +/* map a property name (C string) to a property ID */ +rsRetVal +propNameStrToID(uchar *pName, propid_t *pPropID) { - uchar *pName; DEFiRet; - assert(pCSPropName != NULL); - assert(pPropID != NULL); - pName = rsCStrGetSzStrNoNULL(pCSPropName); + assert(pName != NULL); /* sometimes there are aliases to the original MonitoWare * property names. These come after || in the ifs below. */ @@ -503,11 +505,6 @@ rsRetVal propNameToID(cstr_t *pCSPropName, propid_t *pPropID) *pPropID = PROP_SYSLOGTAG; } else if(!strcmp((char*) pName, "rawmsg")) { *pPropID = PROP_RAWMSG; - /* enable this, if someone actually uses UxTradMsg, delete after some time has - * passed and nobody complained -- rgerhards, 2009-06-16 - } else if(!strcmp((char*) pName, "uxtradmsg")) { - pRes = getUxTradMsg(pMsg); - */ } else if(!strcmp((char*) pName, "inputname")) { *pPropID = PROP_INPUTNAME; } else if(!strcmp((char*) pName, "fromhost")) { @@ -542,6 +539,8 @@ rsRetVal propNameToID(cstr_t *pCSPropName, propid_t *pPropID) *pPropID = PROP_PROCID; } else if(!strcmp((char*) pName, "msgid")) { *pPropID = PROP_MSGID; + } else if(!strcmp((char*) pName, "parsesuccess")) { + *pPropID = PROP_PARSESUCCESS; /* here start system properties (those, that do not relate to the message itself */ } else if(!strcmp((char*) pName, "$now")) { *pPropID = PROP_SYS_NOW; @@ -561,8 +560,14 @@ rsRetVal propNameToID(cstr_t *pCSPropName, propid_t *pPropID) *pPropID = PROP_SYS_MINUTE; } else if(!strcmp((char*) pName, "$myhostname")) { *pPropID = PROP_SYS_MYHOSTNAME; + } else if(!strcmp((char*) pName, "$!all-json")) { + *pPropID = PROP_CEE_ALL_JSON; + } else if(!strncmp((char*) pName, "$!", 2)) { + *pPropID = PROP_CEE; } else if(!strcmp((char*) pName, "$bom")) { *pPropID = PROP_SYS_BOM; + } else if(!strcmp((char*) pName, "$uptime")) { + *pPropID = PROP_SYS_UPTIME; } else { *pPropID = PROP_INVALID; iRet = RS_RET_VAR_NOT_FOUND; @@ -572,6 +577,21 @@ rsRetVal propNameToID(cstr_t *pCSPropName, propid_t *pPropID) } +/* map a property name (string) to a property ID */ +rsRetVal +propNameToID(cstr_t *pCSPropName, propid_t *pPropID) +{ + uchar *pName; + DEFiRet; + + assert(pCSPropName != NULL); + assert(pPropID != NULL); + pName = rsCStrGetSzStrNoNULL(pCSPropName); + iRet = propNameStrToID(pName, pPropID); + RETiRet; +} + + /* map a property ID to a name string (useful for displaying) */ uchar *propIDToName(propid_t propID) { @@ -586,12 +606,6 @@ uchar *propIDToName(propid_t propID) return UCHAR_CONSTANT("syslogtag"); case PROP_RAWMSG: return UCHAR_CONSTANT("rawmsg"); - /* enable this, if someone actually uses UxTradMsg, delete after some time has - * passed and nobody complained -- rgerhards, 2009-06-16 - case PROP_UXTRADMSG: - pRes = getUxTradMsg(pMsg); - break; - */ case PROP_INPUTNAME: return UCHAR_CONSTANT("inputname"); case PROP_FROMHOST: @@ -626,6 +640,8 @@ uchar *propIDToName(propid_t propID) return UCHAR_CONSTANT("procid"); case PROP_MSGID: return UCHAR_CONSTANT("msgid"); + case PROP_PARSESUCCESS: + return UCHAR_CONSTANT("parsesuccess"); case PROP_SYS_NOW: return UCHAR_CONSTANT("$NOW"); case PROP_SYS_YEAR: @@ -644,6 +660,10 @@ uchar *propIDToName(propid_t propID) return UCHAR_CONSTANT("$MINUTE"); case PROP_SYS_MYHOSTNAME: return UCHAR_CONSTANT("$MYHOSTNAME"); + case PROP_CEE: + return UCHAR_CONSTANT("*CEE-based property*"); + case PROP_CEE_ALL_JSON: + return UCHAR_CONSTANT("$!all-json"); case PROP_SYS_BOM: return UCHAR_CONSTANT("$BOM"); default: @@ -684,6 +704,7 @@ static inline rsRetVal msgBaseConstruct(msg_t **ppThis) pM->flowCtlType = 0; pM->bDoLock = 0; pM->bAlreadyFreed = 0; + pM->bParseSuccess = 0; pM->iRefCount = 1; pM->iSeverity = -1; pM->iFacility = -1; @@ -714,6 +735,7 @@ static inline rsRetVal msgBaseConstruct(msg_t **ppThis) pM->pRcvFromIP = NULL; pM->rcvFrom.pRcvFrom = NULL; pM->pRuleset = NULL; + pM->event = NULL; memset(&pM->tRcvdAt, 0, sizeof(pM->tRcvdAt)); memset(&pM->tTIMESTAMP, 0, sizeof(pM->tTIMESTAMP)); pM->TAG.pszTAG = NULL; @@ -721,6 +743,8 @@ static inline rsRetVal msgBaseConstruct(msg_t **ppThis) pM->pszTimestamp3339[0] = '\0'; pM->pszTIMESTAMP_SecFrac[0] = '\0'; pM->pszRcvdAt_SecFrac[0] = '\0'; + pM->pszTIMESTAMP_Unix[0] = '\0'; + pM->pszRcvdAt_Unix[0] = '\0'; /* DEV debugging only! dbgprintf("msgConstruct\t0x%x, ref 1\n", (int)pM);*/ @@ -849,6 +873,8 @@ CODESTARTobjDestruct(msg) rsCStrDestruct(&pThis->pCSPROCID); if(pThis->pCSMSGID != NULL) rsCStrDestruct(&pThis->pCSMSGID); + if(pThis->event != NULL) + ee_deleteEvent(pThis->event); # ifndef HAVE_ATOMIC_BUILTINS MsgUnlock(pThis); # endif @@ -961,10 +987,6 @@ msg_t* MsgDup(msg_t* pOld) pNew->pInputName = pOld->pInputName; prop.AddRef(pNew->pInputName); } - /* enable this, if someone actually uses UxTradMsg, delete after some time has - * passed and nobody complained -- rgerhards, 2009-06-16 - pNew->offAfterPRI = pOld->offAfterPRI; - */ if(pOld->iLenTAG > 0) { if(pOld->iLenTAG < CONF_TAG_BUFSIZE) { memcpy(pNew->TAG.szBuf, pOld->TAG.szBuf, pOld->iLenTAG + 1); @@ -1036,10 +1058,6 @@ static rsRetVal MsgSerialize(msg_t *pThis, strm_t *pStrm) objSerializeSCALAR(pStrm, ttGenTime, INT); objSerializeSCALAR(pStrm, tRcvdAt, SYSLOGTIME); objSerializeSCALAR(pStrm, tTIMESTAMP, SYSLOGTIME); - /* enable this, if someone actually uses UxTradMsg, delete after some time has - * passed and nobody complained -- rgerhards, 2009-06-16 - objSerializeSCALAR(pStrm, offsAfterPRI, SHORT); - */ CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("pszTAG"), PROPTYPE_PSZ, (void*) ((pThis->iLenTAG < CONF_TAG_BUFSIZE) ? pThis->TAG.szBuf : pThis->TAG.pszTAG))); @@ -1221,7 +1239,7 @@ char *getProtocolVersionString(msg_t *pM) } -static inline void +void getRawMsg(msg_t *pM, uchar **pBuf, int *piLen) { if(pM == NULL) { @@ -1239,18 +1257,6 @@ getRawMsg(msg_t *pM, uchar **pBuf, int *piLen) } -/* enable this, if someone actually uses UxTradMsg, delete after some time has - * passed and nobody complained -- rgerhards, 2009-06-16 -char *getUxTradMsg(msg_t *pM) -{ - if(pM == NULL) - return ""; - else - return (char*)pM->pszRawMsg + pM->offAfterPRI; -} -*/ - - int getMSGLen(msg_t *pM) { return((pM == NULL) ? 0 : pM->iLenMSG); @@ -1345,6 +1351,13 @@ getTimeReported(msg_t *pM, enum tplFormatTypes eFmt) } MsgUnlock(pM); return(pM->pszTIMESTAMP3339); + case tplFmtUnixDate: + MsgLock(pM); + if(pM->pszTIMESTAMP_Unix[0] == '\0') { + datetime.formatTimestampUnix(&pM->tTIMESTAMP, pM->pszTIMESTAMP_Unix); + } + MsgUnlock(pM); + return(pM->pszTIMESTAMP_Unix); case tplFmtSecFrac: if(pM->pszTIMESTAMP_SecFrac[0] == '\0') { MsgLock(pM); @@ -1424,6 +1437,13 @@ static inline char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt) } MsgUnlock(pM); return(pM->pszRcvdAt3339); + case tplFmtUnixDate: + MsgLock(pM); + if(pM->pszRcvdAt_Unix[0] == '\0') { + datetime.formatTimestampUnix(&pM->tRcvdAt, pM->pszRcvdAt_Unix); + } + MsgUnlock(pM); + return(pM->pszRcvdAt_Unix); case tplFmtSecFrac: if(pM->pszRcvdAt_SecFrac[0] == '\0') { MsgLock(pM); @@ -1645,6 +1665,15 @@ finalize_it: } +/* Return state of last parser. If it had success, "OK" is returned, else + * "FAIL". All from the constant pool. + */ +static inline char *getParseSuccess(msg_t *pM) +{ + return (pM->bParseSuccess) ? "OK" : "FAIL"; +} + + /* al, 2011-07-26: LockMsg to avoid race conditions */ static inline char *getMSGID(msg_t *pM) @@ -1660,6 +1689,14 @@ static inline char *getMSGID(msg_t *pM) } } +/* rgerhards 2012-03-15: set parser success (an integer, acutally bool) + */ +void MsgSetParseSuccess(msg_t *pMsg, int bSuccess) +{ + assert(pMsg != NULL); + pMsg->bParseSuccess = bSuccess; +} + /* rgerhards 2009-06-12: set associated ruleset */ void MsgSetRuleset(msg_t *pMsg, ruleset_t *pRuleset) @@ -1675,7 +1712,7 @@ void MsgSetRuleset(msg_t *pMsg, ruleset_t *pRuleset) static void MsgSetRulesetByName(msg_t *pMsg, cstr_t *rulesetName) { - rulesetGetRuleset(&(pMsg->pRuleset), rsCStrGetSzStrNoNULL(rulesetName)); + rulesetGetRuleset(runConf, &(pMsg->pRuleset), rsCStrGetSzStrNoNULL(rulesetName)); } @@ -2242,8 +2279,8 @@ char *textpri(char *pRes, size_t pResLen, int pri) assert(pRes != NULL); assert(pResLen > 0); - snprintf(pRes, pResLen, "%s.%s<%d>", syslog_fac_names[LOG_FAC(pri)], - syslog_severity_names[LOG_PRI(pri)], pri); + snprintf(pRes, pResLen, "%s.%s", syslog_fac_names[LOG_FAC(pri)], + syslog_severity_names[LOG_PRI(pri)]); return pRes; } @@ -2300,6 +2337,200 @@ static uchar *getNOW(eNOWType eNow) #undef tmpBUFSIZE /* clean up */ +/* Get a CEE-Property from libee. This function probably should be + * placed somewhere else, but this smells like a big restructuring + * useful in any case. So for the time being, I'll simply leave the + * function here, as the context seems good enough. -- rgerhards, 2010-12-01 + */ +static inline void +getCEEPropVal(msg_t *pMsg, es_str_t *propName, uchar **pRes, int *buflen, unsigned short *pbMustBeFreed) +{ + es_str_t *str = NULL; + int r; + + if(*pbMustBeFreed) + free(*pRes); + *pRes = NULL; + + if(pMsg->event == NULL) goto finalize_it; + r = ee_getEventFieldAsString(pMsg->event, propName, &str); + + if(r != EE_OK) { + DBGPRINTF("msgGtCEEVar: libee error %d during ee_getEventFieldAsString\n", r); + FINALIZE; + } + *pRes = (unsigned char*) es_str2cstr(str, "#000"); + es_deleteStr(str); + *buflen = (int) ustrlen(*pRes); + *pbMustBeFreed = 1; + +finalize_it: + if(*pRes == NULL) { + /* could not find any value, so set it to empty */ + *pRes = (unsigned char*)""; + *pbMustBeFreed = 0; + } +} + + +/* Encode a JSON value and add it to provided string. Note that + * the string object may be NULL. In this case, it is created + * if and only if escaping is needed. + */ +static rsRetVal +jsonAddVal(uchar *pSrc, unsigned buflen, es_str_t **dst) +{ + unsigned char c; + es_size_t i; + char numbuf[4]; + int j; + DEFiRet; + + for(i = 0 ; i < buflen ; ++i) { + c = pSrc[i]; + if( (c >= 0x23 && c <= 0x5b) + || (c >= 0x5d /* && c <= 0x10FFFF*/) + || c == 0x20 || c == 0x21) { + /* no need to escape */ + if(*dst != NULL) + es_addChar(dst, c); + } else { + if(*dst == NULL) { + if(i == 0) { + /* we hope we have only few escapes... */ + *dst = es_newStr(buflen+10); + } else { + *dst = es_newStrFromBuf((char*)pSrc, i-1); + } + if(*dst == NULL) { + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + } + } + /* we must escape, try RFC4627-defined special sequences first */ + switch(c) { + case '\0': + es_addBuf(dst, "\\u0000", 6); + break; + case '\"': + es_addBuf(dst, "\\\"", 2); + break; + case '/': + es_addBuf(dst, "\\/", 2); + break; + case '\\': + es_addBuf(dst, "\\\\", 2); + break; + case '\010': + es_addBuf(dst, "\\b", 2); + break; + case '\014': + es_addBuf(dst, "\\f", 2); + break; + case '\n': + es_addBuf(dst, "\\n", 2); + break; + case '\r': + es_addBuf(dst, "\\r", 2); + break; + case '\t': + es_addBuf(dst, "\\t", 2); + break; + default: + /* TODO : proper Unicode encoding (see header comment) */ + for(j = 0 ; j < 4 ; ++j) { + numbuf[3-j] = hexdigit[c % 16]; + c = c / 16; + } + es_addBuf(dst, "\\u", 2); + es_addBuf(dst, numbuf, 4); + break; + } + } + } +finalize_it: + RETiRet; +} + + +/* encode a property in JSON escaped format. This is a helper + * to MsgGetProp. It needs to update all provided parameters. + * Note: Code is borrowed from libee (my own code, so ASL 2.0 + * is fine with it); this function may later be replaced by + * some "better" and more complete implementation (maybe from + * libee or its helpers). + * For performance reasons, we begin to copy the string only + * when we recognice that we actually need to do some escaping. + * rgerhards, 2012-03-16 + */ +static rsRetVal +jsonEncode(uchar **ppRes, unsigned short *pbMustBeFreed, int *pBufLen) +{ + unsigned buflen; + uchar *pSrc; + es_str_t *dst = NULL; + DEFiRet; + + pSrc = *ppRes; + buflen = (*pBufLen == -1) ? ustrlen(pSrc) : *pBufLen; + CHKiRet(jsonAddVal(pSrc, buflen, &dst)); + + if(dst != NULL) { + /* we updated the string and need to replace the + * previous data. + */ + if(*pbMustBeFreed) + free(*ppRes); + *ppRes = (uchar*)es_str2cstr(dst, NULL); + *pbMustBeFreed = 1; + *pBufLen = -1; + es_deleteStr(dst); + } + +finalize_it: + RETiRet; +} + + +/* Format a property as JSON field, that means + * "name"="value" + * where value is JSON-escaped (here we assume that the name + * only contains characters from the valid character set). + * Note: this function duplicates code from jsonEncode(). + * TODO: these two functions should be combined, at least if + * that makes any sense from a performance PoV - definitely + * something to consider at a later stage. rgerhards, 2012-04-19 + */ +static rsRetVal +jsonField(struct templateEntry *pTpe, uchar **ppRes, unsigned short *pbMustBeFreed, int *pBufLen) +{ + unsigned buflen; + uchar *pSrc; + es_str_t *dst = NULL; + DEFiRet; + + pSrc = *ppRes; + buflen = (*pBufLen == -1) ? ustrlen(pSrc) : *pBufLen; + /* we hope we have only few escapes... */ + dst = es_newStr(buflen+es_strlen(pTpe->data.field.fieldName)+15); + es_addChar(&dst, '"'); + es_addStr(&dst, pTpe->data.field.fieldName); + es_addBufConstcstr(&dst, "\"=\""); + CHKiRet(jsonAddVal(pSrc, buflen, &dst)); + es_addChar(&dst, '"'); + + if(*pbMustBeFreed) + free(*ppRes); + /* we know we do not have \0 chars - so the size does not change */ + *pBufLen = es_strlen(dst); + *ppRes = (uchar*)es_str2cstr(dst, NULL); + *pbMustBeFreed = 1; + es_deleteStr(dst); + +finalize_it: + RETiRet; +} + + /* This function returns a string-representation of the * requested message property. This is a generic function used * to abstract properties so that these can be easier @@ -2342,7 +2573,7 @@ static uchar *getNOW(eNOWType eNow) *pPropLen = sizeof("**OUT OF MEMORY**") - 1; \ return(UCHAR_CONSTANT("**OUT OF MEMORY**"));} uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, - propid_t propID, size_t *pPropLen, + propid_t propid, es_str_t *propName, size_t *pPropLen, unsigned short *pbMustBeFreed) { uchar *pRes; /* result pointer */ @@ -2351,6 +2582,7 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, uchar *pBuf; int iLen; short iOffs; + es_str_t *str; /* for CEE handling, temp. string */ BEGINfunc assert(pMsg != NULL); @@ -2364,7 +2596,7 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, *pbMustBeFreed = 0; - switch(propID) { + switch(propid) { case PROP_MSG: pRes = getMSG(pMsg); bufLen = getMSGLen(pMsg); @@ -2382,12 +2614,6 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, case PROP_RAWMSG: getRawMsg(pMsg, &pRes, &bufLen); break; - /* enable this, if someone actually uses UxTradMsg, delete after some time has - * passed and nobody complained -- rgerhards, 2009-06-16 - case PROP_UXTRADMSG: - pRes = getUxTradMsg(pMsg); - break; - */ case PROP_INPUTNAME: getInputName(pMsg, &pRes, &bufLen); break; @@ -2446,6 +2672,9 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, case PROP_MSGID: pRes = (uchar*)getMSGID(pMsg); break; + case PROP_PARSESUCCESS: + pRes = (uchar*)getParseSuccess(pMsg); + break; case PROP_SYS_NOW: if((pRes = getNOW(NOW_NOW)) == NULL) { RET_OUT_OF_MEMORY; @@ -2497,17 +2726,50 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, case PROP_SYS_MYHOSTNAME: pRes = glbl.GetLocalHostName(); break; + case PROP_CEE_ALL_JSON: + if(pMsg->event == NULL) { + if(*pbMustBeFreed == 1) + free(pRes); + pRes = (uchar*) ""; + *pbMustBeFreed = 0; + } else { + ee_fmtEventToJSON(pMsg->event, &str); + pRes = (uchar*) es_str2cstr(str, "#000"); + es_deleteStr(str); + *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ + } + break; + case PROP_CEE: + getCEEPropVal(pMsg, propName, &pRes, &bufLen, pbMustBeFreed); + break; case PROP_SYS_BOM: if(*pbMustBeFreed == 1) free(pRes); pRes = (uchar*) "\xEF\xBB\xBF"; *pbMustBeFreed = 0; break; + case PROP_SYS_UPTIME: + { + struct sysinfo s_info; + + if((pRes = (uchar*) MALLOC(sizeof(uchar) * 32)) == NULL) { + RET_OUT_OF_MEMORY; + } + *pbMustBeFreed = 1; + + if(sysinfo(&s_info) < 0) { + *pPropLen = sizeof("**SYSCALL FAILED**") - 1; + return(UCHAR_CONSTANT("**SYSCALL FAILED**")); + } + + snprintf((char*) pRes, sizeof(uchar) * 32, "%ld", s_info.uptime); + } + break; default: /* there is no point in continuing, we may even otherwise render the * error message unreadable. rgerhards, 2007-07-10 */ - dbgprintf("invalid property id: '%d'\n", propID); + dbgprintf("invalid property id: '%d'\n", propid); *pbMustBeFreed = 0; *pPropLen = sizeof("**INVALID PROPERTY NAME**") - 1; return UCHAR_CONSTANT("**INVALID PROPERTY NAME**"); @@ -2515,6 +2777,7 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, /* If we did not receive a template pointer, we are already done... */ if(pTpe == NULL) { + *pPropLen = (bufLen == -1) ? ustrlen(pRes) : bufLen; return pRes; } @@ -3058,8 +3321,8 @@ dbgprintf("prop repl 4, pRes='%s', len %d\n", pRes, bufLen); } } - /* finally, we need to check if the property should be formatted in CSV - * format (we use RFC 4180, and always use double quotes). As of this writing, + /* finally, we need to check if the property should be formatted in CSV or JSON. + * For CSV we use RFC 4180, and always use double quotes. As of this writing, * this should be the last action carried out on the property, but in the * future there may be reasons to change that. -- rgerhards, 2009-04-02 */ @@ -3093,6 +3356,10 @@ dbgprintf("prop repl 4, pRes='%s', len %d\n", pRes, bufLen); pRes = pBStart; bufLen = -1; *pbMustBeFreed = 1; + } else if(pTpe->data.field.options.bJSON) { + jsonEncode(&pRes, pbMustBeFreed, &bufLen); + } else if(pTpe->data.field.options.bJSONf) { + jsonField(pTpe, &pRes, pbMustBeFreed, &bufLen); } if(bufLen == -1) @@ -3105,48 +3372,70 @@ dbgprintf("end prop repl, pRes='%s', len %d\n", pRes, bufLen); } -/* The returns a message variable suitable for use with RainerScript. Most importantly, this means - * that the value is returned in a var_t object. The var_t is constructed inside this function and - * MUST be freed by the caller. - * rgerhards, 2008-02-25 +/* The function returns a cee variable suitable for use with RainerScript. + * Note: caller must free the returned string. + * Note that we need to do a lot of conversions between es_str_t and cstr -- this will go away once + * we have moved larger parts of rsyslog to es_str_t. Acceptable for the moment, especially as we intend + * to rewrite the script engine as well! + * rgerhards, 2010-12-03 */ -rsRetVal -msgGetMsgVar(msg_t *pThis, cstr_t *pstrPropName, var_t **ppVar) +es_str_t* +msgGetCEEVarNew(msg_t *pMsg, char *name) +{ + es_str_t *estr = NULL; + es_str_t *epropName = NULL; + struct ee_field *field; + + ISOBJ_TYPE_assert(pMsg, msg); + + if(pMsg->event == NULL) { + estr = es_newStr(1); + goto done; + } + + epropName = es_newStrFromCStr(name, strlen(name)); // TODO: optimize (in grammar!) + field = ee_getEventField(pMsg->event, epropName); + if(field != NULL) { + ee_getFieldAsString(field, &estr); + } + if(estr == NULL) { + DBGPRINTF("msgGetCEEVar: error obtaining var (field=%p, var='%s')\n", + field, name); + estr = es_newStrFromCStr("*ERROR*", sizeof("*ERROR*") - 1); + } + es_deleteStr(epropName); + +done: + return estr; +} + + +/* Return an es_str_t for given message property. + */ +es_str_t* +msgGetMsgVarNew(msg_t *pThis, uchar *name) { - DEFiRet; - var_t *pVar; size_t propLen; uchar *pszProp = NULL; - cstr_t *pstrProp; propid_t propid; unsigned short bMustBeFreed = 0; + es_str_t *estr; ISOBJ_TYPE_assert(pThis, msg); - ASSERT(pstrPropName != NULL); - ASSERT(ppVar != NULL); - - /* make sure we have a var_t instance */ - CHKiRet(var.Construct(&pVar)); - CHKiRet(var.ConstructFinalize(pVar)); /* always call MsgGetProp() without a template specifier */ /* TODO: optimize propNameToID() call -- rgerhards, 2009-06-26 */ - propNameToID(pstrPropName, &propid); - pszProp = (uchar*) MsgGetProp(pThis, NULL, propid, &propLen, &bMustBeFreed); - - /* now create a string object out of it and hand that over to the var */ - CHKiRet(rsCStrConstructFromszStr(&pstrProp, pszProp)); - CHKiRet(var.SetString(pVar, pstrProp)); - - /* finally store var */ - *ppVar = pVar; + propNameStrToID(name, &propid); + pszProp = (uchar*) MsgGetProp(pThis, NULL, propid, NULL, &propLen, &bMustBeFreed); -finalize_it: + estr = es_newStrFromCStr((char*)pszProp, propLen); if(bMustBeFreed) free(pszProp); - RETiRet; + return estr; } + + /* This function can be used as a generic way to set properties. * We have to handle a lot of legacy, so our return value is not always * 100% correct (called functions do not always provide one, should @@ -3176,11 +3465,6 @@ rsRetVal MsgSetProperty(msg_t *pThis, var_t *pProp) MsgSetMSGoffs(pThis, pProp->val.num); } else if(isProp("pszRawMsg")) { MsgSetRawMsg(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr), cstrLen(pProp->val.pStr)); - /* enable this, if someone actually uses UxTradMsg, delete after some time has - * passed and nobody complained -- rgerhards, 2009-06-16 - } else if(isProp("offAfterPRI")) { - pThis->offAfterPRI = pProp->val.num; - */ } else if(isProp("pszUxTradMsg")) { /*IGNORE*/; /* this *was* a property, but does no longer exist */ } else if(isProp("pszTAG")) { @@ -3264,7 +3548,6 @@ rsRetVal msgQueryInterface(void) { return RS_RET_NOT_IMPLEMENTED; } */ BEGINObjClassInit(msg, 1, OBJ_IS_CORE_MODULE) /* request objects we use */ - CHKiRet(objUse(var, CORE_COMPONENT)); CHKiRet(objUse(datetime, CORE_COMPONENT)); CHKiRet(objUse(glbl, CORE_COMPONENT)); CHKiRet(objUse(prop, CORE_COMPONENT)); diff --git a/runtime/msg.h b/runtime/msg.h index 26a07aca..ed2e9d04 100644 --- a/runtime/msg.h +++ b/runtime/msg.h @@ -29,10 +29,12 @@ #define MSG_H_INCLUDED 1 #include <pthread.h> +#include <libestr.h> #include "obj.h" #include "syslogd-types.h" #include "template.h" #include "atomic.h" +#include "libee/libee.h" /* rgerhards 2004-11-08: The following structure represents a @@ -63,6 +65,7 @@ struct msg { int iRefCount; /* reference counter (0 = unused) */ sbool bDoLock; /* use the mutex? */ sbool bAlreadyFreed; /* aid to help detect a well-hidden bad bug -- TODO: remove when no longer needed */ + sbool bParseSuccess; /* set to reflect state of last executed higher level parser */ short iSeverity; /* the severity 0..7 */ short iFacility; /* Facility code 0 .. 23*/ short offAfterPRI; /* offset, at which raw message WITHOUT PRI part starts in pszRawMsg */ @@ -106,6 +109,7 @@ struct msg { it obviously is solved in way or another...). */ struct syslogTime tRcvdAt;/* time the message entered this program */ struct syslogTime tTIMESTAMP;/* (parsed) value of the timestamp */ + struct ee_event *event; /**< libee event */ /* some fixed-size buffers to save malloc()/free() for frequently used fields (from the default templates) */ uchar szRawMsg[CONF_RAWMSG_BUFSIZE]; /* most messages are small, and these are stored here (without malloc/free!) */ uchar szHOSTNAME[CONF_HOSTNAME_BUFSIZE]; @@ -117,6 +121,8 @@ struct msg { char pszTimestamp3339[CONST_LEN_TIMESTAMP_3339 + 1]; char pszTIMESTAMP_SecFrac[7]; /* Note: a pointer is 64 bits/8 char, so this is actually fewer than a pointer! */ char pszRcvdAt_SecFrac[7]; /* same as above. Both are fractional seconds for their respective timestamp */ + char pszTIMESTAMP_Unix[12]; /* almost as small as a pointer! */ + char pszRcvdAt_Unix[12]; }; @@ -147,6 +153,7 @@ void MsgSetInputName(msg_t *pMsg, prop_t*); rsRetVal MsgSetAPPNAME(msg_t *pMsg, char* pszAPPNAME); rsRetVal MsgSetPROCID(msg_t *pMsg, char* pszPROCID); rsRetVal MsgSetMSGID(msg_t *pMsg, char* pszMSGID); +void MsgSetParseSuccess(msg_t *pMsg, int bSuccess); void MsgSetTAG(msg_t *pMsg, uchar* pszBuf, size_t lenBuf); void MsgSetRuleset(msg_t *pMsg, ruleset_t*); rsRetVal MsgSetFlowControlType(msg_t *pMsg, flowControl_t eFlowCtl); @@ -163,14 +170,19 @@ void MsgSetRawMsgWOSize(msg_t *pMsg, char* pszRawMsg); void MsgSetRawMsg(msg_t *pMsg, char* pszRawMsg, size_t lenMsg); rsRetVal MsgReplaceMSG(msg_t *pThis, uchar* pszMSG, int lenMSG); uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, - propid_t propID, size_t *pPropLen, unsigned short *pbMustBeFreed); + propid_t propid, es_str_t *propName, + size_t *pPropLen, unsigned short *pbMustBeFreed); char *textpri(char *pRes, size_t pResLen, int pri); rsRetVal msgGetMsgVar(msg_t *pThis, cstr_t *pstrPropName, var_t **ppVar); +es_str_t* msgGetMsgVarNew(msg_t *pThis, uchar *name); rsRetVal MsgEnableThreadSafety(void); uchar *getRcvFrom(msg_t *pM); void getTAG(msg_t *pM, uchar **ppBuf, int *piLen); char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt); char *getPRI(msg_t *pMsg); +void getRawMsg(msg_t *pM, uchar **pBuf, int *piLen); +rsRetVal msgGetCEEVar(msg_t *pThis, cstr_t *propName, var_t **ppVar); +es_str_t* msgGetCEEVarNew(msg_t *pMsg, char *name); /* TODO: remove these five (so far used in action.c) */ diff --git a/runtime/net.c b/runtime/net.c index 1264b2cb..dcf9cb52 100644 --- a/runtime/net.c +++ b/runtime/net.c @@ -12,7 +12,7 @@ * long term, but it is good to have it out of syslogd.c. Maybe this here is * an interim location ;) * - * Copyright 2007-2012 Rainer Gerhards and Adiscon GmbH. + * Copyright 2007-2011 Rainer Gerhards and Adiscon GmbH. * * rgerhards, 2008-04-16: I changed this code to LGPL today. I carefully analyzed * that it does not borrow code from the original sysklogd and that I have @@ -65,6 +65,7 @@ #include "obj.h" #include "errmsg.h" #include "net.h" +#include "dnscache.h" #ifdef OS_SOLARIS # define s6_addr32 _S6_un._S6_u32 @@ -1067,108 +1068,6 @@ should_use_so_bsdcompat(void) #define SO_BSDCOMPAT 0 #endif -/* get the hostname of the message source. This was originally in cvthname() - * but has been moved out of it because of clarity and fuctional separation. - * It must be provided by the socket we received the message on as well as - * a NI_MAXHOST size large character buffer for the FQDN. - * 2008-05-16 rgerhards: added field for IP address representation. Must also - * be NI_MAXHOST size large. - * - * Please see http://www.hmug.org/man/3/getnameinfo.php (under Caveats) - * for some explanation of the code found below. We do by default not - * discard message where we detected malicouos DNS PTR records. However, - * there is a user-configurabel option that will tell us if - * we should abort. For this, the return value tells the caller if the - * message should be processed (1) or discarded (0). - */ -static rsRetVal -gethname(struct sockaddr_storage *f, uchar *pszHostFQDN, uchar *ip) -{ - DEFiRet; - int error; - sigset_t omask, nmask; - struct addrinfo hints, *res; - - assert(f != NULL); - assert(pszHostFQDN != NULL); - - error = mygetnameinfo((struct sockaddr *)f, SALEN((struct sockaddr *)f), - (char*) ip, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); - - if (error) { - dbgprintf("Malformed from address %s\n", gai_strerror(error)); - strcpy((char*) pszHostFQDN, "???"); - strcpy((char*) ip, "???"); - ABORT_FINALIZE(RS_RET_INVALID_SOURCE); - } - - if(!glbl.GetDisableDNS()) { - sigemptyset(&nmask); - sigaddset(&nmask, SIGHUP); - pthread_sigmask(SIG_BLOCK, &nmask, &omask); - - error = mygetnameinfo((struct sockaddr *)f, SALEN((struct sockaddr *) f), - (char*)pszHostFQDN, NI_MAXHOST, NULL, 0, NI_NAMEREQD); - - if (error == 0) { - memset (&hints, 0, sizeof (struct addrinfo)); - hints.ai_flags = AI_NUMERICHOST; - - /* we now do a lookup once again. This one should fail, - * because we should not have obtained a non-numeric address. If - * we got a numeric one, someone messed with DNS! - */ - if (getaddrinfo ((char*)pszHostFQDN, NULL, &hints, &res) == 0) { - uchar szErrMsg[1024]; - freeaddrinfo (res); - /* OK, we know we have evil. The question now is what to do about - * it. One the one hand, the message might probably be intended - * to harm us. On the other hand, losing the message may also harm us. - * Thus, the behaviour is controlled by the $DropMsgsWithMaliciousDnsPTRRecords - * option. If it tells us we should discard, we do so, else we proceed, - * but log an error message together with it. - * time being, we simply drop the name we obtained and use the IP - that one - * is OK in any way. We do also log the error message. rgerhards, 2007-07-16 - */ - if(glbl.GetDropMalPTRMsgs() == 1) { - snprintf((char*)szErrMsg, sizeof(szErrMsg) / sizeof(uchar), - "Malicious PTR record, message dropped " - "IP = \"%s\" HOST = \"%s\"", - ip, pszHostFQDN); - errmsg.LogError(0, RS_RET_MALICIOUS_ENTITY, "%s", szErrMsg); - pthread_sigmask(SIG_SETMASK, &omask, NULL); - ABORT_FINALIZE(RS_RET_MALICIOUS_ENTITY); - } - - /* Please note: we deal with a malicous entry. Thus, we have crafted - * the snprintf() below so that all text is in front of the entry - maybe - * it contains characters that make the message unreadable - * (OK, I admit this is more or less impossible, but I am paranoid...) - * rgerhards, 2007-07-16 - */ - snprintf((char*)szErrMsg, sizeof(szErrMsg) / sizeof(uchar), - "Malicious PTR record (message accepted, but used IP " - "instead of PTR name: IP = \"%s\" HOST = \"%s\"", - ip, pszHostFQDN); - errmsg.LogError(0, NO_ERRCODE, "%s", szErrMsg); - - error = 1; /* that will trigger using IP address below. */ - } - } - pthread_sigmask(SIG_SETMASK, &omask, NULL); - } - - if(error || glbl.GetDisableDNS()) { - dbgprintf("Host name for your address (%s) unknown\n", ip); - strcpy((char*) pszHostFQDN, (char*)ip); - ABORT_FINALIZE(RS_RET_ADDRESS_UNKNOWN); - } - -finalize_it: - RETiRet; -} - - /* print out which socket we are listening on. This is only * a debug aid. rgerhards, 2007-07-02 @@ -1232,9 +1131,9 @@ rsRetVal cvthname(struct sockaddr_storage *f, uchar *pszHost, uchar *pszHostFQDN assert(pszHost != NULL); assert(pszHostFQDN != NULL); - iRet = gethname(f, pszHostFQDN, pszIP); + iRet = dnscacheLookup(f, pszHostFQDN, pszIP); - if(iRet == RS_RET_INVALID_SOURCE || iRet == RS_RET_ADDRESS_UNKNOWN) { + if(iRet == RS_RET_INVALID_SOURCE) { strcpy((char*) pszHost, (char*) pszHostFQDN); /* we use whatever was provided as replacement */ ABORT_FINALIZE(RS_RET_OK); /* this is handled, we are happy with it */ } else if(iRet != RS_RET_OK) { diff --git a/runtime/netstrm.c b/runtime/netstrm.c index 3658006f..a6f840a5 100644 --- a/runtime/netstrm.c +++ b/runtime/netstrm.c @@ -64,6 +64,7 @@ ENDobjConstruct(netstrm) /* destructor for the netstrm object */ BEGINobjDestruct(netstrm) /* be sure to specify the object type also in END and CODESTART macros! */ CODESTARTobjDestruct(netstrm) +//printf("destruct driver data %p\n", pThis->pDrvrData); if(pThis->pDrvrData != NULL) iRet = pThis->Drvr.Destruct(&pThis->pDrvrData); ENDobjDestruct(netstrm) @@ -169,6 +170,7 @@ Rcv(netstrm_t *pThis, uchar *pBuf, ssize_t *pLenBuf) { DEFiRet; ISOBJ_TYPE_assert(pThis, netstrm); +//printf("Rcv %p\n", pThis); iRet = pThis->Drvr.Rcv(pThis->pDrvrData, pBuf, pLenBuf); RETiRet; } diff --git a/runtime/netstrms.c b/runtime/netstrms.c index ea2dd9f3..0122064d 100644 --- a/runtime/netstrms.c +++ b/runtime/netstrms.c @@ -32,7 +32,6 @@ #include "rsyslog.h" #include "module-template.h" #include "obj.h" -//#include "errmsg.h" #include "nsd.h" #include "netstrm.h" #include "nssel.h" diff --git a/runtime/nsd.h b/runtime/nsd.h index d1f164ec..ab64c443 100644 --- a/runtime/nsd.h +++ b/runtime/nsd.h @@ -27,6 +27,16 @@ #include <sys/socket.h> +/** + * The following structure is a set of descriptors that need to be processed. + * This set will be the result of the epoll call and be used + * in the actual request processing stage. -- rgerhards, 2011-01-24 + */ +struct nsd_epworkset_s { + int id; + void *pUsr; +}; + enum nsdsel_waitOp_e { NSDSEL_RD = 1, NSDSEL_WR = 2, @@ -90,7 +100,7 @@ BEGINinterface(nsdpoll) /* name must also be changed in ENDinterface macro! */ rsRetVal (*Construct)(nsdpoll_t **ppThis); rsRetVal (*Destruct)(nsdpoll_t **ppThis); rsRetVal (*Ctl)(nsdpoll_t *pNsdpoll, nsd_t *pNsd, int id, void *pUsr, int mode, int op); - rsRetVal (*Wait)(nsdpoll_t *pNsdpoll, int timeout, int *idRdy, void **ppUsr); + rsRetVal (*Wait)(nsdpoll_t *pNsdpoll, int timeout, int *numReady, nsd_epworkset_t workset[]); ENDinterface(nsdpoll) #define nsdpollCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ diff --git a/runtime/nsd_gtls.c b/runtime/nsd_gtls.c index e1192aaf..c2db9c94 100644 --- a/runtime/nsd_gtls.c +++ b/runtime/nsd_gtls.c @@ -52,7 +52,6 @@ #include "nsd_gtls.h" /* things to move to some better place/functionality - TODO */ -#define DH_BITS 1024 #define CRLFILE "crl.pem" @@ -86,7 +85,6 @@ static pthread_mutex_t mutGtlsStrerror; /**< a mutex protecting the potentially /* ------------------------------ GnuTLS specifics ------------------------------ */ static gnutls_certificate_credentials xcred; -static gnutls_dh_params dh_params; #ifdef DEBUG #if 0 /* uncomment, if needed some time again -- DEV Debug only */ @@ -620,7 +618,6 @@ gtlsInitSession(nsd_gtls_t *pThis) /* request client certificate if any. */ gnutls_certificate_server_set_request( session, GNUTLS_CERT_REQUEST); - gnutls_dh_set_prime_bits(session, DH_BITS); pThis->sess = session; @@ -629,23 +626,6 @@ finalize_it: } -static rsRetVal -generate_dh_params(void) -{ - int gnuRet; - DEFiRet; - /* Generate Diffie Hellman parameters - for use with DHE - * kx algorithms. These should be discarded and regenerated - * once a day, once a week or once a month. Depending on the - * security requirements. - */ - CHKgnutls(gnutls_dh_params_init( &dh_params)); - CHKgnutls(gnutls_dh_params_generate2( dh_params, DH_BITS)); -finalize_it: - RETiRet; -} - - /* set up all global things that are needed for server operations * rgerhards, 2008-04-30 */ @@ -659,8 +639,6 @@ gtlsGlblInitLstn(void) * considered legacy. -- rgerhards, 2008-05-05 */ /*CHKgnutls(gnutls_certificate_set_x509_crl_file(xcred, CRLFILE, GNUTLS_X509_FMT_PEM));*/ - CHKiRet(generate_dh_params()); - gnutls_certificate_set_dh_params(xcred, dh_params); /* this is void */ bGlblSrvrInitDone = 1; /* we are all set now */ /* now we need to add our certificate */ @@ -1432,6 +1410,10 @@ AcceptConnReq(nsd_t *pNsd, nsd_t **ppNew) /* we got a handshake, now check authorization */ CHKiRet(gtlsChkPeerAuth(pNew)); } else { + uchar *pGnuErr = gtlsStrerror(gnuRet); + errmsg.LogError(0, RS_RET_TLS_HANDSHAKE_ERR, + "gnutls returned error on handshake: %s\n", pGnuErr); + free(pGnuErr); ABORT_FINALIZE(RS_RET_TLS_HANDSHAKE_ERR); } diff --git a/runtime/nsd_ptcp.c b/runtime/nsd_ptcp.c index 69eb7684..a174899c 100644 --- a/runtime/nsd_ptcp.c +++ b/runtime/nsd_ptcp.c @@ -50,6 +50,7 @@ #include "nsdsel_ptcp.h" #include "nsdpoll_ptcp.h" #include "nsd_ptcp.h" +#include "dnscache.h" MODULE_TYPE_LIB MODULE_TYPE_NOKEEP @@ -248,50 +249,17 @@ Abort(nsd_t *pNsd) * rgerhards, 2008-03-31 */ static rsRetVal -FillRemHost(nsd_ptcp_t *pThis, struct sockaddr *pAddr) +FillRemHost(nsd_ptcp_t *pThis, struct sockaddr_storage *pAddr) { - int error; uchar szIP[NI_MAXHOST] = ""; uchar szHname[NI_MAXHOST] = ""; - struct addrinfo hints, *res; size_t len; DEFiRet; ISOBJ_TYPE_assert(pThis, nsd_ptcp); assert(pAddr != NULL); - error = getnameinfo(pAddr, SALEN(pAddr), (char*)szIP, sizeof(szIP), NULL, 0, NI_NUMERICHOST); - - if(error) { - dbgprintf("Malformed from address %s\n", gai_strerror(error)); - strcpy((char*)szHname, "???"); - strcpy((char*)szIP, "???"); - ABORT_FINALIZE(RS_RET_INVALID_HNAME); - } - - if(!glbl.GetDisableDNS()) { - error = getnameinfo(pAddr, SALEN(pAddr), (char*)szHname, NI_MAXHOST, NULL, 0, NI_NAMEREQD); - if(error == 0) { - memset (&hints, 0, sizeof (struct addrinfo)); - hints.ai_flags = AI_NUMERICHOST; - hints.ai_socktype = SOCK_STREAM; - /* we now do a lookup once again. This one should fail, - * because we should not have obtained a non-numeric address. If - * we got a numeric one, someone messed with DNS! - */ - if(getaddrinfo((char*)szHname, NULL, &hints, &res) == 0) { - freeaddrinfo (res); - /* OK, we know we have evil, so let's indicate this to our caller */ - snprintf((char*)szHname, NI_MAXHOST, "[MALICIOUS:IP=%s]", szIP); - dbgprintf("Malicious PTR record, IP = \"%s\" HOST = \"%s\"", szIP, szHname); - iRet = RS_RET_MALICIOUS_HNAME; - } - } else { - strcpy((char*)szHname, (char*)szIP); - } - } else { - strcpy((char*)szHname, (char*)szIP); - } + CHKiRet(dnscacheLookup(pAddr, szHname, szIP)); /* We now have the names, so now let's allocate memory and store them permanently. * (side note: we may hold on to these values for quite a while, thus we trim their @@ -352,7 +320,7 @@ AcceptConnReq(nsd_t *pNsd, nsd_t **ppNew) * of this function. -- rgerhards, 2008-12-01 */ memcpy(&pNew->remAddr, &addr, sizeof(struct sockaddr_storage)); - CHKiRet(FillRemHost(pNew, (struct sockaddr*) &addr)); + CHKiRet(FillRemHost(pNew, &addr)); /* set the new socket to non-blocking IO -TODO:do we really need to do this here? Do we always want it? */ if((sockflags = fcntl(iNewSock, F_GETFL)) != -1) { @@ -492,7 +460,7 @@ LstnInit(netstrms_t *pNS, void *pUsr, rsRetVal(*fAddLstn)(void*,netstrm_t*), #endif ) { /* TODO: check if *we* bound the socket - else we *have* an error! */ - dbgprintf("error %d while binding tcp socket", errno); + dbgprintf("error %d while binding tcp socket\n", errno); close(sock); sock = -1; continue; @@ -504,7 +472,7 @@ LstnInit(netstrms_t *pNS, void *pUsr, rsRetVal(*fAddLstn)(void*,netstrm_t*), * to a fixed, reasonable, limit that should work. Only if * that fails, too, we give up. */ - dbgprintf("listen with a backlog of %d failed - retrying with default of 32.", + dbgprintf("listen with a backlog of %d failed - retrying with default of 32.\n", iSessMax / 10 + 5); if(listen(sock, 32) < 0) { dbgprintf("tcp listen error %d, suspending\n", errno); @@ -537,7 +505,7 @@ LstnInit(netstrms_t *pNS, void *pUsr, rsRetVal(*fAddLstn)(void*,netstrm_t*), "- this may or may not be an error indication.\n", numSocks, maxs); if(numSocks == 0) { - dbgprintf("No TCP listen sockets could successfully be initialized"); + dbgprintf("No TCP listen sockets could successfully be initialized\n"); ABORT_FINALIZE(RS_RET_COULD_NOT_BIND); } diff --git a/runtime/nsdpoll_ptcp.c b/runtime/nsdpoll_ptcp.c index ef9c37a3..8c90d7fd 100644 --- a/runtime/nsdpoll_ptcp.c +++ b/runtime/nsdpoll_ptcp.c @@ -71,13 +71,16 @@ addEvent(nsdpoll_ptcp_t *pThis, int id, void *pUsr, int mode, nsd_ptcp_t *pSock, pNew->pUsr = pUsr; pNew->pSock = pSock; pNew->event.events = 0; /* TODO: at some time we should be able to use EPOLLET */ + //pNew->event.events = EPOLLET; if(mode & NSDPOLL_IN) pNew->event.events |= EPOLLIN; if(mode & NSDPOLL_OUT) pNew->event.events |= EPOLLOUT; pNew->event.data.u64 = (uint64) pNew; + pthread_mutex_lock(&pThis->mutEvtLst); pNew->pNext = pThis->pRoot; pThis->pRoot = pNew; + pthread_mutex_unlock(&pThis->mutEvtLst); *pEvtLst = pNew; finalize_it: @@ -94,6 +97,7 @@ unlinkEvent(nsdpoll_ptcp_t *pThis, int id, void *pUsr, nsdpoll_epollevt_lst_t ** nsdpoll_epollevt_lst_t *pPrev = NULL; DEFiRet; + pthread_mutex_lock(&pThis->mutEvtLst); pEvtLst = pThis->pRoot; while(pEvtLst != NULL && !(pEvtLst->id == id && pEvtLst->pUsr == pUsr)) { pPrev = pEvtLst; @@ -111,6 +115,7 @@ unlinkEvent(nsdpoll_ptcp_t *pThis, int id, void *pUsr, nsdpoll_epollevt_lst_t ** pPrev->pNext = pEvtLst->pNext; finalize_it: + pthread_mutex_unlock(&pThis->mutEvtLst); RETiRet; } @@ -147,13 +152,27 @@ BEGINobjConstruct(nsdpoll_ptcp) /* be sure to specify the object type also in EN DBGPRINTF("epoll_create1() could not create fd\n"); ABORT_FINALIZE(RS_RET_IO_ERROR); } + pthread_mutex_init(&pThis->mutEvtLst, NULL); finalize_it: ENDobjConstruct(nsdpoll_ptcp) /* destructor for the nsdpoll_ptcp object */ BEGINobjDestruct(nsdpoll_ptcp) /* be sure to specify the object type also in END and CODESTART macros! */ + nsdpoll_epollevt_lst_t *node; + nsdpoll_epollevt_lst_t *nextnode; CODESTARTobjDestruct(nsdpoll_ptcp) + /* we check if the epoll list still holds entries. This may happen, but + * is a bit unusual. + */ + if(pThis->pRoot != NULL) { + for(node = pThis->pRoot ; node != NULL ; node = nextnode) { + nextnode = node->pNext; + dbgprintf("nsdpoll_ptcp destruct, need to destruct node %p\n", node); + delEvent(&node); + } + } + pthread_mutex_destroy(&pThis->mutEvtLst); ENDobjDestruct(nsdpoll_ptcp) @@ -202,23 +221,25 @@ finalize_it: /* Wait for io to become ready. After the successful call, idRdy contains the * id set by the caller for that i/o event, ppUsr is a pointer to a location * where the user pointer shall be stored. - * TODO: this is a trivial implementation that only polls one event at a time. We - * may later extend it to poll for multiple events, what would cause less - * overhead. + * numEntries contains the maximum number of entries on entry and the actual + * number of entries actually read on exit. * rgerhards, 2009-11-18 */ static rsRetVal -Wait(nsdpoll_t *pNsdpoll, int timeout, int *idRdy, void **ppUsr) { +Wait(nsdpoll_t *pNsdpoll, int timeout, int *numEntries, nsd_epworkset_t workset[]) { nsdpoll_ptcp_t *pThis = (nsdpoll_ptcp_t*) pNsdpoll; nsdpoll_epollevt_lst_t *pOurEvt; - struct epoll_event event; + struct epoll_event event[128]; int nfds; + int i; DEFiRet; - assert(idRdy != NULL); - assert(ppUsr != NULL); + assert(workset != NULL); - nfds = epoll_wait(pThis->efd, &event, 1, timeout); + if(*numEntries > 128) + *numEntries = 128; + DBGPRINTF("doing epoll_wait for max %d events\n", *numEntries); + nfds = epoll_wait(pThis->efd, event, *numEntries, timeout); if(nfds == -1) { if(errno == EINTR) { ABORT_FINALIZE(RS_RET_EINTR); @@ -230,10 +251,15 @@ Wait(nsdpoll_t *pNsdpoll, int timeout, int *idRdy, void **ppUsr) { ABORT_FINALIZE(RS_RET_TIMEOUT); } - /* we got a valid event, so tell the caller... */ - pOurEvt = (nsdpoll_epollevt_lst_t*) event.data.u64; - *idRdy = pOurEvt->id; - *ppUsr = pOurEvt->pUsr; + /* we got valid events, so tell the caller... */ +dbgprintf("epoll returned %d entries\n", nfds); + for(i = 0 ; i < nfds ; ++i) { + pOurEvt = (nsdpoll_epollevt_lst_t*) event[i].data.u64; + workset[i].id = pOurEvt->id; + workset[i].pUsr = pOurEvt->pUsr; +dbgprintf("epoll push ppusr[%d]: %p\n", i, pOurEvt->pUsr); + } + *numEntries = nfds; finalize_it: RETiRet; diff --git a/runtime/nsdpoll_ptcp.h b/runtime/nsdpoll_ptcp.h index cea2823d..dfefad1b 100644 --- a/runtime/nsdpoll_ptcp.h +++ b/runtime/nsdpoll_ptcp.h @@ -49,6 +49,7 @@ struct nsdpoll_ptcp_s { BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ int efd; /* file descriptor used by epoll */ nsdpoll_epollevt_lst_t *pRoot; /* Root of the epoll event list */ + pthread_mutex_t mutEvtLst; }; /* interface is defined in nsd.h, we just implement it! */ diff --git a/runtime/nspoll.c b/runtime/nspoll.c index 64927280..a936b255 100644 --- a/runtime/nspoll.c +++ b/runtime/nspoll.c @@ -129,11 +129,11 @@ finalize_it: /* Carries out the actual wait (all done in lower layers) */ static rsRetVal -Wait(nspoll_t *pThis, int timeout, int *idRdy, void **ppUsr) { +Wait(nspoll_t *pThis, int timeout, int *numEntries, nsd_epworkset_t workset[]) { DEFiRet; ISOBJ_TYPE_assert(pThis, nspoll); - assert(idRdy != NULL); - iRet = pThis->Drvr.Wait(pThis->pDrvrData, timeout, idRdy, ppUsr); + assert(workset != NULL); + iRet = pThis->Drvr.Wait(pThis->pDrvrData, timeout, numEntries, workset); RETiRet; } diff --git a/runtime/nspoll.h b/runtime/nspoll.h index a77759c0..037f6c38 100644 --- a/runtime/nspoll.h +++ b/runtime/nspoll.h @@ -50,11 +50,12 @@ BEGINinterface(nspoll) /* name must also be changed in ENDinterface macro! */ rsRetVal (*Construct)(nspoll_t **ppThis); rsRetVal (*ConstructFinalize)(nspoll_t *pThis); rsRetVal (*Destruct)(nspoll_t **ppThis); - rsRetVal (*Wait)(nspoll_t *pNsdpoll, int timeout, int *idRdy, void **ppUsr); + rsRetVal (*Wait)(nspoll_t *pNsdpoll, int timeout, int *numEntries, nsd_epworkset_t workset[]); rsRetVal (*Ctl)(nspoll_t *pNsdpoll, netstrm_t *pStrm, int id, void *pUsr, int mode, int op); rsRetVal (*IsEPollSupported)(void); /* static method */ ENDinterface(nspoll) -#define nspollCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ +#define nspollCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */ +/* interface change in v2 is that wait supports multiple return objects */ /* prototypes */ PROTOTYPEObj(nspoll); diff --git a/runtime/obj.c b/runtime/obj.c index 29ca8117..93fbd281 100644 --- a/runtime/obj.c +++ b/runtime/obj.c @@ -87,7 +87,6 @@ #include "errmsg.h" #include "cfsysline.h" #include "unicode-helper.h" -#include "apc.h" #include "datetime.h" /* static data */ @@ -1152,7 +1151,7 @@ UseObj(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf) if(pObjFile == NULL) { FINALIZE; /* no chance, we have lost... */ } else { - CHKiRet(module.Load(pObjFile)); + CHKiRet(module.Load(pObjFile, 0)); /* NOW, we must find it or we have a problem... */ CHKiRet(FindObjInfo(pStr, &pObjInfo)); } @@ -1331,7 +1330,6 @@ objClassInit(modInfo_t *pModInfo) /* init classes we use (limit to as few as possible!) */ CHKiRet(errmsgClassInit(pModInfo)); CHKiRet(datetimeClassInit(pModInfo)); - CHKiRet(apcClassInit(pModInfo)); CHKiRet(cfsyslineInit()); CHKiRet(varClassInit(pModInfo)); CHKiRet(moduleClassInit(pModInfo)); diff --git a/runtime/objomsr.c b/runtime/objomsr.c index 1ac45e86..7241fa27 100644 --- a/runtime/objomsr.c +++ b/runtime/objomsr.c @@ -60,11 +60,14 @@ rsRetVal OMSRdestruct(omodStringRequest_t *pThis) */ rsRetVal OMSRconstruct(omodStringRequest_t **ppThis, int iNumEntries) { - omodStringRequest_t *pThis; + omodStringRequest_t *pThis = NULL; DEFiRet; assert(ppThis != NULL); assert(iNumEntries >= 0); + if(iNumEntries > CONF_OMOD_NUMSTRINGS_MAXSIZE) { + ABORT_FINALIZE(RS_RET_MAX_OMSR_REACHED); + } CHKmalloc(pThis = calloc(1, sizeof(omodStringRequest_t))); /* got the structure, so fill it */ @@ -94,7 +97,6 @@ finalize_it: rsRetVal OMSRsetEntry(omodStringRequest_t *pThis, int iEntry, uchar *pTplName, int iTplOpts) { assert(pThis != NULL); - assert(pTplName != NULL); assert(iEntry < pThis->iNumEntries); if(pThis->ppTplName[iEntry] != NULL) diff --git a/runtime/parser.c b/runtime/parser.c index 300db1e0..a79f2ce8 100644 --- a/runtime/parser.c +++ b/runtime/parser.c @@ -501,7 +501,7 @@ ParseMsg(msg_t *pMsg) * will cause it to happen. After that, access to the unsanitized message is no * loger possible. */ - pParserList = ruleset.GetParserList(pMsg); + pParserList = ruleset.GetParserList(ourConf, pMsg); if(pParserList == NULL) { pParserList = pDfltParsLst; } diff --git a/runtime/queue.c b/runtime/queue.c index 7893a1db..e08f3936 100644 --- a/runtime/queue.c +++ b/runtime/queue.c @@ -93,6 +93,39 @@ static rsRetVal qDestructDisk(qqueue_t *pThis); #define QUEUE_CHECKPOINT 1 #define QUEUE_NO_CHECKPOINT 0 +/* tables for interfacing with the v6 config system */ +static struct cnfparamdescr cnfpdescr[] = { + { "queue.filename", eCmdHdlrGetWord, 0 }, + { "queue.size", eCmdHdlrSize, 0 }, + { "queue.dequeuebatchsize", eCmdHdlrInt, 0 }, + { "queue.maxdiskspace", eCmdHdlrSize, 0 }, + { "queue.highwatermark", eCmdHdlrInt, 0 }, + { "queue.lowwatermark", eCmdHdlrInt, 0 }, + { "queue.fulldelaymark", eCmdHdlrInt, 0 }, + { "queue.lightdelaymark", eCmdHdlrInt, 0 }, + { "queue.discardmark", eCmdHdlrInt, 0 }, + { "queue.discardseverity", eCmdHdlrFacility, 0 }, + { "queue.checkpointinterval", eCmdHdlrInt, 0 }, + { "queue.syncqueuefiles", eCmdHdlrBinary, 0 }, + { "queue.type", eCmdHdlrQueueType, 0 }, + { "queue.workerthreads", eCmdHdlrInt, 0 }, + { "queue.timeoutshutdown", eCmdHdlrInt, 0 }, + { "queue.timeoutactioncompletion", eCmdHdlrInt, 0 }, + { "queue.timeoutenqueue", eCmdHdlrInt, 0 }, + { "queue.timeoutworkerthreadshutdown", eCmdHdlrInt, 0 }, + { "queue.workerthreadminimummessages", eCmdHdlrInt, 0 }, + { "queue.maxfilesize", eCmdHdlrSize, 0 }, + { "queue.saveonshutdown", eCmdHdlrBinary, 0 }, + { "queue.dequeueslowdown", eCmdHdlrInt, 0 }, + { "queue.dequeuetimebegin", eCmdHdlrInt, 0 }, + { "queue.dequeuetimeend", eCmdHdlrInt, 0 }, +}; +static struct cnfparamblk pblk = + { CNFPARAMBLK_VERSION, + sizeof(cnfpdescr)/sizeof(struct cnfparamdescr), + cnfpdescr + }; + /* debug aid */ static void displayBatchState(batch_t *pBatch) { @@ -1267,8 +1300,8 @@ rsRetVal qqueueConstruct(qqueue_t **ppThis, queueType_t qType, int iWorkerThread ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); /* set some water marks so that we have useful defaults if none are set specifically */ - pThis->iFullDlyMrk = iMaxQueueSize - (iMaxQueueSize / 100) * 3; /* default 97% */ - pThis->iLightDlyMrk = iMaxQueueSize - (iMaxQueueSize / 100) * 30; /* default 70% */ + pThis->iFullDlyMrk = -1; + pThis->iLightDlyMrk = -1; pThis->lenSpoolDir = ustrlen(pThis->pszSpoolDir); pThis->iMaxFileSize = 1024 * 1024; /* default is 1 MiB */ pThis->iQueueSize = 0; @@ -1282,42 +1315,6 @@ rsRetVal qqueueConstruct(qqueue_t **ppThis, queueType_t qType, int iWorkerThread pThis->pszFilePrefix = NULL; pThis->qType = qType; - /* set type-specific handlers and other very type-specific things (we can not totally hide it...) */ - switch(qType) { - case QUEUETYPE_FIXED_ARRAY: - pThis->qConstruct = qConstructFixedArray; - pThis->qDestruct = qDestructFixedArray; - pThis->qAdd = qAddFixedArray; - pThis->qDeq = qDeqFixedArray; - pThis->qDel = qDelFixedArray; - pThis->MultiEnq = qqueueMultiEnqObjNonDirect; - break; - case QUEUETYPE_LINKEDLIST: - pThis->qConstruct = qConstructLinkedList; - pThis->qDestruct = qDestructLinkedList; - pThis->qAdd = qAddLinkedList; - pThis->qDeq = (rsRetVal (*)(qqueue_t*,void**)) qDeqLinkedList; - pThis->qDel = (rsRetVal (*)(qqueue_t*)) qDelLinkedList; - pThis->MultiEnq = qqueueMultiEnqObjNonDirect; - break; - case QUEUETYPE_DISK: - pThis->qConstruct = qConstructDisk; - pThis->qDestruct = qDestructDisk; - pThis->qAdd = qAddDisk; - pThis->qDeq = qDeqDisk; - pThis->qDel = qDelDisk; - pThis->MultiEnq = qqueueMultiEnqObjNonDirect; - /* special handling */ - pThis->iNumWorkerThreads = 1; /* we need exactly one worker */ - break; - case QUEUETYPE_DIRECT: - pThis->qConstruct = qConstructDirect; - pThis->qDestruct = qDestructDirect; - pThis->qAdd = qAddDirect; - pThis->qDel = qDelDirect; - pThis->MultiEnq = qqueueMultiEnqObjDirect; - break; - } INIT_ATOMIC_HELPER_MUT(pThis->mutQueueSize); INIT_ATOMIC_HELPER_MUT(pThis->mutLogDeq); @@ -1328,6 +1325,40 @@ finalize_it: } +/* set default inisde queue object suitable for action queues. + * This shall be called directly after queue construction. This functions has + * been added in support of the new v6 config system. It expect properly pre-initialized + * objects, but we need to differentiate between ruleset main and action queues. + * In order to avoid unnecessary complexity, we provide the necessary defaults + * via specific function calls. + */ +void +qqueueSetDefaultsActionQueue(qqueue_t *pThis) +{ + pThis->qType = QUEUETYPE_DIRECT; /* type of the main message queue above */ + pThis->iMaxQueueSize = 1000; /* size of the main message queue above */ + pThis->iDeqBatchSize = 128; /* default batch size */ + pThis->iHighWtrMrk = 800; /* high water mark for disk-assisted queues */ + pThis->iLowWtrMrk = 200; /* low water mark for disk-assisted queues */ + pThis->iDiscardMrk = 9800; /* begin to discard messages */ + pThis->iDiscardSeverity = 8; /* turn off */ + pThis->iNumWorkerThreads = 1; /* number of worker threads for the mm queue above */ + pThis->iMaxFileSize = 1024*1024; + pThis->iPersistUpdCnt = 0; /* persist queue info every n updates */ + pThis->bSyncQueueFiles = 0; + pThis->toQShutdown = 0; /* queue shutdown */ + pThis->toActShutdown = 1000; /* action shutdown (in phase 2) */ + pThis->toEnq = 2000; /* timeout for queue enque */ + pThis->toWrkShutdown = 60000; /* timeout for worker thread shutdown */ + pThis->iMinMsgsPerWrkr = 100; /* minimum messages per worker needed to start a new one */ + pThis->bSaveOnShutdown = 1; /* save queue on shutdown (when DA enabled)? */ + pThis->sizeOnDiskMax = 0; /* unlimited */ + pThis->iDeqSlowdown = 0; + pThis->iDeqtWinFromHr = 0; + pThis->iDeqtWinToHr = 25; /* disable time-windowed dequeuing by default */ +} + + /* This function checks if the provided message shall be discarded and does so, if needed. * In DA mode, we do not discard any messages as we assume the disk subsystem is fast enough to * provide real-time creation of spool files. @@ -1905,6 +1936,52 @@ qqueueStart(qqueue_t *pThis) /* this is the ConstructionFinalizer */ ASSERT(pThis != NULL); + /* set type-specific handlers and other very type-specific things + * (we can not totally hide it...) + */ + switch(pThis->qType) { + case QUEUETYPE_FIXED_ARRAY: + pThis->qConstruct = qConstructFixedArray; + pThis->qDestruct = qDestructFixedArray; + pThis->qAdd = qAddFixedArray; + pThis->qDeq = qDeqFixedArray; + pThis->qDel = qDelFixedArray; + pThis->MultiEnq = qqueueMultiEnqObjNonDirect; + break; + case QUEUETYPE_LINKEDLIST: + pThis->qConstruct = qConstructLinkedList; + pThis->qDestruct = qDestructLinkedList; + pThis->qAdd = qAddLinkedList; + pThis->qDeq = (rsRetVal (*)(qqueue_t*,void**)) qDeqLinkedList; + pThis->qDel = (rsRetVal (*)(qqueue_t*)) qDelLinkedList; + pThis->MultiEnq = qqueueMultiEnqObjNonDirect; + break; + case QUEUETYPE_DISK: + pThis->qConstruct = qConstructDisk; + pThis->qDestruct = qDestructDisk; + pThis->qAdd = qAddDisk; + pThis->qDeq = qDeqDisk; + pThis->qDel = qDelDisk; + pThis->MultiEnq = qqueueMultiEnqObjNonDirect; + /* special handling */ + pThis->iNumWorkerThreads = 1; /* we need exactly one worker */ + break; + case QUEUETYPE_DIRECT: + pThis->qConstruct = qConstructDirect; + pThis->qDestruct = qDestructDirect; + pThis->qAdd = qAddDirect; + pThis->qDel = qDelDirect; + pThis->MultiEnq = qqueueMultiEnqObjDirect; + break; + } + + if(pThis->iFullDlyMrk == -1) + pThis->iFullDlyMrk = pThis->iMaxQueueSize + - (pThis->iMaxQueueSize / 100) * 3; /* default 97% */ + if(pThis->iLightDlyMrk == -1) + pThis->iLightDlyMrk = pThis->iMaxQueueSize + - (pThis->iMaxQueueSize / 100) * 30; /* default 70% */ + /* we need to do a quick check if our water marks are set plausible. If not, * we correct the most important shortcomings. TODO: do that!!!! -- rgerhards, 2008-03-14 */ @@ -2528,6 +2605,90 @@ finalize_it: } +/* take v6 config list and extract the queue params out of it. Hand the + * param values back to the caller. Caller is responsible for destructing + * them when no longer needed. Caller can use this param block to configure + * all parameters for a newly created queue with one call to qqueueSetParams(). + * rgerhards, 2011-07-22 + */ +rsRetVal +qqueueDoCnfParams(struct nvlst *lst, struct cnfparamvals **ppvals) +{ + *ppvals = nvlstGetParams(lst, &pblk, NULL); + return RS_RET_OK; +} + +/* apply all params from param block to queue. Must be called before + * finalizing. This supports the v6 config system. Defaults were already + * set during queue creation. The pvals object is destructed by this + * function. + */ +rsRetVal +qqueueApplyCnfParam(qqueue_t *pThis, struct cnfparamvals *pvals) +{ + int i; + for(i = 0 ; i < pblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(pblk.descr[i].name, "queue.filename")) { + pThis->pszFilePrefix = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + pThis->lenFilePrefix = es_strlen(pvals[i].val.d.estr); + } else if(!strcmp(pblk.descr[i].name, "queue.size")) { + pThis->iMaxQueueSize = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "queue.dequeuebatchsize")) { + pThis->iDeqBatchSize = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "queue.maxdiskspace")) { + pThis->iMaxFileSize = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "queue.highwatermark")) { + pThis->iHighWtrMrk = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "queue.lowwatermark")) { + pThis->iLowWtrMrk = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "queue.fulldelaymark")) { + pThis->iFullDlyMrk = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "queue.lightdelaymark")) { + pThis->iLightDlyMrk = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "queue.discardmark")) { + pThis->iDiscardMrk = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "queue.discardseverity")) { + pThis->iDiscardSeverity = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "queue.checkpointinterval")) { + pThis->iPersistUpdCnt = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "queue.syncqueuefiles")) { + pThis->bSyncQueueFiles = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "queue.type")) { + pThis->qType = (queueType_t) pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "queue.workerthreads")) { + pThis->iNumWorkerThreads = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "queue.timeoutshutdown")) { + pThis->toQShutdown = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "queue.timeoutactioncompletion")) { + pThis->toActShutdown = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "queue.timeoutenqueue")) { + pThis->toEnq = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "queue.timeoutworkerthreadshutdown")) { + pThis->toWrkShutdown = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "queue.workerthreadminimummessages")) { + pThis->iMinMsgsPerWrkr = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "queue.maxfilesize")) { + pThis->iMaxFileSize = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "queue.saveonshutdown")) { + pThis->bSaveOnShutdown = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "queue.dequeueslowdown")) { + pThis->iDeqSlowdown = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "queue.dequeuetimebegin")) { + pThis->iDeqtWinFromHr = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "queuedequeuetimend.")) { + pThis->iDeqtWinToHr = pvals[i].val.d.n; + } else { + dbgprintf("queue: program error, non-handled " + "param '%s'\n", pblk.descr[i].name); + } + } + cnfparamvalsDestruct(pvals, &pblk); + return RS_RET_OK; +} + + /* some simple object access methods */ DEFpropSetMeth(qqueue, bSyncQueueFiles, int) DEFpropSetMeth(qqueue, iPersistUpdCnt, int) diff --git a/runtime/queue.h b/runtime/queue.h index a575698f..3841615a 100644 --- a/runtime/queue.h +++ b/runtime/queue.h @@ -192,6 +192,10 @@ rsRetVal qqueueSetFilePrefix(qqueue_t *pThis, uchar *pszPrefix, size_t iLenPrefi rsRetVal qqueueConstruct(qqueue_t **ppThis, queueType_t qType, int iWorkerThreads, int iMaxQueueSize, rsRetVal (*pConsumer)(void*,batch_t*, int*)); rsRetVal qqueueEnqObjDirectBatch(qqueue_t *pThis, batch_t *pBatch); +rsRetVal qqueueDoCnfParams(struct nvlst *lst, struct cnfparamvals **ppvals); +rsRetVal qqueueApplyCnfParam(qqueue_t *pThis, struct cnfparamvals *pvals); +void qqueueSetDefaultsActionQueue(qqueue_t *pThis); + PROTOTYPEObjClassInit(qqueue); PROTOTYPEpropSetMeth(qqueue, iPersistUpdCnt, int); PROTOTYPEpropSetMeth(qqueue, bSyncQueueFiles, int); diff --git a/runtime/rsconf.c b/runtime/rsconf.c new file mode 100644 index 00000000..460e69d6 --- /dev/null +++ b/runtime/rsconf.c @@ -0,0 +1,1353 @@ +/* rsconf.c - the rsyslog configuration system. + * + * Module begun 2011-04-19 by Rainer Gerhards + * + * Copyright 2011-2012 Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * -or- + * see COPYING.ASL20 in the source distribution + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <grp.h> +#include <stdarg.h> +#include <sys/resource.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "rsyslog.h" +#include "obj.h" +#include "srUtils.h" +#include "rule.h" +#include "ruleset.h" +#include "modules.h" +#include "conf.h" +#include "queue.h" +#include "rsconf.h" +#include "cfsysline.h" +#include "errmsg.h" +#include "action.h" +#include "glbl.h" +#include "unicode-helper.h" +#include "omshell.h" +#include "omusrmsg.h" +#include "omfwd.h" +#include "omfile.h" +#include "ompipe.h" +#include "omdiscard.h" +#include "pmrfc5424.h" +#include "pmrfc3164.h" +#include "smfile.h" +#include "smtradfile.h" +#include "smfwd.h" +#include "smtradfwd.h" +#include "parser.h" +#include "outchannel.h" +#include "threads.h" +#include "datetime.h" +#include "parserif.h" +#include "dirty.h" + +/* static data */ +DEFobjStaticHelpers +DEFobjCurrIf(rule) +DEFobjCurrIf(ruleset) +DEFobjCurrIf(module) +DEFobjCurrIf(conf) +DEFobjCurrIf(errmsg) +DEFobjCurrIf(glbl) +DEFobjCurrIf(parser) +DEFobjCurrIf(datetime) + +/* exported static data */ +rsconf_t *runConf = NULL;/* the currently running config */ +rsconf_t *loadConf = NULL;/* the config currently being loaded (no concurrent config load supported!) */ + +/* hardcoded standard templates (used for defaults) */ +static uchar template_DebugFormat[] = "\"Debug line with all properties:\nFROMHOST: '%FROMHOST%', fromhost-ip: '%fromhost-ip%', HOSTNAME: '%HOSTNAME%', PRI: %PRI%,\nsyslogtag '%syslogtag%', programname: '%programname%', APP-NAME: '%APP-NAME%', PROCID: '%PROCID%', MSGID: '%MSGID%',\nTIMESTAMP: '%TIMESTAMP%', STRUCTURED-DATA: '%STRUCTURED-DATA%',\nmsg: '%msg%'\nescaped msg: '%msg:::drop-cc%'\ninputname: %inputname% rawmsg: '%rawmsg%'\n\n\""; +static uchar template_SyslogProtocol23Format[] = "\"<%PRI%>1 %TIMESTAMP:::date-rfc3339% %HOSTNAME% %APP-NAME% %PROCID% %MSGID% %STRUCTURED-DATA% %msg%\n\""; +static uchar template_TraditionalFileFormat[] = "=RSYSLOG_TraditionalFileFormat"; +static uchar template_FileFormat[] = "=RSYSLOG_FileFormat"; +static uchar template_ForwardFormat[] = "=RSYSLOG_ForwardFormat"; +static uchar template_TraditionalForwardFormat[] = "=RSYSLOG_TraditionalForwardFormat"; +static uchar template_WallFmt[] = "\"\r\n\7Message from syslogd@%HOSTNAME% at %timegenerated% ...\r\n %syslogtag%%msg%\n\r\""; +static uchar template_StdUsrMsgFmt[] = "\" %syslogtag%%msg%\n\r\""; +static uchar template_StdDBFmt[] = "\"insert into SystemEvents (Message, Facility, FromHost, Priority, DeviceReportedTime, ReceivedAt, InfoUnitID, SysLogTag) values ('%msg%', %syslogfacility%, '%HOSTNAME%', %syslogpriority%, '%timereported:::date-mysql%', '%timegenerated:::date-mysql%', %iut%, '%syslogtag%')\",SQL"; +static uchar template_StdPgSQLFmt[] = "\"insert into SystemEvents (Message, Facility, FromHost, Priority, DeviceReportedTime, ReceivedAt, InfoUnitID, SysLogTag) values ('%msg%', %syslogfacility%, '%HOSTNAME%', %syslogpriority%, '%timereported:::date-pgsql%', '%timegenerated:::date-pgsql%', %iut%, '%syslogtag%')\",STDSQL"; +static uchar template_spoofadr[] = "\"%fromhost-ip%\""; +static uchar template_StdJSONFmt[] = "\"{\\\"message\\\":\\\"%msg%\\\",\\\"fromhost\\\":\\\"%HOSTNAME%\\\",\\\"facility\\\":\\\"%syslogfacility-text%\\\",\\\"priority\\\":\\\"%syslogpriority-text%\\\",\\\"timereported\\\":\\\"%timereported:::date-rfc3339%\\\",\\\"timegenerated\\\":\\\"%timegenerated:::date-rfc3339%\\\"}\",JSON"; +/* end templates */ + +void cnfDoCfsysline(char *ln); + +/* Standard-Constructor + */ +BEGINobjConstruct(rsconf) /* be sure to specify the object type also in END macro! */ + pThis->globals.bDebugPrintTemplateList = 1; + pThis->globals.bDebugPrintModuleList = 1; + pThis->globals.bDebugPrintCfSysLineHandlerList = 1; + pThis->globals.bLogStatusMsgs = DFLT_bLogStatusMsgs; + pThis->globals.bErrMsgToStderr = 1; + pThis->globals.umask = -1; + pThis->templates.root = NULL; + pThis->templates.last = NULL; + pThis->templates.lastStatic = NULL; + pThis->actions.nbrActions = 0; + CHKiRet(llInit(&pThis->rulesets.llRulesets, rulesetDestructForLinkedList, + rulesetKeyDestruct, strcasecmp)); + /* queue params */ + pThis->globals.mainQ.iMainMsgQueueSize = 10000; + pThis->globals.mainQ.iMainMsgQHighWtrMark = 8000; + pThis->globals.mainQ.iMainMsgQLowWtrMark = 2000; + pThis->globals.mainQ.iMainMsgQDiscardMark = 9800; + pThis->globals.mainQ.iMainMsgQDiscardSeverity = 8; + pThis->globals.mainQ.iMainMsgQueueNumWorkers = 1; + pThis->globals.mainQ.MainMsgQueType = QUEUETYPE_FIXED_ARRAY; + pThis->globals.mainQ.pszMainMsgQFName = NULL; + pThis->globals.mainQ.iMainMsgQueMaxFileSize = 1024*1024; + pThis->globals.mainQ.iMainMsgQPersistUpdCnt = 0; + pThis->globals.mainQ.bMainMsgQSyncQeueFiles = 0; + pThis->globals.mainQ.iMainMsgQtoQShutdown = 1500; + pThis->globals.mainQ.iMainMsgQtoActShutdown = 1000; + pThis->globals.mainQ.iMainMsgQtoEnq = 2000; + pThis->globals.mainQ.iMainMsgQtoWrkShutdown = 60000; + pThis->globals.mainQ.iMainMsgQWrkMinMsgs = 100; + pThis->globals.mainQ.iMainMsgQDeqSlowdown = 0; + pThis->globals.mainQ.iMainMsgQueMaxDiskSpace = 0; + pThis->globals.mainQ.iMainMsgQueDeqBatchSize = 32; + pThis->globals.mainQ.bMainMsgQSaveOnShutdown = 1; + pThis->globals.mainQ.iMainMsgQueueDeqtWinFromHr = 0; + pThis->globals.mainQ.iMainMsgQueueDeqtWinToHr = 25; + /* end queue params */ +finalize_it: +ENDobjConstruct(rsconf) + + +/* ConstructionFinalizer + */ +rsRetVal rsconfConstructFinalize(rsconf_t __attribute__((unused)) *pThis) +{ + DEFiRet; + ISOBJ_TYPE_assert(pThis, rsconf); + RETiRet; +} + + +/* destructor for the rsconf object */ +BEGINobjDestruct(rsconf) /* be sure to specify the object type also in END and CODESTART macros! */ +CODESTARTobjDestruct(rsconf) + free(pThis->globals.mainQ.pszMainMsgQFName); + llDestroy(&(pThis->rulesets.llRulesets)); +ENDobjDestruct(rsconf) + + +/* DebugPrint support for the rsconf object */ +BEGINobjDebugPrint(rsconf) /* be sure to specify the object type also in END and CODESTART macros! */ + cfgmodules_etry_t *modNode; + + dbgprintf("configuration object %p\n", pThis); + dbgprintf("Global Settings:\n"); + dbgprintf(" bDebugPrintTemplateList.............: %d\n", + pThis->globals.bDebugPrintTemplateList); + dbgprintf(" bDebugPrintModuleList : %d\n", + pThis->globals.bDebugPrintModuleList); + dbgprintf(" bDebugPrintCfSysLineHandlerList.....: %d\n", + pThis->globals.bDebugPrintCfSysLineHandlerList); + dbgprintf(" bLogStatusMsgs : %d\n", + pThis->globals.bLogStatusMsgs); + dbgprintf(" bErrMsgToStderr.....................: %d\n", + pThis->globals.bErrMsgToStderr); + dbgprintf(" drop Msgs with malicious PTR Record : %d\n", + glbl.GetDropMalPTRMsgs()); + ruleset.DebugPrintAll(pThis); + dbgprintf("\n"); + if(pThis->globals.bDebugPrintTemplateList) + tplPrintList(pThis); + if(pThis->globals.bDebugPrintModuleList) + module.PrintList(); + if(pThis->globals.bDebugPrintCfSysLineHandlerList) + dbgPrintCfSysLineHandlers(); + // TODO: The following code needs to be "streamlined", so far just moved over... + dbgprintf("Main queue size %d messages.\n", pThis->globals.mainQ.iMainMsgQueueSize); + dbgprintf("Main queue worker threads: %d, wThread shutdown: %d, Perists every %d updates.\n", + pThis->globals.mainQ.iMainMsgQueueNumWorkers, + pThis->globals.mainQ.iMainMsgQtoWrkShutdown, pThis->globals.mainQ.iMainMsgQPersistUpdCnt); + dbgprintf("Main queue timeouts: shutdown: %d, action completion shutdown: %d, enq: %d\n", + pThis->globals.mainQ.iMainMsgQtoQShutdown, + pThis->globals.mainQ.iMainMsgQtoActShutdown, pThis->globals.mainQ.iMainMsgQtoEnq); + dbgprintf("Main queue watermarks: high: %d, low: %d, discard: %d, discard-severity: %d\n", + pThis->globals.mainQ.iMainMsgQHighWtrMark, pThis->globals.mainQ.iMainMsgQLowWtrMark, + pThis->globals.mainQ.iMainMsgQDiscardMark, pThis->globals.mainQ.iMainMsgQDiscardSeverity); + dbgprintf("Main queue save on shutdown %d, max disk space allowed %lld\n", + pThis->globals.mainQ.bMainMsgQSaveOnShutdown, pThis->globals.mainQ.iMainMsgQueMaxDiskSpace); + /* TODO: add + iActionRetryCount = 0; + iActionRetryInterval = 30000; + static int iMainMsgQtoWrkMinMsgs = 100; + static int iMainMsgQbSaveOnShutdown = 1; + iMainMsgQueMaxDiskSpace = 0; + setQPROP(qqueueSetiMinMsgsPerWrkr, "$MainMsgQueueWorkerThreadMinimumMessages", 100); + setQPROP(qqueueSetbSaveOnShutdown, "$MainMsgQueueSaveOnShutdown", 1); + */ + dbgprintf("Work Directory: '%s'.\n", glbl.GetWorkDir()); + ochPrintList(); + dbgprintf("Modules used in this configuration:\n"); + for(modNode = pThis->modules.root ; modNode != NULL ; modNode = modNode->next) { + dbgprintf(" %s\n", module.GetName(modNode->pMod)); + } +CODESTARTobjDebugPrint(rsconf) +ENDobjDebugPrint(rsconf) + + +rsRetVal +cnfDoActlst(struct cnfactlst *actlst, rule_t *pRule) +{ + struct cnfcfsyslinelst *cflst; + action_t *pAction; + uchar *str; + DEFiRet; + + while(actlst != NULL) { + dbgprintf("aclst %p: ", actlst); + if(actlst->actType == CNFACT_V2) { + dbgprintf("v6+ action object\n"); + if(actionNewInst(actlst->data.lst, &pAction) == RS_RET_OK) { + iRet = llAppend(&(pRule)->llActList, NULL, (void*) pAction); + } else { + errmsg.LogError(0, RS_RET_ERR, "errors occured in file '%s' " + "around line %d", actlst->cnfFile, actlst->lineno); + } + } else { + dbgprintf("legacy action line:%s\n", actlst->data.legActLine); + str = (uchar*) actlst->data.legActLine; + CHKiRet(cflineDoAction(loadConf, &str, &pAction)); + iRet = llAppend(&(pRule)->llActList, NULL, (void*) pAction); + } + for( cflst = actlst->syslines + ; cflst != NULL ; cflst = cflst->next) { + cnfDoCfsysline(cflst->line); + } + actlst = actlst->next; + } +finalize_it: + RETiRet; +} + +/* This function returns the current date in different + * variants. It is used to construct the $NOW series of + * system properties. The returned buffer must be freed + * by the caller when no longer needed. If the function + * can not allocate memory, it returns a NULL pointer. + * TODO: this was taken from msg.c and we should consolidate it with the code + * there. This is especially important when we increase the number of system + * variables (what we definitely want to do). + */ +typedef enum ENOWType { NOW_NOW, NOW_YEAR, NOW_MONTH, NOW_DAY, NOW_HOUR, NOW_MINUTE } eNOWType; +static rsRetVal +getNOW(eNOWType eNow, es_str_t **estr) +{ + DEFiRet; + uchar szBuf[16]; + struct syslogTime t; + es_size_t len; + + datetime.getCurrTime(&t, NULL); + switch(eNow) { + case NOW_NOW: + len = snprintf((char*) szBuf, sizeof(szBuf)/sizeof(uchar), + "%4.4d-%2.2d-%2.2d", t.year, t.month, t.day); + break; + case NOW_YEAR: + len = snprintf((char*) szBuf, sizeof(szBuf)/sizeof(uchar), "%4.4d", t.year); + break; + case NOW_MONTH: + len = snprintf((char*) szBuf, sizeof(szBuf)/sizeof(uchar), "%2.2d", t.month); + break; + case NOW_DAY: + len = snprintf((char*) szBuf, sizeof(szBuf)/sizeof(uchar), "%2.2d", t.day); + break; + case NOW_HOUR: + len = snprintf((char*) szBuf, sizeof(szBuf)/sizeof(uchar), "%2.2d", t.hour); + break; + case NOW_MINUTE: + len = snprintf((char*) szBuf, sizeof(szBuf)/sizeof(uchar), "%2.2d", t.minute); + break; + } + + /* now create a string object out of it and hand that over to the var */ + *estr = es_newStrFromCStr((char*)szBuf, len); + + RETiRet; +} + + + +static inline es_str_t * +getSysVar(char *name) +{ + es_str_t *estr = NULL; + rsRetVal iRet = RS_RET_OK; + + if(!strcmp(name, "now")) { + CHKiRet(getNOW(NOW_NOW, &estr)); + } else if(!strcmp(name, "year")) { + CHKiRet(getNOW(NOW_YEAR, &estr)); + } else if(!strcmp(name, "month")) { + CHKiRet(getNOW(NOW_MONTH, &estr)); + } else if(!strcmp(name, "day")) { + CHKiRet(getNOW(NOW_DAY, &estr)); + } else if(!strcmp(name, "hour")) { + CHKiRet(getNOW(NOW_HOUR, &estr)); + } else if(!strcmp(name, "minute")) { + CHKiRet(getNOW(NOW_MINUTE, &estr)); + } else if(!strcmp(name, "myhostname")) { + char *hn = (char*)glbl.GetLocalHostName(); + estr = es_newStrFromCStr(hn, strlen(hn)); + } else { + ABORT_FINALIZE(RS_RET_SYSVAR_NOT_FOUND); + } +finalize_it: + if(iRet != RS_RET_OK) { + dbgprintf("getSysVar error iRet %d\n", iRet); + if(estr == NULL) + estr = es_newStrFromCStr("*ERROR*", sizeof("*ERROR*") - 1); + } + return estr; +} + +/*------------------------------ interface to flex/bison parser ------------------------------*/ +extern int yylineno; + +void +parser_errmsg(char *fmt, ...) +{ + va_list ap; + char errBuf[1024]; + + va_start(ap, fmt); + if(vsnprintf(errBuf, sizeof(errBuf), fmt, ap) == sizeof(errBuf)) + errBuf[sizeof(errBuf)-1] = '\0'; +dbgprintf("XXXX: msg: %s\n", errBuf); +dbgprintf("XXXX: cnfcurrfn: %s\n", cnfcurrfn); +dbgprintf("XXXX: yylineno: %d\n", yylineno); + errmsg.LogError(0, RS_RET_CONF_PARSE_ERROR, + "error during parsing file %s, on or before line %d: %s", + cnfcurrfn, yylineno, errBuf); + va_end(ap); +} + +int +yyerror(char *s) +{ + parser_errmsg("%s", s); + return 0; +} +void cnfDoObj(struct cnfobj *o) +{ + dbgprintf("cnf:global:obj: "); + cnfobjPrint(o); + switch(o->objType) { + case CNFOBJ_GLOBAL: + glblProcessCnf(o); + break; + case CNFOBJ_ACTION: + actionProcessCnf(o); + break; + } + nvlstChkUnused(o->nvlst); + cnfobjDestruct(o); +} + +void cnfDoRule(struct cnfrule *cnfrule) +{ + rule_t *pRule; + uchar *str; + rsRetVal iRet = RS_RET_OK; //DEFiRet; + + dbgprintf("cnf:global:rule\n"); + cnfrulePrint(cnfrule); + + CHKiRet(rule.Construct(&pRule)); /* create "fresh" selector */ + CHKiRet(rule.SetAssRuleset(pRule, ruleset.GetCurrent(loadConf))); + CHKiRet(rule.ConstructFinalize(pRule)); + + switch(cnfrule->filttype) { + case CNFFILT_NONE: + break; + case CNFFILT_PRI: + str = (uchar*) cnfrule->filt.s; + iRet = cflineProcessTradPRIFilter(&str, pRule); + break; + case CNFFILT_PROP: + dbgprintf("%s\n", cnfrule->filt.s); + str = (uchar*) cnfrule->filt.s; + iRet = cflineProcessPropFilter(&str, pRule); + break; + case CNFFILT_SCRIPT: + pRule->f_filter_type = FILTER_EXPR; + pRule->f_filterData.expr = cnfrule->filt.expr; + break; + } + /* we now check if there are some global (BSD-style) filter conditions + * and, if so, we copy them over. rgerhards, 2005-10-18 + */ + if(pDfltProgNameCmp != NULL) { + CHKiRet(rsCStrConstructFromCStr(&(pRule->pCSProgNameComp), pDfltProgNameCmp)); + } + + if(eDfltHostnameCmpMode != HN_NO_COMP) { + pRule->eHostnameCmpMode = eDfltHostnameCmpMode; + CHKiRet(rsCStrConstructFromCStr(&(pRule->pCSHostnameComp), pDfltHostnameCmp)); + } + + cnfDoActlst(cnfrule->actlst, pRule); + + CHKiRet(ruleset.AddRule(rule.GetAssRuleset(pRule), &pRule)); + +finalize_it: + //TODO: do something with error states + cnfruleDestruct(cnfrule); +} + +void cnfDoCfsysline(char *ln) +{ + DBGPRINTF("cnf:global:cfsysline: %s\n", ln); + /* the legacy system needs the "$" stripped */ + conf.cfsysline((uchar*) ln+1); +} + +void cnfDoBSDTag(char *ln) +{ + DBGPRINTF("cnf:global:BSD tag: %s\n", ln); + cflineProcessTagSelector((uchar**)&ln); +} + +void cnfDoBSDHost(char *ln) +{ + DBGPRINTF("cnf:global:BSD host: %s\n", ln); + cflineProcessHostSelector((uchar**)&ln); +} + +es_str_t* +cnfGetVar(char *name, void *usrptr) +{ + es_str_t *estr; + if(name[0] == '$') { + if(name[1] == '$') + estr = getSysVar(name+2); + else if(name[1] == '!') + estr = msgGetCEEVarNew((msg_t*) usrptr, name+2); + else + estr = msgGetMsgVarNew((msg_t*) usrptr, (uchar*)name+1); + } + if(Debug) { + char *s; + s = es_str2cstr(estr, NULL); + dbgprintf("rainerscript: var '%s': '%s'\n", name, s); + free(s); + } + return estr; +} +/*------------------------------ end interface to flex/bison parser ------------------------------*/ + + + +/* drop to specified group + * if something goes wrong, the function never returns + * Note that such an abort can cause damage to on-disk structures, so we should + * re-design the "interface" in the long term. -- rgerhards, 2008-11-26 + */ +static void doDropPrivGid(int iGid) +{ + int res; + uchar szBuf[1024]; + + res = setgroups(0, NULL); /* remove all supplementary group IDs */ + if(res) { + perror("could not remove supplemental group IDs"); + exit(1); + } + DBGPRINTF("setgroups(0, NULL): %d\n", res); + res = setgid(iGid); + if(res) { + /* if we can not set the userid, this is fatal, so let's unconditionally abort */ + perror("could not set requested group id"); + exit(1); + } + DBGPRINTF("setgid(%d): %d\n", iGid, res); + snprintf((char*)szBuf, sizeof(szBuf)/sizeof(uchar), "rsyslogd's groupid changed to %d", iGid); + logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, szBuf, 0); +} + + +/* drop to specified user + * if something goes wrong, the function never returns + * Note that such an abort can cause damage to on-disk structures, so we should + * re-design the "interface" in the long term. -- rgerhards, 2008-11-19 + */ +static void doDropPrivUid(int iUid) +{ + int res; + uchar szBuf[1024]; + + res = setuid(iUid); + if(res) { + /* if we can not set the userid, this is fatal, so let's unconditionally abort */ + perror("could not set requested userid"); + exit(1); + } + DBGPRINTF("setuid(%d): %d\n", iUid, res); + snprintf((char*)szBuf, sizeof(szBuf)/sizeof(uchar), "rsyslogd's userid changed to %d", iUid); + logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, szBuf, 0); +} + + + +/* drop privileges. This will drop to the configured privileges, if + * set by the user. After this method has been executed, the previous + * privileges can no be re-gained. + */ +static inline rsRetVal +dropPrivileges(rsconf_t *cnf) +{ + DEFiRet; + + /* If instructed to do so, we now drop privileges. Note that this is not 100% secure, + * because outputs are already running at this time. However, we can implement + * dropping of privileges rather quickly and it will work in many cases. While it is not + * the ultimate solution, the current one is still much better than not being able to + * drop privileges at all. Doing it correctly, requires a change in architecture, which + * we should do over time. TODO -- rgerhards, 2008-11-19 + */ + if(cnf->globals.gidDropPriv != 0) { + doDropPrivGid(ourConf->globals.gidDropPriv); + DBGPRINTF("group privileges have been dropped to gid %u\n", (unsigned) + ourConf->globals.gidDropPriv); + } + + if(cnf->globals.uidDropPriv != 0) { + doDropPrivUid(ourConf->globals.uidDropPriv); + DBGPRINTF("user privileges have been dropped to uid %u\n", (unsigned) + ourConf->globals.uidDropPriv); + } + + RETiRet; +} + + +/* tell the rsysog core (including ourselfs) that the config load is done and + * we need to prepare to move over to activate mode. + */ +static inline void +tellCoreConfigLoadDone(void) +{ + glblDoneLoadCnf(); +} + + +/* Tell input modules that the config parsing stage is over. */ +static rsRetVal +tellModulesConfigLoadDone(void) +{ + cfgmodules_etry_t *node; + + BEGINfunc + DBGPRINTF("telling modules that config load for %p is done\n", loadConf); + node = module.GetNxtCnfType(loadConf, NULL, eMOD_ANY); + while(node != NULL) { + if(node->pMod->beginCnfLoad != NULL) + node->pMod->endCnfLoad(node->modCnf); + node = module.GetNxtCnfType(runConf, node, eMOD_IN); + } + + ENDfunc + return RS_RET_OK; /* intentional: we do not care about module errors */ +} + + +/* Tell input modules to verify config object */ +static rsRetVal +tellModulesCheckConfig(void) +{ + cfgmodules_etry_t *node; + rsRetVal localRet; + + BEGINfunc + DBGPRINTF("telling modules to check config %p\n", loadConf); + node = module.GetNxtCnfType(loadConf, NULL, eMOD_ANY); + while(node != NULL) { + if(node->pMod->beginCnfLoad != NULL) { + localRet = node->pMod->checkCnf(node->modCnf); + DBGPRINTF("module %s tells us config can %sbe activated\n", + node->pMod->pszName, (localRet == RS_RET_OK) ? "" : "NOT "); + if(localRet == RS_RET_OK) { + node->canActivate = 1; + } else { + node->canActivate = 0; + } + } + node = module.GetNxtCnfType(runConf, node, eMOD_IN); + } + + ENDfunc + return RS_RET_OK; /* intentional: we do not care about module errors */ +} + + +/* Tell modules to activate current running config (pre privilege drop) */ +static rsRetVal +tellModulesActivateConfigPrePrivDrop(void) +{ + cfgmodules_etry_t *node; + rsRetVal localRet; + + BEGINfunc + DBGPRINTF("telling modules to activate config (before dropping privs) %p\n", runConf); + node = module.GetNxtCnfType(runConf, NULL, eMOD_ANY); + while(node != NULL) { + if( node->pMod->beginCnfLoad != NULL + && node->pMod->activateCnfPrePrivDrop != NULL + && node->canActivate) { + DBGPRINTF("pre priv drop activating config %p for module %s\n", + runConf, node->pMod->pszName); + localRet = node->pMod->activateCnfPrePrivDrop(node->modCnf); + if(localRet != RS_RET_OK) { + errmsg.LogError(0, localRet, "activation of module %s failed", + node->pMod->pszName); + node->canActivate = 0; /* in a sense, could not activate... */ + } + } + node = module.GetNxtCnfType(runConf, node, eMOD_IN); + } + + ENDfunc + return RS_RET_OK; /* intentional: we do not care about module errors */ +} + + +/* Tell modules to activate current running config */ +static rsRetVal +tellModulesActivateConfig(void) +{ + cfgmodules_etry_t *node; + rsRetVal localRet; + + BEGINfunc + DBGPRINTF("telling modules to activate config %p\n", runConf); + node = module.GetNxtCnfType(runConf, NULL, eMOD_ANY); + while(node != NULL) { + if(node->pMod->beginCnfLoad != NULL && node->canActivate) { + DBGPRINTF("activating config %p for module %s\n", + runConf, node->pMod->pszName); + localRet = node->pMod->activateCnf(node->modCnf); + if(localRet != RS_RET_OK) { + errmsg.LogError(0, localRet, "activation of module %s failed", + node->pMod->pszName); + node->canActivate = 0; /* in a sense, could not activate... */ + } + } + node = module.GetNxtCnfType(runConf, node, eMOD_IN); + } + + ENDfunc + return RS_RET_OK; /* intentional: we do not care about module errors */ +} + + +/* Actually run the input modules. This happens after privileges are dropped, + * if that is requested. + */ +static rsRetVal +runInputModules(void) +{ + cfgmodules_etry_t *node; + int bNeedsCancel; + + BEGINfunc + node = module.GetNxtCnfType(runConf, NULL, eMOD_IN); + while(node != NULL) { + if(node->canRun) { + DBGPRINTF("running module %s with config %p\n", node->pMod->pszName, node); + bNeedsCancel = (node->pMod->isCompatibleWithFeature(sFEATURENonCancelInputTermination) == RS_RET_OK) ? + 0 : 1; + thrdCreate(node->pMod->mod.im.runInput, node->pMod->mod.im.afterRun, bNeedsCancel, + (node->pMod->cnfName == NULL) ? node->pMod->pszName : node->pMod->cnfName); + } + node = module.GetNxtCnfType(runConf, node, eMOD_IN); + } + + ENDfunc + return RS_RET_OK; /* intentional: we do not care about module errors */ +} + + +/* Make the modules check if they are ready to start. + */ +static rsRetVal +startInputModules(void) +{ + DEFiRet; + cfgmodules_etry_t *node; + + node = module.GetNxtCnfType(runConf, NULL, eMOD_IN); + while(node != NULL) { + if(node->canActivate) { + iRet = node->pMod->mod.im.willRun(); + node->canRun = (iRet == RS_RET_OK); + if(!node->canRun) { + DBGPRINTF("module %s will not run, iRet %d\n", node->pMod->pszName, iRet); + } + } else { + node->canRun = 0; + } + node = module.GetNxtCnfType(runConf, node, eMOD_IN); + } + + ENDfunc + return RS_RET_OK; /* intentional: we do not care about module errors */ +} + + +/* activate the main queue */ +static inline rsRetVal +activateMainQueue() +{ + DEFiRet; + /* create message queue */ + CHKiRet_Hdlr(createMainQueue(&pMsgQueue, UCHAR_CONSTANT("main Q"))) { + /* no queue is fatal, we need to give up in that case... */ + fprintf(stderr, "fatal error %d: could not create message queue - rsyslogd can not run!\n", iRet); + FINALIZE; + } + + bHaveMainQueue = (ourConf->globals.mainQ.MainMsgQueType == QUEUETYPE_DIRECT) ? 0 : 1; + DBGPRINTF("Main processing queue is initialized and running\n"); +finalize_it: + RETiRet; +} + + +/* set the processes umask (upon configuration request) */ +static inline rsRetVal +setUmask(int iUmask) +{ + if(iUmask != -1) { + umask(iUmask); + DBGPRINTF("umask set to 0%3.3o.\n", iUmask); + } + + return RS_RET_OK; +} + + +/* Activate an already-loaded configuration. The configuration will become + * the new running conf (if successful). Note that in theory this method may + * be called when there already is a running conf. In practice, the current + * version of rsyslog does not support this. Future versions probably will. + * Begun 2011-04-20, rgerhards + */ +rsRetVal +activate(rsconf_t *cnf) +{ + DEFiRet; + + /* at this point, we "switch" over to the running conf */ + runConf = cnf; +# if 0 /* currently the DAG is not supported -- code missing! */ + /* TODO: re-enable this functionality some time later! */ + /* check if we need to generate a config DAG and, if so, do that */ + if(ourConf->globals.pszConfDAGFile != NULL) + generateConfigDAG(ourConf->globals.pszConfDAGFile); +# endif + setUmask(cnf->globals.umask); + + /* the output part and the queue is now ready to run. So it is a good time + * to initialize the inputs. Please note that the net code above should be + * shuffled to down here once we have everything in input modules. + * rgerhards, 2007-12-14 + * NOTE: as of 2009-06-29, the input modules are initialized, but not yet run. + * Keep in mind. though, that the outputs already run if the queue was + * persisted to disk. -- rgerhards + */ + tellModulesActivateConfigPrePrivDrop(); + + CHKiRet(dropPrivileges(cnf)); + + tellModulesActivateConfig(); + startInputModules(); + CHKiRet(activateActions()); + CHKiRet(activateMainQueue()); + /* finally let the inputs run... */ + runInputModules(); + + dbgprintf("configuration %p activated\n", cnf); + +finalize_it: + RETiRet; +} + + +/* -------------------- some legacy config handlers -------------------- + * TODO: move to conf.c? + */ + +/* legacy config system: set the action resume interval */ +static rsRetVal setActionResumeInterval(void __attribute__((unused)) *pVal, int iNewVal) +{ + return actionSetGlobalResumeInterval(iNewVal); +} + + +/* Switch the default ruleset (that, what servcies bind to if nothing specific + * is specified). + * rgerhards, 2009-06-12 + */ +static rsRetVal +setDefaultRuleset(void __attribute__((unused)) *pVal, uchar *pszName) +{ + DEFiRet; + + CHKiRet(ruleset.SetDefaultRuleset(ourConf, pszName)); + +finalize_it: + free(pszName); /* no longer needed */ + RETiRet; +} + + +/* Switch to either an already existing rule set or start a new one. The + * named rule set becomes the new "current" rule set (what means that new + * actions are added to it). + * rgerhards, 2009-06-12 + */ +static rsRetVal +setCurrRuleset(void __attribute__((unused)) *pVal, uchar *pszName) +{ + ruleset_t *pRuleset; + rsRetVal localRet; + DEFiRet; + + localRet = ruleset.SetCurrRuleset(ourConf, pszName); + + if(localRet == RS_RET_NOT_FOUND) { + DBGPRINTF("begin new current rule set '%s'\n", pszName); + CHKiRet(ruleset.Construct(&pRuleset)); + CHKiRet(ruleset.SetName(pRuleset, pszName)); + CHKiRet(ruleset.ConstructFinalize(ourConf, pRuleset)); + } else { + ABORT_FINALIZE(localRet); + } + +finalize_it: + free(pszName); /* no longer needed */ + RETiRet; +} + + +/* set the main message queue mode + * rgerhards, 2008-01-03 + */ +static rsRetVal setMainMsgQueType(void __attribute__((unused)) *pVal, uchar *pszType) +{ + DEFiRet; + + if (!strcasecmp((char *) pszType, "fixedarray")) { + loadConf->globals.mainQ.MainMsgQueType = QUEUETYPE_FIXED_ARRAY; + DBGPRINTF("main message queue type set to FIXED_ARRAY\n"); + } else if (!strcasecmp((char *) pszType, "linkedlist")) { + loadConf->globals.mainQ.MainMsgQueType = QUEUETYPE_LINKEDLIST; + DBGPRINTF("main message queue type set to LINKEDLIST\n"); + } else if (!strcasecmp((char *) pszType, "disk")) { + loadConf->globals.mainQ.MainMsgQueType = QUEUETYPE_DISK; + DBGPRINTF("main message queue type set to DISK\n"); + } else if (!strcasecmp((char *) pszType, "direct")) { + loadConf->globals.mainQ.MainMsgQueType = QUEUETYPE_DIRECT; + DBGPRINTF("main message queue type set to DIRECT (no queueing at all)\n"); + } else { + errmsg.LogError(0, RS_RET_INVALID_PARAMS, "unknown mainmessagequeuetype parameter: %s", (char *) pszType); + iRet = RS_RET_INVALID_PARAMS; + } + free(pszType); /* no longer needed */ + + RETiRet; +} + + +/* -------------------- end legacy config handlers -------------------- */ + + +/* set the processes max number ob files (upon configuration request) + * 2009-04-14 rgerhards + */ +static rsRetVal setMaxFiles(void __attribute__((unused)) *pVal, int iFiles) +{ +// TODO this must use a local var, then carry out action during activate! + struct rlimit maxFiles; + char errStr[1024]; + DEFiRet; + + maxFiles.rlim_cur = iFiles; + maxFiles.rlim_max = iFiles; + + if(setrlimit(RLIMIT_NOFILE, &maxFiles) < 0) { + /* NOTE: under valgrind, we seem to be unable to extend the size! */ + rs_strerror_r(errno, errStr, sizeof(errStr)); + errmsg.LogError(0, RS_RET_ERR_RLIM_NOFILE, "could not set process file limit to %d: %s [kernel max %ld]", + iFiles, errStr, (long) maxFiles.rlim_max); + ABORT_FINALIZE(RS_RET_ERR_RLIM_NOFILE); + } +#ifdef USE_UNLIMITED_SELECT + glbl.SetFdSetSize(howmany(iFiles, __NFDBITS) * sizeof (fd_mask)); +#endif + DBGPRINTF("Max number of files set to %d [kernel max %ld].\n", iFiles, (long) maxFiles.rlim_max); + +finalize_it: + RETiRet; +} + + +/* legacy config system: reset config variables to default values. */ +static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal) +{ + loadConf->globals.bLogStatusMsgs = DFLT_bLogStatusMsgs; + loadConf->globals.bDebugPrintTemplateList = 1; + loadConf->globals.bDebugPrintCfSysLineHandlerList = 1; + loadConf->globals.bDebugPrintModuleList = 1; + loadConf->globals.bAbortOnUncleanConfig = 0; + loadConf->globals.bReduceRepeatMsgs = 0; + free(loadConf->globals.mainQ.pszMainMsgQFName); + loadConf->globals.mainQ.pszMainMsgQFName = NULL; + loadConf->globals.mainQ.iMainMsgQueueSize = 10000; + loadConf->globals.mainQ.iMainMsgQHighWtrMark = 8000; + loadConf->globals.mainQ.iMainMsgQLowWtrMark = 2000; + loadConf->globals.mainQ.iMainMsgQDiscardMark = 9800; + loadConf->globals.mainQ.iMainMsgQDiscardSeverity = 8; + loadConf->globals.mainQ.iMainMsgQueMaxFileSize = 1024 * 1024; + loadConf->globals.mainQ.iMainMsgQueueNumWorkers = 1; + loadConf->globals.mainQ.iMainMsgQPersistUpdCnt = 0; + loadConf->globals.mainQ.bMainMsgQSyncQeueFiles = 0; + loadConf->globals.mainQ.iMainMsgQtoQShutdown = 1500; + loadConf->globals.mainQ.iMainMsgQtoActShutdown = 1000; + loadConf->globals.mainQ.iMainMsgQtoEnq = 2000; + loadConf->globals.mainQ.iMainMsgQtoWrkShutdown = 60000; + loadConf->globals.mainQ.iMainMsgQWrkMinMsgs = 100; + loadConf->globals.mainQ.iMainMsgQDeqSlowdown = 0; + loadConf->globals.mainQ.bMainMsgQSaveOnShutdown = 1; + loadConf->globals.mainQ.MainMsgQueType = QUEUETYPE_FIXED_ARRAY; + loadConf->globals.mainQ.iMainMsgQueMaxDiskSpace = 0; + loadConf->globals.mainQ.iMainMsgQueDeqBatchSize = 32; + + return RS_RET_OK; +} + + +/* legacy config system: set the action resume interval */ +static rsRetVal +setModDir(void __attribute__((unused)) *pVal, uchar* pszNewVal) +{ + DEFiRet; + iRet = module.SetModDir(pszNewVal); + free(pszNewVal); + RETiRet; +} + + +/* "load" a build in module and register it for the current load config */ +static rsRetVal +regBuildInModule(rsRetVal (*modInit)(), uchar *name, void *pModHdlr) +{ + modInfo_t *pMod; + DEFiRet; + CHKiRet(module.doModInit(modInit, name, pModHdlr, &pMod)); + addModToCnfList(pMod); +finalize_it: + RETiRet; +} + + +/* load build-in modules + * very first version begun on 2007-07-23 by rgerhards + */ +static rsRetVal +loadBuildInModules() +{ + DEFiRet; + + CHKiRet(regBuildInModule(modInitFile, UCHAR_CONSTANT("builtin-file"), NULL)); + CHKiRet(regBuildInModule(modInitPipe, UCHAR_CONSTANT("builtin-pipe"), NULL)); + CHKiRet(regBuildInModule(modInitShell, UCHAR_CONSTANT("builtin-shell"), NULL)); + CHKiRet(regBuildInModule(modInitDiscard, UCHAR_CONSTANT("builtin-discard"), NULL)); +# ifdef SYSLOG_INET + CHKiRet(regBuildInModule(modInitFwd, UCHAR_CONSTANT("builtin-fwd"), NULL)); +# 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 type 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 + * User names now must begin with: + * [a-zA-Z0-9_.] + */ + CHKiRet(regBuildInModule(modInitUsrMsg, (uchar*) "builtin-usrmsg", NULL)); + + /* load build-in parser modules */ + CHKiRet(regBuildInModule(modInitpmrfc5424, UCHAR_CONSTANT("builtin-pmrfc5424"), NULL)); + CHKiRet(regBuildInModule(modInitpmrfc3164, UCHAR_CONSTANT("builtin-pmrfc3164"), NULL)); + + /* and set default parser modules. Order is *very* important, legacy + * (3164) parser needs to go last! */ + CHKiRet(parser.AddDfltParser(UCHAR_CONSTANT("rsyslog.rfc5424"))); + CHKiRet(parser.AddDfltParser(UCHAR_CONSTANT("rsyslog.rfc3164"))); + + /* load build-in strgen modules */ + CHKiRet(regBuildInModule(modInitsmfile, UCHAR_CONSTANT("builtin-smfile"), NULL)); + CHKiRet(regBuildInModule(modInitsmtradfile, UCHAR_CONSTANT("builtin-smtradfile"), NULL)); + CHKiRet(regBuildInModule(modInitsmfwd, UCHAR_CONSTANT("builtin-smfwd"), NULL)); + CHKiRet(regBuildInModule(modInitsmtradfwd, UCHAR_CONSTANT("builtin-smtradfwd"), NULL)); + +finalize_it: + if(iRet != RS_RET_OK) { + /* we need to do fprintf, as we do not yet have an error reporting system + * in place. + */ + fprintf(stderr, "fatal error: could not activate built-in modules. Error code %d.\n", + iRet); + } + RETiRet; +} + + +/* intialize the legacy config system */ +static inline rsRetVal +initLegacyConf(void) +{ + DEFiRet; + uchar *pTmp; + ruleset_t *pRuleset; + + DBGPRINTF("doing legacy config system init\n"); + /* construct the default ruleset */ + ruleset.Construct(&pRuleset); + ruleset.SetName(pRuleset, UCHAR_CONSTANT("RSYSLOG_DefaultRuleset")); + ruleset.ConstructFinalize(loadConf, pRuleset); + + /* now register config handlers */ + CHKiRet(regCfSysLineHdlr((uchar *)"sleep", 0, eCmdHdlrGoneAway, + NULL, NULL, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"logrsyslogstatusmessages", 0, eCmdHdlrBinary, + NULL, &loadConf->globals.bLogStatusMsgs, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"errormessagestostderr", 0, eCmdHdlrBinary, + NULL, &loadConf->globals.bErrMsgToStderr, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"abortonuncleanconfig", 0, eCmdHdlrBinary, + NULL, &loadConf->globals.bAbortOnUncleanConfig, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"repeatedmsgreduction", 0, eCmdHdlrBinary, + NULL, &loadConf->globals.bReduceRepeatMsgs, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"debugprinttemplatelist", 0, eCmdHdlrBinary, + NULL, &(loadConf->globals.bDebugPrintTemplateList), NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"debugprintmodulelist", 0, eCmdHdlrBinary, + NULL, &(loadConf->globals.bDebugPrintModuleList), NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"debugprintcfsyslinehandlerlist", 0, eCmdHdlrBinary, + NULL, &(loadConf->globals.bDebugPrintCfSysLineHandlerList), NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"privdroptouser", 0, eCmdHdlrUID, + NULL, &loadConf->globals.uidDropPriv, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"privdroptouserid", 0, eCmdHdlrInt, + NULL, &loadConf->globals.uidDropPriv, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"privdroptogroup", 0, eCmdHdlrGID, + NULL, &loadConf->globals.gidDropPriv, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"privdroptogroupid", 0, eCmdHdlrGID, + NULL, &loadConf->globals.gidDropPriv, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"generateconfiggraph", 0, eCmdHdlrGetWord, + NULL, &loadConf->globals.pszConfDAGFile, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"umask", 0, eCmdHdlrFileCreateMode, + NULL, &loadConf->globals.umask, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"maxopenfiles", 0, eCmdHdlrInt, + setMaxFiles, NULL, NULL)); + + CHKiRet(regCfSysLineHdlr((uchar *)"actionresumeinterval", 0, eCmdHdlrInt, + setActionResumeInterval, NULL, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"modload", 0, eCmdHdlrCustomHandler, + conf.doModLoad, NULL, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"defaultruleset", 0, eCmdHdlrGetWord, + setDefaultRuleset, NULL, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"ruleset", 0, eCmdHdlrGetWord, + setCurrRuleset, NULL, NULL)); + + /* handler for "larger" config statements (tie into legacy conf system) */ + CHKiRet(regCfSysLineHdlr((uchar *)"template", 0, eCmdHdlrCustomHandler, + conf.doNameLine, (void*)DIR_TEMPLATE, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"outchannel", 0, eCmdHdlrCustomHandler, + conf.doNameLine, (void*)DIR_OUTCHANNEL, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"allowedsender", 0, eCmdHdlrCustomHandler, + conf.doNameLine, (void*)DIR_ALLOWEDSENDER, NULL)); + + /* the following are parameters for the main message queue. I have the + * strong feeling that this needs to go to a different space, but that + * feeling may be wrong - we'll see how things evolve. + * rgerhards, 2011-04-21 + */ + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuefilename", 0, eCmdHdlrGetWord, + NULL, &loadConf->globals.mainQ.pszMainMsgQFName, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuesize", 0, eCmdHdlrInt, + NULL, &loadConf->globals.mainQ.iMainMsgQueueSize, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuehighwatermark", 0, eCmdHdlrInt, + NULL, &loadConf->globals.mainQ.iMainMsgQHighWtrMark, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuelowwatermark", 0, eCmdHdlrInt, + NULL, &loadConf->globals.mainQ.iMainMsgQLowWtrMark, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuediscardmark", 0, eCmdHdlrInt, + NULL, &loadConf->globals.mainQ.iMainMsgQDiscardMark, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuediscardseverity", 0, eCmdHdlrSeverity, + NULL, &loadConf->globals.mainQ.iMainMsgQDiscardSeverity, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuecheckpointinterval", 0, eCmdHdlrInt, + NULL, &loadConf->globals.mainQ.iMainMsgQPersistUpdCnt, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuesyncqueuefiles", 0, eCmdHdlrBinary, + NULL, &loadConf->globals.mainQ.bMainMsgQSyncQeueFiles, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuetype", 0, eCmdHdlrGetWord, + setMainMsgQueType, NULL, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueueworkerthreads", 0, eCmdHdlrInt, + NULL, &loadConf->globals.mainQ.iMainMsgQueueNumWorkers, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuetimeoutshutdown", 0, eCmdHdlrInt, + NULL, &loadConf->globals.mainQ.iMainMsgQtoQShutdown, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuetimeoutactioncompletion", 0, eCmdHdlrInt, + NULL, &loadConf->globals.mainQ.iMainMsgQtoActShutdown, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuetimeoutenqueue", 0, eCmdHdlrInt, + NULL, &loadConf->globals.mainQ.iMainMsgQtoEnq, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueueworkertimeoutthreadshutdown", 0, eCmdHdlrInt, + NULL, &loadConf->globals.mainQ.iMainMsgQtoWrkShutdown, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuedequeueslowdown", 0, eCmdHdlrInt, + NULL, &loadConf->globals.mainQ.iMainMsgQDeqSlowdown, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueueworkerthreadminimummessages", 0, eCmdHdlrInt, + NULL, &loadConf->globals.mainQ.iMainMsgQWrkMinMsgs, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuemaxfilesize", 0, eCmdHdlrSize, + NULL, &loadConf->globals.mainQ.iMainMsgQueMaxFileSize, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuedequeuebatchsize", 0, eCmdHdlrSize, + NULL, &loadConf->globals.mainQ.iMainMsgQueDeqBatchSize, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuemaxdiskspace", 0, eCmdHdlrSize, + NULL, &loadConf->globals.mainQ.iMainMsgQueMaxDiskSpace, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuesaveonshutdown", 0, eCmdHdlrBinary, + NULL, &loadConf->globals.mainQ.bMainMsgQSaveOnShutdown, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuedequeuetimebegin", 0, eCmdHdlrInt, + NULL, &loadConf->globals.mainQ.iMainMsgQueueDeqtWinFromHr, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuedequeuetimeend", 0, eCmdHdlrInt, + NULL, &loadConf->globals.mainQ.iMainMsgQueueDeqtWinToHr, NULL)); + /* moddir is a bit hard problem -- because it actually needs to + * modify a setting that is specific to module.c. The important point + * is that this action MUST actually be carried out during config load, + * because we must load modules in order to get their config extensions + * (no way around). + * TODO: think about a clean solution + */ + CHKiRet(regCfSysLineHdlr((uchar *)"moddir", 0, eCmdHdlrGetWord, + setModDir, NULL, NULL)); + + /* finally, the reset handler */ + CHKiRet(regCfSysLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, + resetConfigVariables, NULL, NULL)); + + /* initialize the build-in templates */ + pTmp = template_DebugFormat; + tplAddLine(ourConf, "RSYSLOG_DebugFormat", &pTmp); + pTmp = template_SyslogProtocol23Format; + tplAddLine(ourConf, "RSYSLOG_SyslogProtocol23Format", &pTmp); + pTmp = template_FileFormat; /* new format for files with high-precision stamp */ + tplAddLine(ourConf, "RSYSLOG_FileFormat", &pTmp); + pTmp = template_TraditionalFileFormat; + tplAddLine(ourConf, "RSYSLOG_TraditionalFileFormat", &pTmp); + pTmp = template_WallFmt; + tplAddLine(ourConf, " WallFmt", &pTmp); + pTmp = template_ForwardFormat; + tplAddLine(ourConf, "RSYSLOG_ForwardFormat", &pTmp); + pTmp = template_TraditionalForwardFormat; + tplAddLine(ourConf, "RSYSLOG_TraditionalForwardFormat", &pTmp); + pTmp = template_StdUsrMsgFmt; + tplAddLine(ourConf, " StdUsrMsgFmt", &pTmp); + pTmp = template_StdDBFmt; + tplAddLine(ourConf, " StdDBFmt", &pTmp); + pTmp = template_StdPgSQLFmt; + tplAddLine(ourConf, " StdPgSQLFmt", &pTmp); + pTmp = template_StdJSONFmt; + tplAddLine(ourConf, " StdJSONFmt", &pTmp); + pTmp = template_spoofadr; + tplLastStaticInit(ourConf, tplAddLine(ourConf, "RSYSLOG_omudpspoofDfltSourceTpl", &pTmp)); + +finalize_it: + RETiRet; +} + + +/* validate the current configuration, generate error messages, do + * optimizations, etc, etc,... + */ +static inline rsRetVal +validateConf(void) +{ + DEFiRet; + + /* some checks */ + if(ourConf->globals.mainQ.iMainMsgQueueNumWorkers < 1) { + errmsg.LogError(0, NO_ERRCODE, "$MainMsgQueueNumWorkers must be at least 1! Set to 1.\n"); + ourConf->globals.mainQ.iMainMsgQueueNumWorkers = 1; + } + + if(ourConf->globals.mainQ.MainMsgQueType == QUEUETYPE_DISK) { + errno = 0; /* for logerror! */ + if(glbl.GetWorkDir() == NULL) { + errmsg.LogError(0, NO_ERRCODE, "No $WorkDirectory specified - can not run main message queue in 'disk' mode. " + "Using 'FixedArray' instead.\n"); + ourConf->globals.mainQ.MainMsgQueType = QUEUETYPE_FIXED_ARRAY; + } + if(ourConf->globals.mainQ.pszMainMsgQFName == NULL) { + errmsg.LogError(0, NO_ERRCODE, "No $MainMsgQueueFileName specified - can not run main message queue in " + "'disk' mode. Using 'FixedArray' instead.\n"); + ourConf->globals.mainQ.MainMsgQueType = QUEUETYPE_FIXED_ARRAY; + } + } + RETiRet; +} + + +/* Load a configuration. This will do all necessary steps to create + * the in-memory representation of the configuration, including support + * for multiple configuration languages. + * Note that to support the legacy language we must provide some global + * object that holds the currently-being-loaded config ptr. + * Begun 2011-04-20, rgerhards + */ +rsRetVal +load(rsconf_t **cnf, uchar *confFile) +{ + int iNbrActions; + int r; + DEFiRet; + + CHKiRet(rsconfConstruct(&loadConf)); +ourConf = loadConf; // TODO: remove, once ourConf is gone! + + CHKiRet(loadBuildInModules()); + CHKiRet(initLegacyConf()); + + /* open the configuration file */ + r = cnfSetLexFile((char*)confFile); + if(r == 0) { + r = yyparse(); + conf.GetNbrActActions(loadConf, &iNbrActions); + } + + if(r == 1) { + errmsg.LogError(0, RS_RET_CONF_PARSE_ERROR, + "CONFIG ERROR: could not interpret master " + "config file '%s'.", confFile); + ABORT_FINALIZE(RS_RET_CONF_PARSE_ERROR); + } else if(iNbrActions == 0) { + errmsg.LogError(0, RS_RET_NO_ACTIONS, "CONFIG ERROR: there are no " + "active actions configured. Inputs will " + "run, but no output whatsoever is created."); + ABORT_FINALIZE(RS_RET_NO_ACTIONS); + } + tellLexEndParsing(); + + tellCoreConfigLoadDone(); + tellModulesConfigLoadDone(); + + tellModulesCheckConfig(); + CHKiRet(validateConf()); + + /* we are done checking the config - now validate if we should actually run or not. + * If not, terminate. -- rgerhards, 2008-07-25 + * TODO: iConfigVerify -- should it be pulled from the config, or leave as is (option)? + */ + if(iConfigVerify) { + if(iRet == RS_RET_OK) + iRet = RS_RET_VALIDATION_RUN; + FINALIZE; + } + + /* all OK, pass loaded conf to caller */ + *cnf = loadConf; +// TODO: enable this once all config code is moved to here! loadConf = NULL; + + dbgprintf("rsyslog finished loading master config %p\n", loadConf); + rsconfDebugPrint(loadConf); + +finalize_it: + RETiRet; +} + + +/* queryInterface function + */ +BEGINobjQueryInterface(rsconf) +CODESTARTobjQueryInterface(rsconf) + if(pIf->ifVersion != rsconfCURR_IF_VERSION) { /* check for current version, increment on each change */ + ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); + } + + /* ok, we have the right interface, so let's fill it + * Please note that we may also do some backwards-compatibility + * work here (if we can support an older interface version - that, + * of course, also affects the "if" above). + */ + pIf->Construct = rsconfConstruct; + pIf->ConstructFinalize = rsconfConstructFinalize; + pIf->Destruct = rsconfDestruct; + pIf->DebugPrint = rsconfDebugPrint; + pIf->Load = load; + pIf->Activate = activate; +finalize_it: +ENDobjQueryInterface(rsconf) + + +/* Initialize the rsconf class. Must be called as the very first method + * before anything else is called inside this class. + */ +BEGINObjClassInit(rsconf, 1, OBJ_IS_CORE_MODULE) /* class, version */ + /* request objects we use */ + CHKiRet(objUse(ruleset, CORE_COMPONENT)); + CHKiRet(objUse(rule, CORE_COMPONENT)); + CHKiRet(objUse(module, CORE_COMPONENT)); + CHKiRet(objUse(conf, CORE_COMPONENT)); + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + CHKiRet(objUse(glbl, CORE_COMPONENT)); + CHKiRet(objUse(datetime, CORE_COMPONENT)); + CHKiRet(objUse(parser, CORE_COMPONENT)); + + /* now set our own handlers */ + OBJSetMethodHandler(objMethod_DEBUGPRINT, rsconfDebugPrint); + OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, rsconfConstructFinalize); +ENDObjClassInit(rsconf) + + +/* De-initialize the rsconf class. + */ +BEGINObjClassExit(rsconf, OBJ_IS_CORE_MODULE) /* class, version */ + objRelease(rule, CORE_COMPONENT); + objRelease(ruleset, CORE_COMPONENT); + objRelease(module, CORE_COMPONENT); + objRelease(conf, CORE_COMPONENT); + objRelease(errmsg, CORE_COMPONENT); + objRelease(glbl, CORE_COMPONENT); + objRelease(datetime, CORE_COMPONENT); + objRelease(parser, CORE_COMPONENT); +ENDObjClassExit(rsconf) + +/* vi:set ai: + */ diff --git a/runtime/rsconf.h b/runtime/rsconf.h new file mode 100644 index 00000000..8715cf1b --- /dev/null +++ b/runtime/rsconf.h @@ -0,0 +1,182 @@ +/* The rsconf object. It models a complete rsyslog configuration. + * + * Copyright 2011 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 <http://www.gnu.org/licenses/>. + * + * 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 INCLUDED_RSCONF_H +#define INCLUDED_RSCONF_H + +#include "linkedlist.h" +#include "queue.h" + +/* --- configuration objects (the plan is to have ALL upper layers in this file) --- */ + +/* queue config parameters. TODO: move to queue.c? */ +struct queuecnf_s { + int iMainMsgQueueSize; /* size of the main message queue above */ + int iMainMsgQHighWtrMark; /* high water mark for disk-assisted queues */ + int iMainMsgQLowWtrMark; /* low water mark for disk-assisted queues */ + int iMainMsgQDiscardMark; /* begin to discard messages */ + int iMainMsgQDiscardSeverity; /* by default, discard nothing to prevent unintentional loss */ + int iMainMsgQueueNumWorkers; /* number of worker threads for the mm queue above */ + queueType_t MainMsgQueType; /* type of the main message queue above */ + uchar *pszMainMsgQFName; /* prefix for the main message queue file */ + int64 iMainMsgQueMaxFileSize; + int iMainMsgQPersistUpdCnt; /* persist queue info every n updates */ + int bMainMsgQSyncQeueFiles; /* sync queue files on every write? */ + int iMainMsgQtoQShutdown; /* queue shutdown (ms) */ + int iMainMsgQtoActShutdown; /* action shutdown (in phase 2) */ + int iMainMsgQtoEnq; /* timeout for queue enque */ + int iMainMsgQtoWrkShutdown; /* timeout for worker thread shutdown */ + int iMainMsgQWrkMinMsgs; /* minimum messages per worker needed to start a new one */ + int iMainMsgQDeqSlowdown; /* dequeue slowdown (simple rate limiting) */ + int64 iMainMsgQueMaxDiskSpace; /* max disk space allocated 0 ==> unlimited */ + int64 iMainMsgQueDeqBatchSize; /* dequeue batch size */ + int bMainMsgQSaveOnShutdown; /* save queue on shutdown (when DA enabled)? */ + int iMainMsgQueueDeqtWinFromHr; /* hour begin of time frame when queue is to be dequeued */ + int iMainMsgQueueDeqtWinToHr; /* hour begin of time frame when queue is to be dequeued */ +}; + +/* globals are data items that are really global, and can be set only + * once (at least in theory, because the legacy system permits them to + * be re-set as often as the user likes). + */ +struct globals_s { + int bDebugPrintTemplateList; + int bDebugPrintModuleList; + int bDebugPrintCfSysLineHandlerList; + int bLogStatusMsgs; /* log rsyslog start/stop/HUP messages? */ + int bErrMsgToStderr; /* print error messages to stderr + (in addition to everything else)? */ + int bAbortOnUncleanConfig; /* abort run (rather than starting with partial + config) if there was any issue in conf */ + int uidDropPriv; /* user-id to which priveleges should be dropped to */ + int gidDropPriv; /* group-id to which priveleges should be dropped to */ + int umask; /* umask to use */ + uchar *pszConfDAGFile; /* name of config DAG file, non-NULL means generate one */ + + // TODO are the following ones defaults? + int bReduceRepeatMsgs; /* reduce repeated message - 0 - no, 1 - yes */ + + //TODO: other representation for main queue? Or just load it differently? + queuecnf_t mainQ; /* main queue paramters */ +}; + +/* (global) defaults are global in the sense that they are accessible + * to all code, but they can change value and other objects (like + * actions) actually copy the value a global had at the time the action + * was defined. In that sense, a global default is just that, a default, + * wich can (and will) be changed in the course of config file + * processing. Once the config file has been processed, defaults + * can be dropped. The current code does not do this for simplicity. + * That is not a problem, because the defaults do not take up much memory. + * At a later stage, we may think about dropping them. -- rgerhards, 2011-04-19 + */ +struct defaults_s { +}; + + +/* list of modules loaded in this configuration (config specific module list) */ +struct cfgmodules_etry_s { + cfgmodules_etry_t *next; + modInfo_t *pMod; + /* the following data is input module specific */ + void *modCnf; /* pointer to the input module conf */ + sbool canActivate; /* OK to activate this config? */ + sbool canRun; /* OK to run this config? */ +}; + +struct cfgmodules_s { + cfgmodules_etry_t *root; +}; + +/* outchannel-specific data */ +struct outchannels_s { + struct outchannel *ochRoot; /* the root of the outchannel list */ + struct outchannel *ochLast; /* points to the last element of the outchannel list */ +}; + +struct templates_s { + struct template *root; /* the root of the template list */ + struct template *last; /* points to the last element of the template list */ + struct template *lastStatic; /* last static element of the template list */ +}; + + +struct actions_s { + unsigned nbrActions; /* number of actions */ +}; + + +struct rulesets_s { + linkedList_t llRulesets; /* this is NOT a pointer - no typo here ;) */ + + /* support for legacy rsyslog.conf format */ + ruleset_t *pCurr; /* currently "active" ruleset */ + ruleset_t *pDflt; /* current default ruleset, e.g. for binding to actions which have no other */ +}; + + +/* --- end configuration objects --- */ + +/* the rsconf object */ +struct rsconf_s { + BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ + cfgmodules_t modules; + globals_t globals; + defaults_t defaults; + templates_t templates; + outchannels_t och; + actions_t actions; + rulesets_t rulesets; + /* note: rulesets include the complete output part: + * - rules + * - filter (as part of the action) + * - actions + * Of course, we need to debate if we shall change that some time... + */ +}; + + +/* interfaces */ +BEGINinterface(rsconf) /* name must also be changed in ENDinterface macro! */ + INTERFACEObjDebugPrint(rsconf); + rsRetVal (*Construct)(rsconf_t **ppThis); + rsRetVal (*ConstructFinalize)(rsconf_t __attribute__((unused)) *pThis); + rsRetVal (*Destruct)(rsconf_t **ppThis); + rsRetVal (*Load)(rsconf_t **ppThis, uchar *confFile); + rsRetVal (*Activate)(rsconf_t *ppThis); +ENDinterface(rsconf) +// TODO: switch version to 1 for first "complete" version!!!! 2011-04-20 +#define rsconfCURR_IF_VERSION 0 /* increment whenever you change the interface above! */ + + +/* prototypes */ +PROTOTYPEObj(rsconf); + +/* globally-visible external data */ +extern rsconf_t *runConf;/* the currently running config */ +extern rsconf_t *loadConf;/* the config currently being loaded (no concurrent config load supported!) */ + + +/* some defaults (to be removed?) */ +#define DFLT_bLogStatusMsgs 1 + +#endif /* #ifndef INCLUDED_RSCONF_H */ diff --git a/runtime/rsyslog.c b/runtime/rsyslog.c index bdb1c9ff..cbab06b7 100644 --- a/runtime/rsyslog.c +++ b/runtime/rsyslog.c @@ -62,19 +62,13 @@ #include "rsyslog.h" #include "obj.h" -#include "vm.h" -#include "sysvar.h" #include "stringbuf.h" #include "wti.h" #include "wtp.h" -#include "expr.h" -#include "ctok.h" -#include "vmop.h" -#include "vmstk.h" -#include "vmprg.h" #include "datetime.h" #include "queue.h" #include "conf.h" +#include "rsconf.h" #include "glbl.h" #include "errmsg.h" #include "prop.h" @@ -177,22 +171,6 @@ rsrtInit(char **ppErrObj, obj_if_t *pObjIF) CHKiRet(glblClassInit(NULL)); if(ppErrObj != NULL) *ppErrObj = "msg"; CHKiRet(msgClassInit(NULL)); - if(ppErrObj != NULL) *ppErrObj = "ctok_token"; - CHKiRet(ctok_tokenClassInit(NULL)); - if(ppErrObj != NULL) *ppErrObj = "ctok"; - CHKiRet(ctokClassInit(NULL)); - if(ppErrObj != NULL) *ppErrObj = "vmstk"; - CHKiRet(vmstkClassInit(NULL)); - if(ppErrObj != NULL) *ppErrObj = "sysvar"; - CHKiRet(sysvarClassInit(NULL)); - if(ppErrObj != NULL) *ppErrObj = "vm"; - CHKiRet(vmClassInit(NULL)); - if(ppErrObj != NULL) *ppErrObj = "vmop"; - CHKiRet(vmopClassInit(NULL)); - if(ppErrObj != NULL) *ppErrObj = "vmprg"; - CHKiRet(vmprgClassInit(NULL)); - if(ppErrObj != NULL) *ppErrObj = "expr"; - CHKiRet(exprClassInit(NULL)); if(ppErrObj != NULL) *ppErrObj = "rule"; CHKiRet(ruleClassInit(NULL)); if(ppErrObj != NULL) *ppErrObj = "ruleset"; @@ -209,6 +187,8 @@ rsrtInit(char **ppErrObj, obj_if_t *pObjIF) CHKiRet(parserClassInit(NULL)); if(ppErrObj != NULL) *ppErrObj = "strgen"; CHKiRet(strgenClassInit(NULL)); + if(ppErrObj != NULL) *ppErrObj = "rsconf"; + CHKiRet(rsconfClassInit(NULL)); /* dummy "classes" */ if(ppErrObj != NULL) *ppErrObj = "str"; diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index adcd4656..058322bd 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -60,12 +60,12 @@ * rgerhards, 2006-11-30 */ -#define CONF_OMOD_NUMSTRINGS_MAXSIZE 2 /* cache for pointers to output module buffer pointers. All - * rsyslog-provided plugins do NOT need more than two buffers. If +#define CONF_OMOD_NUMSTRINGS_MAXSIZE 3 /* cache for pointers to output module buffer pointers. All + * rsyslog-provided plugins do NOT need more than three buffers. If * more are needed (future developments, third-parties), rsyslog * must be recompiled with a larger parameter. Hardcoding this * saves us some overhead, both in runtime in code complexity. As - * it is doubtful if ever more than 2 parameters are needed, the + * it is doubtful if ever more than 3 parameters are needed, the * approach taken here is considered appropriate. * rgerhards, 2010-06-24 */ @@ -128,6 +128,7 @@ typedef uintTiny propid_t; #define PROP_APP_NAME 20 #define PROP_PROCID 21 #define PROP_MSGID 22 +#define PROP_PARSESUCCESS 23 #define PROP_SYS_NOW 150 #define PROP_SYS_YEAR 151 #define PROP_SYS_MONTH 152 @@ -137,7 +138,10 @@ typedef uintTiny propid_t; #define PROP_SYS_QHOUR 156 #define PROP_SYS_MINUTE 157 #define PROP_SYS_MYHOSTNAME 158 +#define PROP_CEE 200 +#define PROP_CEE_ALL_JSON 201 #define PROP_SYS_BOM 159 +#define PROP_SYS_UPTIME 160 /* The error codes below are orginally "borrowed" from @@ -332,6 +336,11 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_TIMEOUT = -2164, /**< timeout occured during operation */ RS_RET_RCV_ERR = -2165, /**< error occured during socket rcv operation */ RS_RET_NO_SOCK_CONFIGURED = -2166, /**< no socket (name) was configured where one is required */ + RS_RET_CONF_NOT_GLBL = -2167, /**< $Begin not in global scope */ + RS_RET_CONF_IN_GLBL = -2168, /**< $End when in global scope */ + RS_RET_CONF_INVLD_END = -2169, /**< $End for wrong conf object (probably nesting error) */ + RS_RET_CONF_INVLD_SCOPE = -2170,/**< config statement not valid in current scope (e.g. global stmt in action block) */ + RS_RET_CONF_END_NO_ACT = -2171, /**< end of action block, but no actual action specified */ RS_RET_NO_LSTN_DEFINED = -2172, /**< no listener defined (e.g. inside an input module) */ RS_RET_EPOLL_CR_FAILED = -2173, /**< epoll_create() failed */ RS_RET_EPOLL_CTL_FAILED = -2174, /**< epoll_ctl() failed */ @@ -348,7 +357,24 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_MISSING_WHITESPACE = -2185, /**< whitespace is missing in some config construct */ RS_RET_OK_WARN = -2186, /**< config part: everything was OK, but a warning message was emitted */ + RS_RET_INVLD_CONF_OBJ= -2200, /**< invalid config object (e.g. $Begin conf statement) */ + RS_RET_ERR_LIBEE_INIT = -2201, /**< cannot obtain libee ctx */ + RS_RET_ERR_LIBLOGNORM_INIT = -2202,/**< cannot obtain liblognorm ctx */ + RS_RET_ERR_LIBLOGNORM_SAMPDB_LOAD = -2203,/**< liblognorm sampledb load failed */ + RS_RET_CMD_GONE_AWAY = -2204,/**< config directive existed, but no longer supported */ + RS_RET_ERR_SCHED_PARAMS = -2205,/**< there is a problem with configured thread scheduling params */ + RS_RET_SOCKNAME_MISSING = -2206,/**< no socket name configured where one is required */ + RS_RET_CONF_PARSE_ERROR = -2207,/**< (fatal) error parsing config file */ RS_RET_CONF_RQRD_PARAM_MISSING = -2208,/**< required parameter in config object is missing */ + RS_RET_MOD_UNKNOWN = -2209,/**< module (config name) is unknown */ + RS_RET_CONFOBJ_UNSUPPORTED = -2210,/**< config objects (v6 conf) are not supported here */ + RS_RET_MISSING_CNFPARAMS = -2211, /**< missing configuration parameters */ + RS_RET_NO_LISTNERS = -2212, /**< module loaded, but no listeners are defined */ + RS_RET_INVLD_PROTOCOL = -2213, /**< invalid protocol specified in config file */ + RS_RET_CNF_INVLD_FRAMING = -2214, /**< invalid framing specified in config file */ + RS_RET_LEGA_ACT_NOT_SUPPORTED = -2215, /**< the module (no longer) supports legacy action syntax */ + RS_RET_MAX_OMSR_REACHED = -2216, /**< max nbr of string requests reached, not supported by core */ + /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ @@ -473,6 +499,18 @@ rsRetVal rsrtExit(void); int rsrtIsInit(void); rsRetVal rsrtSetErrLogger(rsRetVal (*errLogger)(int, uchar*)); +/* this define below is (later) intended to be used to implement empty + * structs. TODO: check if compilers supports this and, if not, define + * a dummy variable. This requires review of where in code empty structs + * are already defined. -- rgerhards, 2010-07-26 + */ +#define EMPTY_STRUCT + +/* TODO: remove this -- this is only for transition of the config system */ +extern rsconf_t *ourConf; /* defined by syslogd.c, a hack for functions that do not + yet receive a copy, so that we can incrementially + compile and change... -- rgerhars, 2011-04-19 */ + #endif /* multi-include protection */ /* vim:set ai: */ diff --git a/runtime/rule.c b/runtime/rule.c index b27ddb5f..18199230 100644 --- a/runtime/rule.c +++ b/runtime/rule.c @@ -34,18 +34,14 @@ #include "action.h" #include "rule.h" #include "errmsg.h" -#include "vm.h" -#include "var.h" #include "srUtils.h" #include "batch.h" +#include "parserif.h" #include "unicode-helper.h" /* static data */ DEFobjStaticHelpers DEFobjCurrIf(errmsg) -DEFobjCurrIf(expr) -DEFobjCurrIf(var) -DEFobjCurrIf(vm) /* support for simple textual representation of FIOP names @@ -68,6 +64,12 @@ getFIOPName(unsigned iFIOP) case FIOP_REGEX: pRet = "regex"; break; + case FIOP_EREREGEX: + pRet = "ereregex"; + break; + case FIOP_ISEMPTY: + pRet = "isempty"; + break; default: pRet = "NOP"; break; @@ -115,8 +117,6 @@ shouldProcessThisMessage(rule_t *pRule, msg_t *pMsg, sbool *bProcessMsg) uchar *pszPropVal; int bRet = 0; size_t propLen; - vm_t *pVM = NULL; - var_t *pResult = NULL; ISOBJ_TYPE_assert(pRule, rule); assert(pMsg != NULL); @@ -178,17 +178,12 @@ shouldProcessThisMessage(rule_t *pRule, msg_t *pMsg, sbool *bProcessMsg) else bRet = 1; } else if(pRule->f_filter_type == FILTER_EXPR) { - CHKiRet(vm.Construct(&pVM)); - CHKiRet(vm.ConstructFinalize(pVM)); - CHKiRet(vm.SetMsg(pVM, pMsg)); - CHKiRet(vm.ExecProg(pVM, pRule->f_filterData.f_expr->pVmprg)); - CHKiRet(vm.PopBoolFromStack(pVM, &pResult)); - dbgprintf("result of rainerscript filter evaluation: %lld\n", pResult->val.num); - /* VM is destructed on function exit */ - bRet = (pResult->val.num) ? 1 : 0; + bRet = cnfexprEvalBool(pRule->f_filterData.expr, pMsg); + dbgprintf("result of rainerscript filter evaluation: %d\n", bRet); } else { assert(pRule->f_filter_type == FILTER_PROP); /* assert() just in case... */ - pszPropVal = MsgGetProp(pMsg, NULL, pRule->f_filterData.prop.propID, &propLen, &pbMustBeFreed); + pszPropVal = MsgGetProp(pMsg, NULL, pRule->f_filterData.prop.propID, + pRule->f_filterData.prop.propName, &propLen, &pbMustBeFreed); /* Now do the compares (short list currently ;)) */ switch(pRule->f_filterData.prop.operation ) { @@ -196,6 +191,10 @@ shouldProcessThisMessage(rule_t *pRule, msg_t *pMsg, sbool *bProcessMsg) if(rsCStrLocateInSzStr(pRule->f_filterData.prop.pCSCompValue, (uchar*) pszPropVal) != -1) bRet = 1; break; + case FIOP_ISEMPTY: + if(propLen == 0) + bRet = 1; /* process message! */ + break; case FIOP_ISEQUAL: if(rsCStrSzStrCmp(pRule->f_filterData.prop.pCSCompValue, pszPropVal, ustrlen(pszPropVal)) == 0) @@ -228,14 +227,28 @@ shouldProcessThisMessage(rule_t *pRule, msg_t *pMsg, sbool *bProcessMsg) bRet = (bRet == 1) ? 0 : 1; if(Debug) { - dbgprintf("Filter: check for property '%s' (value '%s') ", - propIDToName(pRule->f_filterData.prop.propID), pszPropVal); + char *cstr; + if(pRule->f_filterData.prop.propID == PROP_CEE) { + cstr = es_str2cstr(pRule->f_filterData.prop.propName, NULL); + dbgprintf("Filter: check for CEE property '%s' (value '%s') ", + cstr, pszPropVal); + free(cstr); + } else { + dbgprintf("Filter: check for property '%s' (value '%s') ", + propIDToName(pRule->f_filterData.prop.propID), pszPropVal); + } if(pRule->f_filterData.prop.isNegated) dbgprintf("NOT "); - dbgprintf("%s '%s': %s\n", - getFIOPName(pRule->f_filterData.prop.operation), - rsCStrGetSzStrNoNULL(pRule->f_filterData.prop.pCSCompValue), - bRet ? "TRUE" : "FALSE"); + if(pRule->f_filterData.prop.operation == FIOP_ISEMPTY) { + dbgprintf("%s : %s\n", + getFIOPName(pRule->f_filterData.prop.operation), + bRet ? "TRUE" : "FALSE"); + } else { + dbgprintf("%s '%s': %s\n", + getFIOPName(pRule->f_filterData.prop.operation), + rsCStrGetSzStrNoNULL(pRule->f_filterData.prop.pCSCompValue), + bRet ? "TRUE" : "FALSE"); + } } /* cleanup */ @@ -244,13 +257,6 @@ shouldProcessThisMessage(rule_t *pRule, msg_t *pMsg, sbool *bProcessMsg) } finalize_it: - /* destruct in any case, not just on error, but it makes error handling much easier */ - if(pVM != NULL) - vm.Destruct(&pVM); - - if(pResult != NULL) - var.Destruct(&pResult); - *bProcessMsg = bRet; RETiRet; } @@ -327,9 +333,8 @@ CODESTARTobjDestruct(rule) rsCStrDestruct(&pThis->f_filterData.prop.pCSCompValue); if(pThis->f_filterData.prop.regex_cache != NULL) rsCStrRegexDestruct(&pThis->f_filterData.prop.regex_cache); - } else if(pThis->f_filter_type == FILTER_EXPR) { - if(pThis->f_filterData.f_expr != NULL) - expr.Destruct(&pThis->f_filterData.f_expr); + if(pThis->f_filterData.prop.propName != NULL) + es_deleteStr(pThis->f_filterData.prop.propName); } llDestroy(&pThis->llActList); @@ -371,6 +376,7 @@ DEFFUNC_llExecFunc(dbgPrintInitInfoAction) /* debugprint for the rule object */ BEGINobjDebugPrint(rule) /* be sure to specify the object type also in END and CODESTART macros! */ int i; + char *cstr; CODESTARTobjDebugPrint(rule) dbgoprint((obj_t*) pThis, "rsyslog rule:\n"); if(pThis->pCSProgNameComp != NULL) @@ -391,12 +397,19 @@ CODESTARTobjDebugPrint(rule) } else { dbgprintf("PROPERTY-BASED Filter:\n"); dbgprintf("\tProperty.: '%s'\n", propIDToName(pThis->f_filterData.prop.propID)); + if(pThis->f_filterData.prop.propName != NULL) { + cstr = es_str2cstr(pThis->f_filterData.prop.propName, NULL); + dbgprintf("\tCEE-Prop.: '%s'\n", cstr); + free(cstr); + } dbgprintf("\tOperation: "); if(pThis->f_filterData.prop.isNegated) dbgprintf("NOT "); dbgprintf("'%s'\n", getFIOPName(pThis->f_filterData.prop.operation)); - dbgprintf("\tValue....: '%s'\n", - rsCStrGetSzStrNoNULL(pThis->f_filterData.prop.pCSCompValue)); + if(pThis->f_filterData.prop.pCSCompValue != NULL) { + dbgprintf("\tValue....: '%s'\n", + rsCStrGetSzStrNoNULL(pThis->f_filterData.prop.pCSCompValue)); + } dbgprintf("\tAction...: "); } @@ -439,9 +452,6 @@ ENDobjQueryInterface(rule) */ BEGINObjClassExit(rule, OBJ_IS_CORE_MODULE) /* class, version */ objRelease(errmsg, CORE_COMPONENT); - objRelease(expr, CORE_COMPONENT); - objRelease(var, CORE_COMPONENT); - objRelease(vm, CORE_COMPONENT); ENDObjClassExit(rule) @@ -452,9 +462,6 @@ ENDObjClassExit(rule) BEGINObjClassInit(rule, 1, OBJ_IS_CORE_MODULE) /* class, version */ /* request objects we use */ CHKiRet(objUse(errmsg, CORE_COMPONENT)); - CHKiRet(objUse(expr, CORE_COMPONENT)); - CHKiRet(objUse(var, CORE_COMPONENT)); - CHKiRet(objUse(vm, CORE_COMPONENT)); /* set our own handlers */ OBJSetMethodHandler(objMethod_DEBUGPRINT, ruleDebugPrint); diff --git a/runtime/rule.h b/runtime/rule.h index 2b585879..1b07279b 100644 --- a/runtime/rule.h +++ b/runtime/rule.h @@ -23,9 +23,10 @@ #ifndef INCLUDED_RULE_H #define INCLUDED_RULE_H +#include "libestr.h" #include "linkedlist.h" #include "regexp.h" -#include "expr.h" +#include "rainerscript.h" /* the rule object */ struct rule_s { @@ -47,8 +48,9 @@ struct rule_s { cstr_t *pCSCompValue; /* value to "compare" against */ sbool isNegated; propid_t propID; /* ID of the requested property */ + es_str_t *propName; /* name of property for CEE-based filters */ } prop; - expr_t *f_expr; /* expression object */ + struct cnfexpr *expr; /* expression object */ } f_filterData; ruleset_t *pRuleset; /* associated ruleset */ diff --git a/runtime/ruleset.c b/runtime/ruleset.c index 2f307f05..c384663a 100644 --- a/runtime/ruleset.c +++ b/runtime/ruleset.c @@ -1,7 +1,7 @@ /* ruleset.c - rsyslog's ruleset object * - * We have a two-way structure of linked lists: one global linked list - * (llAllRulesets) hold alls rule sets that we know. Included in each + * We have a two-way structure of linked lists: one config-specifc linked list + * (conf->rulesets.llRulesets) hold alls rule sets that we know. Included in each * list is a list of rules (which contain a list of actions, but that's * a different story). * @@ -11,7 +11,7 @@ * * Module begun 2009-06-10 by Rainer Gerhards * - * Copyright 2009 Rainer Gerhards and Adiscon GmbH. + * Copyright 2009-2011 Rainer Gerhards and Adiscon GmbH. * * This file is part of the rsyslog runtime library. * @@ -34,7 +34,6 @@ #include "config.h" #include <stdlib.h> -#include <string.h> #include <assert.h> #include <ctype.h> @@ -48,6 +47,7 @@ #include "parser.h" #include "batch.h" #include "unicode-helper.h" +#include "rsconf.h" #include "dirty.h" /* for main ruleset queue creation */ /* static data */ @@ -56,26 +56,23 @@ DEFobjCurrIf(errmsg) DEFobjCurrIf(rule) DEFobjCurrIf(parser) -linkedList_t llRulesets; /* this is NOT a pointer - no typo here ;) */ -ruleset_t *pCurrRuleset = NULL; /* currently "active" ruleset */ -ruleset_t *pDfltRuleset = NULL; /* current default ruleset, e.g. for binding to actions which have no other */ - /* forward definitions */ static rsRetVal processBatch(batch_t *pBatch); -/* ---------- linked-list key handling functions ---------- */ + +/* ---------- linked-list key handling functions (ruleset) ---------- */ /* destructor for linked list keys. */ -static rsRetVal keyDestruct(void __attribute__((unused)) *pData) +rsRetVal +rulesetKeyDestruct(void __attribute__((unused)) *pData) { free(pData); return RS_RET_OK; } +/* ---------- END linked-list key handling functions (ruleset) ---------- */ -/* ---------- END linked-list key handling functions ---------- */ - /* driver to iterate over all of this ruleset actions */ typedef struct iterateAllActions_s { @@ -122,7 +119,7 @@ DEFFUNC_llExecFunc(doIterateAllActions) * must be done or a shutdown is pending. */ static rsRetVal -iterateAllActions(rsRetVal (*pFunc)(void*, void*), void* pParam) +iterateAllActions(rsconf_t *conf, rsRetVal (*pFunc)(void*, void*), void* pParam) { iterateAllActions_t params; DEFiRet; @@ -130,7 +127,7 @@ iterateAllActions(rsRetVal (*pFunc)(void*, void*), void* pParam) params.pFunc = pFunc; params.pParam = pParam; - CHKiRet(llExecFunc(&llRulesets, doIterateAllActions, ¶ms)); + CHKiRet(llExecFunc(&(conf->rulesets.llRulesets), doIterateAllActions, ¶ms)); finalize_it: RETiRet; @@ -227,7 +224,7 @@ processBatch(batch_t *pBatch) if(pBatch->bSingleRuleset) { pThis = batchGetRuleset(pBatch); if(pThis == NULL) - pThis = pDfltRuleset; + pThis = ourConf->rulesets.pDflt; ISOBJ_TYPE_assert(pThis, ruleset); CHKiRet(llExecFunc(&pThis->llRules, processBatchDoRules, pBatch)); } else { @@ -245,9 +242,9 @@ finalize_it: * rgerhards, 2009-11-04 */ static parserList_t* -GetParserList(msg_t *pMsg) +GetParserList(rsconf_t *conf, msg_t *pMsg) { - return (pMsg->pRuleset == NULL) ? pDfltRuleset->pParserLst : pMsg->pRuleset->pParserLst; + return (pMsg->pRuleset == NULL) ? conf->rulesets.pDflt->pParserLst : pMsg->pRuleset->pParserLst; } @@ -269,7 +266,7 @@ addRule(ruleset_t *pThis, rule_t **ppRule) rule.Destruct(ppRule); } else { CHKiRet(llAppend(&pThis->llRules, NULL, *ppRule)); - dbgprintf("selector line successfully processed\n"); + dbgprintf("selector line successfully processed, %d actions\n", iActionCnt); } finalize_it: @@ -294,9 +291,9 @@ finalize_it: * is really much more natural to return the pointer directly. */ static ruleset_t* -GetCurrent(void) +GetCurrent(rsconf_t *conf) { - return pCurrRuleset; + return conf->rulesets.pCurr; } @@ -316,13 +313,13 @@ GetRulesetQueue(ruleset_t *pThis) /* Find the ruleset with the given name and return a pointer to its object. */ rsRetVal -rulesetGetRuleset(ruleset_t **ppRuleset, uchar *pszName) +rulesetGetRuleset(rsconf_t *conf, ruleset_t **ppRuleset, uchar *pszName) { DEFiRet; assert(ppRuleset != NULL); assert(pszName != NULL); - CHKiRet(llFind(&llRulesets, pszName, (void*) ppRuleset)); + CHKiRet(llFind(&(conf->rulesets.llRulesets), pszName, (void*) ppRuleset)); finalize_it: RETiRet; @@ -332,14 +329,14 @@ finalize_it: /* Set a new default rule set. If the default can not be found, no change happens. */ static rsRetVal -SetDefaultRuleset(uchar *pszName) +SetDefaultRuleset(rsconf_t *conf, uchar *pszName) { ruleset_t *pRuleset; DEFiRet; assert(pszName != NULL); - CHKiRet(rulesetGetRuleset(&pRuleset, pszName)); - pDfltRuleset = pRuleset; + CHKiRet(rulesetGetRuleset(conf, &pRuleset, pszName)); + conf->rulesets.pDflt = pRuleset; dbgprintf("default rule set changed to %p: '%s'\n", pRuleset, pszName); finalize_it: @@ -350,14 +347,14 @@ finalize_it: /* Set a new current rule set. If the ruleset can not be found, no change happens. */ static rsRetVal -SetCurrRuleset(uchar *pszName) +SetCurrRuleset(rsconf_t *conf, uchar *pszName) { ruleset_t *pRuleset; DEFiRet; assert(pszName != NULL); - CHKiRet(rulesetGetRuleset(&pRuleset, pszName)); - pCurrRuleset = pRuleset; + CHKiRet(rulesetGetRuleset(conf, &pRuleset, pszName)); + conf->rulesets.pCurr = pRuleset; dbgprintf("current rule set changed to %p: '%s'\n", pRuleset, pszName); finalize_it: @@ -389,7 +386,7 @@ ENDobjConstruct(ruleset) * This also adds the rule set to the list of all known rulesets. */ static rsRetVal -rulesetConstructFinalize(ruleset_t *pThis) +rulesetConstructFinalize(rsconf_t *conf, ruleset_t *pThis) { uchar *keyName; DEFiRet; @@ -400,14 +397,14 @@ rulesetConstructFinalize(ruleset_t *pThis) * two separate copies. */ CHKmalloc(keyName = ustrdup(pThis->pszName)); - CHKiRet(llAppend(&llRulesets, keyName, pThis)); + CHKiRet(llAppend(&(conf->rulesets.llRulesets), keyName, pThis)); /* this now also is the new current ruleset */ - pCurrRuleset = pThis; + conf->rulesets.pCurr = pThis; /* and also the default, if so far none has been set */ - if(pDfltRuleset == NULL) - pDfltRuleset = pThis; + if(conf->rulesets.pDflt == NULL) + conf->rulesets.pDflt = pThis; finalize_it: RETiRet; @@ -428,17 +425,6 @@ CODESTARTobjDestruct(ruleset) free(pThis->pszName); ENDobjDestruct(ruleset) -/* this is a special destructor for the linkedList class. LinkedList does NOT - * provide a pointer to the pointer, but rather the raw pointer itself. So we - * must map this, otherwise the destructor will abort. - */ -static rsRetVal -rulesetDestructForLinkedList(void *pData) -{ - ruleset_t *pThis = (ruleset_t*) pData; - return rulesetDestruct(&pThis); -} - /* destruct ALL rule sets that reside in the system. This must * be callable before unloading this module as the module may @@ -447,18 +433,29 @@ rulesetDestructForLinkedList(void *pData) * everything runs stable again. -- rgerhards, 2009-06-10 */ static rsRetVal -destructAllActions(void) +destructAllActions(rsconf_t *conf) { DEFiRet; - CHKiRet(llDestroy(&llRulesets)); - CHKiRet(llInit(&llRulesets, rulesetDestructForLinkedList, keyDestruct, strcasecmp)); - pDfltRuleset = NULL; + CHKiRet(llDestroy(&(conf->rulesets.llRulesets))); + CHKiRet(llInit(&(conf->rulesets.llRulesets), rulesetDestructForLinkedList, rulesetKeyDestruct, strcasecmp)); + conf->rulesets.pDflt = NULL; finalize_it: RETiRet; } +/* this is a special destructor for the linkedList class. LinkedList does NOT + * provide a pointer to the pointer, but rather the raw pointer itself. So we + * must map this, otherwise the destructor will abort. + */ +rsRetVal +rulesetDestructForLinkedList(void *pData) +{ + ruleset_t *pThis = (ruleset_t*) pData; + return rulesetDestruct(&pThis); +} + /* helper for debugPrint(), initiates rule printing */ DEFFUNC_llExecFunc(doDebugPrintRule) { @@ -480,11 +477,11 @@ DEFFUNC_llExecFunc(doDebugPrintAll) /* debug print all rulesets */ static rsRetVal -debugPrintAll(void) +debugPrintAll(rsconf_t *conf) { DEFiRet; dbgprintf("All Rulesets:\n"); - llExecFunc(&llRulesets, doDebugPrintAll, NULL); + llExecFunc(&(conf->rulesets.llRulesets), doDebugPrintAll, NULL); dbgprintf("End of Rulesets.\n"); RETiRet; } @@ -497,19 +494,19 @@ debugPrintAll(void) * considered acceptable for the time being. * rgerhards, 2009-10-27 */ -static rsRetVal -rulesetCreateQueue(void __attribute__((unused)) *pVal, int *pNewVal) +static inline rsRetVal +doRulesetCreateQueue(rsconf_t *conf, int *pNewVal) { uchar *rulesetMainQName; DEFiRet; - if(pCurrRuleset == NULL) { + if(conf->rulesets.pCurr == NULL) { errmsg.LogError(0, RS_RET_NO_CURR_RULESET, "error: currently no specific ruleset specified, thus a " "queue can not be added to it"); ABORT_FINALIZE(RS_RET_NO_CURR_RULESET); } - if(pCurrRuleset->pQueue != NULL) { + if(conf->rulesets.pCurr->pQueue != NULL) { errmsg.LogError(0, RS_RET_RULES_QUEUE_EXISTS, "error: ruleset already has a main queue, can not " "add another one"); ABORT_FINALIZE(RS_RET_RULES_QUEUE_EXISTS); @@ -519,14 +516,19 @@ rulesetCreateQueue(void __attribute__((unused)) *pVal, int *pNewVal) FINALIZE; /* if it is turned off, we do not need to change anything ;) */ dbgprintf("adding a ruleset-specific \"main\" queue"); - rulesetMainQName = (pCurrRuleset->pszName == NULL)? UCHAR_CONSTANT("ruleset") : - pCurrRuleset->pszName; - CHKiRet(createMainQueue(&pCurrRuleset->pQueue, rulesetMainQName)); + rulesetMainQName = (conf->rulesets.pCurr->pszName == NULL)? UCHAR_CONSTANT("ruleset") : + conf->rulesets.pCurr->pszName; + CHKiRet(createMainQueue(&conf->rulesets.pCurr->pQueue, rulesetMainQName)); finalize_it: RETiRet; } +static rsRetVal +rulesetCreateQueue(void __attribute__((unused)) *pVal, int *pNewVal) +{ + return doRulesetCreateQueue(ourConf, pNewVal); +} /* Add a ruleset specific parser to the ruleset. Note that adding the first * parser automatically disables the default parsers. If they are needed as well, @@ -538,12 +540,12 @@ finalize_it: * rgerhards, 2009-11-04 */ static rsRetVal -rulesetAddParser(void __attribute__((unused)) *pVal, uchar *pName) +doRulesetAddParser(rsconf_t *conf, uchar *pName) { parser_t *pParser; DEFiRet; - assert(pCurrRuleset != NULL); + assert(conf->rulesets.pCurr != NULL); CHKiRet(objUse(parser, CORE_COMPONENT)); iRet = parser.FindParser(&pParser, pName); @@ -556,10 +558,10 @@ rulesetAddParser(void __attribute__((unused)) *pVal, uchar *pName) FINALIZE; } - CHKiRet(parser.AddParserToList(&pCurrRuleset->pParserLst, pParser)); + CHKiRet(parser.AddParserToList(&conf->rulesets.pCurr->pParserLst, pParser)); - dbgprintf("added parser '%s' to ruleset '%s'\n", pName, pCurrRuleset->pszName); -RUNLOG_VAR("%p", pCurrRuleset->pParserLst); + dbgprintf("added parser '%s' to ruleset '%s'\n", pName, conf->rulesets.pCurr->pszName); +RUNLOG_VAR("%p", conf->rulesets.pCurr->pParserLst); finalize_it: d_free(pName); /* no longer needed */ @@ -567,6 +569,12 @@ finalize_it: RETiRet; } +static rsRetVal +rulesetAddParser(void __attribute__((unused)) *pVal, uchar *pName) +{ + return doRulesetAddParser(ourConf, pName); +} + /* queryInterface function * rgerhards, 2008-02-21 @@ -607,7 +615,6 @@ ENDobjQueryInterface(ruleset) * rgerhards, 2009-04-06 */ BEGINObjClassExit(ruleset, OBJ_IS_CORE_MODULE) /* class, version */ - llDestroy(&llRulesets); objRelease(errmsg, CORE_COMPONENT); objRelease(rule, CORE_COMPONENT); objRelease(parser, CORE_COMPONENT); @@ -627,9 +634,6 @@ BEGINObjClassInit(ruleset, 1, OBJ_IS_CORE_MODULE) /* class, version */ OBJSetMethodHandler(objMethod_DEBUGPRINT, rulesetDebugPrint); OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, rulesetConstructFinalize); - /* prepare global data */ - CHKiRet(llInit(&llRulesets, rulesetDestructForLinkedList, keyDestruct, strcasecmp)); - /* config file handlers */ CHKiRet(regCfSysLineHdlr((uchar *)"rulesetparser", 0, eCmdHdlrGetWord, rulesetAddParser, NULL, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"rulesetcreatemainqueue", 0, eCmdHdlrBinary, rulesetCreateQueue, NULL, NULL)); diff --git a/runtime/ruleset.h b/runtime/ruleset.h index 52e633eb..f4443e18 100644 --- a/runtime/ruleset.h +++ b/runtime/ruleset.h @@ -38,30 +38,42 @@ struct ruleset_s { /* interfaces */ BEGINinterface(ruleset) /* name must also be changed in ENDinterface macro! */ INTERFACEObjDebugPrint(ruleset); - rsRetVal (*DebugPrintAll)(void); + rsRetVal (*DebugPrintAll)(rsconf_t *conf); rsRetVal (*Construct)(ruleset_t **ppThis); - rsRetVal (*ConstructFinalize)(ruleset_t __attribute__((unused)) *pThis); + rsRetVal (*ConstructFinalize)(rsconf_t *conf, ruleset_t __attribute__((unused)) *pThis); rsRetVal (*Destruct)(ruleset_t **ppThis); - rsRetVal (*IterateAllActions)(rsRetVal (*pFunc)(void*, void*), void* pParam); - rsRetVal (*DestructAllActions)(void); + rsRetVal (*IterateAllActions)(rsconf_t *conf, rsRetVal (*pFunc)(void*, void*), void* pParam); + rsRetVal (*DestructAllActions)(rsconf_t *conf); rsRetVal (*AddRule)(ruleset_t *pThis, rule_t **ppRule); rsRetVal (*SetName)(ruleset_t *pThis, uchar *pszName); rsRetVal (*ProcessBatch)(batch_t*); - rsRetVal (*GetRuleset)(ruleset_t **ppThis, uchar*); - rsRetVal (*SetDefaultRuleset)(uchar*); - rsRetVal (*SetCurrRuleset)(uchar*); - ruleset_t* (*GetCurrent)(void); + rsRetVal (*GetRuleset)(rsconf_t *conf, ruleset_t **ppThis, uchar*); + rsRetVal (*SetDefaultRuleset)(rsconf_t *conf, uchar*); + rsRetVal (*SetCurrRuleset)(rsconf_t *conf, uchar*); + ruleset_t* (*GetCurrent)(rsconf_t *conf); qqueue_t* (*GetRulesetQueue)(ruleset_t*); /* v3, 2009-11-04 */ - parserList_t* (*GetParserList)(msg_t *); - /* v4 */ + parserList_t* (*GetParserList)(rsconf_t *conf, msg_t *); + /* v5, 2011-04-19 + * added support for the rsconf object -- fundamental change + * v6, 2011-07-15 + * removed conf ptr from SetName, AddRule as the flex/bison based + * system uses globals in any case. + */ ENDinterface(ruleset) -#define rulesetCURR_IF_VERSION 4 /* increment whenever you change the interface structure! */ +#define rulesetCURR_IF_VERSION 6 /* increment whenever you change the interface structure! */ /* prototypes */ PROTOTYPEObj(ruleset); +/* TODO: remove these -- currently done dirty for config file + * redo -- rgerhards, 2011-04-19 + * rgerhards, 2012-04-19: actually, it may be way cooler not to remove + * them and use plain c-style conventions at least inside core objects. + */ +rsRetVal rulesetDestructForLinkedList(void *pData); +rsRetVal rulesetKeyDestruct(void __attribute__((unused)) *pData); /* Get name associated to ruleset. This function cannot fail (except, * of course, if previously something went really wrong). Returned @@ -75,5 +87,5 @@ rulesetGetName(ruleset_t *pRuleset) } -rsRetVal rulesetGetRuleset(ruleset_t **ppRuleset, uchar *pszName); +rsRetVal rulesetGetRuleset(rsconf_t *conf, ruleset_t **ppRuleset, uchar *pszName); #endif /* #ifndef INCLUDED_RULESET_H */ diff --git a/runtime/statsobj.c b/runtime/statsobj.c index 367ddb15..a21614f6 100644 --- a/runtime/statsobj.c +++ b/runtime/statsobj.c @@ -34,7 +34,6 @@ #include "unicode-helper.h" #include "obj.h" #include "statsobj.h" -#include "sysvar.h" #include "srUtils.h" #include "stringbuf.h" @@ -167,6 +166,56 @@ finalize_it: RETiRet; } +/* get all the object's countes together as CEE. */ +static rsRetVal +getStatsLineCEE(statsobj_t *pThis, cstr_t **ppcstr) +{ + cstr_t *pcstr; + ctr_t *pCtr; + DEFiRet; + + CHKiRet(cstrConstruct(&pcstr)); + rsCStrAppendStrWithLen(pcstr, UCHAR_CONSTANT("@cee: {"), 7); + + rsCStrAppendStrWithLen(pcstr, UCHAR_CONSTANT("\""), 1); + rsCStrAppendStrWithLen(pcstr, UCHAR_CONSTANT("name"), 4); + rsCStrAppendStrWithLen(pcstr, UCHAR_CONSTANT("\""), 1); + rsCStrAppendStrWithLen(pcstr, UCHAR_CONSTANT(":"), 1); + rsCStrAppendStrWithLen(pcstr, UCHAR_CONSTANT("\""), 1); + rsCStrAppendStr(pcstr, pThis->name); + rsCStrAppendStrWithLen(pcstr, UCHAR_CONSTANT("\""), 1); + rsCStrAppendStrWithLen(pcstr, UCHAR_CONSTANT(","), 1); + + /* now add all counters to this line */ + pthread_mutex_lock(&pThis->mutCtr); + for(pCtr = pThis->ctrRoot ; pCtr != NULL ; pCtr = pCtr->next) { + rsCStrAppendStrWithLen(pcstr, UCHAR_CONSTANT("\""), 1); + rsCStrAppendStr(pcstr, pCtr->name); + rsCStrAppendStrWithLen(pcstr, UCHAR_CONSTANT("\""), 1); + cstrAppendChar(pcstr, ':'); + switch(pCtr->ctrType) { + case ctrType_IntCtr: + rsCStrAppendInt(pcstr, *(pCtr->val.pIntCtr)); // TODO: OK????? + break; + case ctrType_Int: + rsCStrAppendInt(pcstr, *(pCtr->val.pInt)); + break; + } + if (pCtr->next != NULL) { + cstrAppendChar(pcstr, ','); + } else { + cstrAppendChar(pcstr, '}'); + } + + } + pthread_mutex_unlock(&pThis->mutCtr); + + CHKiRet(cstrFinalize(pcstr)); + *ppcstr = pcstr; + +finalize_it: + RETiRet; +} /* get all the object's countes together with object name as one line. */ @@ -213,14 +262,21 @@ finalize_it: * line. If the callback reports an error, processing is stopped. */ static rsRetVal -getAllStatsLines(rsRetVal(*cb)(void*, cstr_t*), void *usrptr) +getAllStatsLines(rsRetVal(*cb)(void*, cstr_t*), void *usrptr, statsFmtType_t fmt) { statsobj_t *o; cstr_t *cstr; DEFiRet; for(o = objRoot ; o != NULL ; o = o->next) { - CHKiRet(getStatsLine(o, &cstr)); + switch(fmt) { + case statsFmt_Legacy: + CHKiRet(getStatsLine(o, &cstr)); + break; + case statsFmt_JSON: + CHKiRet(getStatsLineCEE(o, &cstr)); + break; + } CHKiRet(cb(usrptr, cstr)); rsCStrDestruct(&cstr); } diff --git a/runtime/statsobj.h b/runtime/statsobj.h index 90279883..f7de68ee 100644 --- a/runtime/statsobj.h +++ b/runtime/statsobj.h @@ -43,6 +43,12 @@ typedef enum statsCtrType_e { ctrType_Int } statsCtrType_t; +/* stats line format types */ +typedef enum statsFmtType_e { + statsFmt_Legacy, + statsFmt_JSON +} statsFmtType_t; + /* helper entity, the counter */ typedef struct ctr_s { @@ -76,11 +82,15 @@ BEGINinterface(statsobj) /* name must also be changed in ENDinterface macro! */ rsRetVal (*Destruct)(statsobj_t **ppThis); rsRetVal (*SetName)(statsobj_t *pThis, uchar *name); rsRetVal (*GetStatsLine)(statsobj_t *pThis, cstr_t **ppcstr); - rsRetVal (*GetAllStatsLines)(rsRetVal(*cb)(void*, cstr_t*), void *usrptr); + rsRetVal (*GetAllStatsLines)(rsRetVal(*cb)(void*, cstr_t*), void *usrptr, statsFmtType_t fmt); rsRetVal (*AddCounter)(statsobj_t *pThis, uchar *ctrName, statsCtrType_t ctrType, void *pCtr); rsRetVal (*EnableStats)(void); ENDinterface(statsobj) -#define statsobjCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ +#define statsobjCURR_IF_VERSION 10 /* increment whenever you change the interface structure! */ +/* Changes + * v2-v9 rserved for future use in "older" version branches + * v10, 2012-04-01: GetAllStatsLines got fmt parameter + */ /* prototypes */ diff --git a/runtime/stream.h b/runtime/stream.h index 60c68cb2..a01929f2 100644 --- a/runtime/stream.h +++ b/runtime/stream.h @@ -70,7 +70,6 @@ #include "glbl.h" #include "stream.h" #include "zlibw.h" -#include "apc.h" /* stream types */ typedef enum { @@ -126,7 +125,6 @@ typedef struct strm_s { sbool bStopWriter; /* shall writer thread terminate? */ sbool bDoTimedWait; /* instruct writer thread to do a times wait to support flush timeouts */ int iFlushInterval; /* flush in which interval - 0, no flushing */ - apc_id_t apcID; /* id of current Apc request (used for cancelling) */ pthread_mutex_t mut;/* mutex for flush in async mode */ pthread_cond_t notFull; pthread_cond_t notEmpty; @@ -139,7 +137,6 @@ typedef struct strm_s { size_t lenBuf; } asyncBuf[STREAM_ASYNC_NUMBUFS]; pthread_t writerThreadID; - int apcRequested; /* is an apc Requested? */ /* support for omfile size-limiting commands, special counters, NOT persisted! */ off_t iSizeLimit; /* file size limit, 0 = no limit */ uchar *pszSizeLimitCmd; /* command to carry out when size limit is reached */ diff --git a/runtime/stringbuf.c b/runtime/stringbuf.c index e11d0e3b..e7fd72c2 100644 --- a/runtime/stringbuf.c +++ b/runtime/stringbuf.c @@ -33,6 +33,7 @@ #include <string.h> #include <ctype.h> #include <sys/types.h> +#include <libestr.h> #include "rsyslog.h" #include "stringbuf.h" #include "srUtils.h" @@ -102,6 +103,34 @@ finalize_it: RETiRet; } + +/* construct from es_str_t string + * rgerhards 2010-12-03 + */ +rsRetVal cstrConstructFromESStr(cstr_t **ppThis, es_str_t *str) +{ + DEFiRet; + cstr_t *pThis; + + assert(ppThis != NULL); + + CHKiRet(rsCStrConstruct(&pThis)); + + pThis->iBufSize = pThis->iStrLen = es_strlen(str); + if((pThis->pBuf = (uchar*) MALLOC(sizeof(uchar) * pThis->iStrLen)) == NULL) { + RSFREEOBJ(pThis); + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + } + + /* we do NOT need to copy the \0! */ + memcpy(pThis->pBuf, es_getBufAddr(str), pThis->iStrLen); + + *ppThis = pThis; + +finalize_it: + RETiRet; +} + /* construct from CStr object. only the counted string is * copied, not the szString. * rgerhards 2005-10-18 diff --git a/runtime/stringbuf.h b/runtime/stringbuf.h index b6e22977..bba004a0 100644 --- a/runtime/stringbuf.h +++ b/runtime/stringbuf.h @@ -33,6 +33,7 @@ #define _STRINGBUF_H_INCLUDED__ 1 #include <assert.h> +#include <libestr.h> /** * The dynamic string buffer object. @@ -54,6 +55,7 @@ typedef struct cstr_s */ rsRetVal cstrConstruct(cstr_t **ppThis); #define rsCStrConstruct(x) cstrConstruct((x)) +rsRetVal cstrConstructFromESStr(cstr_t **ppThis, es_str_t *str); rsRetVal rsCStrConstructFromszStr(cstr_t **ppThis, uchar *sz); rsRetVal rsCStrConstructFromCStr(cstr_t **ppThis, cstr_t *pFrom); diff --git a/runtime/sync.c b/runtime/sync.c deleted file mode 100644 index 46b5ce4c..00000000 --- a/runtime/sync.c +++ /dev/null @@ -1,55 +0,0 @@ -/* synrchonization-related stuff. In theory, that should - * help porting to something different from pthreads. - * - * Copyright 2007-2012 Adiscon GmbH. - * - * This file is part of the rsyslog runtime library. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * -or- - * see COPYING.ASL20 in the source distribution - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "config.h" - -#include <stdlib.h> - -#include "rsyslog.h" -#include "sync.h" -#include "debug.h" - - -void -SyncObjInit(pthread_mutex_t **mut) -{ - *mut = (pthread_mutex_t *) MALLOC(sizeof (pthread_mutex_t)); - pthread_mutex_init(*mut, NULL); -} - - -/* This function destroys the mutex and also sets the mutex object - * to NULL. While the later is not strictly necessary, it is a good - * aid when debugging problems. As this function is not exepected to - * be called quite frequently, the additional overhead can well be - * accepted. If this changes over time, setting to NULL may be - * reconsidered. - rgerhards, 2007-11-12 - */ -void -SyncObjExit(pthread_mutex_t **mut) -{ - if(*mut != NULL) { - pthread_mutex_destroy(*mut); - free(*mut); - *mut = NULL; - } -} diff --git a/runtime/sync.h b/runtime/sync.h deleted file mode 100644 index a2c8f122..00000000 --- a/runtime/sync.h +++ /dev/null @@ -1,48 +0,0 @@ -/* Definitions syncrhonization-related stuff. In theory, that should - * help porting to something different from pthreads. - * - * Copyright 2007-2012 Adiscon GmbH. - * - * This file is part of the rsyslog runtime library. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * -or- - * see COPYING.ASL20 in the source distribution - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef INCLUDED_SYNC_H -#define INCLUDED_SYNC_H - -#include <pthread.h> - -/* SYNC_OBJ_TOOL definition must be placed in object to be synced! - * SYNC_OBJ_TOOL_INIT must be called upon of object construction and - * SUNC_OBJ_TOOL_EXIT must be called upon object destruction - */ -#define SYNC_OBJ_TOOL pthread_mutex_t *Sync_mut -#define SYNC_OBJ_TOOL_INIT(x) SyncObjInit(&((x)->Sync_mut)) -#define SYNC_OBJ_TOOL_EXIT(x) SyncObjExit(&((x)->Sync_mut)) - -/* If we run in non-debug (release) mode, we use inline code for the mutex - * operations. If we run in debug mode, we use functions, because they - * are better to trace in the stackframe. - */ -#define LockObj(x) d_pthread_mutex_lock((x)->Sync_mut) -#define UnlockObj(x) d_pthread_mutex_unlock((x)->Sync_mut) - -void SyncObjInit(pthread_mutex_t **mut); -void SyncObjExit(pthread_mutex_t **mut); -extern void lockObj(pthread_mutex_t *mut); -extern void unlockObj(pthread_mutex_t *mut); - -#endif /* #ifndef INCLUDED_SYNC_H */ diff --git a/runtime/sysvar.c b/runtime/sysvar.c deleted file mode 100644 index ac5dd548..00000000 --- a/runtime/sysvar.c +++ /dev/null @@ -1,202 +0,0 @@ -/* sysvar.c - imlements rsyslog system variables - * - * At least for now, this class only has static functions and no - * instances. - * - * Module begun 2008-02-25 by Rainer Gerhards - * - * Copyright (C) 2008-2012 Adiscon GmbH. - * - * This file is part of the rsyslog runtime library. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * -or- - * see COPYING.ASL20 in the source distribution - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "config.h" -#include <stdio.h> -#include <stdlib.h> -#include <assert.h> - -#include "rsyslog.h" -#include "obj.h" -#include "stringbuf.h" -#include "sysvar.h" -#include "datetime.h" - -/* static data */ -DEFobjStaticHelpers -DEFobjCurrIf(var) -DEFobjCurrIf(datetime) -DEFobjCurrIf(glbl) - - -/* Standard-Constructor - */ -BEGINobjConstruct(sysvar) /* be sure to specify the object type also in END macro! */ -ENDobjConstruct(sysvar) - - -/* ConstructionFinalizer - * rgerhards, 2008-01-09 - */ -static rsRetVal -sysvarConstructFinalize(sysvar_t __attribute__((unused)) *pThis) -{ - DEFiRet; - RETiRet; -} - - -/* destructor for the sysvar object */ -BEGINobjDestruct(sysvar) /* be sure to specify the object type also in END and CODESTART macros! */ -CODESTARTobjDestruct(sysvar) -ENDobjDestruct(sysvar) - - -/* This function returns the current date in different - * variants. It is used to construct the $NOW series of - * system properties. The returned buffer must be freed - * by the caller when no longer needed. If the function - * can not allocate memory, it returns a NULL pointer. - * Added 2007-07-10 rgerhards - * TODO: this was taken from msg.c and we should consolidate it with the code - * there. This is especially important when we increase the number of system - * variables (what we definitely want to do). - */ -typedef enum ENOWType { NOW_NOW, NOW_YEAR, NOW_MONTH, NOW_DAY, NOW_HOUR, NOW_MINUTE } eNOWType; -static rsRetVal -getNOW(eNOWType eNow, cstr_t **ppStr) -{ - DEFiRet; - uchar szBuf[16]; - struct syslogTime t; - - datetime.getCurrTime(&t, NULL); - switch(eNow) { - case NOW_NOW: - snprintf((char*) szBuf, sizeof(szBuf)/sizeof(uchar), "%4.4d-%2.2d-%2.2d", t.year, t.month, t.day); - break; - case NOW_YEAR: - snprintf((char*) szBuf, sizeof(szBuf)/sizeof(uchar), "%4.4d", t.year); - break; - case NOW_MONTH: - snprintf((char*) szBuf, sizeof(szBuf)/sizeof(uchar), "%2.2d", t.month); - break; - case NOW_DAY: - snprintf((char*) szBuf, sizeof(szBuf)/sizeof(uchar), "%2.2d", t.day); - break; - case NOW_HOUR: - snprintf((char*) szBuf, sizeof(szBuf)/sizeof(uchar), "%2.2d", t.hour); - break; - case NOW_MINUTE: - snprintf((char*) szBuf, sizeof(szBuf)/sizeof(uchar), "%2.2d", t.minute); - break; - } - - /* now create a string object out of it and hand that over to the var */ - CHKiRet(rsCStrConstructFromszStr(ppStr, szBuf)); - -finalize_it: - RETiRet; -} - - -/* The function returns a system variable suitable for use with RainerScript. Most importantly, this means - * that the value is returned in a var_t object. The var_t is constructed inside this function and - * MUST be freed by the caller. - * rgerhards, 2008-02-25 - */ -static rsRetVal -GetVar(cstr_t *pstrVarName, var_t **ppVar) -{ - DEFiRet; - var_t *pVar; - cstr_t *pstrProp; - - ASSERT(pstrVarName != NULL); - ASSERT(ppVar != NULL); - - /* make sure we have a var_t instance */ - CHKiRet(var.Construct(&pVar)); - CHKiRet(var.ConstructFinalize(pVar)); - - /* now begin the actual variable evaluation */ - if(!rsCStrSzStrCmp(pstrVarName, (uchar*)"now", sizeof("now") - 1)) { - CHKiRet(getNOW(NOW_NOW, &pstrProp)); - } else if(!rsCStrSzStrCmp(pstrVarName, (uchar*)"year", sizeof("year") - 1)) { - CHKiRet(getNOW(NOW_YEAR, &pstrProp)); - } else if(!rsCStrSzStrCmp(pstrVarName, (uchar*)"month", sizeof("month") - 1)) { - CHKiRet(getNOW(NOW_MONTH, &pstrProp)); - } else if(!rsCStrSzStrCmp(pstrVarName, (uchar*)"day", sizeof("day") - 1)) { - CHKiRet(getNOW(NOW_DAY, &pstrProp)); - } else if(!rsCStrSzStrCmp(pstrVarName, (uchar*)"hour", sizeof("hour") - 1)) { - CHKiRet(getNOW(NOW_HOUR, &pstrProp)); - } else if(!rsCStrSzStrCmp(pstrVarName, (uchar*)"minute", sizeof("minute") - 1)) { - CHKiRet(getNOW(NOW_MINUTE, &pstrProp)); - } else if(!rsCStrSzStrCmp(pstrVarName, (uchar*)"myhostname", sizeof("myhostname") - 1)) { - CHKiRet(rsCStrConstructFromszStr(&pstrProp, glbl.GetLocalHostName())); - } else { - ABORT_FINALIZE(RS_RET_SYSVAR_NOT_FOUND); - } - - /* now hand the string over to the var object */ - CHKiRet(var.SetString(pVar, pstrProp)); - - /* finally store var */ - *ppVar = pVar; - -finalize_it: - RETiRet; -} - - -/* queryInterface function - * rgerhards, 2008-02-21 - */ -BEGINobjQueryInterface(sysvar) -CODESTARTobjQueryInterface(sysvar) - if(pIf->ifVersion != sysvarCURR_IF_VERSION) { /* check for current version, increment on each change */ - ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); - } - - /* ok, we have the right interface, so let's fill it - * Please note that we may also do some backwards-compatibility - * work here (if we can support an older interface version - that, - * of course, also affects the "if" above). - */ - pIf->Construct = sysvarConstruct; - pIf->ConstructFinalize = sysvarConstructFinalize; - pIf->Destruct = sysvarDestruct; - pIf->GetVar = GetVar; -finalize_it: -ENDobjQueryInterface(sysvar) - - -/* Initialize the sysvar class. Must be called as the very first method - * before anything else is called inside this class. - * rgerhards, 2008-02-19 - */ -BEGINObjClassInit(sysvar, 1, OBJ_IS_CORE_MODULE) /* class, version */ - /* request objects we use */ - CHKiRet(objUse(var, CORE_COMPONENT)); - CHKiRet(objUse(datetime, CORE_COMPONENT)); - CHKiRet(objUse(glbl, CORE_COMPONENT)); - - /* set our own handlers */ - OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, sysvarConstructFinalize); -ENDObjClassInit(sysvar) - -/* vi:set ai: - */ diff --git a/runtime/sysvar.h b/runtime/sysvar.h deleted file mode 100644 index 3414ff34..00000000 --- a/runtime/sysvar.h +++ /dev/null @@ -1,47 +0,0 @@ -/* The sysvar object. So far, no instance can be defined (makes logically no - * sense). - * - * Copyright 2008-2012 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 <http://www.gnu.org/licenses/>. - * - * 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 INCLUDED_SYSVAR_H -#define INCLUDED_SYSVAR_H - -/* the sysvar object - not really used... */ -typedef struct sysvar_s { - BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ -} sysvar_t; - - -/* interfaces */ -BEGINinterface(sysvar) /* name must also be changed in ENDinterface macro! */ - INTERFACEObjDebugPrint(sysvar); - rsRetVal (*Construct)(sysvar_t **ppThis); - rsRetVal (*ConstructFinalize)(sysvar_t __attribute__((unused)) *pThis); - rsRetVal (*Destruct)(sysvar_t **ppThis); - rsRetVal (*GetVar)(cstr_t *pstrPropName, var_t **ppVar); -ENDinterface(sysvar) -#define sysvarCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ - - -/* prototypes */ -PROTOTYPEObj(sysvar); - -#endif /* #ifndef INCLUDED_SYSVAR_H */ diff --git a/runtime/typedefs.h b/runtime/typedefs.h index d3da7699..f994cbc4 100644 --- a/runtime/typedefs.h +++ b/runtime/typedefs.h @@ -79,6 +79,19 @@ typedef struct parserList_s parserList_t; typedef struct strgen_s strgen_t; typedef struct strgenList_s strgenList_t; typedef struct statsobj_s statsobj_t; +typedef struct nsd_epworkset_s nsd_epworkset_t; +typedef struct templates_s templates_t; +typedef struct queuecnf_s queuecnf_t; +typedef struct rulesets_s rulesets_t; +typedef struct globals_s globals_t; +typedef struct defaults_s defaults_t; +typedef struct actions_s actions_t; +typedef struct rsconf_s rsconf_t; +typedef struct cfgmodules_s cfgmodules_t; +typedef struct cfgmodules_etry_s cfgmodules_etry_t; +typedef struct outchannels_s outchannels_t; +typedef struct modConfData_s modConfData_t; +typedef struct instanceConf_s instanceConf_t; typedef rsRetVal (*prsf_t)(struct vmstk_s*, int); /* pointer to a RainerScript function */ typedef uint64 qDeqID; /* queue Dequeue order ID. 32 bits is considered dangerously few */ @@ -127,9 +140,44 @@ typedef enum { FIOP_ISEQUAL = 2, /* is (exactly) equal? */ FIOP_STARTSWITH = 3, /* starts with a string? */ FIOP_REGEX = 4, /* matches a (BRE) regular expression? */ - FIOP_EREREGEX = 5 /* matches a ERE regular expression? */ + FIOP_EREREGEX = 5, /* matches a ERE regular expression? */ + FIOP_ISEMPTY = 6 /* string empty <=> strlen(s) == 0 ?*/ } fiop_t; +/* 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, + eCmdHdlrString, + eCmdHdlrQueueType, + eCmdHdlrGoneAway /* statment existed, but is no longer supported */ +} ecslCmdHdrlType; + + +/* the next type describes $Begin .. $End block object types + */ +typedef enum cslConfObjType { + eConfObjGlobal = 0, /* global directives */ + eConfObjAction, /* action-specific directives */ + /* now come states that indicate that we wait for a block-end. These are + * states that permit us to do some safety checks and they hopefully ease + * migration to a "real" parser/grammar. + */ + eConfObjActionWaitEnd, + eConfObjAlways /* always valid, very special case (guess $End only!) */ +} ecslConfObjType; + /* multi-submit support. * This is done via a simple data structure, which holds the number of elements diff --git a/runtime/var.c b/runtime/var.c index ef7cc8e6..eecc5d6a 100644 --- a/runtime/var.c +++ b/runtime/var.c @@ -90,324 +90,6 @@ CODESTARTobjDebugPrint(var) ENDobjDebugPrint(var) -/* This function is similar to DebugPrint, but does not send its output to - * the debug log but instead to a caller-provided string. The idea here is that - * we can use this string to get a textual representation of a variable. - * Among others, this is useful for creating testbenches, our first use case for - * it. Here, it enables simple comparison of the resulting program to a - * reference program by simple string compare. - * Note that the caller must initialize the string object. We always add - * data to it. So, it can be easily combined into a chain of methods - * to generate the final string. - * rgerhards, 2008-07-07 - */ -static rsRetVal -Obj2Str(var_t *pThis, cstr_t *pstrPrg) -{ - DEFiRet; - size_t lenBuf; - uchar szBuf[2048]; - - ISOBJ_TYPE_assert(pThis, var); - assert(pstrPrg != NULL); - switch(pThis->varType) { - case VARTYPE_STR: - lenBuf = snprintf((char*) szBuf, sizeof(szBuf), "%s[cstr]", rsCStrGetSzStr(pThis->val.pStr)); - break; - case VARTYPE_NUMBER: - lenBuf = snprintf((char*) szBuf, sizeof(szBuf), "%lld[nbr]", pThis->val.num); - break; - default: - lenBuf = snprintf((char*) szBuf, sizeof(szBuf), "**UNKNOWN**[%d]", pThis->varType); - break; - } - CHKiRet(rsCStrAppendStrWithLen(pstrPrg, szBuf, lenBuf)); - -finalize_it: - RETiRet; -} - - -/* duplicates a var instance - * rgerhards, 2008-02-25 - */ -static rsRetVal -Duplicate(var_t *pThis, var_t **ppNew) -{ - DEFiRet; - var_t *pNew = NULL; - cstr_t *pstr; - - ISOBJ_TYPE_assert(pThis, var); - assert(ppNew != NULL); - - CHKiRet(varConstruct(&pNew)); - CHKiRet(varConstructFinalize(pNew)); - - /* we have the object, now copy value */ - pNew->varType = pThis->varType; - if(pThis->varType == VARTYPE_NUMBER) { - pNew->val.num = pThis->val.num; - } else if(pThis->varType == VARTYPE_STR) { - CHKiRet(rsCStrConstructFromCStr(&pstr, pThis->val.pStr)); - pNew->val.pStr = pstr; - } - - *ppNew = pNew; - -finalize_it: - if(iRet != RS_RET_OK && pNew != NULL) - varDestruct(&pNew); - - RETiRet; -} - - -/* free the current values (destructs objects if necessary) - */ -static rsRetVal -varUnsetValues(var_t *pThis) -{ - DEFiRet; - - ISOBJ_TYPE_assert(pThis, var); - if(pThis->varType == VARTYPE_STR) - rsCStrDestruct(&pThis->val.pStr); - - pThis->varType = VARTYPE_NONE; - - RETiRet; -} - - -/* set a string value - * The caller hands over the string and must n longer use it after this method - * has been called. - */ -static rsRetVal -varSetString(var_t *pThis, cstr_t *pStr) -{ - DEFiRet; - - ISOBJ_TYPE_assert(pThis, var); - - CHKiRet(varUnsetValues(pThis)); - pThis->varType = VARTYPE_STR; - pThis->val.pStr = pStr; - -finalize_it: - RETiRet; -} - - -/* set an int64 value */ -static rsRetVal -varSetNumber(var_t *pThis, number_t iVal) -{ - DEFiRet; - - ISOBJ_TYPE_assert(pThis, var); - - CHKiRet(varUnsetValues(pThis)); - pThis->varType = VARTYPE_NUMBER; - pThis->val.num = iVal; - -finalize_it: - RETiRet; -} - - -/* Change the provided object to be of type number. - * rgerhards, 2008-02-22 - */ -rsRetVal -ConvToNumber(var_t *pThis) -{ - DEFiRet; - number_t n; - - if(pThis->varType == VARTYPE_NUMBER) { - FINALIZE; - } else if(pThis->varType == VARTYPE_STR) { - iRet = rsCStrConvertToNumber(pThis->val.pStr, &n); - if(iRet == RS_RET_NOT_A_NUMBER) { - n = 0; - iRet = RS_RET_OK; /* we accept this as part of the language definition */ - } else if (iRet != RS_RET_OK) { - FINALIZE; - } - - /* we need to destruct the string first, because string and number are - * inside a union and share the memory area! -- rgerhards, 2008-04-03 - */ - rsCStrDestruct(&pThis->val.pStr); - - pThis->val.num = n; - pThis->varType = VARTYPE_NUMBER; - } - -finalize_it: - RETiRet; -} - - -/* convert the provided var to type string. This is always possible - * (except, of course, for things like out of memory...) - * TODO: finish implementation!!!!!!!!! - * rgerhards, 2008-02-24 - */ -rsRetVal -ConvToString(var_t *pThis) -{ - DEFiRet; - uchar szNumBuf[64]; - - if(pThis->varType == VARTYPE_STR) { - FINALIZE; - } else if(pThis->varType == VARTYPE_NUMBER) { - CHKiRet(srUtilItoA((char*)szNumBuf, sizeof(szNumBuf)/sizeof(uchar), pThis->val.num)); - CHKiRet(rsCStrConstructFromszStr(&pThis->val.pStr, szNumBuf)); - pThis->varType = VARTYPE_STR; - } - -finalize_it: - RETiRet; -} - - -/* convert (if necessary) the value to a boolean. In essence, this means the - * value must be a number, but in case of a string special logic is used as - * some string-values may represent a boolean (e.g. "true"). - * rgerhards, 2008-02-25 - */ -rsRetVal -ConvToBool(var_t *pThis) -{ - DEFiRet; - number_t n; - - if(pThis->varType == VARTYPE_NUMBER) { - FINALIZE; - } else if(pThis->varType == VARTYPE_STR) { - iRet = rsCStrConvertToBool(pThis->val.pStr, &n); - if(iRet == RS_RET_NOT_A_NUMBER) { - n = 0; - iRet = RS_RET_OK; /* we accept this as part of the language definition */ - } else if (iRet != RS_RET_OK) { - FINALIZE; - } - - /* we need to destruct the string first, because string and number are - * inside a union and share the memory area! -- rgerhards, 2008-04-03 - */ - rsCStrDestruct(&pThis->val.pStr); - pThis->val.num = n; - pThis->varType = VARTYPE_NUMBER; - } - -finalize_it: - RETiRet; -} - - -/* This function is used to prepare two var_t objects for a common operation, - * e.g before they are added, compared. The function looks at - * the data types of both operands and finds the best data type suitable for - * the operation (in respect to current types). Then, it converts those - * operands that need conversion. Please note that the passed-in var objects - * *are* modified and returned as new type. So do call this function only if - * you actually need the conversion. - * - * This is how the common data type is selected. Note that op1 and op2 are - * just the two operands, their order is irrelevant (this would just take up - * more table space - so string/number is the same thing as number/string). - * - * Common Types: - * op1 op2 operation data type - * string string string - * string number number if op1 can be converted to number, string else - * date string date if op1 can be converted to date, string else - * number number number - * date number string (maybe we can do better?) - * date date date - * none n/a error - * - * If a boolean value is required, we need to have a number inside the - * operand. If it is not, conversion rules to number apply. Once we - * have a number, things get easy: 0 is false, anything else is true. - * Please note that due to this conversion rules, "0" becomes false - * while "-4712" becomes true. Using a date as boolen is not a good - * idea. Depending on the ultimate conversion rules, it may always - * become true or false. As such, using dates as booleans is - * prohibited and the result defined to be undefined. - * - * rgerhards, 2008-02-22 - */ -static rsRetVal -ConvForOperation(var_t *pThis, var_t *pOther) -{ - DEFiRet; - - if(pThis->varType == VARTYPE_NONE || pOther->varType == VARTYPE_NONE) - ABORT_FINALIZE(RS_RET_INVALID_VAR); - - switch(pThis->varType) { - case VARTYPE_NONE: - ABORT_FINALIZE(RS_RET_INVALID_VAR); - break; - case VARTYPE_STR: - switch(pOther->varType) { - case VARTYPE_NONE: - ABORT_FINALIZE(RS_RET_INVALID_VAR); - break; - case VARTYPE_STR: - FINALIZE; /* two strings, we are all set */ - break; - case VARTYPE_NUMBER: - /* check if we can convert pThis to a number, if so use number format. */ - iRet = ConvToNumber(pThis); - if(iRet == RS_RET_NOT_A_NUMBER) { - CHKiRet(ConvToString(pOther)); - } else { - FINALIZE; /* OK or error */ - } - break; - case VARTYPE_SYSLOGTIME: - ABORT_FINALIZE(RS_RET_NOT_IMPLEMENTED); - break; - } - break; - case VARTYPE_NUMBER: - switch(pOther->varType) { - case VARTYPE_NONE: - ABORT_FINALIZE(RS_RET_INVALID_VAR); - break; - case VARTYPE_STR: - iRet = ConvToNumber(pOther); - if(iRet == RS_RET_NOT_A_NUMBER) { - CHKiRet(ConvToString(pThis)); - } else { - FINALIZE; /* OK or error */ - } - break; - case VARTYPE_NUMBER: - FINALIZE; /* two numbers, so we are all set */ - break; - case VARTYPE_SYSLOGTIME: - ABORT_FINALIZE(RS_RET_NOT_IMPLEMENTED); - break; - } - break; - case VARTYPE_SYSLOGTIME: - ABORT_FINALIZE(RS_RET_NOT_IMPLEMENTED); - break; - } - -finalize_it: - RETiRet; -} - - /* queryInterface function * rgerhards, 2008-02-21 */ @@ -426,14 +108,6 @@ CODESTARTobjQueryInterface(var) pIf->ConstructFinalize = varConstructFinalize; pIf->Destruct = varDestruct; pIf->DebugPrint = varDebugPrint; - pIf->Obj2Str = Obj2Str; - pIf->SetNumber = varSetNumber; - pIf->SetString = varSetString; - pIf->ConvForOperation = ConvForOperation; - pIf->ConvToNumber = ConvToNumber; - pIf->ConvToBool = ConvToBool; - pIf->ConvToString = ConvToString; - pIf->Duplicate = Duplicate; finalize_it: ENDobjQueryInterface(var) diff --git a/runtime/var.h b/runtime/var.h index 384463e0..3d0847d9 100644 --- a/runtime/var.h +++ b/runtime/var.h @@ -38,6 +38,7 @@ typedef struct var_s { varType_t varType; union { number_t num; + es_str_t *str; cstr_t *pStr; syslogTime_t vSyslogTime; @@ -51,16 +52,9 @@ BEGINinterface(var) /* name must also be changed in ENDinterface macro! */ rsRetVal (*Construct)(var_t **ppThis); rsRetVal (*ConstructFinalize)(var_t __attribute__((unused)) *pThis); rsRetVal (*Destruct)(var_t **ppThis); - rsRetVal (*SetNumber)(var_t *pThis, number_t iVal); - rsRetVal (*SetString)(var_t *pThis, cstr_t *pCStr); - rsRetVal (*ConvForOperation)(var_t *pThis, var_t *pOther); - rsRetVal (*ConvToNumber)(var_t *pThis); - rsRetVal (*ConvToBool)(var_t *pThis); - rsRetVal (*ConvToString)(var_t *pThis); - rsRetVal (*Obj2Str)(var_t *pThis, cstr_t*); - rsRetVal (*Duplicate)(var_t *pThis, var_t **ppNew); ENDinterface(var) -#define varCURR_IF_VERSION 1 /* increment whenever you change the interface above! */ +#define varCURR_IF_VERSION 2 /* increment whenever you change the interface above! */ +/* v2 - 2011-07-15/rger: on the way to remove var */ /* prototypes */ diff --git a/runtime/vm.c b/runtime/vm.c deleted file mode 100644 index 84ba4bcf..00000000 --- a/runtime/vm.c +++ /dev/null @@ -1,842 +0,0 @@ -/* vm.c - the arithmetic stack of a virtual machine. - * - * Module begun 2008-02-22 by Rainer Gerhards - * - * Copyright 2008 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 <http://www.gnu.org/licenses/>. - * - * 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 <stdlib.h> -#include <string.h> -#include <assert.h> -#include <ctype.h> - -#include "rsyslog.h" -#include "obj.h" -#include "vm.h" -#include "sysvar.h" -#include "stringbuf.h" -#include "unicode-helper.h" - -/* static data */ -DEFobjStaticHelpers -DEFobjCurrIf(vmstk) -DEFobjCurrIf(var) -DEFobjCurrIf(sysvar) - -static pthread_mutex_t mutGetenv; /* we need to make this global because otherwise we can not guarantee proper init! */ - -/* ------------------------------ function registry code and structures ------------------------------ */ - -/* we maintain a registry of known functions */ -/* currently, this is a singly-linked list, this shall become a binary - * tree when we add the real call interface. So far, entries are added - * at the root, only. - */ -typedef struct s_rsf_entry { - cstr_t *pName; /* function name */ - prsf_t rsf; /* pointer to function code */ - struct s_rsf_entry *pNext; /* Pointer to next element or NULL */ -} rsf_entry_t; -rsf_entry_t *funcRegRoot = NULL; - - -/* add a function to the function registry. - * The handed-over cstr_t* object must no longer be used by the caller. - * A duplicate function name is an error. - * rgerhards, 2009-04-06 - */ -static rsRetVal -rsfrAddFunction(uchar *szName, prsf_t rsf) -{ - rsf_entry_t *pEntry; - size_t lenName; - DEFiRet; - - assert(szName != NULL); - assert(rsf != NULL); - - /* first check if we have a duplicate name, with the current approach this means - * we need to go through the whole list. - */ - lenName = strlen((char*)szName); - for(pEntry = funcRegRoot ; pEntry != NULL ; pEntry = pEntry->pNext) - if(!rsCStrSzStrCmp(pEntry->pName, szName, lenName)) - ABORT_FINALIZE(RS_RET_DUP_FUNC_NAME); - - /* unique name, so add to head of list */ - CHKmalloc(pEntry = calloc(1, sizeof(rsf_entry_t))); - CHKiRet(rsCStrConstructFromszStr(&pEntry->pName, szName)); - CHKiRet(cstrFinalize(pEntry->pName)); - pEntry->rsf = rsf; - pEntry->pNext = funcRegRoot; - funcRegRoot = pEntry; - -finalize_it: - if(iRet != RS_RET_OK && iRet != RS_RET_DUP_FUNC_NAME) - free(pEntry); - - RETiRet; -} - - -/* find a function inside the function registry - * The caller provides a cstr_t with the function name and receives - * a function pointer back. If no function is found, an RS_RET_UNKNW_FUNC - * error is returned. So if the function returns with RS_RET_OK, the caller - * can savely assume the function pointer is valid. - * rgerhards, 2009-04-06 - */ -static rsRetVal -findRSFunction(cstr_t *pcsName, prsf_t *prsf) -{ - rsf_entry_t *pEntry; - rsf_entry_t *pFound; - DEFiRet; - - assert(prsf != NULL); - - /* find function by list walkthrough. */ - pFound = NULL; - for(pEntry = funcRegRoot ; pEntry != NULL && pFound == NULL ; pEntry = pEntry->pNext) - if(!rsCStrCStrCmp(pEntry->pName, pcsName)) - pFound = pEntry; - - if(pFound == NULL) - ABORT_FINALIZE(RS_RET_UNKNW_FUNC); - - *prsf = pFound->rsf; - -finalize_it: - RETiRet; -} - - -/* find the name of a RainerScript function whom's function pointer - * is known. This function returns the cstr_t object, which MUST NOT - * be modified by the caller. - * rgerhards, 2009-04-06 - */ -static rsRetVal -findRSFunctionName(prsf_t rsf, cstr_t **ppcsName) -{ - rsf_entry_t *pEntry; - rsf_entry_t *pFound; - DEFiRet; - - assert(rsf != NULL); - assert(ppcsName != NULL); - - /* find function by list walkthrough. */ - pFound = NULL; - for(pEntry = funcRegRoot ; pEntry != NULL && pFound == NULL ; pEntry = pEntry->pNext) - if(pEntry->rsf == rsf) - pFound = pEntry; - - if(pFound == NULL) - ABORT_FINALIZE(RS_RET_UNKNW_FUNC); - - *ppcsName = pFound->pName; - -finalize_it: - RETiRet; -} - - -/* free the whole function registry - */ -static void -rsfrRemoveAll(void) -{ - rsf_entry_t *pEntry; - rsf_entry_t *pEntryDel; - - BEGINfunc - pEntry = funcRegRoot; - while(pEntry != NULL) { - pEntryDel = pEntry; - pEntry = pEntry->pNext; - cstrDestruct(&pEntryDel->pName); - free(pEntryDel); - } - funcRegRoot = NULL; - ENDfunc -} - - -/* ------------------------------ end function registry code and structures ------------------------------ */ - - -/* ------------------------------ instruction set implementation ------------------------------ * - * The following functions implement the VM's instruction set. - */ -#define BEGINop(instruction) \ - static rsRetVal op##instruction(vm_t *pThis, __attribute__((unused)) vmop_t *pOp) \ - { \ - DEFiRet; - -#define CODESTARTop(instruction) \ - ISOBJ_TYPE_assert(pThis, vm); - -#define PUSHRESULTop(operand, res) \ - /* we have a result, so let's push it */ \ - var.SetNumber(operand, res); \ - vmstk.Push(pThis->pStk, operand); /* result */ - -#define ENDop(instruction) \ - RETiRet; \ - } - -/* code generator for boolean operations */ -#define BOOLOP(name, OPERATION) \ -BEGINop(name) /* remember to set the instruction also in the ENDop macro! */ \ - var_t *operand1; \ - var_t *operand2; \ -CODESTARTop(name) \ - vmstk.PopBool(pThis->pStk, &operand1); \ - vmstk.PopBool(pThis->pStk, &operand2); \ - if(operand1->val.num OPERATION operand2->val.num) { \ - CHKiRet(var.SetNumber(operand1, 1)); \ - } else { \ - CHKiRet(var.SetNumber(operand1, 0)); \ - } \ - vmstk.Push(pThis->pStk, operand1); /* result */ \ - var.Destruct(&operand2); /* no longer needed */ \ -finalize_it: \ -ENDop(name) -BOOLOP(OR, ||) -BOOLOP(AND, &&) -#undef BOOLOP - - -/* code generator for numerical operations */ -#define NUMOP(name, OPERATION) \ -BEGINop(name) /* remember to set the instruction also in the ENDop macro! */ \ - var_t *operand1; \ - var_t *operand2; \ -CODESTARTop(name) \ - vmstk.PopNumber(pThis->pStk, &operand1); \ - vmstk.PopNumber(pThis->pStk, &operand2); \ - operand1->val.num = operand1->val.num OPERATION operand2->val.num; \ - vmstk.Push(pThis->pStk, operand1); /* result */ \ - var.Destruct(&operand2); /* no longer needed */ \ -ENDop(name) -NUMOP(PLUS, +) -NUMOP(MINUS, -) -NUMOP(TIMES, *) -NUMOP(DIV, /) -NUMOP(MOD, %) -#undef BOOLOP - - -/* code generator for compare operations */ -#define BEGINCMPOP(name) \ -BEGINop(name) \ - var_t *operand1; \ - var_t *operand2; \ - number_t bRes; \ -CODESTARTop(name) \ - CHKiRet(vmstk.Pop2CommOp(pThis->pStk, &operand1, &operand2)); \ - /* data types are equal (so we look only at operand1), but we must \ - * check which type we have to deal with... \ - */ \ - switch(operand1->varType) { -#define ENDCMPOP(name) \ - default: \ - bRes = 0; /* we do not abort just so that we have a value. TODO: reconsider */ \ - break; \ - } \ - \ - /* we have a result, so let's push it */ \ - var.SetNumber(operand1, bRes); \ - vmstk.Push(pThis->pStk, operand1); /* result */ \ - var.Destruct(&operand2); /* no longer needed */ \ -finalize_it: \ -ENDop(name) - -BEGINCMPOP(CMP_EQ) /* remember to change the name also in the END macro! */ - case VARTYPE_NUMBER: - bRes = operand1->val.num == operand2->val.num; - break; - case VARTYPE_STR: - bRes = !rsCStrCStrCmp(operand1->val.pStr, operand2->val.pStr); - break; -ENDCMPOP(CMP_EQ) - -BEGINCMPOP(CMP_NEQ) /* remember to change the name also in the END macro! */ - case VARTYPE_NUMBER: - bRes = operand1->val.num != operand2->val.num; - break; - case VARTYPE_STR: - bRes = rsCStrCStrCmp(operand1->val.pStr, operand2->val.pStr); - break; -ENDCMPOP(CMP_NEQ) - -BEGINCMPOP(CMP_LT) /* remember to change the name also in the END macro! */ - case VARTYPE_NUMBER: - bRes = operand1->val.num < operand2->val.num; - break; - case VARTYPE_STR: - bRes = rsCStrCStrCmp(operand1->val.pStr, operand2->val.pStr) < 0; - break; -ENDCMPOP(CMP_LT) - -BEGINCMPOP(CMP_GT) /* remember to change the name also in the END macro! */ - case VARTYPE_NUMBER: - bRes = operand1->val.num > operand2->val.num; - break; - case VARTYPE_STR: - bRes = rsCStrCStrCmp(operand1->val.pStr, operand2->val.pStr) > 0; - break; -ENDCMPOP(CMP_GT) - -BEGINCMPOP(CMP_LTEQ) /* remember to change the name also in the END macro! */ - case VARTYPE_NUMBER: - bRes = operand1->val.num <= operand2->val.num; - break; - case VARTYPE_STR: - bRes = rsCStrCStrCmp(operand1->val.pStr, operand2->val.pStr) <= 0; - break; -ENDCMPOP(CMP_LTEQ) - -BEGINCMPOP(CMP_GTEQ) /* remember to change the name also in the END macro! */ - case VARTYPE_NUMBER: - bRes = operand1->val.num >= operand2->val.num; - break; - case VARTYPE_STR: - bRes = rsCStrCStrCmp(operand1->val.pStr, operand2->val.pStr) >= 0; - break; -ENDCMPOP(CMP_GTEQ) - -#undef BEGINCMPOP -#undef ENDCMPOP -/* end regular compare operations */ - -/* comare operations that work on strings, only */ -BEGINop(CMP_CONTAINS) /* remember to set the instruction also in the ENDop macro! */ - var_t *operand1; - var_t *operand2; - number_t bRes; -CODESTARTop(CMP_CONTAINS) - /* operand2 is on top of stack, so needs to be popped first */ - vmstk.PopString(pThis->pStk, &operand2); - vmstk.PopString(pThis->pStk, &operand1); - /* TODO: extend cstr class so that it supports location of cstr inside cstr */ - bRes = (rsCStrLocateInSzStr(operand2->val.pStr, rsCStrGetSzStr(operand1->val.pStr)) == -1) ? 0 : 1; - - /* we have a result, so let's push it */ - PUSHRESULTop(operand1, bRes); - var.Destruct(&operand2); /* no longer needed */ -ENDop(CMP_CONTAINS) - - -BEGINop(CMP_CONTAINSI) /* remember to set the instruction also in the ENDop macro! */ - var_t *operand1; - var_t *operand2; - number_t bRes; -CODESTARTop(CMP_CONTAINSI) - /* operand2 is on top of stack, so needs to be popped first */ - vmstk.PopString(pThis->pStk, &operand2); - vmstk.PopString(pThis->pStk, &operand1); -var.DebugPrint(operand1); \ -var.DebugPrint(operand2); \ - /* TODO: extend cstr class so that it supports location of cstr inside cstr */ - bRes = (rsCStrCaseInsensitiveLocateInSzStr(operand2->val.pStr, rsCStrGetSzStr(operand1->val.pStr)) == -1) ? 0 : 1; - - /* we have a result, so let's push it */ - PUSHRESULTop(operand1, bRes); - var.Destruct(&operand2); /* no longer needed */ -ENDop(CMP_CONTAINSI) - - -BEGINop(CMP_STARTSWITH) /* remember to set the instruction also in the ENDop macro! */ - var_t *operand1; - var_t *operand2; - number_t bRes; -CODESTARTop(CMP_STARTSWITH) - /* operand2 is on top of stack, so needs to be popped first */ - vmstk.PopString(pThis->pStk, &operand2); - vmstk.PopString(pThis->pStk, &operand1); - /* TODO: extend cstr class so that it supports location of cstr inside cstr */ - bRes = (rsCStrStartsWithSzStr(operand1->val.pStr, rsCStrGetSzStr(operand2->val.pStr), - rsCStrLen(operand2->val.pStr)) == 0) ? 1 : 0; - - /* we have a result, so let's push it */ - PUSHRESULTop(operand1, bRes); - var.Destruct(&operand2); /* no longer needed */ -ENDop(CMP_STARTSWITH) - - -BEGINop(CMP_STARTSWITHI) /* remember to set the instruction also in the ENDop macro! */ - var_t *operand1; - var_t *operand2; - number_t bRes; -CODESTARTop(CMP_STARTSWITHI) - /* operand2 is on top of stack, so needs to be popped first */ - vmstk.PopString(pThis->pStk, &operand2); - vmstk.PopString(pThis->pStk, &operand1); - /* TODO: extend cstr class so that it supports location of cstr inside cstr */ - bRes = (rsCStrCaseInsensitveStartsWithSzStr(operand1->val.pStr, rsCStrGetSzStr(operand2->val.pStr), - rsCStrLen(operand2->val.pStr)) == 0) ? 1 : 0; - - /* we have a result, so let's push it */ - PUSHRESULTop(operand1, bRes); - var.Destruct(&operand2); /* no longer needed */ -ENDop(CMP_STARTSWITHI) - -/* end comare operations that work on strings, only */ - -BEGINop(STRADD) /* remember to set the instruction also in the ENDop macro! */ - var_t *operand1; - var_t *operand2; -CODESTARTop(STRADD) - vmstk.PopString(pThis->pStk, &operand2); - vmstk.PopString(pThis->pStk, &operand1); - - CHKiRet(rsCStrAppendCStr(operand1->val.pStr, operand2->val.pStr)); - CHKiRet(cstrFinalize(operand1->val.pStr)); - - /* we have a result, so let's push it */ - vmstk.Push(pThis->pStk, operand1); - var.Destruct(&operand2); /* no longer needed */ -finalize_it: -ENDop(STRADD) - -BEGINop(NOT) /* remember to set the instruction also in the ENDop macro! */ - var_t *operand; -CODESTARTop(NOT) - vmstk.PopBool(pThis->pStk, &operand); - PUSHRESULTop(operand, !operand->val.num); -ENDop(NOT) - -BEGINop(UNARY_MINUS) /* remember to set the instruction also in the ENDop macro! */ - var_t *operand; -CODESTARTop(UNARY_MINUS) - vmstk.PopNumber(pThis->pStk, &operand); - PUSHRESULTop(operand, -operand->val.num); -ENDop(UNARY_MINUS) - - -BEGINop(PUSHCONSTANT) /* remember to set the instruction also in the ENDop macro! */ - var_t *pVarDup; /* we need to duplicate the var, as we need to hand it over */ -CODESTARTop(PUSHCONSTANT) - CHKiRet(var.Duplicate(pOp->operand.pVar, &pVarDup)); - vmstk.Push(pThis->pStk, pVarDup); -finalize_it: -ENDop(PUSHCONSTANT) - - -BEGINop(PUSHMSGVAR) /* remember to set the instruction also in the ENDop macro! */ - var_t *pVal; /* the value to push */ - cstr_t *pstrVal; -CODESTARTop(PUSHMSGVAR) - if(pThis->pMsg == NULL) { - /* TODO: flag an error message! As a work-around, we permit - * execution to continue here with an empty string - */ - /* TODO: create a method in var to create a string var? */ - CHKiRet(var.Construct(&pVal)); - CHKiRet(var.ConstructFinalize(pVal)); - CHKiRet(rsCStrConstructFromszStr(&pstrVal, (uchar*)"")); - CHKiRet(var.SetString(pVal, pstrVal)); - } else { - /* we have a message, so pull value from there */ - CHKiRet(msgGetMsgVar(pThis->pMsg, pOp->operand.pVar->val.pStr, &pVal)); - } - - /* if we reach this point, we have a valid pVal and can push it */ - vmstk.Push(pThis->pStk, pVal); -finalize_it: -ENDop(PUSHMSGVAR) - - -BEGINop(PUSHSYSVAR) /* remember to set the instruction also in the ENDop macro! */ - var_t *pVal; /* the value to push */ -CODESTARTop(PUSHSYSVAR) - CHKiRet(sysvar.GetVar(pOp->operand.pVar->val.pStr, &pVal)); - vmstk.Push(pThis->pStk, pVal); -finalize_it: - if(Debug && iRet != RS_RET_OK) { - if(iRet == RS_RET_SYSVAR_NOT_FOUND) { - DBGPRINTF("rainerscript: sysvar '%s' not found\n", - rsCStrGetSzStrNoNULL(pOp->operand.pVar->val.pStr)); - } else { - DBGPRINTF("rainerscript: error %d trying to obtain sysvar '%s'\n", - iRet, rsCStrGetSzStrNoNULL(pOp->operand.pVar->val.pStr)); - } - } -ENDop(PUSHSYSVAR) - -/* The function call operation is only very roughly implemented. While the plumbing - * to reach this instruction is fine, the instruction itself currently supports only - * functions with a single argument AND with a name that we know. - * TODO: later, we can add here the real logic, that involves looking up function - * names, loading them dynamically ... and all that... - * implementation begun 2009-03-10 by rgerhards - */ -BEGINop(FUNC_CALL) /* remember to set the instruction also in the ENDop macro! */ - var_t *numOperands; -CODESTARTop(FUNC_CALL) - vmstk.PopNumber(pThis->pStk, &numOperands); - CHKiRet((*pOp->operand.rsf)(pThis->pStk, numOperands->val.num)); - var.Destruct(&numOperands); /* no longer needed */ -finalize_it: -ENDop(FUNC_CALL) - - -/* ------------------------------ end instruction set implementation ------------------------------ */ - - -/* ------------------------------ begin built-in function implementation ------------------------------ */ -/* note: this shall probably be moved to a separate module, but for the time being we do it directly - * in here. This is on our way to get from a dirty to a clean solution via baby steps that are - * a bit less dirty each time... - * - * The advantage of doing it here is that we do not yet need to think about how to handle the - * exit case, where we must not unload function modules which functions are still referenced. - * - * CALLING INTERFACE: - * The function must pop its parameters off the stack and pop its result onto - * the stack when it is finished. The number of parameters the function was - * called with is provided to it. If the argument count is less then what the function - * expected, it may handle the situation with defaults (or return an error). If the - * argument count is greater than expected, returnung an error is highly - * recommended (use RS_RET_INVLD_NBR_ARGUMENTS for these cases). - * - * All function names are prefixed with "rsf_" (RainerScript Function) to have - * a separate "name space". - * - * rgerhards, 2009-04-06 - */ - - -/* The strlen function, also probably a prototype of how all functions should be - * implemented. - * rgerhards, 2009-04-06 - */ -static rsRetVal -rsf_strlen(vmstk_t *pStk, int numOperands) -{ - DEFiRet; - var_t *operand1; - int iStrlen; - - if(numOperands != 1) - ABORT_FINALIZE(RS_RET_INVLD_NBR_ARGUMENTS); - - /* pop args and do operaton (trivial case here...) */ - vmstk.PopString(pStk, &operand1); - iStrlen = strlen((char*) rsCStrGetSzStr(operand1->val.pStr)); - - /* Store result and cleanup */ - var.SetNumber(operand1, iStrlen); - vmstk.Push(pStk, operand1); -finalize_it: - RETiRet; -} - - -/* The getenv function. Note that we guard the OS call by a mutex, as that - * function is not guaranteed to be thread-safe. This implementation here is far from - * being optimal, at least we should cache the result. This is left TODO for - * a later revision. - * rgerhards, 2009-11-03 - */ -static rsRetVal -rsf_getenv(vmstk_t *pStk, int numOperands) -{ - DEFiRet; - var_t *operand1; - char *envResult; - cstr_t *pCstr; - - if(numOperands != 1) - ABORT_FINALIZE(RS_RET_INVLD_NBR_ARGUMENTS); - - /* pop args and do operaton (trivial case here...) */ - vmstk.PopString(pStk, &operand1); - d_pthread_mutex_lock(&mutGetenv); - envResult = getenv((char*) rsCStrGetSzStr(operand1->val.pStr)); - DBGPRINTF("rsf_getenv(): envvar '%s', return '%s'\n", rsCStrGetSzStr(operand1->val.pStr), - envResult == NULL ? "(NULL)" : envResult); - iRet = rsCStrConstructFromszStr(&pCstr, (envResult == NULL) ? UCHAR_CONSTANT("") : (uchar*)envResult); - d_pthread_mutex_unlock(&mutGetenv); - if(iRet != RS_RET_OK) - FINALIZE; /* need to do this after mutex is unlocked! */ - - /* Store result and cleanup */ - var.SetString(operand1, pCstr); - vmstk.Push(pStk, operand1); -finalize_it: - RETiRet; -} - - -/* The "tolower" function, which converts its sole argument to lower case. - * Quite honestly, currently this is primarily a test driver for me... - * rgerhards, 2009-04-06 - */ -static rsRetVal -rsf_tolower(vmstk_t *pStk, int numOperands) -{ - DEFiRet; - var_t *operand1; - uchar *pSrc; - cstr_t *pcstr; - int iStrlen; - - if(numOperands != 1) - ABORT_FINALIZE(RS_RET_INVLD_NBR_ARGUMENTS); - - /* pop args and do operaton */ - CHKiRet(cstrConstruct(&pcstr)); - vmstk.PopString(pStk, &operand1); - pSrc = cstrGetSzStr(operand1->val.pStr); - iStrlen = strlen((char*)pSrc); // TODO: use count from string! - while(iStrlen--) { - CHKiRet(cstrAppendChar(pcstr, tolower(*pSrc++))); - } - - /* Store result and cleanup */ - CHKiRet(cstrFinalize(pcstr)); - var.SetString(operand1, pcstr); - vmstk.Push(pStk, operand1); -finalize_it: - RETiRet; -} - - -/* Standard-Constructor - */ -BEGINobjConstruct(vm) /* be sure to specify the object type also in END macro! */ -ENDobjConstruct(vm) - - -/* ConstructionFinalizer - * rgerhards, 2008-01-09 - */ -static rsRetVal -vmConstructFinalize(vm_t __attribute__((unused)) *pThis) -{ - DEFiRet; - ISOBJ_TYPE_assert(pThis, vm); - - CHKiRet(vmstk.Construct(&pThis->pStk)); - CHKiRet(vmstk.ConstructFinalize(pThis->pStk)); - -finalize_it: - RETiRet; -} - - -/* destructor for the vm object */ -BEGINobjDestruct(vm) /* be sure to specify the object type also in END and CODESTART macros! */ -CODESTARTobjDestruct(vm) - if(pThis->pStk != NULL) - vmstk.Destruct(&pThis->pStk); - if(pThis->pMsg != NULL) - msgDestruct(&pThis->pMsg); -ENDobjDestruct(vm) - - -/* debugprint for the vm object */ -BEGINobjDebugPrint(vm) /* be sure to specify the object type also in END and CODESTART macros! */ -CODESTARTobjDebugPrint(vm) - dbgoprint((obj_t*) pThis, "rsyslog virtual machine, currently no state info available\n"); -ENDobjDebugPrint(vm) - - -/* execute a program - */ -static rsRetVal -execProg(vm_t *pThis, vmprg_t *pProg) -{ - DEFiRet; - vmop_t *pCurrOp; /* virtual instruction pointer */ - - ISOBJ_TYPE_assert(pThis, vm); - ISOBJ_TYPE_assert(pProg, vmprg); - -#define doOP(OP) case opcode_##OP: DBGPRINTF("rainerscript: opcode %s\n", #OP); \ - CHKiRet(op##OP(pThis, pCurrOp)); break - pCurrOp = pProg->vmopRoot; /* TODO: do this via a method! */ - while(pCurrOp != NULL && pCurrOp->opcode != opcode_END_PROG) { - DBGPRINTF("rainerscript: executing step, opcode %d...\n", pCurrOp->opcode); - switch(pCurrOp->opcode) { - doOP(OR); - doOP(AND); - doOP(CMP_EQ); - doOP(CMP_NEQ); - doOP(CMP_LT); - doOP(CMP_GT); - doOP(CMP_LTEQ); - doOP(CMP_GTEQ); - doOP(CMP_CONTAINS); - doOP(CMP_CONTAINSI); - doOP(CMP_STARTSWITH); - doOP(CMP_STARTSWITHI); - doOP(NOT); - doOP(PUSHCONSTANT); - doOP(PUSHMSGVAR); - doOP(PUSHSYSVAR); - doOP(STRADD); - doOP(PLUS); - doOP(MINUS); - doOP(TIMES); - doOP(DIV); - doOP(MOD); - doOP(UNARY_MINUS); - doOP(FUNC_CALL); - default: - dbgoprint((obj_t*) pThis, "invalid instruction %d in vmprg\n", pCurrOp->opcode); - ABORT_FINALIZE(RS_RET_INVALID_VMOP); - break; - } - /* so far, we have plain sequential execution, so on to next... */ - pCurrOp = pCurrOp->pNext; - } -#undef doOP - - /* if we reach this point, our program has intintionally terminated - * (no error state). - */ - -finalize_it: - DBGPRINTF("rainerscript: script execution terminated with state %d\n", iRet); - RETiRet; -} - - -/* Set the current message object for the VM. It *is* valid to set a - * NULL message object, what simply means there is none. Message - * objects are properly reference counted. - */ -static rsRetVal -SetMsg(vm_t *pThis, msg_t *pMsg) -{ - DEFiRet; - if(pThis->pMsg != NULL) { - msgDestruct(&pThis->pMsg); - } - - if(pMsg != NULL) { - pThis->pMsg = MsgAddRef(pMsg); - } - - RETiRet; -} - - -/* Pop a var from the stack and return it to caller. The variable type is not - * changed, it is taken from the stack as is. This functionality is - * partly needed. We may (or may not ;)) be able to remove it once we have - * full RainerScript support. -- rgerhards, 2008-02-25 - */ -static rsRetVal -PopVarFromStack(vm_t *pThis, var_t **ppVar) -{ - DEFiRet; - CHKiRet(vmstk.Pop(pThis->pStk, ppVar)); -finalize_it: - RETiRet; -} - - -/* Pop a boolean from the stack and return it to caller. This functionality is - * partly needed. We may (or may not ;)) be able to remove it once we have - * full RainerScript support. -- rgerhards, 2008-02-25 - */ -static rsRetVal -PopBoolFromStack(vm_t *pThis, var_t **ppVar) -{ - DEFiRet; - CHKiRet(vmstk.PopBool(pThis->pStk, ppVar)); -finalize_it: - RETiRet; -} - - -/* queryInterface function - * rgerhards, 2008-02-21 - */ -BEGINobjQueryInterface(vm) -CODESTARTobjQueryInterface(vm) - if(pIf->ifVersion != vmCURR_IF_VERSION) { /* check for current version, increment on each change */ - ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); - } - - /* ok, we have the right interface, so let's fill it - * Please note that we may also do some backwards-compatibility - * work here (if we can support an older interface version - that, - * of course, also affects the "if" above). - */ - pIf->Construct = vmConstruct; - pIf->ConstructFinalize = vmConstructFinalize; - pIf->Destruct = vmDestruct; - pIf->DebugPrint = vmDebugPrint; - pIf->ExecProg = execProg; - pIf->PopBoolFromStack = PopBoolFromStack; - pIf->PopVarFromStack = PopVarFromStack; - pIf->SetMsg = SetMsg; - pIf->FindRSFunction = findRSFunction; - pIf->FindRSFunctionName = findRSFunctionName; -finalize_it: -ENDobjQueryInterface(vm) - - -/* Exit the vm class. - * rgerhards, 2009-04-06 - */ -BEGINObjClassExit(vm, OBJ_IS_CORE_MODULE) /* class, version */ - rsfrRemoveAll(); - objRelease(sysvar, CORE_COMPONENT); - objRelease(var, CORE_COMPONENT); - objRelease(vmstk, CORE_COMPONENT); - - pthread_mutex_destroy(&mutGetenv); -ENDObjClassExit(vm) - - -/* Initialize the vm class. Must be called as the very first method - * before anything else is called inside this class. - * rgerhards, 2008-02-19 - */ -BEGINObjClassInit(vm, 1, OBJ_IS_CORE_MODULE) /* class, version */ - /* request objects we use */ - CHKiRet(objUse(vmstk, CORE_COMPONENT)); - CHKiRet(objUse(var, CORE_COMPONENT)); - CHKiRet(objUse(sysvar, CORE_COMPONENT)); - - /* set our own handlers */ - OBJSetMethodHandler(objMethod_DEBUGPRINT, vmDebugPrint); - OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, vmConstructFinalize); - - /* register built-in functions // TODO: move to its own module */ - CHKiRet(rsfrAddFunction((uchar*)"strlen", rsf_strlen)); - CHKiRet(rsfrAddFunction((uchar*)"tolower", rsf_tolower)); - CHKiRet(rsfrAddFunction((uchar*)"getenv", rsf_getenv)); - - pthread_mutex_init(&mutGetenv, NULL); - -ENDObjClassInit(vm) - -/* vi:set ai: - */ diff --git a/runtime/vm.h b/runtime/vm.h deleted file mode 100644 index 29b99876..00000000 --- a/runtime/vm.h +++ /dev/null @@ -1,66 +0,0 @@ -/* The vm object. - * - * This implements the rsyslog virtual machine. The initial implementation is - * done to support complex user-defined expressions, but it may evolve into a - * much more useful thing over time. - * - * The virtual machine uses rsyslog variables as its memory storage system. - * All computation is done on a stack (vmstk). The vm supports a given - * instruction set and executes programs of type vmprg, which consist of - * single operations defined in vmop (which hold the instruction and the - * data). - * - * Copyright 2008-2012 Rainer Gerhards and Adiscon GmbH. - * - * This file is part of the rsyslog runtime library. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * -or- - * see COPYING.ASL20 in the source distribution - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef INCLUDED_VM_H -#define INCLUDED_VM_H - -#include "msg.h" -#include "vmstk.h" -#include "vmprg.h" - -/* the vm object */ -typedef struct vm_s { - BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ - vmstk_t *pStk; /* The stack */ - msg_t *pMsg; /* the current message (or NULL, if we have none) */ -} vm_t; - - -/* interfaces */ -BEGINinterface(vm) /* name must also be changed in ENDinterface macro! */ - INTERFACEObjDebugPrint(vm); - rsRetVal (*Construct)(vm_t **ppThis); - rsRetVal (*ConstructFinalize)(vm_t __attribute__((unused)) *pThis); - rsRetVal (*Destruct)(vm_t **ppThis); - rsRetVal (*ExecProg)(vm_t *pThis, vmprg_t *pProg); - rsRetVal (*PopBoolFromStack)(vm_t *pThis, var_t **ppVar); /* there are a few cases where we need this... */ - rsRetVal (*PopVarFromStack)(vm_t *pThis, var_t **ppVar); /* there are a few cases where we need this... */ - rsRetVal (*SetMsg)(vm_t *pThis, msg_t *pMsg); /* there are a few cases where we need this... */ - /* v2 (4.1.7) */ - rsRetVal (*FindRSFunction)(cstr_t *pcsName, prsf_t *prsf); /* 2009-06-04 */ - rsRetVal (*FindRSFunctionName)(prsf_t rsf, cstr_t **ppcsName); /* 2009-06-04 */ -ENDinterface(vm) -#define vmCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */ - - -/* prototypes */ -PROTOTYPEObj(vm); - -#endif /* #ifndef INCLUDED_VM_H */ diff --git a/runtime/vmop.c b/runtime/vmop.c deleted file mode 100644 index 9fb6be8c..00000000 --- a/runtime/vmop.c +++ /dev/null @@ -1,305 +0,0 @@ -/* vmop.c - abstracts an operation (instructed) supported by the - * rsyslog virtual machine - * - * Module begun 2008-02-20 by Rainer Gerhards - * - * Copyright 2007-2012 Adiscon GmbH. - * - * This file is part of the rsyslog runtime library. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * -or- - * see COPYING.ASL20 in the source distribution - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "config.h" -#include <stdio.h> -#include <stdlib.h> -#include <assert.h> - -#include "rsyslog.h" -#include "obj.h" -#include "vmop.h" -#include "vm.h" - -/* static data */ -DEFobjStaticHelpers -DEFobjCurrIf(var) -DEFobjCurrIf(vm) - - -/* forward definitions */ -static rsRetVal vmopOpcode2Str(vmop_t *pThis, uchar **ppName); - -/* Standard-Constructor - */ -BEGINobjConstruct(vmop) /* be sure to specify the object type also in END macro! */ -ENDobjConstruct(vmop) - - -/* ConstructionFinalizer - * rgerhards, 2008-01-09 - */ -rsRetVal vmopConstructFinalize(vmop_t __attribute__((unused)) *pThis) -{ - DEFiRet; - ISOBJ_TYPE_assert(pThis, vmop); - RETiRet; -} - - -/* destructor for the vmop object */ -BEGINobjDestruct(vmop) /* be sure to specify the object type also in END and CODESTART macros! */ -CODESTARTobjDestruct(vmop) - if(pThis->opcode != opcode_FUNC_CALL) { - if(pThis->operand.pVar != NULL) - var.Destruct(&pThis->operand.pVar); - } -ENDobjDestruct(vmop) - - -/* DebugPrint support for the vmop object */ -BEGINobjDebugPrint(vmop) /* be sure to specify the object type also in END and CODESTART macros! */ - uchar *pOpcodeName; - cstr_t *pStrVar; -CODESTARTobjDebugPrint(vmop) - vmopOpcode2Str(pThis, &pOpcodeName); - if(pThis->opcode == opcode_FUNC_CALL) { - CHKiRet(vm.FindRSFunctionName(pThis->operand.rsf, &pStrVar)); - assert(pStrVar != NULL); - } else { - CHKiRet(rsCStrConstruct(&pStrVar)); - if(pThis->operand.pVar != NULL) { - CHKiRet(var.Obj2Str(pThis->operand.pVar, pStrVar)); - } - } - CHKiRet(cstrFinalize(pStrVar)); - dbgoprint((obj_t*) pThis, "%.12s\t%s\n", pOpcodeName, rsCStrGetSzStrNoNULL(pStrVar)); - if(pThis->opcode != opcode_FUNC_CALL) - rsCStrDestruct(&pStrVar); -finalize_it: -ENDobjDebugPrint(vmop) - - -/* This function is similar to DebugPrint, but does not send its output to - * the debug log but instead to a caller-provided string. The idea here is that - * we can use this string to get a textual representation of an operation. - * Among others, this is useful for creating testbenches, our first use case for - * it. Here, it enables simple comparison of the resulting program to a - * reference program by simple string compare. - * Note that the caller must initialize the string object. We always add - * data to it. So, it can be easily combined into a chain of methods - * to generate the final string. - * rgerhards, 2008-07-04 - */ -static rsRetVal -Obj2Str(vmop_t *pThis, cstr_t *pstrPrg) -{ - uchar *pOpcodeName; - cstr_t *pcsFuncName; - uchar szBuf[2048]; - size_t lenBuf; - DEFiRet; - - ISOBJ_TYPE_assert(pThis, vmop); - assert(pstrPrg != NULL); - vmopOpcode2Str(pThis, &pOpcodeName); - lenBuf = snprintf((char*) szBuf, sizeof(szBuf), "%s\t", pOpcodeName); - CHKiRet(rsCStrAppendStrWithLen(pstrPrg, szBuf, lenBuf)); - if(pThis->opcode == opcode_FUNC_CALL) { - CHKiRet(vm.FindRSFunctionName(pThis->operand.rsf, &pcsFuncName)); - CHKiRet(rsCStrAppendCStr(pstrPrg, pcsFuncName)); - } else { - if(pThis->operand.pVar != NULL) - CHKiRet(var.Obj2Str(pThis->operand.pVar, pstrPrg)); - } - CHKiRet(cstrAppendChar(pstrPrg, '\n')); - -finalize_it: - RETiRet; -} - - -/* set function - * rgerhards, 2009-04-06 - */ -static rsRetVal -vmopSetFunc(vmop_t *pThis, cstr_t *pcsFuncName) -{ - prsf_t rsf; /* pointer to function */ - DEFiRet; - ISOBJ_TYPE_assert(pThis, vmop); - CHKiRet(vm.FindRSFunction(pcsFuncName, &rsf)); /* check if function exists and obtain pointer to it */ - assert(rsf != NULL); /* just double-check, would be very hard to find! */ - pThis->operand.rsf = rsf; -finalize_it: - RETiRet; -} - - -/* set operand (variant case) - * rgerhards, 2008-02-20 - */ -static rsRetVal -vmopSetVar(vmop_t *pThis, var_t *pVar) -{ - DEFiRet; - ISOBJ_TYPE_assert(pThis, vmop); - ISOBJ_TYPE_assert(pVar, var); - pThis->operand.pVar = pVar; - RETiRet; -} - - -/* set operation - * rgerhards, 2008-02-20 - */ -static rsRetVal -vmopSetOpcode(vmop_t *pThis, opcode_t opcode) -{ - DEFiRet; - ISOBJ_TYPE_assert(pThis, vmop); - pThis->opcode = opcode; - RETiRet; -} - - -/* a way to turn an opcode into a readable string - */ -static rsRetVal -vmopOpcode2Str(vmop_t *pThis, uchar **ppName) -{ - DEFiRet; - ISOBJ_TYPE_assert(pThis, vmop); - - switch(pThis->opcode) { - case opcode_OR: - *ppName = (uchar*) "or"; - break; - case opcode_AND: - *ppName = (uchar*) "and"; - break; - case opcode_PLUS: - *ppName = (uchar*) "add"; - break; - case opcode_MINUS: - *ppName = (uchar*) "sub"; - break; - case opcode_TIMES: - *ppName = (uchar*) "mul"; - break; - case opcode_DIV: - *ppName = (uchar*) "div"; - break; - case opcode_MOD: - *ppName = (uchar*) "mod"; - break; - case opcode_NOT: - *ppName = (uchar*) "not"; - break; - case opcode_CMP_EQ: - *ppName = (uchar*) "cmp_=="; - break; - case opcode_CMP_NEQ: - *ppName = (uchar*) "cmp_!="; - break; - case opcode_CMP_LT: - *ppName = (uchar*) "cmp_<"; - break; - case opcode_CMP_GT: - *ppName = (uchar*) "cmp_>"; - break; - case opcode_CMP_LTEQ: - *ppName = (uchar*) "cmp_<="; - break; - case opcode_CMP_CONTAINS: - *ppName = (uchar*) "contains"; - break; - case opcode_CMP_STARTSWITH: - *ppName = (uchar*) "startswith"; - break; - case opcode_CMP_GTEQ: - *ppName = (uchar*) "cmp_>="; - break; - case opcode_PUSHSYSVAR: - *ppName = (uchar*) "push_sysvar"; - break; - case opcode_PUSHMSGVAR: - *ppName = (uchar*) "push_msgvar"; - break; - case opcode_PUSHCONSTANT: - *ppName = (uchar*) "push_const"; - break; - case opcode_POP: - *ppName = (uchar*) "pop"; - break; - case opcode_UNARY_MINUS: - *ppName = (uchar*) "unary_minus"; - break; - case opcode_STRADD: - *ppName = (uchar*) "strconcat"; - break; - case opcode_FUNC_CALL: - *ppName = (uchar*) "func_call"; - break; - default: - *ppName = (uchar*) "!invalid_opcode!"; - break; - } - - RETiRet; -} - - -/* queryInterface function - * rgerhards, 2008-02-21 - */ -BEGINobjQueryInterface(vmop) -CODESTARTobjQueryInterface(vmop) - if(pIf->ifVersion != vmopCURR_IF_VERSION) { /* check for current version, increment on each change */ - ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); - } - - /* ok, we have the right interface, so let's fill it - * Please note that we may also do some backwards-compatibility - * work here (if we can support an older interface version - that, - * of course, also affects the "if" above). - */ - pIf->Construct = vmopConstruct; - pIf->ConstructFinalize = vmopConstructFinalize; - pIf->Destruct = vmopDestruct; - pIf->DebugPrint = vmopDebugPrint; - pIf->SetFunc = vmopSetFunc; - pIf->SetOpcode = vmopSetOpcode; - pIf->SetVar = vmopSetVar; - pIf->Opcode2Str = vmopOpcode2Str; - pIf->Obj2Str = Obj2Str; -finalize_it: -ENDobjQueryInterface(vmop) - - -/* Initialize the vmop class. Must be called as the very first method - * before anything else is called inside this class. - * rgerhards, 2008-02-19 - */ -BEGINObjClassInit(vmop, 1, OBJ_IS_CORE_MODULE) /* class, version */ - /* request objects we use */ - CHKiRet(objUse(var, CORE_COMPONENT)); - CHKiRet(objUse(vm, CORE_COMPONENT)); - - OBJSetMethodHandler(objMethod_DEBUGPRINT, vmopDebugPrint); - OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, vmopConstructFinalize); -ENDObjClassInit(vmop) - -/* vi:set ai: - */ diff --git a/runtime/vmop.h b/runtime/vmop.h deleted file mode 100644 index 68b173ab..00000000 --- a/runtime/vmop.h +++ /dev/null @@ -1,126 +0,0 @@ -/* The vmop object. - * - * Copyright 2008-2012 Rainer Gerhards and Adiscon GmbH. - * - * This file is part of rsyslog. - * - * This file is part of the rsyslog runtime library. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * -or- - * see COPYING.ASL20 in the source distribution - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef INCLUDED_VMOP_H -#define INCLUDED_VMOP_H - -#include "ctok_token.h" -#include "vmstk.h" -#include "stringbuf.h" - -/* machine instructions types */ -typedef enum { /* do NOT start at 0 to detect uninitialized types after calloc() */ - opcode_INVALID = 0, - /* for simplicity of debugging and reading dumps, we use the same IDs - * that the tokenizer uses where this applicable. - */ - opcode_OR = ctok_OR, - opcode_AND = ctok_AND, - opcode_STRADD= ctok_STRADD, - opcode_PLUS = ctok_PLUS, - opcode_MINUS = ctok_MINUS, - opcode_TIMES = ctok_TIMES, /* "*" */ - opcode_DIV = ctok_DIV, - opcode_MOD = ctok_MOD, - opcode_NOT = ctok_NOT, - opcode_CMP_EQ = ctok_CMP_EQ, /* all compare operations must be in a row */ - opcode_CMP_NEQ = ctok_CMP_NEQ, - opcode_CMP_LT = ctok_CMP_LT, - opcode_CMP_GT = ctok_CMP_GT, - opcode_CMP_LTEQ = ctok_CMP_LTEQ, - opcode_CMP_CONTAINS = ctok_CMP_CONTAINS, - opcode_CMP_STARTSWITH = ctok_CMP_STARTSWITH, - opcode_CMP_CONTAINSI = ctok_CMP_CONTAINSI, - opcode_CMP_STARTSWITHI = ctok_CMP_STARTSWITHI, - opcode_CMP_GTEQ = ctok_CMP_GTEQ, /* end compare operations */ - /* here we start our own codes */ - opcode_POP = 1000, /* requires var operand to receive result */ - opcode_PUSHSYSVAR = 1001, /* requires var operand */ - opcode_PUSHMSGVAR = 1002, /* requires var operand */ - opcode_PUSHCONSTANT = 1003, /* requires var operand */ - opcode_UNARY_MINUS = 1010, - opcode_FUNC_CALL = 1012, - opcode_END_PROG = 2000 -} opcode_t; - - -/* Additional doc, operation specific - - FUNC_CALL - All parameter passing is via the stack. Parameters are placed onto the stack in reverse order, - that means the last parameter is on top of the stack, the first at the bottom location. - At the actual top of the stack is the number of parameters. This permits functions to be - called with variable number of arguments. The function itself is responsible for poping - the right number of parameters of the stack and complaining if the number is incorrect. - On exit, a single return value must be pushed onto the stack. The FUNC_CALL operation - is generic. Its pVar argument contains the function name string (TODO: very slow, make - faster in later releases). - - Sample Function call: sampleFunc(p1, p2, p3) ; returns number 4711 (sample) - Stacklayout on entry (order is top to bottom): - 3 - p3 - p2 - p1 - ... other vars ... - - Stack on exit - 4711 - ... other vars ... - - */ - - -/* the vmop object */ -typedef struct vmop_s { - BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ - opcode_t opcode; - union { - var_t *pVar; - prsf_t rsf; /* pointer to function for "call" instruction */ - } operand; - struct vmop_s *pNext; /* next operation or NULL, if end of program (logically this belongs to vmprg) */ -} vmop_t; - - -/* interfaces */ -BEGINinterface(vmop) /* name must also be changed in ENDinterface macro! */ - INTERFACEObjDebugPrint(vmop); - rsRetVal (*Construct)(vmop_t **ppThis); - rsRetVal (*ConstructFinalize)(vmop_t __attribute__((unused)) *pThis); - rsRetVal (*Destruct)(vmop_t **ppThis); - rsRetVal (*SetOpcode)(vmop_t *pThis, opcode_t opcode); - rsRetVal (*SetVar)(vmop_t *pThis, var_t *pVar); - rsRetVal (*Opcode2Str)(vmop_t *pThis, uchar **ppName); - rsRetVal (*Obj2Str)(vmop_t *pThis, cstr_t *pstr); - /* v2 */ - rsRetVal (*SetFunc)(vmop_t *pThis, cstr_t *pcsFuncName); -ENDinterface(vmop) -#define vmopCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */ -/* interface changes, v1 -> v2 - * added SetFuct after existing function pointers -- rgerhards, 2009-04-06 - */ - -/* the remaining prototypes */ -PROTOTYPEObj(vmop); - -#endif /* #ifndef INCLUDED_VMOP_H */ diff --git a/runtime/vmprg.c b/runtime/vmprg.c deleted file mode 100644 index c73f8919..00000000 --- a/runtime/vmprg.c +++ /dev/null @@ -1,234 +0,0 @@ -/* vmprg.c - abstracts a program (bytecode) for the rsyslog virtual machine - * - * Module begun 2008-02-20 by Rainer Gerhards - * - * Copyright 2007-2012 Rainer Gerhards and Adiscon GmbH. - * - * This file is part of the rsyslog runtime library. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * -or- - * see COPYING.ASL20 in the source distribution - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "config.h" -#include <stdio.h> -#include <stdlib.h> -#include <assert.h> - -#include "rsyslog.h" -#include "obj.h" -#include "vmprg.h" -#include "stringbuf.h" - -/* static data */ -DEFobjStaticHelpers -DEFobjCurrIf(vmop) - - -/* Standard-Constructor - */ -BEGINobjConstruct(vmprg) /* be sure to specify the object type also in END macro! */ -ENDobjConstruct(vmprg) - - -/* ConstructionFinalizer - * rgerhards, 2008-01-09 - */ -static rsRetVal -vmprgConstructFinalize(vmprg_t __attribute__((unused)) *pThis) -{ - DEFiRet; - ISOBJ_TYPE_assert(pThis, vmprg); - RETiRet; -} - - -/* destructor for the vmprg object */ -BEGINobjDestruct(vmprg) /* be sure to specify the object type also in END and CODESTART macros! */ - vmop_t *pOp; - vmop_t *pTmp; -CODESTARTobjDestruct(vmprg) - /* we need to destruct the program elements! */ - for(pOp = pThis->vmopRoot ; pOp != NULL ; ) { - pTmp = pOp; - pOp = pOp->pNext; - vmop.Destruct(&pTmp); - } -ENDobjDestruct(vmprg) - - -/* destructor for the vmop object */ -BEGINobjDebugPrint(vmprg) /* be sure to specify the object type also in END and CODESTART macros! */ - vmop_t *pOp; -CODESTARTobjDebugPrint(vmprg) - dbgoprint((obj_t*) pThis, "VM Program:\n"); - for(pOp = pThis->vmopRoot ; pOp != NULL ; pOp = pOp->pNext) { - vmop.DebugPrint(pOp); - } -ENDobjDebugPrint(vmprg) - - -/* This function is similar to DebugPrint, but does not send its output to - * the debug log but instead to a caller-provided string. The idea here is that - * we can use this string to get a textual representation of a bytecode program. - * Among others, this is useful for creating testbenches, our first use case for - * it. Here, it enables simple comparison of the resulting program to a - * reference program by simple string compare. - * Note that the caller must initialize the string object. We always add - * data to it. So, it can be easily combined into a chain of methods - * to generate the final string. - * rgerhards, 2008-07-04 - */ -static rsRetVal -Obj2Str(vmprg_t *pThis, cstr_t *pstrPrg) -{ - uchar szAddr[12]; - vmop_t *pOp; - int i; - int lenAddr; - DEFiRet; - - ISOBJ_TYPE_assert(pThis, vmprg); - assert(pstrPrg != NULL); - i = 0; /* "program counter" */ - for(pOp = pThis->vmopRoot ; pOp != NULL ; pOp = pOp->pNext) { - lenAddr = snprintf((char*)szAddr, sizeof(szAddr), "%8.8d: ", i++); - CHKiRet(rsCStrAppendStrWithLen(pstrPrg, szAddr, lenAddr)); - vmop.Obj2Str(pOp, pstrPrg); - } - -finalize_it: - RETiRet; -} - - -/* add an operation (instruction) to the end of the current program. This - * function is expected to be called while creating the program, but never - * again after this is done and it is being executed. Results are undefined if - * it is called after execution. - */ -static rsRetVal -vmprgAddOperation(vmprg_t *pThis, vmop_t *pOp) -{ - DEFiRet; - - ISOBJ_TYPE_assert(pThis, vmprg); - ISOBJ_TYPE_assert(pOp, vmop); - - if(pThis->vmopRoot == NULL) { - pThis->vmopRoot = pOp; - } else { - pThis->vmopLast->pNext = pOp; - } - pThis->vmopLast = pOp; - - RETiRet; -} - - -/* this is a shortcut for high-level callers. It creates a new vmop, sets its - * parameters and adds it to the program - all in one big step. If there is no - * var associated with this operation, the caller can simply supply NULL as - * pVar. - */ -static rsRetVal -vmprgAddVarOperation(vmprg_t *pThis, opcode_t opcode, var_t *pVar) -{ - DEFiRet; - vmop_t *pOp; - - ISOBJ_TYPE_assert(pThis, vmprg); - - /* construct and fill vmop */ - CHKiRet(vmop.Construct(&pOp)); - CHKiRet(vmop.ConstructFinalize(pOp)); - CHKiRet(vmop.SetOpcode(pOp, opcode)); - if(pVar != NULL) - CHKiRet(vmop.SetVar(pOp, pVar)); - - /* and add it to the program */ - CHKiRet(vmprgAddOperation(pThis, pOp)); - -finalize_it: - RETiRet; -} - - -/* this is another shortcut for high-level callers. It is similar to vmprgAddVarOperation - * but adds a call operation. Among others, this include a check if the function - * is known. - */ -static rsRetVal -vmprgAddCallOperation(vmprg_t *pThis, cstr_t *pcsName) -{ - DEFiRet; - vmop_t *pOp; - - ISOBJ_TYPE_assert(pThis, vmprg); - - /* construct and fill vmop */ - CHKiRet(vmop.Construct(&pOp)); - CHKiRet(vmop.ConstructFinalize(pOp)); - CHKiRet(vmop.SetFunc(pOp, pcsName)); - CHKiRet(vmop.SetOpcode(pOp, opcode_FUNC_CALL)); - - /* and add it to the program */ - CHKiRet(vmprgAddOperation(pThis, pOp)); - -finalize_it: - RETiRet; -} - - -/* queryInterface function - * rgerhards, 2008-02-21 - */ -BEGINobjQueryInterface(vmprg) -CODESTARTobjQueryInterface(vmprg) - if(pIf->ifVersion != vmprgCURR_IF_VERSION) { /* check for current version, increment on each change */ - ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); - } - - /* ok, we have the right interface, so let's fill it - * Please note that we may also do some backwards-compatibility - * work here (if we can support an older interface version - that, - * of course, also affects the "if" above). - */ - pIf->Construct = vmprgConstruct; - pIf->ConstructFinalize = vmprgConstructFinalize; - pIf->Destruct = vmprgDestruct; - pIf->DebugPrint = vmprgDebugPrint; - pIf->Obj2Str = Obj2Str; - pIf->AddOperation = vmprgAddOperation; - pIf->AddVarOperation = vmprgAddVarOperation; - pIf->AddCallOperation = vmprgAddCallOperation; -finalize_it: -ENDobjQueryInterface(vmprg) - - -/* Initialize the vmprg class. Must be called as the very first method - * before anything else is called inside this class. - * rgerhards, 2008-02-19 - */ -BEGINObjClassInit(vmprg, 1, OBJ_IS_CORE_MODULE) /* class, version */ - /* request objects we use */ - CHKiRet(objUse(vmop, CORE_COMPONENT)); - - /* set our own handlers */ - OBJSetMethodHandler(objMethod_DEBUGPRINT, vmprgDebugPrint); - OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, vmprgConstructFinalize); -ENDObjClassInit(vmprg) - -/* vi:set ai: - */ diff --git a/runtime/vmprg.h b/runtime/vmprg.h deleted file mode 100644 index 6a2eedda..00000000 --- a/runtime/vmprg.h +++ /dev/null @@ -1,67 +0,0 @@ -/* The vmprg object. - * - * The program is made up of vmop_t's, one after another. When we support - * branching (or user-defined functions) at some time, well do this via - * special branch opcodes. They will then contain the actual memory - * address of a logical program entry that we shall branch to. Other than - * that, all execution is serial - that is one opcode is executed after - * the other. This class implements a logical program store, modelled - * after real main memory. A linked list of opcodes is used to implement it. - * In the future, we may use linked lists of array's to enhance performance, - * but for the time being we have taken the simplistic approach (which also - * reduces risk of bugs during initial development). The necessary pointers - * for this are already implemented in vmop. Though this is not the 100% - * correct place, we have opted this time in favor of performance, which - * made them go there. - * - * Copyright 2008-2012 Rainer Gerhards and Adiscon GmbH. - * - * This file is part of the rsyslog runtime library. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * -or- - * see COPYING.ASL20 in the source distribution - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef INCLUDED_VMPRG_H -#define INCLUDED_VMPRG_H - -#include "vmop.h" -#include "stringbuf.h" - -/* the vmprg object */ -typedef struct vmprg_s { - BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ - vmop_t *vmopRoot; /* start of program */ - vmop_t *vmopLast; /* last vmop of program (for adding new ones) */ -} vmprg_t; - - -/* interfaces */ -BEGINinterface(vmprg) /* name must also be changed in ENDinterface macro! */ - INTERFACEObjDebugPrint(vmprg); - rsRetVal (*Construct)(vmprg_t **ppThis); - rsRetVal (*ConstructFinalize)(vmprg_t __attribute__((unused)) *pThis); - rsRetVal (*Destruct)(vmprg_t **ppThis); - rsRetVal (*AddOperation)(vmprg_t *pThis, vmop_t *pOp); - rsRetVal (*AddVarOperation)(vmprg_t *pThis, opcode_t opcode, var_t *pVar); - rsRetVal (*Obj2Str)(vmprg_t *pThis, cstr_t *pstr); - /* v2 (4.1.7) */ - rsRetVal (*AddCallOperation)(vmprg_t *pThis, cstr_t *pVar); /* added 2009-04-06 */ -ENDinterface(vmprg) -#define vmprgCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */ - - -/* prototypes */ -PROTOTYPEObj(vmprg); - -#endif /* #ifndef INCLUDED_VMPRG_H */ diff --git a/runtime/vmstk.c b/runtime/vmstk.c deleted file mode 100644 index c45480ad..00000000 --- a/runtime/vmstk.c +++ /dev/null @@ -1,232 +0,0 @@ -/* vmstk.c - the arithmetic stack of a virtual machine. - * - * Module begun 2008-02-21 by Rainer Gerhards - * - * Copyright 2008-2012 Rainer Gerhards and Adiscon GmbH. - * - * This file is part of the rsyslog runtime library. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * -or- - * see COPYING.ASL20 in the source distribution - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "config.h" -#include <stdlib.h> -#include <assert.h> - -#include "rsyslog.h" -#include "obj.h" -#include "vmstk.h" - -/* static data */ -DEFobjStaticHelpers -DEFobjCurrIf(var) - - -/* Standard-Constructor - */ -BEGINobjConstruct(vmstk) /* be sure to specify the object type also in END macro! */ -ENDobjConstruct(vmstk) - - -/* ConstructionFinalizer - * rgerhards, 2008-01-09 - */ -static rsRetVal -vmstkConstructFinalize(vmstk_t __attribute__((unused)) *pThis) -{ - DEFiRet; - ISOBJ_TYPE_assert(pThis, vmstk); - RETiRet; -} - - -/* destructor for the vmstk object */ -BEGINobjDestruct(vmstk) /* be sure to specify the object type also in END and CODESTART macros! */ -CODESTARTobjDestruct(vmstk) -ENDobjDestruct(vmstk) - - -/* debugprint for the vmstk object */ -BEGINobjDebugPrint(vmstk) /* be sure to specify the object type also in END and CODESTART macros! */ -CODESTARTobjDebugPrint(vmstk) - dbgoprint((obj_t*) pThis, "stack contents:\n"); -ENDobjDebugPrint(vmstk) - - -/* push a value on the stack. The provided pVar is now owned - * by the stack. If the user intends to continue use it, it - * must be duplicated. - */ -static rsRetVal -push(vmstk_t *pThis, var_t *pVar) -{ - DEFiRet; - - ISOBJ_TYPE_assert(pThis, vmstk); - ISOBJ_TYPE_assert(pVar, var); - - if(pThis->iStkPtr >= VMSTK_SIZE) - ABORT_FINALIZE(RS_RET_OUT_OF_STACKSPACE); - - pThis->vStk[pThis->iStkPtr++] = pVar; - -finalize_it: - RETiRet; -} - - -/* pop a value from the stack - * IMPORTANT: the stack pointer always points to the NEXT FREE entry. So in - * order to pop, we must access the element one below the stack pointer. - * The user is responsible for destructing the ppVar returned. - */ -static rsRetVal -pop(vmstk_t *pThis, var_t **ppVar) -{ - DEFiRet; - - ISOBJ_TYPE_assert(pThis, vmstk); - ASSERT(ppVar != NULL); - - if(pThis->iStkPtr == 0) - ABORT_FINALIZE(RS_RET_STACK_EMPTY); - - *ppVar = pThis->vStk[--pThis->iStkPtr]; - -finalize_it: - RETiRet; -} - - -/* pop a boolean value from the stack - * The user is responsible for destructing the ppVar returned. - */ -static rsRetVal -popBool(vmstk_t *pThis, var_t **ppVar) -{ - DEFiRet; - - /* assertions are done in pop(), we do not duplicate here */ - CHKiRet(pop(pThis, ppVar)); - CHKiRet(var.ConvToBool(*ppVar)); - -finalize_it: - RETiRet; -} - - -/* pop a number value from the stack - * The user is responsible for destructing the ppVar returned. - */ -static rsRetVal -popNumber(vmstk_t *pThis, var_t **ppVar) -{ - DEFiRet; - - /* assertions are done in pop(), we do not duplicate here */ - CHKiRet(pop(pThis, ppVar)); - CHKiRet(var.ConvToNumber(*ppVar)); - -finalize_it: - RETiRet; -} - - -/* pop a number value from the stack - * The user is responsible for destructing the ppVar returned. - */ -static rsRetVal -popString(vmstk_t *pThis, var_t **ppVar) -{ - DEFiRet; - - /* assertions are done in pop(), we do not duplicate here */ - CHKiRet(pop(pThis, ppVar)); - CHKiRet(var.ConvToString(*ppVar)); - -finalize_it: - RETiRet; -} - - -/* pop two variables for a common operation, e.g. a compare. When this - * functions returns, both variables have the same type, but the type - * is not set to anything specific. - * The user is responsible for destructing the ppVar's returned. - * A quick note on the name: it means pop 2 variable for a common - * opertion - just in case you wonder (I don't really like the name, - * but I didn't come up with a better one...). - * rgerhards, 2008-02-25 - */ -static rsRetVal -pop2CommOp(vmstk_t *pThis, var_t **ppVar1, var_t **ppVar2) -{ - DEFiRet; - - /* assertions are done in pop(), we do not duplicate here */ - /* operand two must be popped first, because it is at the top of stack */ - CHKiRet(pop(pThis, ppVar2)); - CHKiRet(pop(pThis, ppVar1)); - CHKiRet(var.ConvForOperation(*ppVar1, *ppVar2)); - -finalize_it: - RETiRet; -} - - -/* queryInterface function - * rgerhards, 2008-02-21 - */ -BEGINobjQueryInterface(vmstk) -CODESTARTobjQueryInterface(vmstk) - if(pIf->ifVersion != vmstkCURR_IF_VERSION) { /* check for current version, increment on each change */ - ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); - } - - /* ok, we have the right interface, so let's fill it - * Please note that we may also do some backwards-compatibility - * work here (if we can support an older interface version - that, - * of course, also affects the "if" above). - */ - pIf->Construct = vmstkConstruct; - pIf->ConstructFinalize = vmstkConstructFinalize; - pIf->Destruct = vmstkDestruct; - pIf->DebugPrint = vmstkDebugPrint; - pIf->Push = push; - pIf->Pop = pop; - pIf->PopBool = popBool; - pIf->PopNumber = popNumber; - pIf->PopString = popString; - pIf->Pop2CommOp = pop2CommOp; - -finalize_it: -ENDobjQueryInterface(vmstk) - - -/* Initialize the vmstk class. Must be called as the very first method - * before anything else is called inside this class. - * rgerhards, 2008-02-19 - */ -BEGINObjClassInit(vmstk, 1, OBJ_IS_CORE_MODULE) /* class, version */ - /* request objects we use */ - CHKiRet(objUse(var, CORE_COMPONENT)); - - /* set our own handlers */ - OBJSetMethodHandler(objMethod_DEBUGPRINT, vmstkDebugPrint); - OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, vmstkConstructFinalize); -ENDObjClassInit(vmstk) - -/* vi:set ai: - */ diff --git a/runtime/vmstk.h b/runtime/vmstk.h deleted file mode 100644 index cf8936e7..00000000 --- a/runtime/vmstk.h +++ /dev/null @@ -1,54 +0,0 @@ -/* The vmstk object. - * - * Copyright 2008-2012 Rainer Gerhards and Adiscon GmbH. - * - * This file is part of the rsyslog runtime library. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * -or- - * see COPYING.ASL20 in the source distribution - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef INCLUDED_VMSTK_H -#define INCLUDED_VMSTK_H - -/* The max size of the stack - TODO: make configurable */ -#define VMSTK_SIZE 256 - -/* the vmstk object */ -struct vmstk_s { - BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ - var_t *vStk[VMSTK_SIZE];/* the actual stack */ - int iStkPtr; /* stack pointer, points to next free location, grows from 0 --> topend */ -}; - - -/* interfaces */ -BEGINinterface(vmstk) /* name must also be changed in ENDinterface macro! */ - INTERFACEObjDebugPrint(vmstk); - rsRetVal (*Construct)(vmstk_t **ppThis); - rsRetVal (*ConstructFinalize)(vmstk_t __attribute__((unused)) *pThis); - rsRetVal (*Destruct)(vmstk_t **ppThis); - rsRetVal (*Push)(vmstk_t *pThis, var_t *pVar); - rsRetVal (*Pop)(vmstk_t *pThis, var_t **ppVar); - rsRetVal (*PopBool)(vmstk_t *pThis, var_t **ppVar); - rsRetVal (*PopNumber)(vmstk_t *pThis, var_t **ppVar); - rsRetVal (*PopString)(vmstk_t *pThis, var_t **ppVar); - rsRetVal (*Pop2CommOp)(vmstk_t *pThis, var_t **ppVar1, var_t **ppVar2); -ENDinterface(vmstk) -#define vmstkCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ - - -/* prototypes */ -PROTOTYPEObj(vmstk); - -#endif /* #ifndef INCLUDED_VMSTK_H */ |