diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | iminternal.c | 189 | ||||
-rw-r--r-- | iminternal.h | 48 | ||||
-rw-r--r-- | linkedlist.c | 59 | ||||
-rw-r--r-- | linkedlist.h | 1 | ||||
-rw-r--r-- | module-template.h | 1 | ||||
-rw-r--r-- | msg.h | 1 | ||||
-rw-r--r-- | ommysql.c | 21 | ||||
-rw-r--r-- | rsyslog.h | 1 | ||||
-rw-r--r-- | syslogd.c | 90 | ||||
-rw-r--r-- | template.h | 2 |
12 files changed, 386 insertions, 33 deletions
diff --git a/Makefile.am b/Makefile.am index 95aac156..a51173bf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -7,7 +7,7 @@ rfc3195d_SOURCES=rfc3195d.c rsyslog.h man_MANS = rfc3195d.8 rklogd.8 rsyslogd.8 rsyslog.conf.5 -rsyslogd_SOURCES=syslogd.c pidfile.c template.c outchannel.c stringbuf.c srUtils.c parse.c syslogd-types.h template.h outchannel.h syslogd.h stringbuf.h parse.h srUtils.h liblogging-stub.h net.c net.h msg.c msg.h omshell.c omshell.h omusrmsg.c omusrmsg.h ommysql.c ommysql.h omfwd.c omfwd.h tcpsyslog.c tcpsyslog.h omfile.h omfile.c omdiscard.c omdiscard.h modules.c modules.h module-template.h objomsr.c objomsr.h cfsysline.c cfsysline.h linkedlist.c linkedlist.h +rsyslogd_SOURCES=syslogd.c pidfile.c template.c outchannel.c stringbuf.c srUtils.c parse.c syslogd-types.h template.h outchannel.h syslogd.h stringbuf.h parse.h srUtils.h liblogging-stub.h net.c net.h msg.c msg.h omshell.c omshell.h omusrmsg.c omusrmsg.h ommysql.c ommysql.h omfwd.c omfwd.h tcpsyslog.c tcpsyslog.h omfile.h omfile.c omdiscard.c omdiscard.h modules.c modules.h module-template.h objomsr.c objomsr.h cfsysline.c cfsysline.h linkedlist.c linkedlist.h iminternal.c iminternal.h rsyslogd_CPPFLAGS=$(mysql_includes) rsyslogd_LDADD=$(mysql_libs) $(zlib_libs) $(pthreads_libs) diff --git a/configure.ac b/configure.ac index 34104d32..a8c46b67 100644 --- a/configure.ac +++ b/configure.ac @@ -2,8 +2,8 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) -AC_INIT(rsyslog, 1.17.7, rsyslog@lists.adiscon.com.) -AM_INIT_AUTOMAKE(rsyslog, 1.17.7) +AC_INIT(rsyslog, 1.18.0, rsyslog@lists.adiscon.com.) +AM_INIT_AUTOMAKE(rsyslog, 1.18.0) AC_CONFIG_SRCDIR([syslogd.c]) AC_CONFIG_HEADER([config.h]) diff --git a/iminternal.c b/iminternal.c new file mode 100644 index 00000000..2f898e46 --- /dev/null +++ b/iminternal.c @@ -0,0 +1,189 @@ +/* iminternal.c + * This file set implements the internal messages input module for rsyslog. + * Note: we currently do not have an input module spec, but + * we will have one in the future. This module needs then to be + * adapted. + * + * File begun on 2007-08-03 by RGerhards + * + * Copyright 2007 Rainer Gerhards and Adiscon GmbH. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + */ +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "rsyslog.h" +#include "syslogd.h" +#include "linkedlist.h" +#include "iminternal.h" + +static linkedList_t llMsgs; + + +/* destructs an iminternal object + */ +static rsRetVal iminternalDestruct(iminternal_t *pThis) +{ + DEFiRet; + + assert(pThis != NULL); + + if(pThis->pMsg != NULL) + MsgDestruct(pThis->pMsg); + + free(pThis); + + return iRet; +} + + +/* Construct an iminternal object + */ +static rsRetVal iminternalConstruct(iminternal_t **ppThis) +{ + DEFiRet; + iminternal_t *pThis; + + assert(ppThis != NULL); + + if((pThis = (iminternal_t*) calloc(1, sizeof(iminternal_t))) == NULL) { + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + } + +finalize_it: + if(iRet != RS_RET_OK) { + if(pThis != NULL) + iminternalDestruct(pThis); + } + + *ppThis = pThis; + + return iRet; +}; + + +/* add a message to the linked list + * Note: the pMsg reference counter is not incremented. Consequently, + * the caller must NOT decrement it. The caller actually hands over + * full ownership of the pMsg object. + * The interface of this function is modelled after syslogd/logmsg(), + * for which it is an "replacement". + */ +rsRetVal iminternalAddMsg(int pri, msg_t *pMsg, int flags) +{ + DEFiRet; + iminternal_t *pThis; + + assert(pMsg != NULL); + + CHKiRet(iminternalConstruct(&pThis)); + + pThis->pri = pri; + pThis->pMsg = pMsg; + pThis->flags = flags; + + CHKiRet(llAppend(&llMsgs, NULL, (void*) pThis)); + +finalize_it: + if(iRet != RS_RET_OK) { + dprintf("iminternalAddMsg() error %d - can not otherwise report this error, message lost\n", iRet); + if(pThis != NULL) + iminternalDestruct(pThis); + } + + return iRet; +} + + +/* pull the first error message from the linked list, remove it + * from the list and return it to the caller. The caller is + * responsible for freeing the message! + */ +rsRetVal iminternalRemoveMsg(int *pPri, msg_t **ppMsg, int *pFlags) +{ + DEFiRet; + iminternal_t *pThis; + linkedListCookie_t llCookie = NULL; + + assert(pPri != NULL); + assert(ppMsg != NULL); + assert(pFlags != NULL); + + CHKiRet(llGetNextElt(&llMsgs, &llCookie, (void**) &pThis)); + *pPri = pThis->pri; + *pFlags = pThis->flags; + *ppMsg = pThis->pMsg; + pThis->pMsg = NULL; /* we do no longer own it - important for destructor */ + + if(llDestroyRootElt(&llMsgs) != RS_RET_OK) { + dprintf("Root element of iminternal linked list could not be destroyed - there is " + "nothing we can do against it, we ignore it for now. Things may go wild " + "from here on. This is most probably a program logic error.\n"); + } + +finalize_it: + return iRet; +} + +/* tell the caller if we have any messages ready for processing. + * 0 means we have none, everything else means there is at least + * one message ready. + */ +rsRetVal iminternalHaveMsgReady(int* pbHaveOne) +{ + assert(pbHaveOne != NULL); + + return llGetNumElts(&llMsgs, pbHaveOne); +} + + +/* initialize the iminternal subsystem + * must be called once at the start of the program + */ +rsRetVal modInitIminternal(void) +{ + DEFiRet; + + iRet = llInit(&llMsgs, iminternalDestruct, NULL, NULL); + + return iRet; +} + + +/* de-initialize the iminternal subsystem + * must be called once at the end of the program + * Note: the error list must have been pulled first. We do + * NOT care if there are any errors left - we simply destroy + * them. + */ +rsRetVal modExitIminternal(void) +{ + DEFiRet; + + iRet = llDestroy(&llMsgs); + + return iRet; +} + +/* + * vi:set ai: + */ diff --git a/iminternal.h b/iminternal.h new file mode 100644 index 00000000..0677f814 --- /dev/null +++ b/iminternal.h @@ -0,0 +1,48 @@ +/* Definition of the internal messages input module. + * + * Note: we currently do not have an input module spec, but + * we will have one in the future. This module needs then to be + * adapted. + * + * Copyright 2007 Rainer Gerhards and Adiscon GmbH. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + */ + +#ifndef IMINTERNAL_H_INCLUDED +#define IMINTERNAL_H_INCLUDED +#include "template.h" + +/* 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 iminternal_s { /* config file sysline parse entry */ + int pri; + msg_t *pMsg; /* the message (in all its glory) */ + int flags; +}; +typedef struct iminternal_s iminternal_t; + +/* prototypes */ +rsRetVal modInitIminternal(void); +rsRetVal modExitIminternal(void); +rsRetVal iminternalAddMsg(int pri, msg_t *pMsg, int flags); +rsRetVal iminternalHaveMsgReady(int* pbHaveOne); +rsRetVal iminternalRemoveMsg(int *pPri, msg_t **ppMsg, int *pFlags); + +#endif /* #ifndef IMINTERNAL_H_INCLUDED */ diff --git a/linkedlist.c b/linkedlist.c index 4037193b..7ebadf5a 100644 --- a/linkedlist.c +++ b/linkedlist.c @@ -59,6 +59,31 @@ rsRetVal llInit(linkedList_t *pThis, rsRetVal (*pEltDestructor)(), rsRetVal (*pK }; +/* llDestroyEltData - destroys a list element + * It is a separate function as the + * functionality is needed in multiple code-pathes. + */ +static rsRetVal llDestroyElt(linkedList_t *pList, llElt_t *pElt) +{ + DEFiRet; + + assert(pList != NULL); + assert(pElt != NULL); + + /* we ignore errors during destruction, as we need to try + * free the element in any case. + */ + if(pElt->pData != NULL) + pList->pEltDestruct(pElt->pData); + if(pElt->pKey != NULL) + pList->pKeyDestruct(pElt->pKey); + free(pElt); + pList->iNumElts--; /* one less */ + + return iRet; +} + + /* llDestroy - destroys a COMPLETE linkedList */ rsRetVal llDestroy(linkedList_t *pThis) @@ -76,13 +101,37 @@ rsRetVal llDestroy(linkedList_t *pThis) /* we ignore errors during destruction, as we need to try * finish the linked list in any case. */ - if(pEltPrev->pData != NULL) - pThis->pEltDestruct(pEltPrev->pData); - if(pEltPrev->pKey != NULL) - pThis->pKeyDestruct(pEltPrev->pKey); - free(pEltPrev); + llDestroyElt(pThis, pEltPrev); + } + + return iRet; +} + +/* llDestroyRootElt - destroy the root element but otherwise + * keeps this list intact. -- rgerhards, 2007-08-03 + */ +rsRetVal llDestroyRootElt(linkedList_t *pThis) +{ + DEFiRet; + llElt_t *pPrev; + + if(pThis->pRoot == NULL) { + ABORT_FINALIZE(RS_RET_EMPTY_LIST); + } + + pPrev = pThis->pRoot; + if(pPrev->pNext == NULL) { + /* it was the only list element */ + pThis->pLast = NULL; + pThis->pRoot = NULL; + } else { + /* there are other list elements */ + pThis->pRoot = pPrev->pNext; } + CHKiRet(llDestroyElt(pThis, pPrev)); + +finalize_it: return iRet; } diff --git a/linkedlist.h b/linkedlist.h index ac67a01b..0b5b36e9 100644 --- a/linkedlist.h +++ b/linkedlist.h @@ -54,6 +54,7 @@ typedef llElt_t* linkedListCookie_t; /* this type avoids exposing internals and /* prototypes */ rsRetVal llInit(linkedList_t *pThis, rsRetVal (*pEltDestructor)(), rsRetVal (*pKeyDestructor)(void*), int (*pCmpOp)()); rsRetVal llDestroy(linkedList_t *pThis); +rsRetVal llDestroyRootElt(linkedList_t *pThis); rsRetVal llGetNextElt(linkedList_t *pThis, linkedListCookie_t *ppElt, void **ppUsr); rsRetVal llAppend(linkedList_t *pThis, void *pKey, void *pData); rsRetVal llFind(linkedList_t *pThis, void *pKey, void **ppData); diff --git a/module-template.h b/module-template.h index 54f18cfa..ce13bce6 100644 --- a/module-template.h +++ b/module-template.h @@ -333,6 +333,7 @@ finalize_it:\ return iRet;\ } + /* definitions for host API queries */ #define CODEmodInit_QueryRegCFSLineHdlr \ CHKiRet(pHostQueryEtryPt((uchar*)"regCfSysLineHdlr", &omsdRegCFSLineHdlr)); @@ -24,6 +24,7 @@ #ifndef MSG_H_INCLUDED #define MSG_H_INCLUDED 1 +#include "syslogd-types.h" #include "template.h" /* rgerhards 2004-11-08: The following structure represents a @@ -1,4 +1,4 @@ -/* omusrmsg.c +/* ommysql.c * This is the implementation of the build-in output module for MySQL. * * NOTE: read comments in module-template.h to understand how this file @@ -115,7 +115,7 @@ ENDgetWriteFDForSelect * We check if we have a valid MySQL handle. If not, we simply * report an error, but can not be specific. RGerhards, 2007-01-30 */ -static void reportDBError(instanceData *pData) +static void reportDBError(instanceData *pData, int bSilent) { char errMsg[512]; @@ -128,7 +128,10 @@ static void reportDBError(instanceData *pData) } else { /* we can ask mysql for the error description... */ snprintf(errMsg, sizeof(errMsg)/sizeof(char), "db error (%d): %s\n", mysql_errno(pData->f_hmysql), mysql_error(pData->f_hmysql)); - logerror(errMsg); + if(bSilent) + dprintf("mysql, DBError(silent): %s\n", errMsg); + else + logerror(errMsg); } return; @@ -139,7 +142,7 @@ static void reportDBError(instanceData *pData) * MySQL connection. * Initially added 2004-10-28 mmeckelein */ -static rsRetVal initMySQL(instanceData *pData) +static rsRetVal initMySQL(instanceData *pData, int bSilent) { DEFiRet; @@ -154,7 +157,7 @@ static rsRetVal initMySQL(instanceData *pData) /* Connect to database */ if(mysql_real_connect(pData->f_hmysql, pData->f_dbsrv, pData->f_dbuid, pData->f_dbpwd, pData->f_dbname, 0, NULL, 0) == NULL) { - reportDBError(pData); + reportDBError(pData, bSilent); closeMySQL(pData); /* ignore any error we may get */ iRet = RS_RET_SUSPENDED; } @@ -179,10 +182,10 @@ rsRetVal writeMySQL(uchar *psz, instanceData *pData) if(mysql_query(pData->f_hmysql, (char*)psz)) { /* error occured, try to re-init connection and retry */ closeMySQL(pData); /* close the current handle */ - CHKiRet(initMySQL(pData)); /* try to re-open */ + CHKiRet(initMySQL(pData, 0)); /* try to re-open */ if(mysql_query(pData->f_hmysql, (char*)psz)) { /* re-try insert */ /* we failed, giving up for now */ - reportDBError(pData); + reportDBError(pData, 0); closeMySQL(pData); /* free ressources */ ABORT_FINALIZE(RS_RET_SUSPENDED); } @@ -196,7 +199,7 @@ finalize_it: BEGINtryResume CODESTARTtryResume if(pData->f_hmysql == NULL) { - iRet = initMySQL(pData); + iRet = initMySQL(pData, 1); } ENDtryResume @@ -273,7 +276,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) logerror("Trouble with MySQL connection properties. -MySQL logging disabled"); ABORT_FINALIZE(RS_RET_INVALID_PARAMS); } else { - CHKiRet(initMySQL(pData)); + CHKiRet(initMySQL(pData, 0)); } #endif /* #ifdef WITH_DB */ @@ -68,6 +68,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_END_OF_LINKEDLIST = -2014, /**< end of linked list, not an error, but a status */ RS_RET_CHAIN_NOT_PERMITTED = -2015, /**< chaining (e.g. of config command handlers) not permitted */ RS_RET_INVALID_PARAMS = -2016,/**< supplied parameters are invalid */ + RS_RET_EMPTY_LIST = -2017, /**< linked list is empty */ RS_RET_OK = 0 /**< operation successful */ }; typedef enum rsRetVal_ rsRetVal; /**< friendly type for global return value */ @@ -211,6 +211,7 @@ #include "msg.h" #include "modules.h" #include "tcpsyslog.h" +#include "iminternal.h" #include "cfsysline.h" #include "omshell.h" #include "omusrmsg.h" @@ -715,6 +716,7 @@ static void freeSelectors(void); static rsRetVal processConfFile(uchar *pConfFile); static rsRetVal actionDestruct(action_t *pThis); static rsRetVal selectorAddList(selector_t *f); +static void processImInternal(void); /* Access functions for the selector_t. These functions are primarily * necessary to make things thread-safe. Consequently, they are slim @@ -2377,7 +2379,7 @@ time_t now; * function here probably is only an interim solution and that we need to * think on the best way to do this. */ -static void logmsgInternal(int pri, char * msg, int flags) +static void logmsgInternal(int pri, char *msg, int flags) { msg_t *pMsg; @@ -2407,9 +2409,17 @@ static void logmsgInternal(int pri, char * msg, int flags) pMsg->iSeverity = LOG_PRI(pri); pMsg->bParseHOSTNAME = 0; getCurrTime(&(pMsg->tTIMESTAMP)); /* use the current time! */ + flags |= INTERNAL_MSG; - logmsg(pri, pMsg, flags | INTERNAL_MSG); - MsgDestruct(pMsg); + if(bRunningMultithreaded == 0) { /* not yet in queued mode */ + iminternalAddMsg(pri, pMsg, flags); + } else { + /* we have the queue, so we can simply provide the + * message to the queue engine. + */ + logmsg(pri, pMsg, flags); + MsgDestruct(pMsg); + } } /* @@ -2539,9 +2549,12 @@ int shouldProcessThisMessage(selector_t *f, msg_t *pMsg) /* doEmergencyLoggin() * ... does exactly do that. It logs messages when the subsystem has not yet * been initialized. This almost always happens during initial startup or - * during HUPing. - * rgerhards, 2007-07-25 - * TODO: add logging to system console + * during HUPing. -- rgerhards, 2007-07-25 + * rgerhards, 2007-08-03: as of now, this can normally no longer happen. All + * startup messages are now buffered until the system is ready to run. I leave + * this minimal implementation here in in the very remote case that it might + * be needed in the future or due to a program bug. Do *not* excpect this + * code to be called. */ static void doEmergencyLogging(msg_t *pMsg) { @@ -2651,6 +2664,8 @@ DEFFUNC_llExecFunc(processMsgDoActions) } else if(iRetMod == RS_RET_SUSPENDED) { /* indicate suspension for next module to be called */ pDoActData->bPrevWasSuspended = 1; + } else { + pDoActData->bPrevWasSuspended = 0; } finalize_it: @@ -3651,7 +3666,7 @@ static void die(int sig) errno = 0; logmsgInternal(LOG_SYSLOG|LOG_INFO, buf, ADDDATE); } - + /* Free ressources and close connections */ freeSelectors(); @@ -4057,6 +4072,12 @@ static void freeSelectors(void) if(Files != NULL) { dprintf("Freeing log structures.\n"); + /* just in case, we flush the emergency log. If error messages occur after + * this stage, we loose them, but that's ok. With multi-threading, this can + * never happen. -- rgerhards, 2007-08-03 + */ + processImInternal(); + /* we need first to flush, then wait for all messages to be processed * (stopWoker() does that), then we can free the structures. */ @@ -5012,9 +5033,7 @@ finalize_it: /* do not overwrite error state! */ OMSRdestruct(pOMSR); if(pAction != NULL) - actionDestruct(pAction); /* this line should take care of the TODO's below */ - /* TODO: free pMod instance data, potential mem leak */ - /* TODO: better said - where is the selector_t AND its elements destroyed? */ + actionDestruct(pAction); } return iRet; @@ -5428,6 +5447,25 @@ static void debugListenInfo(int fd, char *type) } +/* this function pulls all internal messages from the buffer + * and puts them into the processing engine. + * We can only do limited error handling, as this would not + * really help us. TODO: add error messages? + * rgerhards, 2007-08-03 + */ +static void processImInternal(void) +{ + int iPri; + int iFlags; + msg_t *pMsg; + + while(iminternalRemoveMsg(&iPri, &pMsg, &iFlags) == RS_RET_OK) { + logmsg(iPri, pMsg, iFlags); + MsgDestruct(pMsg); + } +} + + /* helper function for mainloop(). This is used to add all module * writeFDsfor Select via llExecFunc(). * rgerhards, 2007-08-02 @@ -5490,6 +5528,10 @@ static void mainloop(void) int fd; char line[MAXLINE +1]; int maxfds; + int nfds; + errno = 0; + FD_ZERO(&readfds); + maxfds = 0; #ifdef SYSLOG_INET mainloopWriteFDSInfo_t writeFDSInfo; fd_set writefds; @@ -5507,13 +5549,10 @@ static void mainloop(void) #endif #endif - - /* --------------------- Main loop begins here. ----------------------------------------- */ while(!bFinished){ - int nfds; - errno = 0; - FD_ZERO(&readfds); - maxfds = 0; + /* first check if we have any internal messages queued and spit them out */ + processImInternal(); + #ifdef SYSLOG_UNIXAF /* Add the Unix Domain Sockets to the list of read * descriptors. @@ -5926,6 +5965,11 @@ int main(int argc, char **argv) } /* doing some core initializations */ + if((iRet = modInitIminternal()) != RS_RET_OK) { + fprintf(stderr, "fatal error: could not initialize errbuf object (error code %d).\n", + iRet); + exit(1); /* "good" exit, leaving at init for fatal error */ + } #ifdef USE_PTHREADS /* create message queue */ @@ -6208,6 +6252,20 @@ int main(int argc, char **argv) */ mainloop(); + + /* de-init some modules */ + modExitIminternal(); + + /* TODO: this would also be the right place to de-init the builtin output modules. We + * do not currently do that, because the module interface does not allow for + * it. This will come some time later (it's essential with loadable modules). + * For the time being, this is a memory leak on exit, but as the process is + * terminated, we do not really bother about it. + * rgerhards, 2007-08-03 + */ + + /* end de-init's */ + die(bFinished); return 0; } @@ -8,6 +8,8 @@ #define TEMPLATE_H_INCLUDED 1 +#include "stringbuf.h" + #ifdef FEATURE_REGEXP /* Include regular expressions */ #include <regex.h> |