summaryrefslogtreecommitdiffstats
path: root/tools/syslogd.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/syslogd.c')
-rw-r--r--tools/syslogd.c2164
1 files changed, 554 insertions, 1610 deletions
diff --git a/tools/syslogd.c b/tools/syslogd.c
index 0b860448..1ba3ef2b 100644
--- a/tools/syslogd.c
+++ b/tools/syslogd.c
@@ -16,12 +16,9 @@
* parts of the code have been rewritten.
*
* This Project was intiated and is maintained by
- * Rainer Gerhards <rgerhards@hq.adiscon.com>. See
- * AUTHORS to learn who helped make it become a reality.
+ * Rainer Gerhards <rgerhards@hq.adiscon.com>.
*
- * If you have questions about rsyslogd in general, please email
- * info@adiscon.com. To learn more about rsyslogd, please visit
- * http://www.rsyslog.com.
+ * For further information, please see http://www.rsyslog.com
*
* \author Rainer Gerhards <rgerhards@adiscon.com>
* \date 2003-10-17
@@ -36,7 +33,7 @@
* to the database).
*
* rsyslog - An Enhanced syslogd Replacement.
- * Copyright 2003-2008 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2003-2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -127,66 +124,55 @@
#include "omusrmsg.h"
#include "omfwd.h"
#include "omfile.h"
+#include "ompipe.h"
#include "omdiscard.h"
+#include "pmrfc5424.h"
+#include "pmrfc3164.h"
#include "threads.h"
+#include "wti.h"
#include "queue.h"
#include "stream.h"
#include "conf.h"
-#include "vm.h"
#include "errmsg.h"
#include "datetime.h"
#include "parser.h"
-#include "sysvar.h"
+#include "batch.h"
#include "unicode-helper.h"
+#include "ruleset.h"
+#include "rule.h"
+#include "net.h"
+#include "vm.h"
+#include "prop.h"
/* definitions for objects we access */
DEFobjCurrIf(obj)
DEFobjCurrIf(glbl)
-DEFobjCurrIf(datetime)
+DEFobjCurrIf(datetime) /* TODO: make go away! */
DEFobjCurrIf(conf)
DEFobjCurrIf(expr)
-DEFobjCurrIf(vm)
-DEFobjCurrIf(var)
DEFobjCurrIf(module)
DEFobjCurrIf(errmsg)
+DEFobjCurrIf(rule)
+DEFobjCurrIf(ruleset)
+DEFobjCurrIf(prop)
+DEFobjCurrIf(parser)
DEFobjCurrIf(net) /* TODO: make go away! */
/* forward definitions */
static rsRetVal GlobalClassExit(void);
-/* We define our own set of syslog defintions so that we
- * do not need to rely on (possibly different) implementations.
- * 2007-07-19 rgerhards
- */
-/* missing definitions for solaris
- * 2006-02-16 Rger
- */
-#ifdef __sun
-# define LOG_AUTHPRIV LOG_AUTH
-#endif
-#define INTERNAL_NOPRI 0x10 /* the "no priority" priority */
-#define LOG_FTP (11<<3) /* ftp daemon */
-
-
-#ifndef UTMP_FILE
-#ifdef UTMP_FILENAME
-#define UTMP_FILE UTMP_FILENAME
-#else
-#ifdef _PATH_UTMP
-#define UTMP_FILE _PATH_UTMP
-#else
-#define UTMP_FILE "/etc/utmp"
-#endif
-#endif
-#endif
#ifndef _PATH_LOGCONF
#define _PATH_LOGCONF "/etc/rsyslog.conf"
#endif
#ifndef _PATH_MODDIR
-#define _PATH_MODDIR "/lib/rsyslog/"
+# if defined(__FreeBSD__)
+# define _PATH_MODDIR "/usr/local/lib/rsyslog/"
+# else
+# define _PATH_MODDIR "/lib/rsyslog/"
+# endif
#endif
#if defined(SYSLOGD_PIDNAME)
@@ -215,14 +201,12 @@ static rsRetVal GlobalClassExit(void);
# endif
#endif
-#ifndef _PATH_DEV
-# define _PATH_DEV "/dev/"
-#endif
-
#ifndef _PATH_TTY
-#define _PATH_TTY "/dev/tty"
+# define _PATH_TTY "/dev/tty"
#endif
+static prop_t *pInternalInputName = NULL; /* there is only one global inputName for all internally-generated messages */
+static prop_t *pLocalHostIP = NULL; /* there is only one global IP for all internally-generated messages */
static uchar *ConfFile = (uchar*) _PATH_LOGCONF; /* read-only after startup */
static char *PidFile = _PATH_LOGPID; /* read-only after startup */
@@ -230,8 +214,6 @@ static pid_t myPid; /* our pid for use in self-generated messages, e.g. on start
/* mypid is read-only after the initial fork() */
static int bHadHUP = 0; /* did we have a HUP? */
-static int bParseHOSTNAMEandTAG = 1; /* global config var: should the hostname and tag be
- * parsed inside message - rgerhards, 2006-03-13 */
static int bFinished = 0; /* used by termination signal handler, read-only except there
* is either 0 or the number of the signal that requested the
* termination.
@@ -247,8 +229,6 @@ int repeatinterval[2] = { 30, 60 }; /* # of secs before flush */
#define LIST_DELIMITER ':' /* delimiter between two hosts */
-struct filed *Files = NULL; /* read-only after init() (but beware of sigusr1!) */
-
static pid_t ppid; /* This is a quick and dirty hack used for spliting main/startup thread */
typedef struct legacyOptsLL_s {
@@ -258,19 +238,18 @@ typedef struct legacyOptsLL_s {
legacyOptsLL_t *pLegacyOptsLL = NULL;
/* global variables for config file state */
-int bDropTrailingLF = 1; /* drop trailing LF's on reception? */
int iCompatibilityMode = 0; /* version we should be compatible with; 0 means sysklogd. It is
the default, so if no -c<n> option is given, we make ourselvs
as compatible to sysklogd as possible. */
+#define DFLT_bLogStatusMsgs 1
+static int bLogStatusMsgs = DFLT_bLogStatusMsgs; /* log rsyslog start/stop/HUP messages? */
static int bDebugPrintTemplateList = 1;/* output template list in debug mode? */
static int bDebugPrintCfSysLineHandlerList = 1;/* output cfsyslinehandler list in debug mode? */
static int bDebugPrintModuleList = 1;/* output module list in debug mode? */
-uchar cCCEscapeChar = '\\';/* character to be used to start an escape sequence for control chars */
-int bEscapeCCOnRcv = 1; /* escape control characters on reception: 0 - no, 1 - yes */
static int bErrMsgToStderr = 1; /* print error messages to stderr (in addition to everything else)? */
int bReduceRepeatMsgs; /* reduce repeated message - 0 - no, 1 - yes */
+int bAbortOnUncleanConfig = 0; /* abort run (rather than starting with partial config) if there was any issue in conf */
int bActExecWhenPrevSusp; /* execute action only when previous one was suspended? */
-int iActExecOnceInterval = 0; /* execute action once every nn seconds */
/* end global config file state variables */
int MarkInterval = 20 * 60; /* interval between marks in seconds - read-only after startup */
@@ -287,7 +266,7 @@ extern int errno;
static uchar *pszConfDAGFile = NULL; /* name of config DAG file, non-NULL means generate one */
/* main message queue and its configuration parameters */
-static qqueue_t *pMsgQueue = NULL; /* the main message queue */
+qqueue_t *pMsgQueue = NULL; /* the main message queue */
static int iMainMsgQueueSize = 10000; /* size of the main message queue above */
static int iMainMsgQHighWtrMark = 8000; /* high water mark for disk-assisted queues */
static int iMainMsgQLowWtrMark = 2000; /* low water mark for disk-assisted queues */
@@ -298,58 +277,32 @@ static queueType_t MainMsgQueType = QUEUETYPE_FIXED_ARRAY; /* type of the main m
static uchar *pszMainMsgQFName = NULL; /* prefix for the main message queue file */
static int64 iMainMsgQueMaxFileSize = 1024*1024;
static int iMainMsgQPersistUpdCnt = 0; /* persist queue info every n updates */
-static int iMainMsgQtoQShutdown = 0; /* queue shutdown */
+static int bMainMsgQSyncQeueFiles = 0; /* sync queue files on every write? */
+static int iMainMsgQtoQShutdown = 1500; /* queue shutdown (ms) */
static int iMainMsgQtoActShutdown = 1000; /* action shutdown (in phase 2) */
static int iMainMsgQtoEnq = 2000; /* timeout for queue enque */
static int iMainMsgQtoWrkShutdown = 60000; /* timeout for worker thread shutdown */
static int iMainMsgQWrkMinMsgs = 100; /* minimum messages per worker needed to start a new one */
static int iMainMsgQDeqSlowdown = 0; /* dequeue slowdown (simple rate limiting) */
static int64 iMainMsgQueMaxDiskSpace = 0; /* max disk space allocated 0 ==> unlimited */
+static int64 iMainMsgQueDeqBatchSize = 32; /* dequeue batch size */
static int bMainMsgQSaveOnShutdown = 1; /* save queue on shutdown (when DA enabled)? */
static int iMainMsgQueueDeqtWinFromHr = 0; /* hour begin of time frame when queue is to be dequeued */
static int iMainMsgQueueDeqtWinToHr = 25; /* hour begin of time frame when queue is to be dequeued */
-/* support for simple textual representation of FIOP names
- * rgerhards, 2005-09-27
- */
-static char* getFIOPName(unsigned iFIOP)
-{
- char *pRet;
- switch(iFIOP) {
- case FIOP_CONTAINS:
- pRet = "contains";
- break;
- case FIOP_ISEQUAL:
- pRet = "isequal";
- break;
- case FIOP_STARTSWITH:
- pRet = "startswith";
- break;
- case FIOP_REGEX:
- pRet = "regex";
- break;
- default:
- pRet = "NOP";
- break;
- }
- return pRet;
-}
-
-
/* Reset config variables to default values.
* rgerhards, 2007-07-17
*/
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
- cCCEscapeChar = '#';
+ bLogStatusMsgs = DFLT_bLogStatusMsgs;
bActExecWhenPrevSusp = 0;
- iActExecOnceInterval = 0;
bDebugPrintTemplateList = 1;
bDebugPrintCfSysLineHandlerList = 1;
bDebugPrintModuleList = 1;
- bEscapeCCOnRcv = 1; /* default is to escape control characters */
bReduceRepeatMsgs = 0;
+ bAbortOnUncleanConfig = 0;
free(pszMainMsgQFName);
pszMainMsgQFName = NULL;
iMainMsgQueueSize = 10000;
@@ -360,7 +313,8 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
iMainMsgQueMaxFileSize = 1024 * 1024;
iMainMsgQueueNumWorkers = 1;
iMainMsgQPersistUpdCnt = 0;
- iMainMsgQtoQShutdown = 0;
+ bMainMsgQSyncQeueFiles = 0;
+ iMainMsgQtoQShutdown = 1500;
iMainMsgQtoActShutdown = 1000;
iMainMsgQtoEnq = 2000;
iMainMsgQtoWrkShutdown = 60000;
@@ -369,6 +323,7 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
bMainMsgQSaveOnShutdown = 1;
MainMsgQueType = QUEUETYPE_FIXED_ARRAY;
iMainMsgQueMaxDiskSpace = 0;
+ iMainMsgQueDeqBatchSize = 32;
glbliActionResumeRetryCount = 0;
return RS_RET_OK;
@@ -395,8 +350,6 @@ static char **crunch_list(char *list);
static void reapchild();
static void debug_switch();
static void sighup_handler();
-static void freeSelectors(void);
-static void processImInternal(void);
static int usage(void)
@@ -404,7 +357,7 @@ static int usage(void)
fprintf(stderr, "usage: rsyslogd [-c<version>] [-46AdnqQvwx] [-l<hostlist>] [-s<domainlist>]\n"
" [-f<conffile>] [-i<pidfile>] [-N<level>] [-M<module load path>]\n"
" [-u<number>]\n"
- "To run rsyslogd in native mode, use \"rsyslogd -c3 <other options>\"\n\n"
+ "To run rsyslogd in native mode, use \"rsyslogd -c5 <other options>\"\n\n"
"For further information see http://www.rsyslog.com/doc\n");
exit(1); /* "good" exit - done to terminate usage() */
}
@@ -416,13 +369,16 @@ static int usage(void)
*/
/* return back the approximate current number of messages in the main message queue
+ * This number includes the messages that reside in an associated DA queue (if
+ * it exists) -- rgerhards, 2009-10-14
*/
rsRetVal
diagGetMainMsgQSize(int *piSize)
{
DEFiRet;
assert(piSize != NULL);
- *piSize = pMsgQueue->iQueueSize;
+ *piSize = (pMsgQueue->pqDA != NULL) ? pMsgQueue->pqDA->iQueueSize : 0;
+ *piSize += pMsgQueue->iQueueSize;
RETiRet;
}
@@ -430,69 +386,8 @@ diagGetMainMsgQSize(int *piSize)
/* ------------------------------ end support functions for imdiag ------------------------------ */
-/* function to destruct a selector_t object
- * rgerhards, 2007-08-01
- */
-rsRetVal
-selectorDestruct(void *pVal)
-{
- selector_t *pThis = (selector_t *) pVal;
-
- assert(pThis != NULL);
-
- if(pThis->pCSHostnameComp != NULL)
- rsCStrDestruct(&pThis->pCSHostnameComp);
- if(pThis->pCSProgNameComp != NULL)
- rsCStrDestruct(&pThis->pCSProgNameComp);
-
- if(pThis->f_filter_type == FILTER_PROP) {
- if(pThis->f_filterData.prop.pCSPropName != NULL)
- rsCStrDestruct(&pThis->f_filterData.prop.pCSPropName);
- if(pThis->f_filterData.prop.pCSCompValue != NULL)
- 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);
- }
-
- llDestroy(&pThis->llActList);
- free(pThis);
-
- return RS_RET_OK;
-}
-
-
-/* function to construct a selector_t object
- * rgerhards, 2007-08-01
- */
-rsRetVal
-selectorConstruct(selector_t **ppThis)
-{
- DEFiRet;
- selector_t *pThis;
-
- assert(ppThis != NULL);
-
- if((pThis = (selector_t*) calloc(1, sizeof(selector_t))) == NULL) {
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- }
- CHKiRet(llInit(&pThis->llActList, actionDestruct, NULL, NULL));
-
-finalize_it:
- if(iRet != RS_RET_OK) {
- if(pThis != NULL) {
- selectorDestruct(pThis);
- }
- }
- *ppThis = pThis;
- RETiRet;
-}
-
-
/* rgerhards, 2005-10-24: crunch_list is called only during option processing. So
- * it is never called once rsyslogd is running (not even when HUPed). This code
+ * it is never called once rsyslogd is running. This code
* contains some exits, but they are considered safe because they only happen
* during startup. Anyhow, when we review the code here, we might want to
* reconsider the exit()s.
@@ -520,7 +415,7 @@ static char **crunch_list(char *list)
for (count=i=0; p[i]; i++)
if (p[i] == LIST_DELIMITER) count++;
- if ((result = (char **)malloc(sizeof(char *) * (count+2))) == NULL) {
+ if ((result = (char **)MALLOC(sizeof(char *) * (count+2))) == NULL) {
printf ("Sorry, can't get enough memory, exiting.\n");
exit(0); /* safe exit, because only called during startup */
}
@@ -532,7 +427,7 @@ static char **crunch_list(char *list)
*/
count = 0;
while ((q=strchr(p, LIST_DELIMITER))) {
- result[count] = (char *) malloc((q - p + 1) * sizeof(char));
+ result[count] = (char *) MALLOC((q - p + 1) * sizeof(char));
if (result[count] == NULL) {
printf ("Sorry, can't get enough memory, exiting.\n");
exit(0); /* safe exit, because only called during startup */
@@ -543,7 +438,7 @@ static char **crunch_list(char *list)
count++;
}
if ((result[count] = \
- (char *)malloc(sizeof(char) * strlen(p) + 1)) == NULL) {
+ (char *)MALLOC(sizeof(char) * strlen(p) + 1)) == NULL) {
printf ("Sorry, can't get enough memory, exiting.\n");
exit(0); /* safe exit, because only called during startup */
}
@@ -553,7 +448,7 @@ static char **crunch_list(char *list)
#if 0
count=0;
while (result[count])
- dbgprintf("#%d: %s\n", count, StripDomains[count++]);
+ DBGPRINTF("#%d: %s\n", count, StripDomains[count++]);
#endif
return result;
}
@@ -562,7 +457,7 @@ static char **crunch_list(char *list)
void untty(void)
#ifdef HAVE_SETSID
{
- if ( !Debug ) {
+ if(!Debug) {
setsid();
}
return;
@@ -571,352 +466,61 @@ void untty(void)
{
int i;
- if ( !Debug ) {
+ if(!Debug) {
i = open(_PATH_TTY, O_RDWR|O_CLOEXEC);
if (i >= 0) {
# if !defined(__hpux)
- (void) ioctl(i, (int) TIOCNOTTY, (char *)0);
+ (void) ioctl(i, (int) TIOCNOTTY, NULL);
# else
/* TODO: we need to implement something for HP UX! -- rgerhards, 2008-03-04 */
/* actually, HP UX should have setsid, so the code directly above should
* trigger. So the actual question is why it doesn't do that...
*/
# endif
- (void) close(i);
+ close(i);
}
}
}
#endif
-/* Take a raw input line, decode the message, and print the message
- * on the appropriate log files.
- * rgerhards 2004-11-08: Please note
- * that this function does only a partial decoding. At best, it splits
- * the PRI part. No further decode happens. The rest is done in
- * logmsg().
- * Added the iSource parameter so that we know if we have to parse
- * HOSTNAME or not. rgerhards 2004-11-16.
- * changed parameter iSource to bParseHost. For details, see comment in
- * printchopped(). rgerhards 2005-10-06
- * rgerhards: 2008-03-06: added "flags" to allow an input module to specify
- * flags, most importantly to request ignoring the messages' timestamp.
- *
- * rgerhards, 2008-03-19:
- * I added an additional calling parameter to permit specifying the flow
- * control capability of the source.
- *
- * rgerhards, 2008-05-16:
- * I added an additional calling parameter (hnameIP) to enable specifying the IP
- * of a remote host.
- *
- * rgerhards, 2008-09-11:
- * Interface change: added new parameter "InputName", permits the input to provide
- * a string that identifies it. May be NULL, but must be a valid char* pointer if
- * non-NULL.
- *
- * rgerhards, 2008-10-06:
- * Interface change: added new parameter "stTime", which enables the caller to provide
- * a timestamp that is to be used as timegenerated instead of the current system time.
- * This is meant to facilitate performance optimization. Some inputs support such modes.
- * If stTime is NULL, the current system time is used.
- *
- * rgerhards, 2008-10-09:
- * interface change: bParseHostname removed, now in flags
+/* This takes a received message that must be decoded and submits it to
+ * the main message queue. This is a legacy function which is being provided
+ * to aid older input plugins that do not support message creation via
+ * the new interfaces themselves. It is not recommended to use this
+ * function for new plugins. -- rgerhards, 2009-10-12
*/
-static inline rsRetVal printline(uchar *hname, uchar *hnameIP, uchar *msg, int flags, flowControl_t flowCtlType,
- uchar *pszInputName, struct syslogTime *stTime, time_t ttGenTime)
+rsRetVal
+parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int flags, flowControl_t flowCtlType,
+ prop_t *pInputName, struct syslogTime *stTime, time_t ttGenTime)
{
- DEFiRet;
- register uchar *p;
- int pri;
+ prop_t *pProp = NULL;
msg_t *pMsg;
+ DEFiRet;
- /* Now it is time to create the message object (rgerhards) */
+ /* we now create our own message object and submit it to the queue */
if(stTime == NULL) {
CHKiRet(msgConstruct(&pMsg));
} else {
CHKiRet(msgConstructWithTime(&pMsg, stTime, ttGenTime));
}
- if(pszInputName != NULL)
- MsgSetInputName(pMsg, pszInputName);
+ if(pInputName != NULL)
+ MsgSetInputName(pMsg, pInputName);
+ MsgSetRawMsg(pMsg, (char*)msg, len);
MsgSetFlowControlType(pMsg, flowCtlType);
- MsgSetRawMsg(pMsg, (char*)msg);
-
- /* test for special codes */
- pri = DEFUPRI;
- p = msg;
- if (*p == '<') {
- pri = 0;
- while (isdigit((int) *++p))
- {
- pri = 10 * pri + (*p - '0');
- }
- if (*p == '>')
- ++p;
- }
- if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
- pri = DEFUPRI;
- pMsg->iFacility = LOG_FAC(pri);
- pMsg->iSeverity = LOG_PRI(pri);
+ pMsg->msgFlags = flags | NEEDS_PARSING;
- /* Now we look at the HOSTNAME. That is a bit complicated...
- * If we have a locally received message, it does NOT
- * contain any hostname information in the message itself.
- * As such, the HOSTNAME is the same as the system that
- * the message was received from (that, for obvious reasons,
- * being the local host). rgerhards 2004-11-16
- */
- if((pMsg->msgFlags & PARSE_HOSTNAME) == 0)
- MsgSetHOSTNAME(pMsg, hname);
- MsgSetRcvFrom(pMsg, hname);
- CHKiRet(MsgSetRcvFromIP(pMsg, hnameIP));
-
- /* rgerhards 2004-11-19: well, well... we've now seen that we
- * have the "hostname problem" also with the traditional Unix
- * message. As we like to emulate it, we need to add the hostname
- * to it.
- */
- if(MsgSetUxTradMsg(pMsg, (char*)p) != 0)
- ABORT_FINALIZE(RS_RET_ERR);
-
- logmsg(pMsg, flags);
+ MsgSetRcvFromStr(pMsg, hname, ustrlen(hname), &pProp);
+ CHKiRet(prop.Destruct(&pProp));
+ CHKiRet(MsgSetRcvFromIPStr(pMsg, hnameIP, ustrlen(hnameIP), &pProp));
+ CHKiRet(prop.Destruct(&pProp));
+ CHKiRet(submitMsg(pMsg));
finalize_it:
RETiRet;
}
-/* This takes a received message that must be decoded and submits it to
- * the main message queue. The function calls the necessary parser.
- *
- * rgerhards, 2006-11-30: I have greatly changed this function. Formerly,
- * it tried to reassemble multi-part messages, which is a legacy stock
- * sysklogd concept. In essence, that was that messages not ending with
- * \0 were glued together. As far as I can see, this is a sysklogd
- * specific feature and, from looking at the code, seems to be used
- * pretty seldom (if at all). I remove this now, not the least because it is totally
- * incompatible with upcoming IETF syslog standards. If you experience
- * strange behaviour with messages beeing split across multiple lines,
- * this function here might be the place to look at.
- *
- * Some previous history worth noting:
- * I added the "iSource" parameter. This is needed to distinguish between
- * messages that have a hostname in them (received from the internet) and
- * those that do not have (most prominently /dev/log). rgerhards 2004-11-16
- * And now I removed the "iSource" parameter and changed it to be "bParseHost",
- * because all that it actually controls is whether the host is parsed or not.
- * For rfc3195 support, we needed to modify the algo for host parsing, so we can
- * no longer rely just on the source (rfc3195d forwarded messages arrive via
- * unix domain sockets but contain the hostname). rgerhards, 2005-10-06
- *
- * rgerhards, 2008-02-18:
- * This function was previously called "printchopped"() and has been renamed
- * as part of the effort to create a clean internal message submission interface.
- * It also has been adopted to our usual calling interface, but currently does
- * not provide any useful return states. But we now have the hook and things can
- * improve in the future. <-- TODO!
- *
- * rgerhards, 2008-03-19:
- * I added an additional calling parameter to permit specifying the flow
- * control capability of the source.
- *
- * rgerhards, 2008-05-16:
- * I added an additional calling parameter (hnameIP) to enable specifying the IP
- * of a remote host.
- *
- * rgerhards, 2008-09-11:
- * Interface change: added new parameter "InputName", permits the input to provide
- * a string that identifies it. May be NULL, but must be a valid char* pointer if
- * non-NULL.
- *
- * rgerhards, 2008-10-06:
- * Interface change: added new parameter "stTime", which enables the caller to provide
- * a timestamp that is to be used as timegenerated instead of the current system time.
- * This is meant to facilitate performance optimization. Some inputs support such modes.
- * If stTime is NULL, the current system time is used.
- *
- * rgerhards, 2008-10-09:
- * interface change: bParseHostname removed, now in flags
- */
-rsRetVal
-parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int flags, flowControl_t flowCtlType,
- uchar *pszInputName, struct syslogTime *stTime, time_t ttGenTime)
-{
- DEFiRet;
- register int iMsg;
- uchar *pMsg;
- uchar *pData;
- uchar *pEnd;
- int iMaxLine;
- uchar *tmpline = NULL;
-# ifdef USE_NETZIP
- uchar *deflateBuf = NULL;
- uLongf iLenDefBuf;
-# endif
-
- assert(hname != NULL);
- assert(hnameIP != NULL);
- assert(msg != NULL);
- assert(len >= 0);
-
- /* we first allocate work buffers large enough to hold the configured maximum
- * size of a message. Over time, we should change this to a more optimal way, i.e.
- * by calling the function with the actual length of the message to be parsed.
- * rgerhards, 2008-09-02
- *
- * TODO: optimize buffer handling */
- iMaxLine = glbl.GetMaxLine();
- CHKmalloc(tmpline = malloc(sizeof(uchar) * (iMaxLine + 1)));
-
- /* we first check if we have a NUL character at the very end of the
- * message. This seems to be a frequent problem with a number of senders.
- * So I have now decided to drop these NULs. However, if they are intentional,
- * that may cause us some problems, e.g. with syslog-sign. On the other hand,
- * current code always has problems with intentional NULs (as it needs to escape
- * them to prevent problems with the C string libraries), so that does not
- * really matter. Just to be on the save side, we'll log destruction of such
- * NULs in the debug log.
- * rgerhards, 2007-09-14
- */
- if(*(msg + len - 1) == '\0') {
- dbgprintf("dropped NUL at very end of message\n");
- len--;
- }
-
- /* then we check if we need to drop trailing LFs, which often make
- * their way into syslog messages unintentionally. In order to remain
- * compatible to recent IETF developments, we allow the user to
- * turn on/off this handling. rgerhards, 2007-07-23
- */
- if(bDropTrailingLF && *(msg + len - 1) == '\n') {
- dbgprintf("dropped LF at very end of message (DropTrailingLF is set)\n");
- len--;
- }
-
- iMsg = 0; /* initialize receiving buffer index */
- pMsg = tmpline; /* set receiving buffer pointer */
- pData = msg; /* set source buffer pointer */
- pEnd = msg + len; /* this is one off, which is intensional */
-
-# ifdef USE_NETZIP
- /* we first need to check if we have a compressed record. If so,
- * we must decompress it.
- */
- if(len > 0 && *msg == 'z') { /* compressed data present? (do NOT change order if conditions!) */
- /* we have compressed data, so let's deflate it. We support a maximum
- * message size of iMaxLine. If it is larger, an error message is logged
- * and the message is dropped. We do NOT try to decompress larger messages
- * as such might be used for denial of service. It might happen to later
- * builds that such functionality be added as an optional, operator-configurable
- * feature.
- */
- int ret;
- iLenDefBuf = iMaxLine;
- CHKmalloc(deflateBuf = malloc(sizeof(uchar) * (iMaxLine + 1)));
- ret = uncompress((uchar *) deflateBuf, &iLenDefBuf, (uchar *) msg+1, len-1);
- dbgprintf("Compressed message uncompressed with status %d, length: new %ld, old %d.\n",
- ret, (long) iLenDefBuf, len-1);
- /* Now check if the uncompression worked. If not, there is not much we can do. In
- * that case, we log an error message but ignore the message itself. Storing the
- * compressed text is dangerous, as it contains control characters. So we do
- * not do this. If someone would like to have a copy, this code here could be
- * modified to do a hex-dump of the buffer in question. We do not include
- * this functionality right now.
- * rgerhards, 2006-12-07
- */
- if(ret != Z_OK) {
- errmsg.LogError(0, NO_ERRCODE, "Uncompression of a message failed with return code %d "
- "- enable debug logging if you need further information. "
- "Message ignored.", ret);
- FINALIZE; /* unconditional exit, nothing left to do... */
- }
- pData = deflateBuf;
- pEnd = deflateBuf + iLenDefBuf;
- }
-# else /* ifdef USE_NETZIP */
- /* in this case, we still need to check if the message is compressed. If so, we must
- * tell the user we can not accept it.
- */
- if(len > 0 && *msg == 'z') {
- errmsg.LogError(0, NO_ERRCODE, "Received a compressed message, but rsyslogd does not have compression "
- "support enabled. The message will be ignored.");
- FINALIZE;
- }
-# endif /* ifdef USE_NETZIP */
-
- while(pData < pEnd) {
- if(iMsg >= iMaxLine) {
- /* emergency, we now need to flush, no matter if
- * we are at end of message or not...
- */
- if(iMsg == iMaxLine) {
- *(pMsg + iMsg) = '\0'; /* space *is* reserved for this! */
- printline(hname, hnameIP, tmpline, flags, flowCtlType, pszInputName, stTime, ttGenTime);
- } else {
- /* This case in theory never can happen. If it happens, we have
- * a logic error. I am checking for it, because if I would not,
- * we would address memory invalidly with the code above. I
- * do not care much about this case, just a debug log entry
- * (I couldn't do any more smart things anyway...).
- * rgerhards, 2007-9-20
- */
- dbgprintf("internal error: iMsg > max msg size in printchopped()\n");
- }
- FINALIZE; /* in this case, we are done... nothing left we can do */
- }
- if(*pData == '\0') { /* guard against \0 characters... */
- /* changed to the sequence (somewhat) proposed in
- * draft-ietf-syslog-protocol-19. rgerhards, 2006-11-30
- */
- if(iMsg + 3 < iMaxLine) { /* do we have space? */
- *(pMsg + iMsg++) = cCCEscapeChar;
- *(pMsg + iMsg++) = '0';
- *(pMsg + iMsg++) = '0';
- *(pMsg + iMsg++) = '0';
- } /* if we do not have space, we simply ignore the '\0'... */
- /* log an error? Very questionable... rgerhards, 2006-11-30 */
- /* decided: we do not log an error, it won't help... rger, 2007-06-21 */
- ++pData;
- } else if(bEscapeCCOnRcv && iscntrl((int) *pData)) {
- /* we are configured to escape control characters. Please note
- * that this most probably break non-western character sets like
- * Japanese, Korean or Chinese. rgerhards, 2007-07-17
- * Note: sysklogd logs octal values only for DEL and CCs above 127.
- * For others, it logs ^n where n is the control char converted to an
- * alphabet character. We like consistency and thus escape it to octal
- * in all cases. If someone complains, we may change the mode. At least
- * we known now what's going on.
- * rgerhards, 2007-07-17
- */
- if(iMsg + 3 < iMaxLine) { /* do we have space? */
- *(pMsg + iMsg++) = cCCEscapeChar;
- *(pMsg + iMsg++) = '0' + ((*pData & 0300) >> 6);
- *(pMsg + iMsg++) = '0' + ((*pData & 0070) >> 3);
- *(pMsg + iMsg++) = '0' + ((*pData & 0007));
- } /* again, if we do not have space, we ignore the char - see comment at '\0' */
- ++pData;
- } else {
- *(pMsg + iMsg++) = *pData++;
- }
- }
-
- *(pMsg + iMsg) = '\0'; /* space *is* reserved for this! */
-
- /* typically, we should end up here! */
- printline(hname, hnameIP, tmpline, flags, flowCtlType, pszInputName, stTime, ttGenTime);
-
-finalize_it:
- if(tmpline != NULL)
- free(tmpline);
-# ifdef USE_NETZIP
- if(deflateBuf != NULL)
- free(deflateBuf);
-# endif
- RETiRet;
-}
-
-
/* this is a special function used to submit an error message. This
* function is also passed to the runtime library as the generic error
* message handler. -- rgerhards, 2008-04-17
@@ -931,13 +535,7 @@ submitErrMsg(int iErr, uchar *msg)
/* rgerhards 2004-11-09: the following is a function that can be used
- * to log a message orginating from the syslogd itself. In sysklogd code,
- * this is done by simply calling logmsg(). However, logmsg() is changed in
- * rsyslog so that it takes a msg "object". So it can no longer be called
- * directly. This method here solves the need. It provides an interface that
- * allows to construct a locally-generated message. Please note that this
- * function here probably is only an interim solution and that we need to
- * think on the best way to do this.
+ * to log a message orginating from the syslogd itself.
*/
rsRetVal
logmsgInternal(int iErr, int pri, uchar *msg, int flags)
@@ -947,26 +545,26 @@ logmsgInternal(int iErr, int pri, uchar *msg, int flags)
DEFiRet;
CHKiRet(msgConstruct(&pMsg));
- MsgSetInputName(pMsg, UCHAR_CONSTANT("rsyslogd"));
- MsgSetUxTradMsg(pMsg, (char*)msg);
- MsgSetRawMsg(pMsg, (char*)msg);
- MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName());
- MsgSetRcvFrom(pMsg, glbl.GetLocalHostName());
- MsgSetRcvFromIP(pMsg, UCHAR_CONSTANT("127.0.0.1"));
+ MsgSetInputName(pMsg, pInternalInputName);
+ MsgSetRawMsgWOSize(pMsg, (char*)msg);
+ MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName(), ustrlen(glbl.GetLocalHostName()));
+ MsgSetRcvFrom(pMsg, glbl.GetLocalHostNameProp());
+ MsgSetRcvFromIP(pMsg, pLocalHostIP);
+ MsgSetMSGoffs(pMsg, 0);
/* check if we have an error code associated and, if so,
- * adjust the tag. -- r5gerhards, 2008-06-27
+ * adjust the tag. -- rgerhards, 2008-06-27
*/
if(iErr == NO_ERRCODE) {
- MsgSetTAG(pMsg, "rsyslogd:");
+ MsgSetTAG(pMsg, UCHAR_CONSTANT("rsyslogd:"), sizeof("rsyslogd:") - 1);
} else {
- snprintf((char*)pszTag, sizeof(pszTag), "rsyslogd%d:", iErr);
+ size_t len = snprintf((char*)pszTag, sizeof(pszTag), "rsyslogd%d:", iErr);
pszTag[32] = '\0'; /* just to make sure... */
- MsgSetTAG(pMsg, (char*)pszTag);
+ MsgSetTAG(pMsg, pszTag, len);
}
pMsg->iFacility = LOG_FAC(pri);
pMsg->iSeverity = LOG_PRI(pri);
- pMsg->bParseHOSTNAME = 0;
flags |= INTERNAL_MSG;
+ pMsg->msgFlags = flags;
/* we now check if we should print internal messages out to stderr. This was
* suggested by HKS as a way to help people troubleshoot rsyslog configuration
@@ -976,746 +574,185 @@ logmsgInternal(int iErr, int pri, uchar *msg, int flags)
* permits us to process unmodified config files which otherwise contain a
* supressor statement.
*/
- if(bErrMsgToStderr || iConfigVerify) {
- fprintf(stderr, "rsyslogd: %s\n", msg);
+ if(((Debug == DEBUG_FULL || NoFork) && bErrMsgToStderr) || iConfigVerify) {
+ if(LOG_PRI(pri) == LOG_ERR)
+ fprintf(stderr, "rsyslogd: %s\n", msg);
}
if(bHaveMainQueue == 0) { /* not yet in queued mode */
- iminternalAddMsg(pri, pMsg, flags);
+ iminternalAddMsg(pri, pMsg);
} else {
/* we have the queue, so we can simply provide the
* message to the queue engine.
*/
- logmsg(pMsg, flags);
+ submitMsg(pMsg);
}
finalize_it:
RETiRet;
}
-/* This functions looks at the given message and checks if it matches the
- * provided filter condition. If so, it returns true, else it returns
- * false. This is a helper to logmsg() and meant to drive the decision
- * process if a message is to be processed or not. As I expect this
- * decision code to grow more complex over time AND logmsg() is already
- * a very lengthy function, I thought a separate function is more appropriate.
- * 2005-09-19 rgerhards
- * 2008-02-25 rgerhards: changed interface, now utilizes iRet, bProcessMsg
- * returns is message should be procesed.
+/* check message against ACL set
+ * rgerhards, 2009-11-16
*/
-static rsRetVal shouldProcessThisMessage(selector_t *f, msg_t *pMsg, int *bProcessMsg)
-{
- DEFiRet;
- unsigned short pbMustBeFreed;
- char *pszPropVal;
- int bRet = 0;
- vm_t *pVM = NULL;
- var_t *pResult = NULL;
-
- assert(f != NULL);
- assert(pMsg != NULL);
-
- /* we first have a look at the global, BSD-style block filters (for tag
- * and host). Only if they match, we evaluate the actual filter.
- * rgerhards, 2005-10-18
- */
- if(f->eHostnameCmpMode == HN_NO_COMP) {
- /* EMPTY BY INTENSION - we check this value first, because
- * it is the one most often used, so this saves us time!
- */
- } else if(f->eHostnameCmpMode == HN_COMP_MATCH) {
- if(rsCStrSzStrCmp(f->pCSHostnameComp, (uchar*) getHOSTNAME(pMsg), getHOSTNAMELen(pMsg))) {
- /* not equal, so we are already done... */
- dbgprintf("hostname filter '+%s' does not match '%s'\n",
- rsCStrGetSzStrNoNULL(f->pCSHostnameComp), getHOSTNAME(pMsg));
- FINALIZE;
- }
- } else { /* must be -hostname */
- if(!rsCStrSzStrCmp(f->pCSHostnameComp, (uchar*) getHOSTNAME(pMsg), getHOSTNAMELen(pMsg))) {
- /* not equal, so we are already done... */
- dbgprintf("hostname filter '-%s' does not match '%s'\n",
- rsCStrGetSzStrNoNULL(f->pCSHostnameComp), getHOSTNAME(pMsg));
- FINALIZE;
- }
- }
-
- if(f->pCSProgNameComp != NULL) {
- int bInv = 0, bEqv = 0, offset = 0;
- if(*(rsCStrGetSzStrNoNULL(f->pCSProgNameComp)) == '-') {
- if(*(rsCStrGetSzStrNoNULL(f->pCSProgNameComp) + 1) == '-')
- offset = 1;
- else {
- bInv = 1;
- offset = 1;
+#if 0
+static inline rsRetVal
+chkMsgAgainstACL() {
+ /* if we reach this point, we had a good receive and can process the packet received */
+ /* check if we have a different sender than before, if so, we need to query some new values */
+ if(net.CmpHost(&frominet, frominetPrev, socklen) != 0) {
+ CHKiRet(net.cvthname(&frominet, fromHost, fromHostFQDN, fromHostIP));
+ memcpy(frominetPrev, &frominet, socklen); /* update cache indicator */
+ /* Here we check if a host is permitted to send us
+ * syslog messages. If it isn't, we do not further
+ * process the message but log a warning (if we are
+ * configured to do this).
+ * rgerhards, 2005-09-26
+ */
+ *pbIsPermitted = net.isAllowedSender((uchar*)"UDP",
+ (struct sockaddr *)&frominet, (char*)fromHostFQDN);
+
+ if(!*pbIsPermitted) {
+ DBGPRINTF("%s is not an allowed sender\n", (char*)fromHostFQDN);
+ if(glbl.GetOption_DisallowWarning) {
+ time_t tt;
+
+ datetime.GetTime(&tt);
+ if(tt > ttLastDiscard + 60) {
+ ttLastDiscard = tt;
+ errmsg.LogError(0, NO_ERRCODE,
+ "UDP message from disallowed sender %s discarded",
+ (char*)fromHost);
+ }
}
}
- if(!rsCStrOffsetSzStrCmp(f->pCSProgNameComp, offset, (uchar*) getProgramName(pMsg), getProgramNameLen(pMsg)))
- bEqv = 1;
-
- if((!bEqv && !bInv) || (bEqv && bInv)) {
- /* not equal or inverted selection, so we are already done... */
- dbgprintf("programname filter '%s' does not match '%s'\n",
- rsCStrGetSzStrNoNULL(f->pCSProgNameComp), getProgramName(pMsg));
- FINALIZE;
- }
- }
-
- /* done with the BSD-style block filters */
-
- if(f->f_filter_type == FILTER_PRI) {
- /* skip messages that are incorrect priority */
- if ( (f->f_filterData.f_pmask[pMsg->iFacility] == TABLE_NOPRI) || \
- ((f->f_filterData.f_pmask[pMsg->iFacility] & (1<<pMsg->iSeverity)) == 0) )
- bRet = 0;
- else
- bRet = 1;
- } else if(f->f_filter_type == FILTER_EXPR) {
- CHKiRet(vm.Construct(&pVM));
- CHKiRet(vm.ConstructFinalize(pVM));
- CHKiRet(vm.SetMsg(pVM, pMsg));
- CHKiRet(vm.ExecProg(pVM, f->f_filterData.f_expr->pVmprg));
- CHKiRet(vm.PopBoolFromStack(pVM, &pResult));
- dbgprintf("result of expression evaluation: %lld\n", pResult->val.num);
- /* VM is destructed on function exit */
- bRet = (pResult->val.num) ? 1 : 0;
- } else {
- assert(f->f_filter_type == FILTER_PROP); /* assert() just in case... */
- pszPropVal = MsgGetProp(pMsg, NULL, f->f_filterData.prop.pCSPropName, &pbMustBeFreed);
-
- /* Now do the compares (short list currently ;)) */
- switch(f->f_filterData.prop.operation ) {
- case FIOP_CONTAINS:
- if(rsCStrLocateInSzStr(f->f_filterData.prop.pCSCompValue, (uchar*) pszPropVal) != -1)
- bRet = 1;
- break;
- case FIOP_ISEQUAL:
- if(rsCStrSzStrCmp(f->f_filterData.prop.pCSCompValue,
- (uchar*) pszPropVal, strlen(pszPropVal)) == 0)
- bRet = 1; /* process message! */
- break;
- case FIOP_STARTSWITH:
- if(rsCStrSzStrStartsWithCStr(f->f_filterData.prop.pCSCompValue,
- (uchar*) pszPropVal, strlen(pszPropVal)) == 0)
- bRet = 1; /* process message! */
- break;
- case FIOP_REGEX:
- if(rsCStrSzStrMatchRegex(f->f_filterData.prop.pCSCompValue,
- (unsigned char*) pszPropVal, 0, &f->f_filterData.prop.regex_cache) == RS_RET_OK)
- bRet = 1;
- break;
- case FIOP_EREREGEX:
- if(rsCStrSzStrMatchRegex(f->f_filterData.prop.pCSCompValue,
- (unsigned char*) pszPropVal, 1, &f->f_filterData.prop.regex_cache) == RS_RET_OK)
- bRet = 1;
- break;
- default:
- /* here, it handles NOP (for performance reasons) */
- assert(f->f_filterData.prop.operation == FIOP_NOP);
- bRet = 1; /* as good as any other default ;) */
- break;
- }
-
- /* now check if the value must be negated */
- if(f->f_filterData.prop.isNegated)
- bRet = (bRet == 1) ? 0 : 1;
-
- if(Debug) {
- dbgprintf("Filter: check for property '%s' (value '%s') ",
- rsCStrGetSzStrNoNULL(f->f_filterData.prop.pCSPropName),
- pszPropVal);
- if(f->f_filterData.prop.isNegated)
- dbgprintf("NOT ");
- dbgprintf("%s '%s': %s\n",
- getFIOPName(f->f_filterData.prop.operation),
- rsCStrGetSzStrNoNULL(f->f_filterData.prop.pCSCompValue),
- bRet ? "TRUE" : "FALSE");
- }
-
- /* cleanup */
- if(pbMustBeFreed)
- free(pszPropVal);
}
-
-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;
}
+#endif
-/* helper to processMsg(), used to call the configured actions. It is
- * executed from within llExecFunc() of the action list.
- * rgerhards, 2007-08-02
+/* consumes a single messages - this function is primarily used to shuffle
+ * out some code from msgConsumer(). After this function, the message is
+ * (by definition!) considered committed.
+ * rgerhards, 2009-11-16
*/
-typedef struct processMsgDoActions_s {
- int bPrevWasSuspended; /* was the previous action suspended? */
- msg_t *pMsg;
-} processMsgDoActions_t;
-DEFFUNC_llExecFunc(processMsgDoActions)
-{
+static inline rsRetVal
+msgConsumeOne(msg_t *pMsg, prop_t **propFromHost, prop_t **propFromHostIP) {
+ uchar fromHost[NI_MAXHOST];
+ uchar fromHostIP[NI_MAXHOST];
+ uchar fromHostFQDN[NI_MAXHOST];
+ int bIsPermitted;
DEFiRet;
- rsRetVal iRetMod; /* return value of module - we do not always pass that back */
- action_t *pAction = (action_t*) pData;
- processMsgDoActions_t *pDoActData = (processMsgDoActions_t*) pParam;
- assert(pAction != NULL);
-
- if((pAction->bExecWhenPrevSusp == 1) && (pDoActData->bPrevWasSuspended == 0)) {
- dbgprintf("not calling action because the previous one is not suspended\n");
- ABORT_FINALIZE(RS_RET_OK);
+ if((pMsg->msgFlags & NEEDS_ACLCHK_U) != 0) {
+ dbgprintf("msgConsumer: UDP ACL must be checked for message (hostname-based)\n");
+ CHKiRet(net.cvthname(pMsg->rcvFrom.pfrominet, fromHost, fromHostFQDN, fromHostIP));
+ bIsPermitted = net.isAllowedSender2((uchar*)"UDP",
+ (struct sockaddr *)pMsg->rcvFrom.pfrominet, (char*)fromHostFQDN, 1);
+ if(!bIsPermitted) {
+ DBGPRINTF("Message from '%s' discarded, not a permitted sender host\n",
+ fromHostFQDN);
+ ABORT_FINALIZE(RS_RET_ERR);
+ /* save some of the info we obtained */
+ MsgSetRcvFromStr(pMsg, fromHost, ustrlen(fromHost), propFromHost);
+ CHKiRet(MsgSetRcvFromIPStr(pMsg, fromHostIP, ustrlen(fromHostIP), propFromHostIP));
+ pMsg->msgFlags &= ~NEEDS_ACLCHK_U;
+ }
}
- iRetMod = actionCallAction(pAction, pDoActData->pMsg);
- if(iRetMod == RS_RET_DISCARDMSG) {
- ABORT_FINALIZE(RS_RET_DISCARDMSG);
- } else if(iRetMod == RS_RET_SUSPENDED) {
- /* indicate suspension for next module to be called */
- pDoActData->bPrevWasSuspended = 1;
- } else {
- pDoActData->bPrevWasSuspended = 0;
- }
+ if((pMsg->msgFlags & NEEDS_PARSING) != 0)
+ CHKiRet(parser.ParseMsg(pMsg));
+ ruleset.ProcessMsg(pMsg);
finalize_it:
RETiRet;
}
-/* Process (consume) a received message. Calls the actions configured.
- * rgerhards, 2005-10-13
- */
-static void
-processMsg(msg_t *pMsg)
-{
- selector_t *f;
- int bContinue;
- int bProcessMsg;
- processMsgDoActions_t DoActData;
- rsRetVal iRet;
-
- BEGINfunc
- assert(pMsg != NULL);
-
- /* log the message to the particular outputs */
-
- bContinue = 1;
- for (f = Files; f != NULL && bContinue ; f = f->f_next) {
- /* first check the filters... */
- iRet = shouldProcessThisMessage(f, pMsg, &bProcessMsg);
- if(!bProcessMsg) {
- continue;
- }
-
- /* ok -- from here, we have action-specific code, nothing really selector-specific -- rger 2007-08-01 */
- DoActData.pMsg = pMsg;
- DoActData.bPrevWasSuspended = 0;
- if(llExecFunc(&f->llActList, processMsgDoActions, (void*)&DoActData) == RS_RET_DISCARDMSG)
- bContinue = 0;
- }
- ENDfunc
-}
-
-
/* The consumer of dequeued messages. This function is called by the
* queue engine on dequeueing of a message. It runs on a SEPARATE
- * THREAD.
- * Please note: the message object is destructed by the queue itself!
+ * THREAD. It receives an array of pointers, which it must iterate
+ * over. We do not do any further batching, as this is of no benefit
+ * for the main queue.
*/
static rsRetVal
-msgConsumer(void __attribute__((unused)) *notNeeded, void *pUsr)
+msgConsumer(void __attribute__((unused)) *notNeeded, batch_t *pBatch, int *pbShutdownImmediate)
{
+ int i;
+ prop_t *propFromHost = NULL;
+ prop_t *propFromHostIP = NULL;
DEFiRet;
- msg_t *pMsg = (msg_t*) pUsr;
- assert(pMsg != NULL);
+ assert(pBatch != NULL);
- if((pMsg->msgFlags & NEEDS_PARSING) != 0) {
- parseMsg(pMsg);
+ for(i = 0 ; i < pBatch->nElem && !*pbShutdownImmediate ; i++) {
+ DBGPRINTF("msgConsumer processes msg %d/%d\n", i, pBatch->nElem);
+ msgConsumeOne((msg_t*) pBatch->pElem[i].pUsrp, &propFromHost, &propFromHostIP);
+ pBatch->pElem[i].state = BATCH_STATE_COMM;
}
- processMsg(pMsg);
- msgDestruct(&pMsg);
+ if(propFromHost != NULL)
+ prop.Destruct(&propFromHost);
+ if(propFromHostIP != NULL)
+ prop.Destruct(&propFromHostIP);
RETiRet;
}
-/* Helper to parseRFCSyslogMsg. This function parses a field up to
- * (and including) the SP character after it. The field contents is
- * returned in a caller-provided buffer. The parsepointer is advanced
- * to after the terminating SP. The caller must ensure that the
- * provided buffer is large enough to hold the to be extracted value.
- * Returns 0 if everything is fine or 1 if either the field is not
- * SP-terminated or any other error occurs.
- * rger, 2005-11-24
- */
-static int parseRFCField(uchar **pp2parse, uchar *pResult)
-{
- uchar *p2parse;
- int iRet = 0;
-
- assert(pp2parse != NULL);
- assert(*pp2parse != NULL);
- assert(pResult != NULL);
-
- p2parse = *pp2parse;
-
- /* this is the actual parsing loop */
- while(*p2parse && *p2parse != ' ') {
- *pResult++ = *p2parse++;
- }
-
- if(*p2parse == ' ')
- ++p2parse; /* eat SP, but only if not at end of string */
- else
- iRet = 1; /* there MUST be an SP! */
- *pResult = '\0';
-
- /* set the new parse pointer */
- *pp2parse = p2parse;
- return 0;
-}
-
-
-/* Helper to parseRFCSyslogMsg. This function parses the structured
- * data field of a message. It does NOT parse inside structured data,
- * just gets the field as whole. Parsing the single entities is left
- * to other functions. The parsepointer is advanced
- * to after the terminating SP. The caller must ensure that the
- * provided buffer is large enough to hold the to be extracted value.
- * Returns 0 if everything is fine or 1 if either the field is not
- * SP-terminated or any other error occurs.
- * rger, 2005-11-24
- */
-static int parseRFCStructuredData(uchar **pp2parse, uchar *pResult)
-{
- uchar *p2parse;
- int bCont = 1;
- int iRet = 0;
-
- assert(pp2parse != NULL);
- assert(*pp2parse != NULL);
- assert(pResult != NULL);
-
- p2parse = *pp2parse;
-
- /* this is the actual parsing loop
- * Remeber: structured data starts with [ and includes any characters
- * until the first ] followed by a SP. There may be spaces inside
- * structured data. There may also be \] inside the structured data, which
- * do NOT terminate an element.
- */
- if(*p2parse != '[')
- return 1; /* this is NOT structured data! */
-
- while(bCont) {
- if(*p2parse == '\0') {
- iRet = 1; /* this is not valid! */
- bCont = 0;
- } else if(*p2parse == '\\' && *(p2parse+1) == ']') {
- /* this is escaped, need to copy both */
- *pResult++ = *p2parse++;
- *pResult++ = *p2parse++;
- } else if(*p2parse == ']' && *(p2parse+1) == ' ') {
- /* found end, just need to copy the ] and eat the SP */
- *pResult++ = *p2parse;
- p2parse += 2;
- bCont = 0;
- } else {
- *pResult++ = *p2parse++;
- }
- }
-
- if(*p2parse == ' ')
- ++p2parse; /* eat SP, but only if not at end of string */
- else
- iRet = 1; /* there MUST be an SP! */
- *pResult = '\0';
-
- /* set the new parse pointer */
- *pp2parse = p2parse;
- return 0;
-}
-
-/* parse a RFC-formatted syslog message. This function returns
- * 0 if processing of the message shall continue and 1 if something
- * went wrong and this messe should be ignored. This function has been
- * implemented in the effort to support syslog-protocol. Please note that
- * the name (parse *RFC*) stems from the hope that syslog-protocol will
- * some time become an RFC. Do not confuse this with informational
- * RFC 3164 (which is legacy syslog).
- *
- * currently supported format:
- *
- * <PRI>VERSION SP TIMESTAMP SP HOSTNAME SP APP-NAME SP PROCID SP MSGID SP [SD-ID]s SP MSG
- *
- * <PRI> is already stripped when this function is entered. VERSION already
- * has been confirmed to be "1", but has NOT been stripped from the message.
- *
- * rger, 2005-11-24
- */
-int parseRFCSyslogMsg(msg_t *pMsg, int flags)
-{
- uchar *p2parse;
- uchar *pBuf;
- int bContParse = 1;
-
- BEGINfunc
- assert(pMsg != NULL);
- assert(pMsg->pszUxTradMsg != NULL);
- p2parse = pMsg->pszUxTradMsg;
-
- /* do a sanity check on the version and eat it */
- assert(p2parse[0] == '1' && p2parse[1] == ' ');
- p2parse += 2;
-
- /* Now get us some memory we can use as a work buffer while parsing.
- * We simply allocated a buffer sufficiently large to hold all of the
- * message, so we can not run into any troubles. I think this is
- * more wise then to use individual buffers.
- */
- if((pBuf = malloc(sizeof(uchar) * ustrlen(p2parse) + 1)) == NULL)
- return 1;
-
- /* IMPORTANT NOTE:
- * Validation is not actually done below nor are any errors handled. I have
- * NOT included this for the current proof of concept. However, it is strongly
- * advisable to add it when this code actually goes into production.
- * rgerhards, 2005-11-24
- */
-
- /* TIMESTAMP */
- if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse) == RS_RET_OK) {
- if(flags & IGNDATE) {
- /* we need to ignore the msg data, so simply copy over reception date */
- memcpy(&pMsg->tTIMESTAMP, &pMsg->tRcvdAt, sizeof(struct syslogTime));
- }
- } else {
- dbgprintf("no TIMESTAMP detected!\n");
- bContParse = 0;
- }
-
- /* HOSTNAME */
- if(bContParse) {
- parseRFCField(&p2parse, pBuf);
- MsgSetHOSTNAME(pMsg, pBuf);
- } else {
- /* we can not parse, so we get the system we
- * received the data from.
- */
- MsgSetHOSTNAME(pMsg, getRcvFrom(pMsg));
- }
-
- /* APP-NAME */
- if(bContParse) {
- parseRFCField(&p2parse, pBuf);
- MsgSetAPPNAME(pMsg, (char*)pBuf);
- }
-
- /* PROCID */
- if(bContParse) {
- parseRFCField(&p2parse, pBuf);
- MsgSetPROCID(pMsg, (char*)pBuf);
- }
-
- /* MSGID */
- if(bContParse) {
- parseRFCField(&p2parse, pBuf);
- MsgSetMSGID(pMsg, (char*)pBuf);
- }
-
- /* STRUCTURED-DATA */
- if(bContParse) {
- parseRFCStructuredData(&p2parse, pBuf);
- MsgSetStructuredData(pMsg, (char*)pBuf);
- }
-
- /* MSG */
- MsgSetMSG(pMsg, (char*)p2parse);
-
- free(pBuf);
- ENDfunc
- return 0; /* all ok */
-}
-
-
-/* parse a legay-formatted syslog message. This function returns
- * 0 if processing of the message shall continue and 1 if something
- * went wrong and this messe should be ignored. This function has been
- * implemented in the effort to support syslog-protocol.
- * rger, 2005-11-24
- * As of 2006-01-10, I am removing the logic to continue parsing only
- * when a valid TIMESTAMP is detected. Validity of other fields already
- * is ignored. This is due to the fact that the parser has grown smarter
- * and is now more able to understand different dialects of the syslog
- * message format. I do not expect any bad side effects of this change,
- * but I thought I log it in this comment.
- * rgerhards, 2006-01-10
- */
-int parseLegacySyslogMsg(msg_t *pMsg, int flags)
-{
- uchar *p2parse;
- char *pBuf;
- char *pWork;
- cstr_t *pStrB;
- int iCnt;
- int bTAGCharDetected;
- BEGINfunc
-
- assert(pMsg != NULL);
- assert(pMsg->pszUxTradMsg != NULL);
- p2parse = pMsg->pszUxTradMsg;
-
- /* Check to see if msg contains a timestamp. We start by assuming
- * that the message timestamp is the time of reciption (which we
- * generated ourselfs and then try to actually find one inside the
- * message. There we go from high-to low precison and are done
- * when we find a matching one. -- rgerhards, 2008-09-16
- */
- if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse) == RS_RET_OK) {
- /* we are done - parse pointer is moved by ParseTIMESTAMP3339 */;
- } else if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &p2parse) == RS_RET_OK) {
- /* we are done - parse pointer is moved by ParseTIMESTAMP3164 */;
- } else if(*p2parse == ' ') { /* try to see if it is slighly malformed - HP procurve seems to do that sometimes */
- ++p2parse; /* move over space */
- if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &p2parse) == RS_RET_OK) {
- /* indeed, we got it! */
- /* we are done - parse pointer is moved by ParseTIMESTAMP3164 */;
- } else {
- /* parse pointer needs to be restored, as we moved it off-by-one
- * for this try.
- */
- --p2parse;
- }
- }
-
- if(flags & IGNDATE) {
- /* we need to ignore the msg data, so simply copy over reception date */
- memcpy(&pMsg->tTIMESTAMP, &pMsg->tRcvdAt, sizeof(struct syslogTime));
- }
-
- /* rgerhards, 2006-03-13: next, we parse the hostname and tag. But we
- * do this only when the user has not forbidden this. I now introduce some
- * code that allows a user to configure rsyslogd to treat the rest of the
- * message as MSG part completely. In this case, the hostname will be the
- * machine that we received the message from and the tag will be empty. This
- * is meant to be an interim solution, but for now it is in the code.
- */
- if(bParseHOSTNAMEandTAG && !(flags & INTERNAL_MSG)) {
- /* parse HOSTNAME - but only if this is network-received!
- * rger, 2005-11-14: we still have a problem with BSD messages. These messages
- * do NOT include a host name. In most cases, this leads to the TAG to be treated
- * as hostname and the first word of the message as the TAG. Clearly, this is not
- * of advantage ;) I think I have now found a way to handle this situation: there
- * are certain characters which are frequently used in TAG (e.g. ':'), which are
- * *invalid* in host names. So while parsing the hostname, I check for these characters.
- * If I find them, I set a simple flag but continue. After parsing, I check the flag.
- * If it was set, then we most probably do not have a hostname but a TAG. Thus, I change
- * the fields. I think this logic shall work with any type of syslog message.
- */
- bTAGCharDetected = 0;
- if(flags & PARSE_HOSTNAME) {
- /* TODO: quick and dirty memory allocation */
- /* the memory allocated is far too much in most cases. But on the plus side,
- * it is quite fast... - rgerhards, 2007-09-20
- */
- if((pBuf = malloc(sizeof(char)* (ustrlen(p2parse) +1))) == NULL)
- return 1;
- pWork = pBuf;
- /* this is the actual parsing loop */
- while(*p2parse && *p2parse != ' ' && *p2parse != ':') {
- if(*p2parse == '[' || *p2parse == ']' || *p2parse == '/')
- bTAGCharDetected = 1;
- *pWork++ = *p2parse++;
- }
- /* we need to handle ':' seperately, because it terminates the
- * TAG - so we also need to terminate the parser here!
- * rgerhards, 2007-09-10 *p2parse points to a valid address here in
- * any case. We can reach this point only if we are at end of string,
- * or we have a ':' or ' '. What the if below does is check if we are
- * not at end of string and, if so, advance the parse pointer. If we
- * are already at end of string, *p2parse is equal to '\0', neither if
- * will be true and the parse pointer remain as is. This is perfectly
- * well.
- */
- if(*p2parse == ':') {
- bTAGCharDetected = 1;
- /* We will move hostname to tag, so preserve ':' (otherwise we
- * will needlessly change the message format) */
- *pWork++ = *p2parse++;
- } else if(*p2parse == ' ')
- ++p2parse;
- *pWork = '\0';
- MsgAssignHOSTNAME(pMsg, pBuf);
- }
- /* check if we seem to have a TAG */
- if(bTAGCharDetected) {
- /* indeed, this smells like a TAG, so lets use it for this. We take
- * the HOSTNAME from the sender system instead.
- */
- dbgprintf("HOSTNAME contains invalid characters, assuming it to be a TAG.\n");
- moveHOSTNAMEtoTAG(pMsg);
- MsgSetHOSTNAME(pMsg, getRcvFrom(pMsg));
- }
-
- /* now parse TAG - that should be present in message from all sources.
- * This code is somewhat not compliant with RFC 3164. As of 3164,
- * the TAG field is ended by any non-alphanumeric character. In
- * practice, however, the TAG often contains dashes and other things,
- * which would end the TAG. So it is not desirable. As such, we only
- * accept colon and SP to be terminators. Even there is a slight difference:
- * a colon is PART of the TAG, while a SP is NOT part of the tag
- * (it is CONTENT). Starting 2008-04-04, we have removed the 32 character
- * size limit (from RFC3164) on the tag. This had bad effects on existing
- * envrionments, as sysklogd didn't obey it either (probably another bug
- * in RFC3164...). We now receive the full size, but will modify the
- * outputs so that only 32 characters max are used by default.
- */
- /* The following code in general is quick & dirty - I need to get
- * it going for a test, rgerhards 2004-11-16 */
- /* lol.. we tried to solve it, just to remind ourselfs that 32 octets
- * is the max size ;) we need to shuffle the code again... Just for
- * the records: the code is currently clean, but we could optimize it! */
- if(!bTAGCharDetected) {
- uchar *pszTAG;
- if(rsCStrConstruct(&pStrB) != RS_RET_OK)
- return 1;
- rsCStrSetAllocIncrement(pStrB, 33);
- pWork = pBuf;
- iCnt = 0;
- while(*p2parse && *p2parse != ':' && *p2parse != ' ') {
- rsCStrAppendChar(pStrB, *p2parse++);
- ++iCnt;
- }
- if(*p2parse == ':') {
- ++p2parse;
- rsCStrAppendChar(pStrB, ':');
- }
- rsCStrFinish(pStrB);
-
- rsCStrConvSzStrAndDestruct(pStrB, &pszTAG, 1);
- if(pszTAG == NULL)
- { /* rger, 2005-11-10: no TAG found - this implies that what
- * we have considered to be the HOSTNAME is most probably the
- * TAG. We consider it so probable, that we now adjust it
- * that way. So we pick up the previously set hostname, assign
- * it to tag and use the sender system (from IP stack) as
- * the hostname. This situation is the standard case with
- * stock BSD syslogd.
- */
- dbgprintf("No TAG in message, assuming that HOSTNAME is missing.\n");
- moveHOSTNAMEtoTAG(pMsg);
- MsgSetHOSTNAME(pMsg, getRcvFrom(pMsg));
- } else { /* we have a TAG, so we can happily set it ;) */
- MsgAssignTAG(pMsg, pszTAG);
- }
- } else {
- /* we have no TAG, so we ... */
- /*DO NOTHING*/;
- }
- } else {
- /* we enter this code area when the user has instructed rsyslog NOT
- * to parse HOSTNAME and TAG - rgerhards, 2006-03-13
- */
- if(!(flags & INTERNAL_MSG))
- {
- dbgprintf("HOSTNAME and TAG not parsed by user configuraton.\n");
- MsgSetHOSTNAME(pMsg, getRcvFrom(pMsg));
- }
- }
-
- /* The rest is the actual MSG */
- MsgSetMSG(pMsg, (char*)p2parse);
-
- ENDfunc
- return 0; /* all ok */
-}
-
-
-/* submit a fully created message to the main message queue. The message is
- * fully processed and parsed, so no parsing at all happens. This is primarily
+/* submit a message to the main message queue. This is primarily
* a hook to prevent the need for callers to know about the main message queue
- * (which may change in the future as we will probably have multiple rule
- * sets and thus queues...).
* rgerhards, 2008-02-13
*/
rsRetVal
submitMsg(msg_t *pMsg)
{
+ qqueue_t *pQueue;
+ ruleset_t *pRuleset;
DEFiRet;
ISOBJ_TYPE_assert(pMsg, msg);
+ pRuleset = MsgGetRuleset(pMsg);
+
+ pQueue = (pRuleset == NULL) ? pMsgQueue : ruleset.GetRulesetQueue(pRuleset);
MsgPrepareEnqueue(pMsg);
- qqueueEnqObj(pMsgQueue, pMsg->flowCtlType, (void*) pMsg);
+ qqueueEnqObj(pQueue, pMsg->flowCtlType, (void*) pMsg);
RETiRet;
}
-/* Log a message to the appropriate log files, users, etc. based on
- * the priority.
- * rgerhards 2004-11-08: actually, this also decodes all but the PRI part.
- * rgerhards 2004-11-09: ... but only, if syslogd could properly be initialized
- * if not, we use emergency logging to the console and in
- * this case, no further decoding happens.
- * changed to no longer receive a plain message but a msg object instead.
- * rgerhards-2004-11-16: OK, we are now up to another change... This method
- * actually needs to PARSE the message. How exactly this needs to happen depends on
- * a number of things. Most importantly, it depends on the source. For example,
- * locally received messages (SOURCE_UNIXAF) do NOT have a hostname in them. So
- * we need to treat them differntly form network-received messages which have.
- * Well, actually not all network-received message really have a hostname. We
- * can just hope they do, but we can not be sure. So this method tries to find
- * whatever can be found in the message and uses that... Obviously, there is some
- * potential for misinterpretation, which we simply can not solve under the
- * circumstances given.
+/* submit multiple messages at once, very similar to submitMsg, just
+ * for multi_submit_t. All messages need to go into the SAME queue!
+ * rgerhards, 2009-06-16
*/
-void
-logmsg(msg_t *pMsg, int flags)
+rsRetVal
+multiSubmitMsg(multi_submit_t *pMultiSub)
{
- char *msg;
+ int i;
+ qqueue_t *pQueue;
+ ruleset_t *pRuleset;
+ DEFiRet;
+ assert(pMultiSub != NULL);
- BEGINfunc
- assert(pMsg != NULL);
- assert(pMsg->pszUxTradMsg != NULL);
- msg = (char*) pMsg->pszUxTradMsg;
- dbgprintf("logmsg: flags %x, from '%s', msg %s\n", flags, getRcvFrom(pMsg), msg);
-
- /* rger 2005-11-24 (happy thanksgiving!): we now need to check if we have
- * a traditional syslog message or one formatted according to syslog-protocol.
- * We need to apply different parsers depending on that. We use the
- * -protocol VERSION field for the detection.
- */
- if(msg[0] == '1' && msg[1] == ' ') {
- dbgprintf("Message has syslog-protocol format.\n");
- setProtocolVersion(pMsg, 1);
- if(parseRFCSyslogMsg(pMsg, flags) == 1) {
- msgDestruct(&pMsg);
- return;
- }
- } else { /* we have legacy syslog */
- dbgprintf("Message has legacy syslog format.\n");
- setProtocolVersion(pMsg, 0);
- if(parseLegacySyslogMsg(pMsg, flags) == 1) {
- msgDestruct(&pMsg);
- return;
- }
+ if(pMultiSub->nElem == 0)
+ FINALIZE;
+
+ for(i = 0 ; i < pMultiSub->nElem ; ++i) {
+ MsgPrepareEnqueue(pMultiSub->ppMsgs[i]);
}
- /* ---------------------- END PARSING ---------------- */
-
- /* now submit the message to the main queue - then we are done */
- pMsg->msgFlags = flags;
- MsgPrepareEnqueue(pMsg);
- qqueueEnqObj(pMsgQueue, pMsg->flowCtlType, (void*) pMsg);
- ENDfunc
+ pRuleset = MsgGetRuleset(pMultiSub->ppMsgs[0]);
+ pQueue = (pRuleset == NULL) ? pMsgQueue : ruleset.GetRulesetQueue(pRuleset);
+ iRet = qqueueMultiEnqObj(pQueue, pMultiSub);
+ pMultiSub->nElem = 0;
+
+finalize_it:
+ RETiRet;
}
+
+
static void
reapchild()
{
@@ -1738,7 +775,6 @@ reapchild()
DEFFUNC_llExecFunc(flushRptdMsgsActions)
{
action_t *pAction = (action_t*) pData;
-
assert(pAction != NULL);
BEGINfunc
@@ -1747,8 +783,8 @@ DEFFUNC_llExecFunc(flushRptdMsgsActions)
* the beginn of the llExec(). This makes it slightly less correct, but
* in an acceptable way. -- rgerhards, 2008-09-16
*/
- if (pAction->f_prevcount && time(NULL) >= REPEATTIME(pAction)) {
- dbgprintf("flush %s: repeated %d times, %d sec.\n",
+ if (pAction->f_prevcount && datetime.GetTime(NULL) >= REPEATTIME(pAction)) {
+ DBGPRINTF("flush %s: repeated %d times, %d sec.\n",
module.GetStateName(pAction->pMod), pAction->f_prevcount,
repeatinterval[pAction->f_repeatcount]);
actionWriteToAction(pAction);
@@ -1761,32 +797,38 @@ DEFFUNC_llExecFunc(flushRptdMsgsActions)
}
-/* This method flushes reapeat messages.
+/* This method flushes repeat messages.
*/
static void
doFlushRptdMsgs(void)
{
- register selector_t *f;
-
- /* see if we need to flush any "message repeated n times"...
- * Note that this interferes with objects running on other threads.
- * We are using appropriate locking inside the function to handle that.
- */
- for (f = Files; f != NULL ; f = f->f_next) {
- llExecFunc(&f->llActList, flushRptdMsgsActions, NULL);
- }
+ ruleset.IterateAllActions(flushRptdMsgsActions, NULL);
}
static void debug_switch()
{
+ time_t tTime;
+ struct tm tp;
struct sigaction sigAct;
+ datetime.GetTime(&tTime);
+ localtime_r(&tTime, &tp);
if(debugging_on == 0) {
debugging_on = 1;
- dbgprintf("Switching debugging_on to true\n");
+ dbgprintf("\n");
+ dbgprintf("\n");
+ dbgprintf("********************************************************************************\n");
+ dbgprintf("Switching debugging_on to true at %2.2d:%2.2d:%2.2d\n",
+ tp.tm_hour, tp.tm_min, tp.tm_sec);
+ dbgprintf("********************************************************************************\n");
} else {
- dbgprintf("Switching debugging_on to false\n");
+ dbgprintf("********************************************************************************\n");
+ dbgprintf("Switching debugging_on to false at %2.2d:%2.2d:%2.2d\n",
+ tp.tm_hour, tp.tm_min, tp.tm_sec);
+ dbgprintf("********************************************************************************\n");
+ dbgprintf("\n");
+ dbgprintf("\n");
debugging_on = 0;
}
@@ -1801,7 +843,7 @@ void legacyOptsEnq(uchar *line)
{
legacyOptsLL_t *pNew;
- pNew = malloc(sizeof(legacyOptsLL_t));
+ pNew = MALLOC(sizeof(legacyOptsLL_t));
if(line == NULL)
pNew->line = NULL;
else
@@ -1930,7 +972,7 @@ void legacyOptsParseTCP(char ch, char *arg)
* a minimal delay, but it is much cleaner than the approach of doing everything
* inside the signal handler.
* rgerhards, 2005-10-26
- * Note: we do not call dbgprintf() as this may cause us to block in case something
+ * Note: we do not call DBGPRINTF() as this may cause us to block in case something
* with the threading is wrong.
*/
static void doDie(int sig)
@@ -1938,10 +980,12 @@ static void doDie(int sig)
# define MSG1 "DoDie called.\n"
# define MSG2 "DoDie called 5 times - unconditional exit\n"
static int iRetries = 0; /* debug aid */
+ dbgprintf(MSG1);
if(Debug)
+ if(Debug == DEBUG_FULL)
write(1, MSG1, sizeof(MSG1) - 1);
if(iRetries++ == 4) {
- if(Debug)
+ if(Debug == DEBUG_FULL)
write(1, MSG2, sizeof(MSG2) - 1);
abort();
}
@@ -1965,6 +1009,16 @@ freeAllDynMemForTermination(void)
}
+/* Finalize and destruct all actions.
+ */
+static inline void
+destructAllActions(void)
+{
+ ruleset.DestructAllActions();
+ bHaveMainQueue = 0; // flag that internal messages need to be temporarily stored
+}
+
+
/* die() is called when the program shall end. This typically only occurs
* during sigterm or during the initialization.
* As die() is intended to shutdown rsyslogd, it is
@@ -1978,7 +1032,7 @@ die(int sig)
{
char buf[256];
- dbgprintf("exiting on signal %d\n", sig);
+ DBGPRINTF("exiting on signal %d\n", sig);
/* IMPORTANT: we should close the inputs first, and THEN send our termination
* message. If we do it the other way around, logmsgInternal() may block on
@@ -1993,11 +1047,12 @@ die(int sig)
*/
/* close the inputs */
- dbgprintf("Terminating input threads...\n");
+ DBGPRINTF("Terminating input threads...\n");
+ glbl.SetGlobalInputTermination();
thrdTerminateAll();
/* and THEN send the termination log message (see long comment above) */
- if (sig) {
+ if(sig && bLogStatusMsgs) {
(void) snprintf(buf, sizeof(buf) / sizeof(char),
" [origin software=\"rsyslogd\" " "swVersion=\"" VERSION \
"\" x-pid=\"%d\" x-info=\"http://www.rsyslog.com\"]" " exiting on signal %d.",
@@ -2007,17 +1062,17 @@ die(int sig)
}
/* drain queue (if configured so) and stop main queue worker thread pool */
- dbgprintf("Terminating main queue...\n");
+ DBGPRINTF("Terminating main queue...\n");
qqueueDestruct(&pMsgQueue);
pMsgQueue = NULL;
/* Free ressources and close connections. This includes flushing any remaining
* repeated msgs.
*/
- dbgprintf("Terminating outputs...\n");
- freeSelectors();
+ DBGPRINTF("Terminating outputs...\n");
+ destructAllActions();
- dbgprintf("all primary multi-thread sources have been terminated - now doing aux cleanup...\n");
+ DBGPRINTF("all primary multi-thread sources have been terminated - now doing aux cleanup...\n");
/* rger 2005-02-22
* now clean up the in-memory structures. OK, the OS
* would also take care of that, but if we do it
@@ -2026,8 +1081,6 @@ die(int sig)
*/
tplDeleteAll();
- remove_pid(PidFile);
-
/* de-init some modules */
modExitIminternal();
@@ -2040,22 +1093,18 @@ die(int sig)
legacyOptsFree();
+ /* destruct our global properties */
+ if(pInternalInputName != NULL)
+ prop.Destruct(&pInternalInputName);
+ if(pLocalHostIP != NULL)
+ prop.Destruct(&pLocalHostIP);
+
/* terminate the remaining classes */
GlobalClassExit();
- /* 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
- * I have added some code now, but all that mod init/de-init should be moved to
- * init, so that modules are unloaded and reloaded on HUP to. Eventually it should go
- * into freeSelectors() - but that needs to be seen. -- rgerhards, 2007-08-09
- */
module.UnloadAndDestructAll(eMOD_LINK_ALL);
- dbgprintf("Clean shutdown completed, bye\n");
+ DBGPRINTF("Clean shutdown completed, bye\n");
/* dbgClassExit MUST be the last one, because it de-inits the debug system */
dbgClassExit();
@@ -2065,6 +1114,9 @@ die(int sig)
*/
freeAllDynMemForTermination();
/* NO CODE HERE - feeelAllDynMemForTermination() must be the last thing before exit()! */
+
+ remove_pid(PidFile);
+
exit(0); /* "good" exit, this is the terminator function for rsyslog [die()] */
}
@@ -2080,7 +1132,7 @@ static void doexit()
/* set the maximum message size */
-static rsRetVal setMaxMsgSize(void __attribute__((unused)) *pVal, int iNewVal)
+static rsRetVal setMaxMsgSize(void __attribute__((unused)) *pVal, long iNewVal)
{
return glbl.SetMaxLine(iNewVal);
}
@@ -2112,7 +1164,10 @@ static rsRetVal setMaxFiles(void __attribute__((unused)) *pVal, int iFiles)
iFiles, errStr, (long) maxFiles.rlim_max);
ABORT_FINALIZE(RS_RET_ERR_RLIM_NOFILE);
}
- dbgprintf("Max number of files set to %d [kernel max %ld].\n", iFiles, (long) maxFiles.rlim_max);
+#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;
@@ -2123,7 +1178,7 @@ finalize_it:
static rsRetVal setUmask(void __attribute__((unused)) *pVal, int iUmask)
{
umask(iUmask);
- dbgprintf("umask set to 0%3.3o.\n", iUmask);
+ DBGPRINTF("umask set to 0%3.3o.\n", iUmask);
return RS_RET_OK;
}
@@ -2179,56 +1234,6 @@ static void doDropPrivUid(int iUid)
}
-/* helper to freeSelectors(), used with llExecFunc() to flush
- * pending output. -- rgerhards, 2007-08-02
- * We do not need to lock the action object here as the processing
- * queue is already empty and no other threads are running when
- * we call this function. -- rgerhards, 2007-12-12
- */
-DEFFUNC_llExecFunc(freeSelectorsActions)
-{
- action_t *pAction = (action_t*) pData;
-
- assert(pAction != NULL);
-
- /* flush any pending output */
- if(pAction->f_prevcount) {
- actionWriteToAction(pAction);
- }
-
- return RS_RET_OK; /* never fails ;) */
-}
-
-
-/* Close all open log files and free selector descriptor array.
- */
-static void freeSelectors(void)
-{
- selector_t *f;
- selector_t *fPrev;
-
- if(Files != NULL) {
- dbgprintf("Freeing log structures.\n");
-
- for(f = Files ; f != NULL ; f = f->f_next) {
- llExecFunc(&f->llActList, freeSelectorsActions, NULL);
- }
-
- /* actions flushed and ready for destruction - so do that... */
- f = Files;
- while (f != NULL) {
- fPrev = f;
- f = f->f_next;
- selectorDestruct(fPrev);
- }
-
- /* Reflect the deletion of the selectors linked list. */
- Files = NULL;
- bHaveMainQueue = 0;
- }
-}
-
-
/* helper to generateConfigDAG, to print out all actions via
* the llExecFunc() facility.
* rgerhards, 2007-08-02
@@ -2305,14 +1310,14 @@ DEFFUNC_llExecFunc(generateConfigDAGAction)
static rsRetVal
generateConfigDAG(uchar *pszDAGFile)
{
- selector_t *f;
+ //rule_t *f;
FILE *fp;
int iActUnit = 1;
- int bHasFilter = 0; /* filter associated with this action unit? */
- int bHadFilter;
- int i;
+ //int bHasFilter = 0; /* filter associated with this action unit? */
+ //int bHadFilter;
+ //int i;
struct dag_info dagInfo;
- char *pszFilterName;
+ //char *pszFilterName;
char szConnectingNode[64];
DEFiRet;
@@ -2341,6 +1346,8 @@ generateConfigDAG(uchar *pszDAGFile)
strcpy(szConnectingNode, "act0_0");
dagInfo.bDiscarded = 0;
+/* TODO: re-enable! */
+#if 0
for(f = Files; f != NULL ; f = f->f_next) {
/* BSD-Style filters are currently ignored */
bHadFilter = bHasFilter;
@@ -2396,6 +1403,7 @@ generateConfigDAG(uchar *pszDAGFile)
++iActUnit;
}
+#endif
fprintf(fp, "\t%s -> act%d_0\n", szConnectingNode, iActUnit);
fprintf(fp, "\tact%d_0\t\t[label=discard shape=box]\n"
@@ -2407,20 +1415,6 @@ finalize_it:
}
-/* helper to dbPrintInitInfo, to print out all actions via
- * the llExecFunc() facility.
- * rgerhards, 2007-08-02
- */
-DEFFUNC_llExecFunc(dbgPrintInitInfoAction)
-{
- DEFiRet;
- iRet = actionDbgPrint((action_t*) pData);
- dbgprintf("\n");
-
- RETiRet;
-}
-
-
/* print debug information as part of init(). This pretty much
* outputs the whole config of rsyslogd. I've moved this code
* out of init() to clean it somewhat up.
@@ -2428,47 +1422,8 @@ DEFFUNC_llExecFunc(dbgPrintInitInfoAction)
*/
static void dbgPrintInitInfo(void)
{
- selector_t *f;
- int iSelNbr = 1;
- int i;
-
- dbgprintf("\nActive selectors:\n");
- for (f = Files; f != NULL ; f = f->f_next) {
- dbgprintf("Selector %d:\n", iSelNbr++);
- if(f->pCSProgNameComp != NULL)
- dbgprintf("tag: '%s'\n", rsCStrGetSzStrNoNULL(f->pCSProgNameComp));
- if(f->eHostnameCmpMode != HN_NO_COMP)
- dbgprintf("hostname: %s '%s'\n",
- f->eHostnameCmpMode == HN_COMP_MATCH ?
- "only" : "allbut",
- rsCStrGetSzStrNoNULL(f->pCSHostnameComp));
- if(f->f_filter_type == FILTER_PRI) {
- for (i = 0; i <= LOG_NFACILITIES; i++)
- if (f->f_filterData.f_pmask[i] == TABLE_NOPRI)
- dbgprintf(" X ");
- else
- dbgprintf("%2X ", f->f_filterData.f_pmask[i]);
- } else if(f->f_filter_type == FILTER_EXPR) {
- dbgprintf("EXPRESSION-BASED Filter: can currently not be displayed");
- } else {
- dbgprintf("PROPERTY-BASED Filter:\n");
- dbgprintf("\tProperty.: '%s'\n",
- rsCStrGetSzStrNoNULL(f->f_filterData.prop.pCSPropName));
- dbgprintf("\tOperation: ");
- if(f->f_filterData.prop.isNegated)
- dbgprintf("NOT ");
- dbgprintf("'%s'\n", getFIOPName(f->f_filterData.prop.operation));
- dbgprintf("\tValue....: '%s'\n",
- rsCStrGetSzStrNoNULL(f->f_filterData.prop.pCSCompValue));
- dbgprintf("\tAction...: ");
- }
-
- dbgprintf("\nActions:\n");
- llExecFunc(&f->llActList, dbgPrintInitInfoAction, NULL); /* actions */
-
- dbgprintf("\n");
- }
- dbgprintf("\n");
+ ruleset.DebugPrintAll();
+ DBGPRINTF("\n");
if(bDebugPrintTemplateList)
tplPrintList();
if(bDebugPrintModuleList)
@@ -2478,24 +1433,17 @@ static void dbgPrintInitInfo(void)
if(bDebugPrintCfSysLineHandlerList)
dbgPrintCfSysLineHandlers();
- dbgprintf("Messages with malicious PTR DNS Records are %sdropped.\n",
+ DBGPRINTF("Messages with malicious PTR DNS Records are %sdropped.\n",
glbl.GetDropMalPTRMsgs() ? "" : "not ");
- dbgprintf("Control characters are %sreplaced upon reception.\n",
- bEscapeCCOnRcv? "" : "not ");
-
- if(bEscapeCCOnRcv)
- dbgprintf("Control character escape sequence prefix is '%c'.\n",
- cCCEscapeChar);
-
- dbgprintf("Main queue size %d messages.\n", iMainMsgQueueSize);
- dbgprintf("Main queue worker threads: %d, wThread shutdown: %d, Perists every %d updates.\n",
+ DBGPRINTF("Main queue size %d messages.\n", iMainMsgQueueSize);
+ DBGPRINTF("Main queue worker threads: %d, wThread shutdown: %d, Perists every %d updates.\n",
iMainMsgQueueNumWorkers, iMainMsgQtoWrkShutdown, iMainMsgQPersistUpdCnt);
- dbgprintf("Main queue timeouts: shutdown: %d, action completion shutdown: %d, enq: %d\n",
+ DBGPRINTF("Main queue timeouts: shutdown: %d, action completion shutdown: %d, enq: %d\n",
iMainMsgQtoQShutdown, iMainMsgQtoActShutdown, iMainMsgQtoEnq);
- dbgprintf("Main queue watermarks: high: %d, low: %d, discard: %d, discard-severity: %d\n",
+ DBGPRINTF("Main queue watermarks: high: %d, low: %d, discard: %d, discard-severity: %d\n",
iMainMsgQHighWtrMark, iMainMsgQLowWtrMark, iMainMsgQDiscardMark, iMainMsgQDiscardSeverity);
- dbgprintf("Main queue save on shutdown %d, max disk space allowed %lld\n",
+ DBGPRINTF("Main queue save on shutdown %d, max disk space allowed %lld\n",
bMainMsgQSaveOnShutdown, iMainMsgQueMaxDiskSpace);
/* TODO: add
iActionRetryCount = 0;
@@ -2506,7 +1454,34 @@ static void dbgPrintInitInfo(void)
setQPROP(qqueueSetiMinMsgsPerWrkr, "$MainMsgQueueWorkerThreadMinimumMessages", 100);
setQPROP(qqueueSetbSaveOnShutdown, "$MainMsgQueueSaveOnShutdown", 1);
*/
- dbgprintf("Work Directory: '%s'.\n", glbl.GetWorkDir());
+ DBGPRINTF("Work Directory: '%s'.\n", glbl.GetWorkDir());
+}
+
+
+/* Actually run the input modules. This happens after privileges are dropped,
+ * if that is requested.
+ */
+static rsRetVal
+runInputModules(void)
+{
+ modInfo_t *pMod;
+ int bNeedsCancel;
+
+ BEGINfunc
+ /* loop through all modules and activate them (brr...) */
+ pMod = module.GetNxtType(NULL, eMOD_IN);
+ while(pMod != NULL) {
+ if(pMod->mod.im.bCanRun) {
+ /* activate here */
+ bNeedsCancel = (pMod->isCompatibleWithFeature(sFEATURENonCancelInputTermination) == RS_RET_OK) ?
+ 0 : 1;
+ thrdCreate(pMod->mod.im.runInput, pMod->mod.im.afterRun, bNeedsCancel);
+ }
+ pMod = module.GetNxtType(pMod, eMOD_IN);
+ }
+
+ ENDfunc
+ return RS_RET_OK; /* intentional: we do not care about module errors */
}
@@ -2525,11 +1500,10 @@ startInputModules(void)
/* loop through all modules and activate them (brr...) */
pMod = module.GetNxtType(NULL, eMOD_IN);
while(pMod != NULL) {
- if((iRet = pMod->mod.im.willRun()) == RS_RET_OK) {
- /* activate here */
- thrdCreate(pMod->mod.im.runInput, pMod->mod.im.afterRun);
- } else {
- dbgprintf("module %lx will not run, iRet %d\n", (unsigned long) pMod, iRet);
+ iRet = pMod->mod.im.willRun();
+ pMod->mod.im.bCanRun = (iRet == RS_RET_OK);
+ if(!pMod->mod.im.bCanRun) {
+ DBGPRINTF("module %lx will not run, iRet %d\n", (unsigned long) pMod, iRet);
}
pMod = module.GetNxtType(pMod, eMOD_IN);
}
@@ -2539,60 +1513,92 @@ startInputModules(void)
}
-/* INIT -- Initialize syslogd from configuration table
- * init() is called at initial startup AND each time syslogd is HUPed
+/* create a main message queue, now also used for ruleset queues. This function
+ * needs to be moved to some other module, but it is considered acceptable for
+ * the time being (remember that we want to restructure config processing at large!).
+ * rgerhards, 2009-10-27
+ */
+rsRetVal createMainQueue(qqueue_t **ppQueue, uchar *pszQueueName)
+{
+ DEFiRet;
+
+ /* switch the message object to threaded operation, if necessary */
+ if(MainMsgQueType == QUEUETYPE_DIRECT || iMainMsgQueueNumWorkers > 1) {
+ MsgEnableThreadSafety();
+ }
+
+ /* create message queue */
+ CHKiRet_Hdlr(qqueueConstruct(ppQueue, MainMsgQueType, iMainMsgQueueNumWorkers, iMainMsgQueueSize, msgConsumer)) {
+ /* no queue is fatal, we need to give up in that case... */
+ errmsg.LogError(0, iRet, "could not create (ruleset) main message queue"); \
+ }
+ /* name our main queue object (it's not fatal if it fails...) */
+ obj.SetName((obj_t*) (*ppQueue), pszQueueName);
+
+ /* ... set some properties ... */
+# define setQPROP(func, directive, data) \
+ CHKiRet_Hdlr(func(*ppQueue, data)) { \
+ errmsg.LogError(0, NO_ERRCODE, "Invalid " #directive ", error %d. Ignored, running with default setting", iRet); \
+ }
+# define setQPROPstr(func, directive, data) \
+ CHKiRet_Hdlr(func(*ppQueue, data, (data == NULL)? 0 : strlen((char*) data))) { \
+ errmsg.LogError(0, NO_ERRCODE, "Invalid " #directive ", error %d. Ignored, running with default setting", iRet); \
+ }
+
+ setQPROP(qqueueSetMaxFileSize, "$MainMsgQueueFileSize", iMainMsgQueMaxFileSize);
+ setQPROP(qqueueSetsizeOnDiskMax, "$MainMsgQueueMaxDiskSpace", iMainMsgQueMaxDiskSpace);
+ setQPROP(qqueueSetiDeqBatchSize, "$MainMsgQueueDequeueBatchSize", iMainMsgQueDeqBatchSize);
+ setQPROPstr(qqueueSetFilePrefix, "$MainMsgQueueFileName", pszMainMsgQFName);
+ setQPROP(qqueueSetiPersistUpdCnt, "$MainMsgQueueCheckpointInterval", iMainMsgQPersistUpdCnt);
+ setQPROP(qqueueSetbSyncQueueFiles, "$MainMsgQueueSyncQueueFiles", bMainMsgQSyncQeueFiles);
+ setQPROP(qqueueSettoQShutdown, "$MainMsgQueueTimeoutShutdown", iMainMsgQtoQShutdown );
+ setQPROP(qqueueSettoActShutdown, "$MainMsgQueueTimeoutActionCompletion", iMainMsgQtoActShutdown);
+ setQPROP(qqueueSettoWrkShutdown, "$MainMsgQueueWorkerTimeoutThreadShutdown", iMainMsgQtoWrkShutdown);
+ setQPROP(qqueueSettoEnq, "$MainMsgQueueTimeoutEnqueue", iMainMsgQtoEnq);
+ setQPROP(qqueueSetiHighWtrMrk, "$MainMsgQueueHighWaterMark", iMainMsgQHighWtrMark);
+ setQPROP(qqueueSetiLowWtrMrk, "$MainMsgQueueLowWaterMark", iMainMsgQLowWtrMark);
+ setQPROP(qqueueSetiDiscardMrk, "$MainMsgQueueDiscardMark", iMainMsgQDiscardMark);
+ setQPROP(qqueueSetiDiscardSeverity, "$MainMsgQueueDiscardSeverity", iMainMsgQDiscardSeverity);
+ setQPROP(qqueueSetiMinMsgsPerWrkr, "$MainMsgQueueWorkerThreadMinimumMessages", iMainMsgQWrkMinMsgs);
+ setQPROP(qqueueSetbSaveOnShutdown, "$MainMsgQueueSaveOnShutdown", bMainMsgQSaveOnShutdown);
+ setQPROP(qqueueSetiDeqSlowdown, "$MainMsgQueueDequeueSlowdown", iMainMsgQDeqSlowdown);
+ setQPROP(qqueueSetiDeqtWinFromHr, "$MainMsgQueueDequeueTimeBegin", iMainMsgQueueDeqtWinFromHr);
+ setQPROP(qqueueSetiDeqtWinToHr, "$MainMsgQueueDequeueTimeEnd", iMainMsgQueueDeqtWinToHr);
+
+# undef setQPROP
+# undef setQPROPstr
+
+ /* ... and finally start the queue! */
+ CHKiRet_Hdlr(qqueueStart(*ppQueue)) {
+ /* no queue is fatal, we need to give up in that case... */
+ errmsg.LogError(0, iRet, "could not start (ruleset) main message queue"); \
+ }
+ RETiRet;
+}
+
+
+/* INIT -- Initialize syslogd
* Note that if iConfigVerify is set, only the config file is verified but nothing
* else happens. -- rgerhards, 2008-07-28
*/
static rsRetVal
init(void)
{
- DEFiRet;
rsRetVal localRet;
int iNbrActions;
+ int bHadConfigErr = 0;
+ ruleset_t *pRuleset;
char cbuf[BUFSIZ];
char bufStartUpMsg[512];
struct sigaction sigAct;
+ DEFiRet;
- thrdTerminateAll(); /* stop all running input threads - TODO: reconsider location! */
-
- /* initialize some static variables */
- pDfltHostnameCmp = NULL;
- pDfltProgNameCmp = NULL;
- eDfltHostnameCmpMode = HN_NO_COMP;
-
- dbgprintf("rsyslog %s - called init()\n", VERSION);
-
- /* delete the message queue, which also flushes all messages left over */
- if(pMsgQueue != NULL) {
- dbgprintf("deleting main message queue\n");
- qqueueDestruct(&pMsgQueue); /* delete pThis here! */
- pMsgQueue = NULL;
- }
-
- /* Close all open log files and free log descriptor array. This also frees
- * all output-modules instance data.
- */
- freeSelectors();
-
- /* Unload all non-static modules */
- dbgprintf("Unloading non-static modules.\n");
- module.UnloadAndDestructAll(eMOD_LINK_DYNAMIC_LOADED);
-
- dbgprintf("Clearing templates.\n");
- tplDeleteNew();
-
- /* re-setting values to defaults (where applicable) */
- /* once we have loadable modules, we must re-visit this code. The reason is
- * that config variables are not re-set, because the module is not yet loaded. On
- * the other hand, that doesn't matter, because the module got unloaded and is then
- * re-loaded, so the variables should be re-set via that way. And this is exactly how
- * it works. Loadable module's variables are initialized on load, the rest here.
- * rgerhards, 2008-04-28
- */
- conf.cfsysline((uchar*)"ResetConfigVariables");
+ DBGPRINTF("rsyslog %s - called init()\n", VERSION);
- conf.ReInitConf();
+ /* construct the default ruleset */
+ ruleset.Construct(&pRuleset);
+ ruleset.SetName(pRuleset, UCHAR_CONSTANT("RSYSLOG_DefaultRuleset"));
+ ruleset.ConstructFinalize(pRuleset);
/* open the configuration file */
localRet = conf.processConfFile(ConfFile);
@@ -2600,12 +1606,14 @@ init(void)
if(localRet != RS_RET_OK) {
errmsg.LogError(0, localRet, "CONFIG ERROR: could not interpret master config file '%s'.", ConfFile);
+ bHadConfigErr = 1;
} 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.");
+ bHadConfigErr = 1;
}
- if(localRet != RS_RET_OK || iNbrActions == 0) {
+ if((localRet != RS_RET_OK && localRet != RS_RET_NONFATAL_CONFIG_ERR) || iNbrActions == 0) {
/* rgerhards: this code is executed to set defaults when the
* config file could not be opened. We might think about
* abandoning the run in this case - but this, too, is not
@@ -2613,38 +1621,27 @@ init(void)
* We ignore any errors while doing this - we would be lost anyhow...
*/
errmsg.LogError(0, NO_ERRCODE, "EMERGENCY CONFIGURATION ACTIVATED - fix rsyslog config file!");
- selector_t *f = NULL;
/* note: we previously used _POSIY_TTY_NAME_MAX+1, but this turned out to be
* too low on linux... :-S -- rgerhards, 2008-07-28
*/
char szTTYNameBuf[128];
- conf.cfline((uchar*)"*.ERR\t" _PATH_CONSOLE, &f);
- conf.cfline((uchar*)"syslog.*\t" _PATH_CONSOLE, &f);
- conf.cfline((uchar*)"*.PANIC\t*", &f);
- conf.cfline((uchar*)"syslog.*\troot", &f);
+ rule_t *pRule = NULL; /* initialization to NULL is *vitally* important! */
+ conf.cfline(UCHAR_CONSTANT("*.ERR\t" _PATH_CONSOLE), &pRule);
+ conf.cfline(UCHAR_CONSTANT("syslog.*\t" _PATH_CONSOLE), &pRule);
+ conf.cfline(UCHAR_CONSTANT("*.PANIC\t*"), &pRule);
+ conf.cfline(UCHAR_CONSTANT("syslog.*\troot"), &pRule);
if(ttyname_r(0, szTTYNameBuf, sizeof(szTTYNameBuf)) == 0) {
snprintf(cbuf,sizeof(cbuf), "*.*\t%s", szTTYNameBuf);
- conf.cfline((uchar*)cbuf, &f);
+ conf.cfline((uchar*)cbuf, &pRule);
} else {
- dbgprintf("error %d obtaining controlling terminal, not using that emergency rule\n", errno);
+ DBGPRINTF("error %d obtaining controlling terminal, not using that emergency rule\n", errno);
}
- selectorAddList(f);
+ ruleset.AddRule(ruleset.GetCurrent(), &pRule);
}
legacyOptsHook();
- /* we are now done with reading the configuration. This is the right time to
- * free some objects that were just needed for loading it. rgerhards 2005-10-19
- */
- if(pDfltHostnameCmp != NULL) {
- rsCStrDestruct(&pDfltHostnameCmp);
- }
-
- if(pDfltProgNameCmp != NULL) {
- rsCStrDestruct(&pDfltProgNameCmp);
- }
-
/* some checks */
if(iMainMsgQueueNumWorkers < 1) {
errmsg.LogError(0, NO_ERRCODE, "$MainMsgQueueNumWorkers must be at least 1! Set to 1.\n");
@@ -2672,68 +1669,39 @@ init(void)
/* we are done checking the config - now validate if we should actually run or not.
* If not, terminate. -- rgerhards, 2008-07-25
*/
- if(iConfigVerify)
+ if(iConfigVerify) {
+ if(bHadConfigErr) {
+ /* a bit dirty, but useful... */
+ exit(1);
+ }
ABORT_FINALIZE(RS_RET_VALIDATION_RUN);
+ }
- /* switch the message object to threaded operation, if necessary */
- if(MainMsgQueType == QUEUETYPE_DIRECT || iMainMsgQueueNumWorkers > 1) {
- MsgEnableThreadSafety();
+ if(bAbortOnUncleanConfig && bHadConfigErr) {
+ fprintf(stderr, "rsyslogd: $AbortOnUncleanConfig is set, and config is not clean.\n"
+ "Check error log for details, fix errors and restart. As a last\n"
+ "resort, you may want to remove $AbortOnUncleanConfig to permit a\n"
+ "startup with a dirty config.\n");
+ exit(2);
}
/* create message queue */
- CHKiRet_Hdlr(qqueueConstruct(&pMsgQueue, MainMsgQueType, iMainMsgQueueNumWorkers, iMainMsgQueueSize, msgConsumer)) {
+ 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);
exit(1);
}
- /* name our main queue object (it's not fatal if it fails...) */
- obj.SetName((obj_t*) pMsgQueue, (uchar*) "main queue");
-
- /* ... set some properties ... */
-# define setQPROP(func, directive, data) \
- CHKiRet_Hdlr(func(pMsgQueue, data)) { \
- errmsg.LogError(0, NO_ERRCODE, "Invalid " #directive ", error %d. Ignored, running with default setting", iRet); \
- }
-# define setQPROPstr(func, directive, data) \
- CHKiRet_Hdlr(func(pMsgQueue, data, (data == NULL)? 0 : strlen((char*) data))) { \
- errmsg.LogError(0, NO_ERRCODE, "Invalid " #directive ", error %d. Ignored, running with default setting", iRet); \
- }
-
- setQPROP(qqueueSetMaxFileSize, "$MainMsgQueueFileSize", iMainMsgQueMaxFileSize);
- setQPROP(qqueueSetsizeOnDiskMax, "$MainMsgQueueMaxDiskSpace", iMainMsgQueMaxDiskSpace);
- setQPROPstr(qqueueSetFilePrefix, "$MainMsgQueueFileName", pszMainMsgQFName);
- setQPROP(qqueueSetiPersistUpdCnt, "$MainMsgQueueCheckpointInterval", iMainMsgQPersistUpdCnt);
- setQPROP(qqueueSettoQShutdown, "$MainMsgQueueTimeoutShutdown", iMainMsgQtoQShutdown );
- setQPROP(qqueueSettoActShutdown, "$MainMsgQueueTimeoutActionCompletion", iMainMsgQtoActShutdown);
- setQPROP(qqueueSettoWrkShutdown, "$MainMsgQueueWorkerTimeoutThreadShutdown", iMainMsgQtoWrkShutdown);
- setQPROP(qqueueSettoEnq, "$MainMsgQueueTimeoutEnqueue", iMainMsgQtoEnq);
- setQPROP(qqueueSetiHighWtrMrk, "$MainMsgQueueHighWaterMark", iMainMsgQHighWtrMark);
- setQPROP(qqueueSetiLowWtrMrk, "$MainMsgQueueLowWaterMark", iMainMsgQLowWtrMark);
- setQPROP(qqueueSetiDiscardMrk, "$MainMsgQueueDiscardMark", iMainMsgQDiscardMark);
- setQPROP(qqueueSetiDiscardSeverity, "$MainMsgQueueDiscardSeverity", iMainMsgQDiscardSeverity);
- setQPROP(qqueueSetiMinMsgsPerWrkr, "$MainMsgQueueWorkerThreadMinimumMessages", iMainMsgQWrkMinMsgs);
- setQPROP(qqueueSetbSaveOnShutdown, "$MainMsgQueueSaveOnShutdown", bMainMsgQSaveOnShutdown);
- setQPROP(qqueueSetiDeqSlowdown, "$MainMsgQueueDequeueSlowdown", iMainMsgQDeqSlowdown);
- setQPROP(qqueueSetiDeqtWinFromHr, "$MainMsgQueueDequeueTimeBegin", iMainMsgQueueDeqtWinFromHr);
- setQPROP(qqueueSetiDeqtWinToHr, "$MainMsgQueueDequeueTimeEnd", iMainMsgQueueDeqtWinToHr);
-
-# undef setQPROP
-# undef setQPROPstr
-
- /* ... and finally start the queue! */
- CHKiRet_Hdlr(qqueueStart(pMsgQueue)) {
- /* no queue is fatal, we need to give up in that case... */
- fprintf(stderr, "fatal error %d: could not start message queue - rsyslogd can not run!\n", iRet);
- exit(1);
- }
bHaveMainQueue = (MainMsgQueType == QUEUETYPE_DIRECT) ? 0 : 1;
- dbgprintf("Main processing queue is initialized and running\n");
+ DBGPRINTF("Main processing queue is initialized and running\n");
/* the output part and the queue is now ready to run. So it is a good time
- * to start the inputs. Please note that the net code above should be
+ * 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
*/
startInputModules();
@@ -2741,70 +1709,71 @@ init(void)
dbgPrintInitInfo();
}
- /* we now generate the startup message. It now includes everything to
- * identify this instance. -- rgerhards, 2005-08-17
- */
- snprintf(bufStartUpMsg, sizeof(bufStartUpMsg)/sizeof(char),
- " [origin software=\"rsyslogd\" " "swVersion=\"" VERSION \
- "\" x-pid=\"%d\" x-info=\"http://www.rsyslog.com\"] restart",
- (int) myPid);
- logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)bufStartUpMsg, 0);
-
memset(&sigAct, 0, sizeof (sigAct));
sigemptyset(&sigAct.sa_mask);
sigAct.sa_handler = sighup_handler;
sigaction(SIGHUP, &sigAct, NULL);
- dbgprintf(" (re)started.\n");
+ DBGPRINTF(" started.\n");
+
+ /* we now generate the startup message. It now includes everything to
+ * identify this instance. -- rgerhards, 2005-08-17
+ */
+ if(bLogStatusMsgs) {
+ snprintf(bufStartUpMsg, sizeof(bufStartUpMsg)/sizeof(char),
+ " [origin software=\"rsyslogd\" " "swVersion=\"" VERSION \
+ "\" x-pid=\"%d\" x-info=\"http://www.rsyslog.com\"] start",
+ (int) myPid);
+ logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)bufStartUpMsg, 0);
+ }
finalize_it:
RETiRet;
}
-/* add a completely-processed selector (after config line parsing) to
- * the linked list of selectors. 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.
- * 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
+/* Switch the default ruleset (that, what servcies bind to if nothing specific
+ * is specified).
+ * rgerhards, 2009-06-12
*/
-rsRetVal
-selectorAddList(selector_t *f)
+static rsRetVal
+setDefaultRuleset(void __attribute__((unused)) *pVal, uchar *pszName)
{
DEFiRet;
- int iActionCnt;
- static selector_t *nextp = NULL; /* TODO: make this go away (see comment below) */
+ CHKiRet(ruleset.SetDefaultRuleset(pszName));
- if(f != NULL) {
- CHKiRet(llGetNumElts(&f->llActList, &iActionCnt));
- if(iActionCnt == 0) {
- errmsg.LogError(0, NO_ERRCODE, "warning: selector line without actions will be discarded");
- selectorDestruct(f);
- } else {
- /* successfully created an entry */
- dbgprintf("selector line successfully processed\n");
- /* TODO: we should use the linked list class for the selector list, else we need to add globals
- * ... well nextp could be added temporarily...
- * Thanks to varmojfekoj for having the idea to just use "Files" to make this
- * code work. I had actually forgotten to fix the code here before moving to 1.18.0.
- * And, of course, I also did not migrate the selector_t structure to the linked list class.
- * However, that should still be one of the very next things to happen.
- * rgerhards, 2007-08-06
- */
- if(Files == NULL) {
- Files = f;
- } else {
- nextp->f_next = f;
- }
- nextp = f;
- }
+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(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(pRuleset));
+ } else {
+ ABORT_FINALIZE(localRet);
}
finalize_it:
+ free(pszName); /* no longer needed */
RETiRet;
}
@@ -2818,16 +1787,16 @@ static rsRetVal setMainMsgQueType(void __attribute__((unused)) *pVal, uchar *psz
if (!strcasecmp((char *) pszType, "fixedarray")) {
MainMsgQueType = QUEUETYPE_FIXED_ARRAY;
- dbgprintf("main message queue type set to FIXED_ARRAY\n");
+ DBGPRINTF("main message queue type set to FIXED_ARRAY\n");
} else if (!strcasecmp((char *) pszType, "linkedlist")) {
MainMsgQueType = QUEUETYPE_LINKEDLIST;
- dbgprintf("main message queue type set to LINKEDLIST\n");
+ DBGPRINTF("main message queue type set to LINKEDLIST\n");
} else if (!strcasecmp((char *) pszType, "disk")) {
MainMsgQueType = QUEUETYPE_DISK;
- dbgprintf("main message queue type set to DISK\n");
+ DBGPRINTF("main message queue type set to DISK\n");
} else if (!strcasecmp((char *) pszType, "direct")) {
MainMsgQueType = QUEUETYPE_DIRECT;
- dbgprintf("main message queue type set to DIRECT (no queueing at all)\n");
+ 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;
@@ -2856,6 +1825,9 @@ void sighup_handler()
sigaction(SIGHUP, &sigAct, NULL);
}
+void sigttin_handler()
+{
+}
/* this function pulls all internal messages from the buffer
* and puts them into the processing engine.
@@ -2863,14 +1835,13 @@ void sighup_handler()
* really help us. TODO: add error messages?
* rgerhards, 2007-08-03
*/
-static void processImInternal(void)
+static inline void processImInternal(void)
{
int iPri;
- int iFlags;
msg_t *pMsg;
- while(iminternalRemoveMsg(&iPri, &pMsg, &iFlags) == RS_RET_OK) {
- logmsg(pMsg, iFlags);
+ while(iminternalRemoveMsg(&iPri, &pMsg) == RS_RET_OK) {
+ submitMsg(pMsg);
}
}
@@ -2896,25 +1867,18 @@ DEFFUNC_llExecFunc(doHUPActions)
static inline void
doHUP(void)
{
- selector_t *f;
char buf[512];
- snprintf(buf, sizeof(buf) / sizeof(char),
- " [origin software=\"rsyslogd\" " "swVersion=\"" VERSION
- "\" x-pid=\"%d\" x-info=\"http://www.rsyslog.com\"] rsyslogd was HUPed, type '%s'.",
- (int) myPid, glbl.GetHUPisRestart() ? "restart" : "lightweight");
- errno = 0;
- logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)buf, 0);
-
- if(glbl.GetHUPisRestart()) {
- DBGPRINTF("Received SIGHUP, configured to be restart, reloading rsyslogd.\n");
- init(); /* main queue is stopped as part of init() */
- } else {
- DBGPRINTF("Received SIGHUP, configured to be a non-restart type of HUP - notifying actions.\n");
- for(f = Files; f != NULL ; f = f->f_next) {
- llExecFunc(&f->llActList, doHUPActions, NULL);
- }
+ if(bLogStatusMsgs) {
+ snprintf(buf, sizeof(buf) / sizeof(char),
+ " [origin software=\"rsyslogd\" " "swVersion=\"" VERSION
+ "\" x-pid=\"%d\" x-info=\"http://www.rsyslog.com\"] rsyslogd was HUPed",
+ (int) myPid);
+ errno = 0;
+ logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)buf, 0);
}
+
+ ruleset.IterateAllActions(doHUPActions, NULL);
}
@@ -2945,6 +1909,7 @@ mainloop(void)
* but a once-a-day wakeup should be quite acceptable. -- rgerhards, 2008-06-09
*/
tvSelectTimeout.tv_sec = (bReduceRepeatMsgs == 1) ? TIMERINTVL : 86400 /*1 day*/;
+ //tvSelectTimeout.tv_sec = TIMERINTVL; /* TODO: change this back to the above code when we have a better solution for apc */
tvSelectTimeout.tv_usec = 0;
select(1, NULL, NULL, NULL, &tvSelectTimeout);
if(bFinished)
@@ -2979,49 +1944,11 @@ mainloop(void)
bHadHUP = 0;
continue;
}
+ // TODO: remove execScheduled(); /* handle Apc calls (if any) */
}
ENDfunc
}
-/* If user is not root, prints warnings or even exits
- * TODO: check all dynafiles for write permission
- * ... but it is probably better to wait here until we have
- * a module interface - rgerhards, 2007-07-23
- */
-static void checkPermissions()
-{
-#if 0
- /* TODO: this function must either be redone or removed - now with the input modules,
- * there is no such simple check we can do. What we can check, however, is if there is
- * any input module active and terminate, if not. -- rgerhards, 2007-12-26
- */
- /* we are not root */
- if (geteuid() != 0)
- {
- fputs("WARNING: Local messages will not be logged! If you want to log them, run rsyslog as root.\n",stderr);
-#ifdef SYSLOG_INET
- /* udp enabled and port number less than or equal to 1024 */
- if ( AcceptRemote && (atoi(LogPort) <= 1024) )
- fprintf(stderr, "WARNING: Will not listen on UDP port %s. Use port number higher than 1024 or run rsyslog as root!\n", LogPort);
-
- /* tcp enabled and port number less or equal to 1024 */
- if( bEnableTCP && (atoi(TCPLstnPort) <= 1024) )
- fprintf(stderr, "WARNING: Will not listen on TCP port %s. Use port number higher than 1024 or run rsyslog as root!\n", TCPLstnPort);
-
- /* Neither explicit high UDP port nor explicit high TCP port.
- * It is useless to run anymore */
- if( !(AcceptRemote && (atoi(LogPort) > 1024)) && !( bEnableTCP && (atoi(TCPLstnPort) > 1024)) )
- {
-#endif
- fprintf(stderr, "ERROR: Nothing to log, no reason to run. Please run rsyslog as root.\n");
- exit(EXIT_FAILURE);
-#ifdef SYSLOG_INET
- }
-#endif
- }
-#endif
-}
-
/* load build-in modules
* very first version begun on 2007-07-23 by rgerhards
@@ -3030,23 +1957,26 @@ static rsRetVal loadBuildInModules(void)
{
DEFiRet;
- if((iRet = module.doModInit(modInitFile, (uchar*) "builtin-file", NULL)) != RS_RET_OK) {
+ if((iRet = module.doModInit(modInitFile, UCHAR_CONSTANT("builtin-file"), NULL)) != RS_RET_OK) {
+ RETiRet;
+ }
+ if((iRet = module.doModInit(modInitPipe, UCHAR_CONSTANT("builtin-pipe"), NULL)) != RS_RET_OK) {
RETiRet;
}
#ifdef SYSLOG_INET
- if((iRet = module.doModInit(modInitFwd, (uchar*) "builtin-fwd", NULL)) != RS_RET_OK) {
+ if((iRet = module.doModInit(modInitFwd, UCHAR_CONSTANT("builtin-fwd"), NULL)) != RS_RET_OK) {
RETiRet;
}
#endif
- if((iRet = module.doModInit(modInitShell, (uchar*) "builtin-shell", NULL)) != RS_RET_OK) {
+ if((iRet = module.doModInit(modInitShell, UCHAR_CONSTANT("builtin-shell"), NULL)) != RS_RET_OK) {
RETiRet;
}
- if((iRet = module.doModInit(modInitDiscard, (uchar*) "builtin-discard", NULL)) != RS_RET_OK) {
+ if((iRet = module.doModInit(modInitDiscard, UCHAR_CONSTANT("builtin-discard"), NULL)) != RS_RET_OK) {
RETiRet;
}
/* dirty, but this must be for the time being: the usrmsg module must always be
- * loaded as last module. This is because it processes any time of action selector.
+ * 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
@@ -3054,8 +1984,15 @@ static rsRetVal loadBuildInModules(void)
* User names now must begin with:
* [a-zA-Z0-9_.]
*/
- if((iRet = module.doModInit(modInitUsrMsg, (uchar*) "builtin-usrmsg", NULL)) != RS_RET_OK)
- RETiRet;
+ CHKiRet(module.doModInit(modInitUsrMsg, (uchar*) "builtin-usrmsg", NULL));
+
+ /* load build-in parser modules */
+ CHKiRet(module.doModInit(modInitpmrfc5424, UCHAR_CONSTANT("builtin-pmrfc5424"), NULL));
+ CHKiRet(module.doModInit(modInitpmrfc3164, UCHAR_CONSTANT("builtin-pmrfc3164"), NULL));
+
+ /* and set default parser modules (order is *very* important, legacy (3164) parse needs to go last! */
+ CHKiRet(parser.AddDfltParser(UCHAR_CONSTANT("rsyslog.rfc5424")));
+ CHKiRet(parser.AddDfltParser(UCHAR_CONSTANT("rsyslog.rfc3164")));
/* ok, initialization of the command handler probably does not 100% belong right in
* this space here. However, with the current design, this is actually quite a good
@@ -3064,7 +2001,10 @@ static rsRetVal loadBuildInModules(void)
* is that rsyslog will terminate if we can not register our built-in config commands.
* This, I think, is the right thing to do. -- rgerhards, 2007-07-31
*/
+ CHKiRet(regCfSysLineHdlr((uchar *)"logrsyslogstatusmessages", 0, eCmdHdlrBinary, NULL, &bLogStatusMsgs, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionresumeretrycount", 0, eCmdHdlrInt, NULL, &glbliActionResumeRetryCount, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"defaultruleset", 0, eCmdHdlrGetWord, setDefaultRuleset, NULL, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"ruleset", 0, eCmdHdlrGetWord, setCurrRuleset, NULL, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuefilename", 0, eCmdHdlrGetWord, NULL, &pszMainMsgQFName, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuesize", 0, eCmdHdlrInt, NULL, &iMainMsgQueueSize, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuehighwatermark", 0, eCmdHdlrInt, NULL, &iMainMsgQHighWtrMark, NULL));
@@ -3072,6 +2012,7 @@ static rsRetVal loadBuildInModules(void)
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuediscardmark", 0, eCmdHdlrInt, NULL, &iMainMsgQDiscardMark, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuediscardseverity", 0, eCmdHdlrSeverity, NULL, &iMainMsgQDiscardSeverity, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuecheckpointinterval", 0, eCmdHdlrInt, NULL, &iMainMsgQPersistUpdCnt, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuesyncqueuefiles", 0, eCmdHdlrBinary, NULL, &bMainMsgQSyncQeueFiles, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuetype", 0, eCmdHdlrGetWord, setMainMsgQueType, NULL, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueueworkerthreads", 0, eCmdHdlrInt, NULL, &iMainMsgQueueNumWorkers, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuetimeoutshutdown", 0, eCmdHdlrInt, NULL, &iMainMsgQtoQShutdown, NULL));
@@ -3081,17 +2022,15 @@ static rsRetVal loadBuildInModules(void)
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuedequeueslowdown", 0, eCmdHdlrInt, NULL, &iMainMsgQDeqSlowdown, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueueworkerthreadminimummessages", 0, eCmdHdlrInt, NULL, &iMainMsgQWrkMinMsgs, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuemaxfilesize", 0, eCmdHdlrSize, NULL, &iMainMsgQueMaxFileSize, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuedequeuebatchsize", 0, eCmdHdlrSize, NULL, &iMainMsgQueDeqBatchSize, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuemaxdiskspace", 0, eCmdHdlrSize, NULL, &iMainMsgQueMaxDiskSpace, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuesaveonshutdown", 0, eCmdHdlrBinary, NULL, &bMainMsgQSaveOnShutdown, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuedequeuetimebegin", 0, eCmdHdlrInt, NULL, &iMainMsgQueueDeqtWinFromHr, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuedequeuetimeend", 0, eCmdHdlrInt, NULL, &iMainMsgQueueDeqtWinToHr, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"abortonuncleanconfig", 0, eCmdHdlrBinary, NULL, &bAbortOnUncleanConfig, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"repeatedmsgreduction", 0, eCmdHdlrBinary, NULL, &bReduceRepeatMsgs, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionexeconlywhenpreviousissuspended", 0, eCmdHdlrBinary, NULL, &bActExecWhenPrevSusp, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionexeconlyonceeveryinterval", 0, eCmdHdlrInt, NULL, &iActExecOnceInterval, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionresumeinterval", 0, eCmdHdlrInt, setActionResumeInterval, NULL, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"controlcharacterescapeprefix", 0, eCmdHdlrGetChar, NULL, &cCCEscapeChar, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"escapecontrolcharactersonreceive", 0, eCmdHdlrBinary, NULL, &bEscapeCCOnRcv, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"droptrailinglfonreception", 0, eCmdHdlrBinary, NULL, &bDropTrailingLF, NULL));
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));
@@ -3113,11 +2052,6 @@ static rsRetVal loadBuildInModules(void)
CHKiRet(regCfSysLineHdlr((uchar *)"privdroptogroup", 0, eCmdHdlrGID, NULL, &gidDropPriv, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"privdroptogroupid", 0, eCmdHdlrGID, NULL, &gidDropPriv, NULL));
- /* now add other modules handlers (we should work on that to be able to do it in ClassInit(), but so far
- * that is not possible). -- rgerhards, 2008-01-28
- */
- CHKiRet(actionAddCfSysLineHdrl());
-
finalize_it:
RETiRet;
}
@@ -3206,8 +2140,9 @@ static rsRetVal mainThread()
CHKiRet(init());
if(Debug && debugging_on) {
- dbgprintf("Debugging enabled, SIGUSR1 to turn off debugging.\n");
+ DBGPRINTF("Debugging enabled, SIGUSR1 to turn off debugging.\n");
}
+
/* Send a signal to the parent so it can terminate.
*/
if(myPid != ppid)
@@ -3215,7 +2150,7 @@ static rsRetVal mainThread()
/* If instructed to do so, we now drop privileges. Note that this is not 100% secure,
- * because inputs and outputs are already running at this time. However, we can implement
+ * 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
@@ -3223,22 +2158,31 @@ static rsRetVal mainThread()
*/
if(gidDropPriv != 0) {
doDropPrivGid(gidDropPriv);
- glbl.SetHUPisRestart(0); /* we can not do restart-type HUPs with dropped privs */
}
if(uidDropPriv != 0) {
doDropPrivUid(uidDropPriv);
- glbl.SetHUPisRestart(0); /* we can not do restart-type HUPs with dropped privs */
}
+ /* finally let the inputs run... */
+ runInputModules();
/* END OF INTIALIZATION
- * ... but keep in mind that we might do a restart and thus init() might
- * be called again. If that happens, we must shut down the worker thread,
- * do the init() and then restart things.
- * rgerhards, 2005-10-24
*/
- dbgprintf("initialization completed, transitioning to regular run mode\n");
+ DBGPRINTF("initialization completed, transitioning to regular run mode\n");
+
+ /* close stderr and stdout if they are kept open during a fork. Note that this
+ * may introduce subtle security issues: if we are in a jail, one may break out of
+ * it via these descriptors. But if I close them earlier, error messages will (once
+ * again) not be emitted to the user that starts the daemon. As root jail support
+ * is still in its infancy (and not really done), we currently accept this issue.
+ * rgerhards, 2009-06-29
+ */
+ if(!(Debug == DEBUG_FULL || NoFork)) {
+ close(1);
+ close(2);
+ bErrMsgToStderr = 0;
+ }
mainloop();
@@ -3269,24 +2213,26 @@ InitGlobalClasses(void)
CHKiRet(objUse(errmsg, CORE_COMPONENT));
pErrObj = "module";
CHKiRet(objUse(module, CORE_COMPONENT));
- pErrObj = "var";
- CHKiRet(objUse(var, CORE_COMPONENT));
pErrObj = "datetime";
CHKiRet(objUse(datetime, CORE_COMPONENT));
- pErrObj = "vm";
- CHKiRet(objUse(vm, CORE_COMPONENT));
pErrObj = "expr";
CHKiRet(objUse(expr, CORE_COMPONENT));
+ pErrObj = "rule";
+ CHKiRet(objUse(rule, CORE_COMPONENT));
+ pErrObj = "ruleset";
+ CHKiRet(objUse(ruleset, CORE_COMPONENT));
pErrObj = "conf";
CHKiRet(objUse(conf, CORE_COMPONENT));
+ pErrObj = "prop";
+ CHKiRet(objUse(prop, CORE_COMPONENT));
+ pErrObj = "parser";
+ CHKiRet(objUse(parser, CORE_COMPONENT));
/* intialize some dummy classes that are not part of the runtime */
pErrObj = "action";
CHKiRet(actionClassInit());
pErrObj = "template";
CHKiRet(templateInit());
- pErrObj = "parser";
- CHKiRet(parserClassInit());
/* TODO: the dependency on net shall go away! -- rgerhards, 2008-03-07 */
pErrObj = "net";
@@ -3319,34 +2265,16 @@ GlobalClassExit(void)
/* first, release everything we used ourself */
objRelease(net, LM_NET_FILENAME);/* TODO: the dependency on net shall go away! -- rgerhards, 2008-03-07 */
+ objRelease(prop, CORE_COMPONENT);
objRelease(conf, CORE_COMPONENT);
+ objRelease(ruleset, CORE_COMPONENT);
+ objRelease(rule, CORE_COMPONENT);
objRelease(expr, CORE_COMPONENT);
vmClassExit(); /* this is hack, currently core_modules do not get this automatically called */
- objRelease(vm, CORE_COMPONENT);
- objRelease(var, CORE_COMPONENT);
+ parserClassExit(); /* this is hack, currently core_modules do not get this automatically called */
objRelease(datetime, CORE_COMPONENT);
/* TODO: implement the rest of the deinit */
-#if 0
- CHKiRet(datetimeClassInit(NULL));
- CHKiRet(msgClassInit(NULL));
- CHKiRet(strmClassInit(NULL));
- CHKiRet(wtiClassInit(NULL));
- CHKiRet(wtpClassInit(NULL));
- CHKiRet(qqueueClassInit(NULL));
- CHKiRet(vmstkClassInit(NULL));
- CHKiRet(sysvarClassInit(NULL));
- CHKiRet(vmClassInit(NULL));
- CHKiRet(vmopClassInit(NULL));
- CHKiRet(vmprgClassInit(NULL));
- CHKiRet(ctok_tokenClassInit(NULL));
- CHKiRet(ctokClassInit(NULL));
- CHKiRet(exprClassInit(NULL));
-
- /* dummy "classes" */
- CHKiRet(actionClassInit());
- CHKiRet(templateInit());
-#endif
/* dummy "classes */
strExit();
@@ -3384,7 +2312,7 @@ bufOptAdd(char opt, char *arg)
DEFiRet;
bufOpt_t *pBuf;
- if((pBuf = malloc(sizeof(bufOpt_t))) == NULL)
+ if((pBuf = MALLOC(sizeof(bufOpt_t))) == NULL)
ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
pBuf->optchar = opt;
@@ -3440,12 +2368,11 @@ doGlblProcessInit(void)
int i;
DEFiRet;
- checkPermissions();
thrdInit();
- if( !(Debug || NoFork) )
+ if( !(Debug == DEBUG_FULL || NoFork) )
{
- dbgprintf("Checking pidfile.\n");
+ DBGPRINTF("Checking pidfile.\n");
if (!check_pid(PidFile))
{
memset(&sigAct, 0, sizeof (sigAct));
@@ -3467,7 +2394,9 @@ doGlblProcessInit(void)
exit(1); /* "good" exit - after forking, not diasabling anything */
}
num_fds = getdtablesize();
- for (i= 0; i < num_fds; i++)
+ close(0);
+ /* we keep stdout and stderr open in case we have to emit something */
+ for (i = 3; i < num_fds; i++)
(void) close(i);
untty();
}
@@ -3476,12 +2405,10 @@ doGlblProcessInit(void)
fputs(" Already running.\n", stderr);
exit(1); /* "good" exit, done if syslogd is already running */
}
- } else {
- debugging_on = 1;
}
/* tuck my process id away */
- dbgprintf("Writing pidfile %s.\n", PidFile);
+ DBGPRINTF("Writing pidfile %s.\n", PidFile);
if (!check_pid(PidFile))
{
if (!write_pid(PidFile))
@@ -3513,6 +2440,8 @@ doGlblProcessInit(void)
sigaction(SIGCHLD, &sigAct, NULL);
sigAct.sa_handler = Debug ? debug_switch : SIG_IGN;
sigaction(SIGUSR1, &sigAct, NULL);
+ sigAct.sa_handler = sigttin_handler;
+ sigaction(SIGTTIN, &sigAct, NULL); /* (ab)used to interrupt input threads */
sigAct.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sigAct, NULL);
sigaction(SIGXFSZ, &sigAct, NULL); /* do not abort if 2gig file limit is hit */
@@ -3585,6 +2514,7 @@ int realMain(int argc, char **argv)
iCompatibilityMode = atoi(optarg);
break;
case 'd': /* debug - must be handled now, so that debug is active during init! */
+ debugging_on = 1;
Debug = 1;
break;
case 'e': /* log every message (no repeat message supression) */
@@ -3627,7 +2557,7 @@ int realMain(int argc, char **argv)
if ((argc -= optind))
usage();
- dbgprintf("rsyslogd %s startup, compatibility mode %d, module path '%s'\n",
+ DBGPRINTF("rsyslogd %s startup, compatibility mode %d, module path '%s'\n",
VERSION, iCompatibilityMode, glblModPath == NULL ? "" : (char*)glblModPath);
/* we are done with the initial option parsing and processing. Now we init the system. */
@@ -3644,6 +2574,15 @@ int realMain(int argc, char **argv)
/* doing some core initializations */
+ /* we need to create the inputName property (only once during our lifetime) */
+ CHKiRet(prop.Construct(&pInternalInputName));
+ CHKiRet(prop.SetString(pInternalInputName, UCHAR_CONSTANT("rsyslogd"), sizeof("rsyslgod") - 1));
+ CHKiRet(prop.ConstructFinalize(pInternalInputName));
+
+ CHKiRet(prop.Construct(&pLocalHostIP));
+ CHKiRet(prop.SetString(pLocalHostIP, UCHAR_CONSTANT("127.0.0.1"), sizeof("127.0.0.1") - 1));
+ CHKiRet(prop.ConstructFinalize(pLocalHostIP));
+
/* get our host and domain names - we need to do this early as we may emit
* error log messages, which need the correct hostname. -- rgerhards, 2008-04-04
*/
@@ -3692,6 +2631,7 @@ int realMain(int argc, char **argv)
*/
glbl.SetLocalHostName(LocalHostName);
glbl.SetLocalDomain(LocalDomain);
+ glbl.GenerateLocalHostNameProperty(); /* must be redone after conf processing, FQDN setting may have changed */
/* initialize the objects */
if((iRet = modInitIminternal()) != RS_RET_OK) {
@@ -3709,7 +2649,7 @@ int realMain(int argc, char **argv)
/* END core initializations - we now come back to carrying out command line options*/
while((iRet = bufOptRemove(&ch, &arg)) == RS_RET_OK) {
- dbgprintf("deque option %c, optarg '%s'\n", ch, (arg == NULL) ? "" : arg);
+ DBGPRINTF("deque option %c, optarg '%s'\n", ch, (arg == NULL) ? "" : arg);
switch((char)ch) {
case '4':
glbl.SetDefPFFamily(PF_INET);
@@ -3793,11 +2733,12 @@ int realMain(int argc, char **argv)
} else {
fprintf(stderr, "error -p is no longer supported, use module imuxsock instead");
}
+ break;
case 'q': /* add hostname if DNS resolving has failed */
- *net.pACLAddHostnameOnFail = 1;
+ *(net.pACLAddHostnameOnFail) = 1;
break;
case 'Q': /* dont resolve hostnames in ACL to IPs */
- *net.pACLDontResolve = 1;
+ *(net.pACLDontResolve) = 1;
break;
case 'r': /* accept remote messages */
if(iCompatibilityMode < 3) {
@@ -3829,7 +2770,7 @@ int realMain(int argc, char **argv)
case 'u': /* misc user settings */
iHelperUOpt = atoi(arg);
if(iHelperUOpt & 0x01)
- bParseHOSTNAMEandTAG = 0;
+ glbl.SetParseHOSTNAMEandTAG(0);
if(iHelperUOpt & 0x02)
bChDirRoot = 0;
break;
@@ -3888,6 +2829,9 @@ int realMain(int argc, char **argv)
if(!iConfigVerify)
CHKiRet(doGlblProcessInit());
+ /* re-generate local host name property, as the config may have changed our FQDN settings */
+ glbl.GenerateLocalHostNameProperty();
+
CHKiRet(mainThread());
/* do any de-init's that need to be done AFTER this comment */