diff options
author | Michael Biebl <biebl@debian.org> | 2008-04-05 19:13:49 +0200 |
---|---|---|
committer | Michael Biebl <biebl@debian.org> | 2008-04-05 19:13:49 +0200 |
commit | 85df627b6c1cfc388a70ef5b01681d5d952a9dd7 (patch) | |
tree | 5b4136af33910f18d7f24c8506a2519620c6da1d /syslogd.c | |
parent | 6eda8c4f5837480a4f897513d6951e3f5743723d (diff) | |
download | rsyslog-85df627b6c1cfc388a70ef5b01681d5d952a9dd7.tar.gz |
Imported Upstream version 3.14.1upstream/3.14.1
Diffstat (limited to 'syslogd.c')
-rw-r--r-- | syslogd.c | 5719 |
1 files changed, 1219 insertions, 4500 deletions
@@ -7,41 +7,13 @@ * * to learn more about it and discuss any questions you may have. * - * Please note that as of now, a lot of the code in this file stems - * from the sysklogd project. To learn more over this project, please - * visit - * - * http://www.infodrom.org/projects/sysklogd/ - * + * rsyslog had initially been forked from the sysklogd project. * I would like to express my thanks to the developers of the sysklogd * package - without it, I would have had a much harder start... * - * Please note that I made quite some changes to the orignal package. - * I expect to do even more changes - up - * to a full rewrite - to meet my design goals, which among others - * contain a (at least) dual-thread design with a memory buffer for - * storing received bursts of data. This is also the reason why I - * kind of "forked" a completely new branch of the package. My intension - * is to do many changes and only this initial release will look - * similar to sysklogd (well, one never knows...). - * - * As I have made a lot of modifications, please assume that all bugs - * in this package are mine and not those of the sysklogd team. - * - * As of this writing, there already exist heavy - * modifications to the orginal sysklogd package. I suggest to no - * longer rely too much on code knowledge you eventually have with - * sysklogd - rgerhards 2005-07-05 - * The code is now almost completely different. Be careful! - * rgerhards, 2006-11-30 - * - * I have decided to put my code under the GPL. The sysklog package - * is distributed under the BSD license. As such, this package here - * currently comes with two licenses. Both are given below. As it is - * probably hard for you to see what was part of the sysklogd package - * and what is part of my code, I suggest that you visit the - * sysklogd site on the URL above if you would like to base your - * development on a version that is not under the GPL. + * Please note that while rsyslog started from the sysklogd code base, + * it nowadays has almost nothing left in common with it. Allmost all + * parts of the code have been rewritten. * * This Project was intiated and is maintained by * Rainer Gerhards <rgerhards@hq.adiscon.com>. See @@ -64,31 +36,28 @@ * to the database). * * rsyslog - An Enhanced syslogd Replacement. - * Copyright 2003-2007 Rainer Gerhards and Adiscon GmbH. + * Copyright 2003-2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of rsyslog. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. + * Rsyslog is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, + * Rsyslog is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>. * * A copy of the GPL can be found in the file "COPYING" in this distribution. */ #include "config.h" #include "rsyslog.h" -#ifdef __FreeBSD__ -#define BSD -#endif - /* change the following setting to e.g. 32768 if you would like to * support large message sizes for IHE (32k is the current maximum * needed for IHE). I was initially tempted to increase it to 32k, @@ -116,31 +85,10 @@ * I have increased the default message size to 2048 to be in sync * with recent IETF syslog standardization efforts. * rgerhards, 2006-11-30 - * - * I have removed syslogdPanic(). That function was supposed to be used - * for logging in low-memory conditons. Ever since it was introduced, it - * was a wrapper for dbgprintf(). A more intelligent choice was hard to - * find. After all, if we are short on memory, doing anything fance will - * again cause memory problems. I have now modified the code so that - * those elements for which we do not get memory are simply discarded. - * That might be a single property like the TAG, but it might also be - * a complete message. The overall goal of this code change is to keep - * rsyslogd up and running, while we sacrifice some messages to reach - * that goal. It also keeps the code cleaner. A real out of memory - * condition is highly unlikely. If it happens, there will probably be - * much more trouble on the system in question. Anyhow - rsyslogd will - * most probably be able to survive it and carry on with processing - * once the situation has been resolved. */ #define DEFUPRI (LOG_USER|LOG_NOTICE) -#define DEFSPRI (LOG_KERN|LOG_CRIT) #define TIMERINTVL 30 /* interval for checking flush, mark */ -#define CONT_LINE 1 /* Allow continuation lines */ - -#ifdef MTRACE -#include <mcheck.h> -#endif #include <unistd.h> #include <stdlib.h> #include <stdio.h> @@ -151,48 +99,28 @@ #include <string.h> #include <stdarg.h> #include <time.h> -#include <dlfcn.h> +#include <assert.h> +#include <libgen.h> -#include <sys/syslog.h> -#include <sys/param.h> #ifdef __sun -#include <errno.h> +# include <errno.h> #else -#include <sys/errno.h> +# include <sys/errno.h> #endif #include <sys/ioctl.h> #include <sys/wait.h> -#include <sys/socket.h> #include <sys/file.h> -#include <sys/un.h> -#include <sys/time.h> #if HAVE_SYS_TIMESPEC_H # include <sys/timespec.h> #endif -#include <sys/resource.h> -#include <signal.h> - -#include <netinet/in.h> -#include <netdb.h> -#include <fnmatch.h> -#include <dirent.h> -#include <glob.h> -#include <sys/types.h> -#include <sys/stat.h> - -#include <arpa/nameser.h> -#include <arpa/inet.h> -#include <resolv.h> -#include "pidfile.h" - -#include <assert.h> - -#ifdef USE_PTHREADS -#include <pthread.h> +#if HAVE_SYS_STAT_H +# include <sys/stat.h> #endif +#include <signal.h> + #if HAVE_PATHS_H #include <paths.h> #endif @@ -201,22 +129,14 @@ #include <zlib.h> #endif -/* handle some defines missing on more than one platform */ -#ifndef SUN_LEN -#define SUN_LEN(su) \ - (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path)) -#endif - +#include "pidfile.h" #include "srUtils.h" #include "stringbuf.h" #include "syslogd-types.h" #include "template.h" #include "outchannel.h" #include "syslogd.h" -#include "net.h" /* struct NetAddr */ -#include "sync.h" /* struct NetAddr */ -#include "parse.h" #include "msg.h" #include "modules.h" #include "action.h" @@ -228,6 +148,36 @@ #include "omfwd.h" #include "omfile.h" #include "omdiscard.h" +#include "threads.h" +#include "queue.h" +#include "stream.h" +#include "wti.h" +#include "wtp.h" +#include "expr.h" +#include "ctok.h" +#include "conf.h" +#include "vmop.h" +#include "vmstk.h" +#include "vm.h" +#include "vmprg.h" +#include "errmsg.h" +#include "datetime.h" +#include "sysvar.h" + +/* definitions for objects we access */ +DEFobjCurrIf(obj) +DEFobjCurrIf(datetime) +DEFobjCurrIf(conf) +DEFobjCurrIf(expr) +DEFobjCurrIf(vm) +DEFobjCurrIf(var) +DEFobjCurrIf(module) +DEFobjCurrIf(errmsg) +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. @@ -239,12 +189,8 @@ #ifdef __sun # define LOG_AUTHPRIV LOG_AUTH #endif -#define LOG_MAKEPRI(fac, pri) (((fac) << 3) | (pri)) -#define LOG_PRI(p) ((p) & LOG_PRIMASK) -#define LOG_FAC(p) (((p) & LOG_FACMASK) >> 3) #define INTERNAL_NOPRI 0x10 /* the "no priority" priority */ #define LOG_FTP (11<<3) /* ftp daemon */ -#define INTERNAL_MARK LOG_MAKEPRI((LOG_NFACILITIES<<3), 0) #ifndef UTMP_FILE @@ -268,30 +214,33 @@ #endif #if defined(SYSLOGD_PIDNAME) -#undef _PATH_LOGPID -#if defined(FSSTND) -#ifdef BSD -#define _PATH_VARRUN "/var/run/" -#endif -#ifdef __sun -#define _PATH_VARRUN "/var/run/" -#endif -#define _PATH_LOGPID _PATH_VARRUN SYSLOGD_PIDNAME -#else -#define _PATH_LOGPID "/etc/" SYSLOGD_PIDNAME -#endif -#else -#ifndef _PATH_LOGPID -#if defined(FSSTND) -#define _PATH_LOGPID _PATH_VARRUN "rsyslogd.pid" +# undef _PATH_LOGPID +# if defined(FSSTND) +# ifdef BSD +# define _PATH_VARRUN "/var/run/" +# endif +# if defined(__sun) || defined(__hpux) +# define _PATH_VARRUN "/var/run/" +# endif +# define _PATH_LOGPID _PATH_VARRUN SYSLOGD_PIDNAME +# else +# define _PATH_LOGPID "/etc/" SYSLOGD_PIDNAME +# endif #else -#define _PATH_LOGPID "/etc/rsyslogd.pid" -#endif -#endif +# ifndef _PATH_LOGPID +# if defined(__sun) || defined(__hpux) +# define _PATH_VARRUN "/var/run/" +# endif +# if defined(FSSTND) +# define _PATH_LOGPID _PATH_VARRUN "rsyslogd.pid" +# else +# define _PATH_LOGPID "/etc/rsyslogd.pid" +# endif +# endif #endif #ifndef _PATH_DEV -#define _PATH_DEV "/dev/" +# define _PATH_DEV "/dev/" #endif #ifndef _PATH_CONSOLE @@ -302,81 +251,16 @@ #define _PATH_TTY "/dev/tty" #endif -#ifndef _PATH_LOG -#ifdef BSD -#define _PATH_LOG "/var/run/log" -#else -#define _PATH_LOG "/dev/log" -#endif -#endif - - static uchar *ConfFile = (uchar*) _PATH_LOGCONF; /* read-only after startup */ static char *PidFile = _PATH_LOGPID; /* read-only after startup */ -static uchar *pModDir = NULL; /* read-only after startup */ char ctty[] = _PATH_CONSOLE; /* this is read-only; used by omfile -- TODO: remove that dependency */ static pid_t myPid; /* our pid for use in self-generated messages, e.g. on startup */ /* mypid is read-only after the initial fork() */ -static int debugging_on = 0; /* read-only, except on sig USR1 */ static int restart = 0; /* do restart (config read) - multithread safe */ -static int bRequestDoMark = 0; /* do mark processing? (multithread safe) */ -#define MAXFUNIX 20 - int glblHadMemShortage = 0; /* indicates if we had memory shortage some time during the run */ -int startIndexUxLocalSockets = 0; /* process funix from that index on (used to - * suppress local logging. rgerhards 2005-08-01 - * read-only after startup - */ -int funixParseHost[MAXFUNIX] = { 0, }; /* should parser parse host name? read-only after startup */ -char *funixn[MAXFUNIX] = { _PATH_LOG }; /* read-only after startup */ -int funix[MAXFUNIX] = { -1, }; /* read-only after startup */ - -#define INTERNAL_NOPRI 0x10 /* the "no priority" priority */ -#define TABLE_NOPRI 0 /* Value to indicate no priority in f_pmask */ -#define TABLE_ALLPRI 0xFF /* Value to indicate all priorities in f_pmask */ -#define LOG_MARK LOG_MAKEPRI(LOG_NFACILITIES, 0) /* mark "facility" */ - -/* definitions used for doNameLine to differentiate between different command types - * (with otherwise identical code). This is a left-over from the previous config - * system. It stays, because it is still useful. So do not wonder why it looks - * somewhat strange (at least its name). -- rgerhards, 2007-08-01 - */ -enum eDirective { DIR_TEMPLATE = 0, DIR_OUTCHANNEL = 1, DIR_ALLOWEDSENDER = 2}; - -/* The following global variables are used for building - * tag and host selector lines during startup and config reload. - * This is stored as a global variable pool because of its ease. It is - * also fairly compatible with multi-threading as the stratup code must - * be run in a single thread anyways. So there can be no race conditions. These - * variables are no longer used once the configuration has been loaded (except, - * of course, during a reload). rgerhards 2005-10-18 - */ -static EHostnameCmpMode eDfltHostnameCmpMode; -static rsCStrObj *pDfltHostnameCmp; -static rsCStrObj *pDfltProgNameCmp; - -/* supporting structures for multithreading */ -#ifdef USE_PTHREADS -/* this is the first approach to a queue, this time with static - * memory. - */ -typedef struct { - void** pbuf; - long head, tail; - int full, empty; - pthread_mutex_t *mut; - pthread_cond_t *notFull, *notEmpty; -} msgQueue; - -int iMainMsgQueueSize; -int bRunningMultithreaded = 0; /* Is this program running in multithreaded mode? */ -msgQueue *pMsgQueue = NULL; -static pthread_t thrdWorker; -static int bGlblDone = 0; -#endif -/* END supporting structures for multithreading */ + static int bParseHOSTNAMEandTAG = 1; /* global config var: should the hostname and tag be * parsed inside message - rgerhards, 2006-03-13 */ @@ -385,180 +269,77 @@ static int bFinished = 0; /* used by termination signal handler, read-only excep * termination. */ -/* - * Intervals at which we flush out "message repeated" messages, +/* Intervals at which we flush out "message repeated" messages, * in seconds after previous message is logged. After each flush, * we move to the next interval until we reach the largest. + * TODO: this shall go into action object! -- rgerhards, 2008-01-29 */ -int repeatinterval[] = { 30, 60 }; /* # of secs before flush */ -#define MAXREPEAT ((int)((sizeof(repeatinterval) / sizeof(repeatinterval[0])) - 1)) -#define REPEATTIME(f) ((f)->f_time + repeatinterval[(f)->f_repeatcount]) -#define BACKOFF(f) { if (++(f)->f_repeatcount > MAXREPEAT) \ - (f)->f_repeatcount = MAXREPEAT; \ - } -#ifdef SYSLOG_INET -union sockunion { - struct sockinet { - u_char si_len; - u_char si_family; - } su_si; - struct sockaddr_in su_sin; - struct sockaddr_in6 su_sin6; -}; -#endif +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!) */ -struct code { - char *c_name; - int c_val; -}; - -static struct code PriNames[] = { - {"alert", LOG_ALERT}, - {"crit", LOG_CRIT}, - {"debug", LOG_DEBUG}, - {"emerg", LOG_EMERG}, - {"err", LOG_ERR}, - {"error", LOG_ERR}, /* DEPRECATED */ - {"info", LOG_INFO}, - {"none", INTERNAL_NOPRI}, /* INTERNAL */ - {"notice", LOG_NOTICE}, - {"panic", LOG_EMERG}, /* DEPRECATED */ - {"warn", LOG_WARNING}, /* DEPRECATED */ - {"warning", LOG_WARNING}, - {"*", TABLE_ALLPRI}, - {NULL, -1} -}; - -static struct code FacNames[] = { - {"auth", LOG_AUTH}, - {"authpriv", LOG_AUTHPRIV}, - {"cron", LOG_CRON}, - {"daemon", LOG_DAEMON}, - {"kern", LOG_KERN}, - {"lpr", LOG_LPR}, - {"mail", LOG_MAIL}, - {"mark", LOG_MARK}, /* INTERNAL */ - {"news", LOG_NEWS}, - {"security", LOG_AUTH}, /* DEPRECATED */ - {"syslog", LOG_SYSLOG}, - {"user", LOG_USER}, - {"uucp", LOG_UUCP}, -#if defined(LOG_FTP) - {"ftp", LOG_FTP}, -#endif - {"local0", LOG_LOCAL0}, - {"local1", LOG_LOCAL1}, - {"local2", LOG_LOCAL2}, - {"local3", LOG_LOCAL3}, - {"local4", LOG_LOCAL4}, - {"local5", LOG_LOCAL5}, - {"local6", LOG_LOCAL6}, - {"local7", LOG_LOCAL7}, - {NULL, -1}, -}; - static pid_t ppid; /* This is a quick and dirty hack used for spliting main/startup thread */ +typedef struct legacyOptsLL_s { + uchar *line; + struct legacyOptsLL_s *next; +} legacyOptsLL_t; +legacyOptsLL_t *pLegacyOptsLL = NULL; + /* global variables for config file state */ static int bDropTrailingLF = 1; /* drop trailing LF's on reception? */ -int Debug; /* debug flag - read-only after startup */ +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. */ 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? */ int bDropMalPTRMsgs = 0;/* Drop messages which have malicious PTR records during DNS lookup */ static uchar cCCEscapeChar = '\\';/* character to be used to start an escape sequence for control chars */ static int bEscapeCCOnRcv = 1; /* escape control characters on reception: 0 - no, 1 - yes */ -static int bReduceRepeatMsgs; /* reduce repeated message - 0 - no, 1 - yes */ -static int bActExecWhenPrevSusp; /* execute action only when previous one was suspended? */ -static int logEveryMsg = 0;/* no repeat message processing - read-only after startup - * 0 - suppress duplicate messages - * 1 - do NOT suppress duplicate messages - */ +int bReduceRepeatMsgs; /* reduce repeated message - 0 - no, 1 - yes */ +int bActExecWhenPrevSusp; /* execute action only when previous one was suspended? */ +uchar *pszWorkDir = NULL;/* name of rsyslog's spool directory (without trailing slash) */ /* end global config file state variables */ -static unsigned int Forwarding = 0; -static int nfunix = 1; /* number of Unix sockets open / read-only after startup */ char LocalHostName[MAXHOSTNAMELEN+1];/* our hostname - read-only after startup */ char *LocalDomain; /* our local domain name - read-only after startup */ -int *finet = NULL; /* Internet datagram sockets, first element is nbr of elements - * read-only after init(), but beware of restart! */ -static char *LogPort = "514"; /* port number for INET connections */ -static int MarkInterval = 20 * 60; /* interval between marks in seconds - read-only after startup */ +int MarkInterval = 20 * 60; /* interval between marks in seconds - read-only after startup */ int family = PF_UNSPEC; /* protocol family (IPv4, IPv6 or both), set via cmdline */ int send_to_all = 0; /* send message to all IPv4/IPv6 addresses */ -static int MarkSeq = 0; /* mark sequence number - modified in domark() only */ static int NoFork = 0; /* don't fork - don't run in daemon mode - read-only after startup */ -static int AcceptRemote = 0;/* receive messages that come via UDP - read-only after startup */ -int ACLAddHostnameOnFail = 0; /* add hostname to acl when DNS resolving has failed */ -int ACLDontResolve = 0; /* add hostname to acl instead of resolving it to IP(s) */ int DisableDNS = 0; /* don't look up IP addresses of remote messages */ char **StripDomains = NULL;/* these domains may be stripped before writing logs - r/o after s.u., never touched by init */ char **LocalHosts = NULL;/* these hosts are logged with their hostname - read-only after startup, never touched by init */ -int NoHops = 1; /* Can we bounce syslog messages through an - intermediate host. Read-only after startup */ -static int Initialized = 0; /* set when we have initialized ourselves - * rgerhards 2004-11-09: and by initialized, we mean that - * the configuration file could be properly read AND the - * syslog/udp port could be obtained (the later is debatable). - * It is mainly a setting used for emergency logging: if - * something really goes wild, we can not do as indicated in - * the log file, but we still log messages to the system - * console. This is probably the best that can be done in - * such a case. - * read-only after startup, but modified during restart - */ +static int bHaveMainQueue = 0;/* set to 1 if the main queue - in queueing mode - is available + * If the main queue is either not yet ready or not running in + * queueing mode (mode DIRECT!), then this is set to 0. + */ extern int errno; - -/* This structure represents the files that will have log - * copies printed. - * RGerhards 2004-11-08: Each instance of the filed structure - * describes what I call an "output channel". This is important - * to mention as we now allow database connections to be - * present in the filed structure. If helps immensely, if we - * think of it as the abstraction of an output channel. - * rgerhards, 2005-10-26: The structure below provides ample - * opportunity for non-thread-safety. Each of the variable - * accesses must be carefully evaluated, many of them probably - * be guarded by mutexes. But beware of deadlocks... - * rgerhards, 2007-08-01: as you can see, the structure has shrunk pretty much. I will - * remove some of the comments some time. It's still the structure that controls much - * of the processing that goes on in syslogd, but it now has lots of helpers. - */ -struct filed { - struct filed *f_next; /* next in linked list */ - /* filter properties */ - enum { - FILTER_PRI = 0, /* traditional PRI based filer */ - FILTER_PROP = 1 /* extended filter, property based */ - } f_filter_type; - EHostnameCmpMode eHostnameCmpMode; - rsCStrObj *pCSHostnameComp; /* hostname to check */ - rsCStrObj *pCSProgNameComp; /* tag to check or NULL, if not to be checked */ - union { - u_char f_pmask[LOG_NFACILITIES+1]; /* priority mask */ - struct { - rsCStrObj *pCSPropName; - enum { - FIOP_NOP = 0, /* do not use - No Operation */ - FIOP_CONTAINS = 1, /* contains string? */ - FIOP_ISEQUAL = 2, /* is (exactly) equal? */ - FIOP_STARTSWITH = 3, /* starts with a string? */ - FIOP_REGEX = 4 /* matches a regular expression? */ - } operation; - rsCStrObj *pCSCompValue; /* value to "compare" against */ - char isNegated; /* actually a boolean ;) */ - } prop; - } f_filterData; - - linkedList_t llActList; /* list of configured actions */ -}; -typedef struct filed selector_t; /* new type name */ +/* main message queue and its configuration parameters */ +static queue_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 */ +static int iMainMsgQDiscardMark = 9800; /* begin to discard messages */ +static int iMainMsgQDiscardSeverity = 8; /* by default, discard nothing to prevent unintentional loss */ +static int iMainMsgQueueNumWorkers = 1; /* number of worker threads for the mm queue above */ +static queueType_t MainMsgQueType = QUEUETYPE_FIXED_ARRAY; /* type of the main message queue above */ +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 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 int bMainMsgQSaveOnShutdown = 1; /* save queue on shutdown (when DA enabled)? */ +static int64 iMainMsgQueMaxDiskSpace = 0; /* max disk space allocated 0 ==> unlimited */ /* support for simple textual representation of FIOP names @@ -599,50 +380,50 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a bDebugPrintCfSysLineHandlerList = 1; bDebugPrintModuleList = 1; bEscapeCCOnRcv = 1; /* default is to escape control characters */ - bReduceRepeatMsgs = (logEveryMsg == 1) ? 0 : 1; + bReduceRepeatMsgs = 0; bDropMalPTRMsgs = 0; - if(pModDir != NULL) { - free(pModDir); - pModDir = NULL; + if(pszWorkDir != NULL) { + free(pszWorkDir); + pszWorkDir = NULL; } -#ifdef USE_PTHREADS - iMainMsgQueueSize = 10000; -#endif -#if defined(SYSLOG_INET) && defined(USE_GSSAPI) - if (gss_listen_service_name != NULL) { - free(gss_listen_service_name); - gss_listen_service_name = NULL; + if(pszMainMsgQFName != NULL) { + free(pszMainMsgQFName); + pszMainMsgQFName = NULL; } -#endif + iMainMsgQueueSize = 10000; + iMainMsgQHighWtrMark = 8000; + iMainMsgQLowWtrMark = 2000; + iMainMsgQDiscardMark = 9800; + iMainMsgQDiscardSeverity = 4; + iMainMsgQueMaxFileSize = 1024 * 1024; + iMainMsgQueueNumWorkers = 1; + iMainMsgQPersistUpdCnt = 0; + iMainMsgQtoQShutdown = 0; + iMainMsgQtoActShutdown = 1000; + iMainMsgQtoEnq = 2000; + iMainMsgQtoWrkShutdown = 60000; + iMainMsgQWrkMinMsgs = 100; + iMainMsgQDeqSlowdown = 0; + bMainMsgQSaveOnShutdown = 1; + MainMsgQueType = QUEUETYPE_FIXED_ARRAY; + iMainMsgQueMaxDiskSpace = 0; + glbliActionResumeRetryCount = 0; return RS_RET_OK; } -/* support for defining allowed TCP and UDP senders. We use the same - * structure to implement this (a linked list), but we define two different - * list roots, one for UDP and one for TCP. - * rgerhards, 2005-09-26 - */ -#ifdef SYSLOG_INET -/* All of the five below are read-only after startup */ -static struct AllowedSenders *pAllowedSenders_UDP = NULL; /* the roots of the allowed sender */ -struct AllowedSenders *pAllowedSenders_TCP = NULL; /* lists. If NULL, all senders are ok! */ -static struct AllowedSenders *pLastAllowedSenders_UDP = NULL; /* and now the pointers to the last */ -static struct AllowedSenders *pLastAllowedSenders_TCP = NULL; /* element in the respective list */ -#ifdef USE_GSSAPI -struct AllowedSenders *pAllowedSenders_GSS = NULL; -static struct AllowedSenders *pLastAllowedSenders_GSS = NULL; -#endif -#endif /* #ifdef SYSLOG_INET */ int option_DisallowWarning = 1; /* complain if message from disallowed sender is received */ /* hardcoded standard templates (used for defaults) */ -static uchar template_TraditionalFormat[] = "\"%TIMESTAMP% %HOSTNAME% %syslogtag%%msg:::drop-last-lf%\n\""; +static uchar template_SyslogProtocol23Format[] = "\"<%PRI%>1 %TIMESTAMP:::date-rfc3339% %HOSTNAME% %APP-NAME% %PROCID% %MSGID% %STRUCTURED-DATA% %msg%\n\""; +static uchar template_TraditionalFileFormat[] = "\"%TIMESTAMP% %HOSTNAME% %syslogtag%%msg:::drop-last-lf%\n\""; +static uchar template_FileFormat[] = "\"%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag%%msg:::drop-last-lf%\n\""; static uchar template_WallFmt[] = "\"\r\n\7Message from syslogd@%HOSTNAME% at %timegenerated% ...\r\n %syslogtag%%msg%\n\r\""; -static uchar template_StdFwdFmt[] = "\"<%PRI%>%TIMESTAMP% %HOSTNAME% %syslogtag%%msg%\""; +static uchar template_ForwardFormat[] = "\"<%PRI%>%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag:1:32%%msg%\""; +static uchar template_TraditionalForwardFormat[] = "\"<%PRI%>%TIMESTAMP% %HOSTNAME% %syslogtag:1:32%%msg%\""; static uchar template_StdUsrMsgFmt[] = "\" %syslogtag%%msg%\n\r\""; static uchar template_StdDBFmt[] = "\"insert into SystemEvents (Message, Facility, FromHost, Priority, DeviceReportedTime, ReceivedAt, InfoUnitID, SysLogTag) values ('%msg%', %syslogfacility%, '%HOSTNAME%', %syslogpriority%, '%timereported:::date-mysql%', '%timegenerated:::date-mysql%', %iut%, '%syslogtag%')\",SQL"; static uchar template_StdPgSQLFmt[] = "\"insert into SystemEvents (Message, Facility, FromHost, Priority, DeviceReportedTime, ReceivedAt, InfoUnitID, SysLogTag) values ('%msg%', %syslogfacility%, '%HOSTNAME%', %syslogpriority%, '%timereported:::date-pgsql%', '%timegenerated:::date-pgsql%', %iut%, '%syslogtag%')\",STDSQL"; @@ -650,1203 +431,48 @@ static uchar template_StdPgSQLFmt[] = "\"insert into SystemEvents (Message, Faci /* up to the next comment, prototypes that should be removed by reordering */ -#ifdef USE_PTHREADS -static msgQueue *queueInit (void); -static void *singleWorker(); /* REMOVEME later 2005-10-24 */ -#endif /* Function prototypes. */ static char **crunch_list(char *list); -static void printline(char *hname, char *msg, int iSource); -static void logmsg(int pri, msg_t*, int flags); -static rsRetVal fprintlog(action_t *pAction); static void reapchild(); static void debug_switch(); -static rsRetVal cfline(uchar *line, selector_t **pfCurr); -static int decode(uchar *name, struct code *codetab); static void sighup_handler(); -static void die(int sig); static void freeSelectors(void); -static rsRetVal processConfFile(uchar *pConfFile); -static rsRetVal selectorAddList(selector_t *f); static void processImInternal(void); -/* Code for handling allowed/disallowed senders - */ -#ifdef SYSLOG_INET -static inline void MaskIP6 (struct in6_addr *addr, uint8_t bits) { - register uint8_t i; - - assert (addr != NULL); - assert (bits <= 128); - - i = bits/32; - if (bits%32) - addr->s6_addr32[i++] &= htonl(0xffffffff << (32 - (bits % 32))); - for (; i < (sizeof addr->s6_addr32)/4; i++) - addr->s6_addr32[i] = 0; -} - -static inline void MaskIP4 (struct in_addr *addr, uint8_t bits) { - - assert (addr != NULL); - assert (bits <=32 ); - - addr->s_addr &= htonl(0xffffffff << (32 - bits)); -} - -#define SIN(sa) ((struct sockaddr_in *)(sa)) -#define SIN6(sa) ((struct sockaddr_in6 *)(sa)) - -/* This function adds an allowed sender entry to the ACL linked list. - * In any case, a single entry is added. If an error occurs, the - * function does its error reporting itself. All validity checks - * must already have been done by the caller. - * This is a helper to AddAllowedSender(). - * rgerhards, 2007-07-17 - */ -static rsRetVal AddAllowedSenderEntry(struct AllowedSenders **ppRoot, struct AllowedSenders **ppLast, - struct NetAddr *iAllow, uint8_t iSignificantBits) -{ - struct AllowedSenders *pEntry = NULL; - - assert(ppRoot != NULL); - assert(ppLast != NULL); - assert(iAllow != NULL); - - if((pEntry = (struct AllowedSenders*) calloc(1, sizeof(struct AllowedSenders))) == NULL) { - glblHadMemShortage = 1; - return RS_RET_OUT_OF_MEMORY; /* no options left :( */ - } - - memcpy(&(pEntry->allowedSender), iAllow, sizeof (struct NetAddr)); - pEntry->pNext = NULL; - pEntry->SignificantBits = iSignificantBits; - - /* enqueue */ - if(*ppRoot == NULL) { - *ppRoot = pEntry; - } else { - (*ppLast)->pNext = pEntry; - } - *ppLast = pEntry; - - return RS_RET_OK; -} - -/* function to clear the allowed sender structure in cases where - * it must be freed (occurs most often when HUPed. - * TODO: reconsider recursive implementation - */ -static void clearAllowedSenders (struct AllowedSenders *pAllow) { - if (pAllow != NULL) { - if (pAllow->pNext != NULL) - clearAllowedSenders (pAllow->pNext); - else { - if (F_ISSET(pAllow->allowedSender.flags, ADDR_NAME)) - free (pAllow->allowedSender.addr.HostWildcard); - else - free (pAllow->allowedSender.addr.NetAddr); - - free (pAllow); - } - } -} - -/* function to add an allowed sender to the allowed sender list. The - * root of the list is caller-provided, so it can be used for all - * supported lists. The caller must provide a pointer to the root, - * as it eventually needs to be updated. Also, a pointer to the - * pointer to the last element must be provided (to speed up adding - * list elements). - * rgerhards, 2005-09-26 - * If a hostname is given there are possible multiple entries - * added (all addresses from that host). - */ -static rsRetVal AddAllowedSender(struct AllowedSenders **ppRoot, struct AllowedSenders **ppLast, - struct NetAddr *iAllow, uint8_t iSignificantBits) -{ - DEFiRet; - - assert(ppRoot != NULL); - assert(ppLast != NULL); - assert(iAllow != NULL); - - if (!F_ISSET(iAllow->flags, ADDR_NAME)) { - if(iSignificantBits == 0) - /* we handle this seperatly just to provide a better - * error message. - */ - logerror("You can not specify 0 bits of the netmask, this would " - "match ALL systems. If you really intend to do that, " - "remove all $AllowedSender directives."); - - switch (iAllow->addr.NetAddr->sa_family) { - case AF_INET: - if((iSignificantBits < 1) || (iSignificantBits > 32)) { - logerrorInt("Invalid bit number in IPv4 address - adjusted to 32", - (int)iSignificantBits); - iSignificantBits = 32; - } - - MaskIP4 (&(SIN(iAllow->addr.NetAddr)->sin_addr), iSignificantBits); - break; - case AF_INET6: - if((iSignificantBits < 1) || (iSignificantBits > 128)) { - logerrorInt("Invalid bit number in IPv6 address - adjusted to 128", - iSignificantBits); - iSignificantBits = 128; - } - - MaskIP6 (&(SIN6(iAllow->addr.NetAddr)->sin6_addr), iSignificantBits); - break; - default: - /* rgerhards, 2007-07-16: We have an internal program error in this - * case. However, there is not much we can do against it right now. Of - * course, we could abort, but that would probably cause more harm - * than good. So we continue to run. We simply do not add this line - the - * worst thing that happens is that one host will not be allowed to - * log. - */ - logerrorInt("Internal error caused AllowedSender to be ignored, AF = %d", - iAllow->addr.NetAddr->sa_family); - return RS_RET_ERR; - } - /* OK, entry constructed, now lets add it to the ACL list */ - iRet = AddAllowedSenderEntry(ppRoot, ppLast, iAllow, iSignificantBits); - } else { - /* we need to process a hostname ACL */ - if (DisableDNS) { - logerror ("Ignoring hostname based ACLs because DNS is disabled."); - return RS_RET_OK; - } - - if (!strchr (iAllow->addr.HostWildcard, '*') && - !strchr (iAllow->addr.HostWildcard, '?') && - ACLDontResolve == 0) { - /* single host - in this case, we pull its IP addresses from DNS - * and add IP-based ACLs. - */ - struct addrinfo hints, *res, *restmp; - struct NetAddr allowIP; - - memset (&hints, 0, sizeof (struct addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_DGRAM; -# ifdef AI_ADDRCONFIG /* seems not to be present on all systems */ - hints.ai_flags = AI_ADDRCONFIG; -# endif - - if (getaddrinfo (iAllow->addr.HostWildcard, NULL, &hints, &res) != 0) { - logerrorSz("DNS error: Can't resolve \"%s\"", iAllow->addr.HostWildcard); - - if (ACLAddHostnameOnFail) { - logerrorSz("Adding hostname \"%s\" to ACL as a wildcard entry.", iAllow->addr.HostWildcard); - return AddAllowedSenderEntry(ppRoot, ppLast, iAllow, iSignificantBits); - } else { - logerrorSz("Hostname \"%s\" WON\'T be added to ACL.", iAllow->addr.HostWildcard); - return RS_RET_NOENTRY; - } - } - - for (restmp = res ; res != NULL ; res = res->ai_next) { - switch (res->ai_family) { - case AF_INET: /* add IPv4 */ - iSignificantBits = 32; - allowIP.flags = 0; - if((allowIP.addr.NetAddr = malloc(res->ai_addrlen)) == NULL) { - glblHadMemShortage = 1; - return RS_RET_OUT_OF_MEMORY; - } - memcpy(allowIP.addr.NetAddr, res->ai_addr, res->ai_addrlen); - - if((iRet = AddAllowedSenderEntry(ppRoot, ppLast, &allowIP, iSignificantBits)) - != RS_RET_OK) - return(iRet); - break; - case AF_INET6: /* IPv6 - but need to check if it is a v6-mapped IPv4 */ - if(IN6_IS_ADDR_V4MAPPED (&SIN6(res->ai_addr)->sin6_addr)) { - /* extract & add IPv4 */ - - iSignificantBits = 32; - allowIP.flags = 0; - if((allowIP.addr.NetAddr = malloc(sizeof(struct sockaddr_in))) - == NULL) { - glblHadMemShortage = 1; - return RS_RET_OUT_OF_MEMORY; - } - SIN(allowIP.addr.NetAddr)->sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - SIN(allowIP.addr.NetAddr)->sin_len = sizeof (struct sockaddr_in); -#endif - SIN(allowIP.addr.NetAddr)->sin_port = 0; - memcpy(&(SIN(allowIP.addr.NetAddr)->sin_addr.s_addr), - &(SIN6(res->ai_addr)->sin6_addr.s6_addr32[3]), - sizeof (struct sockaddr_in)); - - if((iRet = AddAllowedSenderEntry(ppRoot, ppLast, &allowIP, - iSignificantBits)) - != RS_RET_OK) - return(iRet); - } else { - /* finally add IPv6 */ - - iSignificantBits = 128; - allowIP.flags = 0; - if((allowIP.addr.NetAddr = malloc(res->ai_addrlen)) == NULL) { - glblHadMemShortage = 1; - return RS_RET_OUT_OF_MEMORY; - } - memcpy(allowIP.addr.NetAddr, res->ai_addr, res->ai_addrlen); - - if((iRet = AddAllowedSenderEntry(ppRoot, ppLast, &allowIP, - iSignificantBits)) - != RS_RET_OK) - return(iRet); - } - break; - } - } - freeaddrinfo (restmp); - } else { - /* wildcards in hostname - we need to add a text-based ACL. - * For this, we already have everything ready and just need - * to pass it along... - */ - iRet = AddAllowedSenderEntry(ppRoot, ppLast, iAllow, iSignificantBits); - } - } - - return iRet; -} -#endif /* #ifdef SYSLOG_INET */ - - -#ifdef SYSLOG_INET -/* Print an allowed sender list. The caller must tell us which one. - * iListToPrint = 1 means UDP, 2 means TCP - * rgerhards, 2005-09-27 - */ -static void PrintAllowedSenders(int iListToPrint) -{ - struct AllowedSenders *pSender; - uchar szIP[64]; - - assert((iListToPrint == 1) || (iListToPrint == 2) -#ifdef USE_GSSAPI - || (iListToPrint == 3) -#endif - ); - - printf("\nAllowed %s Senders:\n", - (iListToPrint == 1) ? "UDP" : -#ifdef USE_GSSAPI - (iListToPrint == 3) ? "GSS" : -#endif - "TCP"); - - pSender = (iListToPrint == 1) ? pAllowedSenders_UDP : -#ifdef USE_GSSAPI - (iListToPrint == 3) ? pAllowedSenders_GSS : -#endif - pAllowedSenders_TCP; - if(pSender == NULL) { - printf("\tNo restrictions set.\n"); - } else { - while(pSender != NULL) { - if (F_ISSET(pSender->allowedSender.flags, ADDR_NAME)) - printf ("\t%s\n", pSender->allowedSender.addr.HostWildcard); - else { - if(getnameinfo (pSender->allowedSender.addr.NetAddr, - SALEN(pSender->allowedSender.addr.NetAddr), - (char*)szIP, 64, NULL, 0, NI_NUMERICHOST) == 0) { - printf ("\t%s/%u\n", szIP, pSender->SignificantBits); - } else { - /* getnameinfo() failed - but as this is only a - * debug function, we simply spit out an error and do - * not care much about it. - */ - dbgprintf("\tERROR in getnameinfo() - something may be wrong " - "- ignored for now\n"); - } - } - pSender = pSender->pNext; - } - } -} - - -/* compares a host to an allowed sender list entry. Handles all subleties - * including IPv4/v6 as well as domain name wildcards. - * This is a helper to isAllowedSender. As it is only called once, it is - * declared inline. - * Returns 0 if they do not match, something else otherwise. - * contributed 1007-07-16 by mildew@gmail.com - */ -static inline int MaskCmp(struct NetAddr *pAllow, uint8_t bits, struct sockaddr *pFrom, const char *pszFromHost) -{ - assert(pAllow != NULL); - assert(pFrom != NULL); - - if(F_ISSET(pAllow->flags, ADDR_NAME)) { - dbgprintf("MaskCmp: host=\"%s\"; pattern=\"%s\"\n", pszFromHost, pAllow->addr.HostWildcard); - - return(fnmatch(pAllow->addr.HostWildcard, pszFromHost, FNM_NOESCAPE|FNM_CASEFOLD) == 0); - } else {/* We need to compare an IP address */ - switch (pFrom->sa_family) { - case AF_INET: - if (AF_INET == pAllow->addr.NetAddr->sa_family) - return(( SIN(pFrom)->sin_addr.s_addr & htonl(0xffffffff << (32 - bits)) ) - == SIN(pAllow->addr.NetAddr)->sin_addr.s_addr); - else - return 0; - break; - case AF_INET6: - switch (pAllow->addr.NetAddr->sa_family) { - case AF_INET6: { - struct in6_addr ip, net; - register uint8_t i; - - memcpy (&ip, &(SIN6(pFrom))->sin6_addr, sizeof (struct in6_addr)); - memcpy (&net, &(SIN6(pAllow->addr.NetAddr))->sin6_addr, sizeof (struct in6_addr)); - - i = bits/32; - if (bits % 32) - ip.s6_addr32[i++] &= htonl(0xffffffff << (32 - (bits % 32))); - for (; i < (sizeof ip.s6_addr32)/4; i++) - ip.s6_addr32[i] = 0; - - return (memcmp (ip.s6_addr, net.s6_addr, sizeof ip.s6_addr) == 0 && - (SIN6(pAllow->addr.NetAddr)->sin6_scope_id != 0 ? - SIN6(pFrom)->sin6_scope_id == SIN6(pAllow->addr.NetAddr)->sin6_scope_id : 1)); - } - case AF_INET: { - struct in6_addr *ip6 = &(SIN6(pFrom))->sin6_addr; - struct in_addr *net = &(SIN(pAllow->addr.NetAddr))->sin_addr; - - if ((ip6->s6_addr32[3] & (u_int32_t) htonl((0xffffffff << (32 - bits)))) == net->s_addr && -#if BYTE_ORDER == LITTLE_ENDIAN - (ip6->s6_addr32[2] == (u_int32_t)0xffff0000) && -#else - (ip6->s6_addr32[2] == (u_int32_t)0x0000ffff) && -#endif - (ip6->s6_addr32[1] == 0) && (ip6->s6_addr32[0] == 0)) - return 1; - else - return 0; - } - default: - /* Unsupported AF */ - return 0; - } - default: - /* Unsupported AF */ - return 0; - } - } -} - - -/* check if a sender is allowed. The root of the the allowed sender. - * list must be proveded by the caller. As such, this function can be - * used to check both UDP and TCP allowed sender lists. - * returns 1, if the sender is allowed, 0 otherwise. - * rgerhards, 2005-09-26 - */ -int isAllowedSender(struct AllowedSenders *pAllowRoot, struct sockaddr *pFrom, const char *pszFromHost) -{ - struct AllowedSenders *pAllow; - - assert(pFrom != NULL); - - if(pAllowRoot == NULL) - return 1; /* checking disabled, everything is valid! */ - - /* now we loop through the list of allowed senders. As soon as - * we find a match, we return back (indicating allowed). We loop - * until we are out of allowed senders. If so, we fall through the - * loop and the function's terminal return statement will indicate - * that the sender is disallowed. - */ - for(pAllow = pAllowRoot ; pAllow != NULL ; pAllow = pAllow->pNext) { - if (MaskCmp (&(pAllow->allowedSender), pAllow->SignificantBits, pFrom, pszFromHost)) - return 1; - } - return 0; -} -#endif /* #ifdef SYSLOG_INET */ - - -/* code to free all sockets within a socket table. - * A socket table is a descriptor table where the zero - * element has the count of elements. This is used for - * listening sockets. The socket table itself is also - * freed. - * A POINTER to this structure must be provided, thus - * double indirection! - * rgerhards, 2007-06-28 - */ -void freeAllSockets(int **socks) -{ - assert(socks != NULL); - assert(*socks != NULL); - while(**socks) { - dbgprintf("Closing socket %d.\n", (*socks)[**socks]); - close((*socks)[**socks]); - (**socks)--; - } - free(*socks); - socks = NULL; -} - - - - -/******************************************************************* - * BEGIN CODE-LIBLOGGING * - ******************************************************************* - * Code in this section is borrowed from liblogging. This is an - * interim solution. Once liblogging is fully integrated, this is - * to be removed (see http://www.monitorware.com/liblogging for - * more details. 2004-11-16 rgerhards - * - * Please note that the orginal liblogging code is modified so that - * it fits into the context of the current version of syslogd.c. - * - * DO NOT PUT ANY OTHER CODE IN THIS BEGIN ... END BLOCK!!!! - */ - -/** - * Parse a 32 bit integer number from a string. - * - * \param ppsz Pointer to the Pointer to the string being parsed. It - * must be positioned at the first digit. Will be updated - * so that on return it points to the first character AFTER - * the integer parsed. - * \retval The number parsed. - */ - -static int srSLMGParseInt32(char** ppsz) -{ - int i; - - i = 0; - while(isdigit((int) **ppsz)) - { - i = i * 10 + **ppsz - '0'; - ++(*ppsz); - } - - return i; -} - - -/** - * Parse a TIMESTAMP-3339. - * updates the parse pointer position. - */ -static int srSLMGParseTIMESTAMP3339(struct syslogTime *pTime, char** ppszTS) -{ - char *pszTS = *ppszTS; - - assert(pTime != NULL); - assert(ppszTS != NULL); - assert(pszTS != NULL); - - pTime->year = srSLMGParseInt32(&pszTS); - - /* We take the liberty to accept slightly malformed timestamps e.g. in - * the format of 2003-9-1T1:0:0. This doesn't hurt on receiving. Of course, - * with the current state of affairs, we would never run into this code - * here because at postion 11, there is no "T" in such cases ;) - */ - if(*pszTS++ != '-') - return FALSE; - pTime->month = srSLMGParseInt32(&pszTS); - if(pTime->month < 1 || pTime->month > 12) - return FALSE; - - if(*pszTS++ != '-') - return FALSE; - pTime->day = srSLMGParseInt32(&pszTS); - if(pTime->day < 1 || pTime->day > 31) - return FALSE; - - if(*pszTS++ != 'T') - return FALSE; - - pTime->hour = srSLMGParseInt32(&pszTS); - if(pTime->hour < 0 || pTime->hour > 23) - return FALSE; - - if(*pszTS++ != ':') - return FALSE; - pTime->minute = srSLMGParseInt32(&pszTS); - if(pTime->minute < 0 || pTime->minute > 59) - return FALSE; - - if(*pszTS++ != ':') - return FALSE; - pTime->second = srSLMGParseInt32(&pszTS); - if(pTime->second < 0 || pTime->second > 60) - return FALSE; - - /* Now let's see if we have secfrac */ - if(*pszTS == '.') - { - char *pszStart = ++pszTS; - pTime->secfrac = srSLMGParseInt32(&pszTS); - pTime->secfracPrecision = (int) (pszTS - pszStart); - } - else - { - pTime->secfracPrecision = 0; - pTime->secfrac = 0; - } - - /* check the timezone */ - if(*pszTS == 'Z') - { - pszTS++; /* eat Z */ - pTime->OffsetMode = 'Z'; - pTime->OffsetHour = 0; - pTime->OffsetMinute = 0; - } - else if((*pszTS == '+') || (*pszTS == '-')) - { - pTime->OffsetMode = *pszTS; - pszTS++; - - pTime->OffsetHour = srSLMGParseInt32(&pszTS); - if(pTime->OffsetHour < 0 || pTime->OffsetHour > 23) - return FALSE; - - if(*pszTS++ != ':') - return FALSE; - pTime->OffsetMinute = srSLMGParseInt32(&pszTS); - if(pTime->OffsetMinute < 0 || pTime->OffsetMinute > 59) - return FALSE; - } - else - /* there MUST be TZ information */ - return FALSE; - - /* OK, we actually have a 3339 timestamp, so let's indicated this */ - if(*pszTS == ' ') - ++pszTS; - else - return FALSE; - - /* update parse pointer */ - *ppszTS = pszTS; - - return TRUE; -} - - -/** - * Parse a TIMESTAMP-3164. - * Returns TRUE on parse OK, FALSE on parse error. - */ -static int srSLMGParseTIMESTAMP3164(struct syslogTime *pTime, char* pszTS) -{ - assert(pTime != NULL); - assert(pszTS != NULL); - - getCurrTime(pTime); /* obtain the current year and UTC offsets! */ - - /* If we look at the month (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec), - * we may see the following character sequences occur: - * - * J(an/u(n/l)), Feb, Ma(r/y), A(pr/ug), Sep, Oct, Nov, Dec - * - * We will use this for parsing, as it probably is the - * fastest way to parse it. - * - * 2005-07-18, well sometimes it pays to be a bit more verbose, even in C... - * Fixed a bug that lead to invalid detection of the data. The issue was that - * we had an if(++pszTS == 'x') inside of some of the consturcts below. However, - * there were also some elseifs (doing the same ++), which than obviously did not - * check the orginal character but the next one. Now removed the ++ and put it - * into the statements below. Was a really nasty bug... I didn't detect it before - * june, when it first manifested. This also lead to invalid parsing of the rest - * of the message, as the time stamp was not detected to be correct. - rgerhards - */ - switch(*pszTS++) - { - case 'J': - if(*pszTS == 'a') { - ++pszTS; - if(*pszTS == 'n') { - ++pszTS; - pTime->month = 1; - } else - return FALSE; - } else if(*pszTS == 'u') { - ++pszTS; - if(*pszTS == 'n') { - ++pszTS; - pTime->month = 6; - } else if(*pszTS == 'l') { - ++pszTS; - pTime->month = 7; - } else - return FALSE; - } else - return FALSE; - break; - case 'F': - if(*pszTS == 'e') { - ++pszTS; - if(*pszTS == 'b') { - ++pszTS; - pTime->month = 2; - } else - return FALSE; - } else - return FALSE; - break; - case 'M': - if(*pszTS == 'a') { - ++pszTS; - if(*pszTS == 'r') { - ++pszTS; - pTime->month = 3; - } else if(*pszTS == 'y') { - ++pszTS; - pTime->month = 5; - } else - return FALSE; - } else - return FALSE; - break; - case 'A': - if(*pszTS == 'p') { - ++pszTS; - if(*pszTS == 'r') { - ++pszTS; - pTime->month = 4; - } else - return FALSE; - } else if(*pszTS == 'u') { - ++pszTS; - if(*pszTS == 'g') { - ++pszTS; - pTime->month = 8; - } else - return FALSE; - } else - return FALSE; - break; - case 'S': - if(*pszTS == 'e') { - ++pszTS; - if(*pszTS == 'p') { - ++pszTS; - pTime->month = 9; - } else - return FALSE; - } else - return FALSE; - break; - case 'O': - if(*pszTS == 'c') { - ++pszTS; - if(*pszTS == 't') { - ++pszTS; - pTime->month = 10; - } else - return FALSE; - } else - return FALSE; - break; - case 'N': - if(*pszTS == 'o') { - ++pszTS; - if(*pszTS == 'v') { - ++pszTS; - pTime->month = 11; - } else - return FALSE; - } else - return FALSE; - break; - case 'D': - if(*pszTS == 'e') { - ++pszTS; - if(*pszTS == 'c') { - ++pszTS; - pTime->month = 12; - } else - return FALSE; - } else - return FALSE; - break; - default: - return FALSE; - } - - /* done month */ - - if(*pszTS++ != ' ') - return FALSE; - - /* we accept a slightly malformed timestamp when receiving. This is - * we accept one-digit days - */ - if(*pszTS == ' ') - ++pszTS; - - pTime->day = srSLMGParseInt32(&pszTS); - if(pTime->day < 1 || pTime->day > 31) - return FALSE; - - if(*pszTS++ != ' ') - return FALSE; - pTime->hour = srSLMGParseInt32(&pszTS); - if(pTime->hour < 0 || pTime->hour > 23) - return FALSE; - - if(*pszTS++ != ':') - return FALSE; - pTime->minute = srSLMGParseInt32(&pszTS); - if(pTime->minute < 0 || pTime->minute > 59) - return FALSE; - - if(*pszTS++ != ':') - return FALSE; - pTime->second = srSLMGParseInt32(&pszTS); - if(pTime->second < 0 || pTime->second > 60) - return FALSE; - if(*pszTS++ != ':') - - /* OK, we actually have a 3164 timestamp, so let's indicate this - * and fill the rest of the properties. */ - pTime->timeType = 1; - pTime->secfracPrecision = 0; - pTime->secfrac = 0; - return TRUE; -} - -/******************************************************************* - * END CODE-LIBLOGGING * - *******************************************************************/ - -/** - * Format a syslogTimestamp into format required by MySQL. - * We are using the 14 digits format. For example 20041111122600 - * is interpreted as '2004-11-11 12:26:00'. - * The caller must provide the timestamp as well as a character - * buffer that will receive the resulting string. The function - * returns the size of the timestamp written in bytes (without - * the string terminator). If 0 is returend, an error occured. - */ -int formatTimestampToMySQL(struct syslogTime *ts, char* pDst, size_t iLenDst) -{ - /* currently we do not consider localtime/utc. This may later be - * added. If so, I recommend using a property replacer option - * and/or a global configuration option. However, we should wait - * on user requests for this feature before doing anything. - * rgerhards, 2007-06-26 - */ - assert(ts != NULL); - assert(pDst != NULL); - - if (iLenDst < 15) /* we need at least 14 bytes - 14 digits for timestamp + '\n' */ - return(0); - - return(snprintf(pDst, iLenDst, "%4.4d%2.2d%2.2d%2.2d%2.2d%2.2d", - ts->year, ts->month, ts->day, ts->hour, ts->minute, ts->second)); - -} - -int formatTimestampToPgSQL(struct syslogTime *ts, char *pDst, size_t iLenDst) -{ - /* see note in formatTimestampToMySQL, applies here as well */ - assert(ts != NULL); - assert(pDst != NULL); - - if (iLenDst < 21) /* we need 20 bytes + '\n' */ - return(0); - - return(snprintf(pDst, iLenDst, "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d", - ts->year, ts->month, ts->day, ts->hour, ts->minute, ts->second)); -} - -/** - * Format a syslogTimestamp to a RFC3339 timestamp string (as - * specified in syslog-protocol). - * The caller must provide the timestamp as well as a character - * buffer that will receive the resulting string. The function - * returns the size of the timestamp written in bytes (without - * the string terminator). If 0 is returend, an error occured. - */ -int formatTimestamp3339(struct syslogTime *ts, char* pBuf, size_t iLenBuf) -{ - int iRet; - char szTZ[7]; /* buffer for TZ information */ - - assert(ts != NULL); - assert(pBuf != NULL); - - if(iLenBuf < 20) - return(0); /* we NEED at least 20 bytes */ - - /* do TZ information first, this is easier to take care of "Z" zone in rfc3339 */ - if(ts->OffsetMode == 'Z') { - szTZ[0] = 'Z'; - szTZ[1] = '\0'; - } else { - snprintf(szTZ, sizeof(szTZ) / sizeof(char), "%c%2.2d:%2.2d", - ts->OffsetMode, ts->OffsetHour, ts->OffsetMinute); - } - - if(ts->secfracPrecision > 0) - { /* we now need to include fractional seconds. While doing so, we must look at - * the precision specified. For example, if we have millisec precision (3 digits), a - * secFrac value of 12 is not equivalent to ".12" but ".012". Obviously, this - * is a huge difference ;). To avoid this, we first create a format string with - * the specific precision and *then* use that format string to do the actual - * formating (mmmmhhh... kind of self-modifying code... ;)). - */ - char szFmtStr[64]; - /* be careful: there is ONE actual %d in the format string below ;) */ - snprintf(szFmtStr, sizeof(szFmtStr), - "%%04d-%%02d-%%02dT%%02d:%%02d:%%02d.%%0%dd%%s", - ts->secfracPrecision); - iRet = snprintf(pBuf, iLenBuf, szFmtStr, ts->year, ts->month, ts->day, - ts->hour, ts->minute, ts->second, ts->secfrac, szTZ); - } - else - iRet = snprintf(pBuf, iLenBuf, - "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d%s", - ts->year, ts->month, ts->day, - ts->hour, ts->minute, ts->second, szTZ); - return(iRet); -} - -/** - * Format a syslogTimestamp to a RFC3164 timestamp sring. - * The caller must provide the timestamp as well as a character - * buffer that will receive the resulting string. The function - * returns the size of the timestamp written in bytes (without - * the string termnator). If 0 is returend, an error occured. - */ -int formatTimestamp3164(struct syslogTime *ts, char* pBuf, size_t iLenBuf) -{ - static char* monthNames[13] = {"ERR", "Jan", "Feb", "Mar", - "Apr", "May", "Jun", "Jul", - "Aug", "Sep", "Oct", "Nov", "Dec"}; - assert(ts != NULL); - assert(pBuf != NULL); - - if(iLenBuf < 16) - return(0); /* we NEED 16 bytes */ - return(snprintf(pBuf, iLenBuf, "%s %2d %2.2d:%2.2d:%2.2d", - monthNames[ts->month], ts->day, ts->hour, - ts->minute, ts->second - )); -} - -/** - * Format a syslogTimestamp to a text format. - * The caller must provide the timestamp as well as a character - * buffer that will receive the resulting string. The function - * returns the size of the timestamp written in bytes (without - * the string termnator). If 0 is returend, an error occured. - */ -#if 0 /* This method is currently not called, be we like to preserve it */ -static int formatTimestamp(struct syslogTime *ts, char* pBuf, size_t iLenBuf) -{ - assert(ts != NULL); - assert(pBuf != NULL); - - if(ts->timeType == 1) { - return(formatTimestamp3164(ts, pBuf, iLenBuf)); - } - - if(ts->timeType == 2) { - return(formatTimestamp3339(ts, pBuf, iLenBuf)); - } - - return(0); -} -#endif - - -/** - * Get the current date/time in the best resolution the operating - * system has to offer (well, actually at most down to the milli- - * second level. - * - * The date and time is returned in separate fields as this is - * most portable and removes the need for additional structures - * (but I have to admit it is somewhat "bulky";)). - * - * Obviously, all caller-provided pointers must not be NULL... - */ -void getCurrTime(struct syslogTime *t) -{ - struct timeval tp; - struct tm *tm; - struct tm tmBuf; - long lBias; - - assert(t != NULL); - gettimeofday(&tp, NULL); - tm = localtime_r((time_t*) &(tp.tv_sec), &tmBuf); - - t->year = tm->tm_year + 1900; - t->month = tm->tm_mon + 1; - t->day = tm->tm_mday; - t->hour = tm->tm_hour; - t->minute = tm->tm_min; - t->second = tm->tm_sec; - t->secfrac = tp.tv_usec; - t->secfracPrecision = 6; - -# if __sun - /* Solaris uses a different method of exporting the time zone. - * It is UTC - localtime, which is the opposite sign of mins east of GMT. - */ - lBias = -(daylight ? altzone : timezone); -# else - lBias = tm->tm_gmtoff; -# endif - if(lBias < 0) - { - t->OffsetMode = '-'; - lBias *= -1; - } - else - t->OffsetMode = '+'; - t->OffsetHour = lBias / 3600; - t->OffsetMinute = lBias % 3600; -} -/* rgerhards 2004-11-09: end of helper routines. On to the - * "real" code ;) - */ - static int usage(void) { - fprintf(stderr, "usage: rsyslogd [-46AdhqQvw] [-l hostlist] [-m markinterval] [-n] [-p path]\n" \ - " [-s domainlist] [-r[port]] [-tport[,max-sessions]] [-gport[,max-sessions]] [-f conffile] [-i pidfile] [-x]\n"); + fprintf(stderr, "usage: rsyslogd [-cversion] [-46AdnqQvwx] [-lhostlist] [-sdomainlist]\n" + " [-fconffile] [-ipidfile]\n" + "To run rsyslogd in native mode, use \"rsyslogd -c3 <other options>\"\n\n" + "For further information see http://www.rsyslog.com/doc\n"); exit(1); /* "good" exit - done to terminate usage() */ } -#ifdef SYSLOG_UNIXAF -static int create_unix_socket(const char *path) -{ - struct sockaddr_un sunx; - int fd; - char line[MAXLINE +1]; - - if (path[0] == '\0') - return -1; - - (void) unlink(path); - - memset(&sunx, 0, sizeof(sunx)); - sunx.sun_family = AF_UNIX; - (void) strncpy(sunx.sun_path, path, sizeof(sunx.sun_path)); - fd = socket(AF_UNIX, SOCK_DGRAM, 0); - if (fd < 0 || bind(fd, (struct sockaddr *) &sunx, - SUN_LEN(&sunx)) < 0 || - chmod(path, 0666) < 0) { - snprintf(line, sizeof(line), "cannot create %s", path); - logerror(line); - dbgprintf("cannot create %s (%d).\n", path, errno); - close(fd); - return -1; - } - return fd; -} -#endif - -#ifdef SYSLOG_INET -/* closes the UDP listen sockets (if they exist) and frees - * all dynamically assigned memory. - */ -static void closeUDPListenSockets() -{ - register int i; - - if(finet != NULL) { - for (i = 0; i < *finet; i++) - close(finet[i+1]); - free(finet); - finet = NULL; - } -} - - -/* creates the UDP listen sockets - */ -static int *create_udp_socket() -{ - struct addrinfo hints, *res, *r; - int error, maxs, *s, *socks, on = 1; - int sockflags; - - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; - hints.ai_family = family; - hints.ai_socktype = SOCK_DGRAM; - error = getaddrinfo(NULL, LogPort, &hints, &res); - if(error) { - logerror((char*) gai_strerror(error)); - logerror("UDP message reception disabled due to error logged in last message.\n"); - return NULL; - } - - /* Count max number of sockets we may open */ - for (maxs = 0, r = res; r != NULL ; r = r->ai_next, maxs++) - /* EMPTY */; - socks = malloc((maxs+1) * sizeof(int)); - if (socks == NULL) { - logerror("couldn't allocate memory for UDP sockets, suspending UDP message reception"); - freeaddrinfo(res); - return NULL; - } - - *socks = 0; /* num of sockets counter at start of array */ - s = socks + 1; - for (r = res; r != NULL ; r = r->ai_next) { - *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol); - if (*s < 0) { - if(!(r->ai_family == PF_INET6 && errno == EAFNOSUPPORT)) - logerror("create_udp_socket(), socket"); - /* it is debatable if PF_INET with EAFNOSUPPORT should - * also be ignored... - */ - continue; - } - -# ifdef IPV6_V6ONLY - if (r->ai_family == AF_INET6) { - int ion = 1; - if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY, - (char *)&ion, sizeof (ion)) < 0) { - logerror("setsockopt"); - close(*s); - *s = -1; - continue; - } - } -# endif - - /* if we have an error, we "just" suspend that socket. Eventually - * other sockets will work. At the end of this function, we check - * if we managed to open at least one socket. If not, we'll write - * a "inet suspended" message and declare failure. Else we use - * what we could obtain. - * rgerhards, 2007-06-22 - */ - if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, - (char *) &on, sizeof(on)) < 0 ) { - logerror("setsockopt(REUSEADDR)"); - close(*s); - *s = -1; - continue; - } - - /* We need to enable BSD compatibility. Otherwise an attacker - * could flood our log files by sending us tons of ICMP errors. - */ -#ifndef BSD - if (should_use_so_bsdcompat()) { - if (setsockopt(*s, SOL_SOCKET, SO_BSDCOMPAT, - (char *) &on, sizeof(on)) < 0) { - logerror("setsockopt(BSDCOMPAT)"); - close(*s); - *s = -1; - continue; - } - } -#endif - /* We must not block on the network socket, in case a packet - * gets lost between select and recv, otherwise the process - * will stall until the timeout, and other processes trying to - * log will also stall. - * Patch vom Colin Phipps <cph@cph.demon.co.uk> to the original - * sysklogd source. Applied to rsyslogd on 2005-10-19. - */ - if ((sockflags = fcntl(*s, F_GETFL)) != -1) { - sockflags |= O_NONBLOCK; - /* SETFL could fail too, so get it caught by the subsequent - * error check. - */ - sockflags = fcntl(*s, F_SETFL, sockflags); - } - if (sockflags == -1) { - logerror("fcntl(O_NONBLOCK)"); - close(*s); - *s = -1; - continue; - } - - /* rgerhards, 2007-06-22: if we run on a kernel that does not support - * the IPV6_V6ONLY socket option, we need to use a work-around. On such - * systems the IPv6 socket does also accept IPv4 sockets. So an IPv4 - * socket can not listen on the same port as an IPv6 socket. The only - * workaround is to ignore the "socket in use" error. This is what we - * do if we have to. - */ - if( (bind(*s, r->ai_addr, r->ai_addrlen) < 0) -# ifndef IPV6_V6ONLY - && (errno != EADDRINUSE) -# endif - ) { - logerror("bind"); - close(*s); - *s = -1; - continue; - } - - (*socks)++; - s++; - } - - if(res != NULL) - freeaddrinfo(res); - - if(Debug && *socks != maxs) - dbgprintf("We could initialize %d UDP listen sockets out of %d we received " - "- this may or may not be an error indication.\n", *socks, maxs); - - if(*socks == 0) { - logerror("No UDP listen socket could successfully be initialized, " - "message reception via UDP disabled.\n"); - /* we do NOT need to free any sockets, because there were none... */ - free(socks); - return(NULL); - } - - return(socks); -} -#endif - /* function to destruct a selector_t object * rgerhards, 2007-08-01 */ -static rsRetVal selectorDestruct(void *pVal) +rsRetVal +selectorDestruct(void *pVal) { selector_t *pThis = (selector_t *) pVal; assert(pThis != NULL); if(pThis->pCSHostnameComp != NULL) - rsCStrDestruct(pThis->pCSHostnameComp); + rsCStrDestruct(&pThis->pCSHostnameComp); if(pThis->pCSProgNameComp != NULL) - rsCStrDestruct(pThis->pCSProgNameComp); + rsCStrDestruct(&pThis->pCSProgNameComp); if(pThis->f_filter_type == FILTER_PROP) { if(pThis->f_filterData.prop.pCSPropName != NULL) - rsCStrDestruct(pThis->f_filterData.prop.pCSPropName); + rsCStrDestruct(&pThis->f_filterData.prop.pCSPropName); if(pThis->f_filterData.prop.pCSCompValue != NULL) - rsCStrDestruct(pThis->f_filterData.prop.pCSCompValue); + rsCStrDestruct(&pThis->f_filterData.prop.pCSCompValue); + } 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); @@ -1859,7 +485,8 @@ static rsRetVal selectorDestruct(void *pVal) /* function to construct a selector_t object * rgerhards, 2007-08-01 */ -static rsRetVal selectorConstruct(selector_t **ppThis) +rsRetVal +selectorConstruct(selector_t **ppThis) { DEFiRet; selector_t *pThis; @@ -1879,7 +506,7 @@ finalize_it: } } *ppThis = pThis; - return iRet; + RETiRet; } @@ -1966,7 +593,14 @@ void untty(void) if ( !Debug ) { i = open(_PATH_TTY, O_RDWR); if (i >= 0) { - (void) ioctl(i, (int) TIOCNOTTY, (char *)0); +# if !defined(__hpux) + (void) ioctl(i, (int) TIOCNOTTY, (char *)0); +# 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); } } @@ -1974,7 +608,84 @@ void untty(void) #endif -/* rgerhards, 2006-11-30: I have greatly changed this function. Formerly, +/* 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. + */ +rsRetVal printline(char *hname, char *msg, int bParseHost, int flags, flowControl_t flowCtlType) +{ + DEFiRet; + register char *p; + int pri; + msg_t *pMsg; + + /* Now it is time to create the message object (rgerhards) + */ + CHKiRet(msgConstruct(&pMsg)); + MsgSetFlowControlType(pMsg, flowCtlType); + MsgSetRawMsg(pMsg, msg); + + pMsg->bParseHOSTNAME = bParseHost; + /* 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); + + /* 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(bParseHost == 0) + MsgSetHOSTNAME(pMsg, hname); + MsgSetRcvFrom(pMsg, hname); + + /* 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, p) != 0) + ABORT_FINALIZE(RS_RET_ERR); + + logmsg(pMsg, flags | SYNC_FILE); + +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 @@ -1993,9 +704,22 @@ void untty(void) * 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. */ -void printchopped(char *hname, char *msg, int len, int fd, int bParseHost) +rsRetVal +parseAndSubmitMessage(char *hname, char *msg, int len, int bParseHost, int flags, flowControl_t flowCtlType) { + DEFiRet; register int iMsg; char *pMsg; char *pData; @@ -2010,8 +734,6 @@ void printchopped(char *hname, char *msg, int len, int fd, int bParseHost) assert(msg != NULL); assert(len >= 0); - dbgprintf("Message length: %d, File descriptor: %d.\n", len, fd); - /* 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, @@ -2057,8 +779,8 @@ void printchopped(char *hname, char *msg, int len, int fd, int bParseHost) int ret; iLenDefBuf = MAXLINE; ret = uncompress((uchar *) deflateBuf, &iLenDefBuf, (uchar *) msg+1, len-1); - dbgprintf("Compressed message uncompressed with status %d, length: new %d, old %d.\n", - ret, iLenDefBuf, 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 @@ -2068,10 +790,10 @@ void printchopped(char *hname, char *msg, int len, int fd, int bParseHost) * rgerhards, 2006-12-07 */ if(ret != Z_OK) { - logerrorInt("Uncompression of a message failed with return code %d " + errmsg.LogError(NO_ERRCODE, "Uncompression of a message failed with return code %d " "- enable debug logging if you need further information. " "Message ignored.", ret); - return; /* unconditional exit, nothing left to do... */ + FINALIZE; /* unconditional exit, nothing left to do... */ } pData = deflateBuf; pEnd = deflateBuf + iLenDefBuf; @@ -2081,9 +803,9 @@ void printchopped(char *hname, char *msg, int len, int fd, int bParseHost) * tell the user we can not accept it. */ if(len > 0 && *msg == 'z') { - logerror("Received a compressed message, but rsyslogd does not have compression " + errmsg.LogError(NO_ERRCODE, "Received a compressed message, but rsyslogd does not have compression " "support enabled. The message will be ignored."); - return; + FINALIZE; } # endif /* ifdef USE_NETZIP */ @@ -2094,7 +816,7 @@ void printchopped(char *hname, char *msg, int len, int fd, int bParseHost) */ if(iMsg == MAXLINE) { *(pMsg + iMsg) = '\0'; /* space *is* reserved for this! */ - printline(hname, tmpline, bParseHost); + printline(hname, tmpline, bParseHost, flags, flowCtlType); } 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, @@ -2105,7 +827,7 @@ void printchopped(char *hname, char *msg, int len, int fd, int bParseHost) */ dbgprintf("internal error: iMsg > MAXLINE in printchopped()\n"); } - return; /* in this case, we are done... nothing left we can do */ + 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 @@ -2146,79 +868,10 @@ void printchopped(char *hname, char *msg, int len, int fd, int bParseHost) *(pMsg + iMsg) = '\0'; /* space *is* reserved for this! */ /* typically, we should end up here! */ - printline(hname, tmpline, bParseHost); - - return; -} - -/* 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 - */ -void printline(char *hname, char *msg, int bParseHost) -{ - register char *p; - int pri; - msg_t *pMsg; - - /* Now it is time to create the message object (rgerhards) - */ - if((pMsg = MsgConstruct()) == NULL){ - /* rgerhards, 2007-06-21: if we can not get memory, we discard this - * message but continue to run (in the hope that things improve) - */ - glblHadMemShortage = 1; - dbgprintf("Memory shortage in printline(): Could not construct Msg object.\n"); - return; - } - MsgSetRawMsg(pMsg, msg); - - pMsg->bParseHOSTNAME = bParseHost; - /* 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); - - /* 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(bParseHost == 0) - MsgSetHOSTNAME(pMsg, hname); - MsgSetRcvFrom(pMsg, hname); + printline(hname, tmpline, bParseHost, flags, flowCtlType); - /* 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, p) != 0) return; - - logmsg(pri, pMsg, SYNC_FILE); - - return; +finalize_it: + RETiRet; } /* rgerhards 2004-11-09: the following is a function that can be used @@ -2230,29 +883,13 @@ void printline(char *hname, char *msg, int bParseHost) * function here probably is only an interim solution and that we need to * think on the best way to do this. */ -static void +rsRetVal logmsgInternal(int pri, char *msg, int flags) { + DEFiRet; msg_t *pMsg; - if((pMsg = MsgConstruct()) == NULL){ - /* rgerhards 2004-11-09: calling panic might not be the - * brightest idea - however, it is the best I currently have - * (think a bit more about this). - * rgehards, 2007-06-21: I have now thought a bit more about - * it. If we are so low on memory, there is few we can do. calling - * panic so far only write a debug line - this is seomthing we keep. - * Other than that, however, we ignore the error and hope that - * memory shortage will be resolved while we continue to run. In any - * case, there is no valid point in aborting the syslogd for this - * reason - that would be counter-productive. So we ignore the - * to be logged message. - */ - glblHadMemShortage = 1; - dbgprintf("Memory shortage in logmsgInternal: could not construct Msg object.\n"); - return; - } - + CHKiRet(msgConstruct(&pMsg)); MsgSetUxTradMsg(pMsg, msg); MsgSetRawMsg(pMsg, msg); MsgSetHOSTNAME(pMsg, LocalHostName); @@ -2261,21 +898,19 @@ logmsgInternal(int pri, char *msg, int flags) pMsg->iFacility = LOG_FAC(pri); pMsg->iSeverity = LOG_PRI(pri); pMsg->bParseHOSTNAME = 0; - getCurrTime(&(pMsg->tTIMESTAMP)); /* use the current time! */ + datetime.getCurrTime(&(pMsg->tTIMESTAMP)); /* use the current time! */ flags |= INTERNAL_MSG; -#ifdef USE_PTHREADS - if(bRunningMultithreaded == 0) { /* not yet in queued mode */ + if(bHaveMainQueue == 0) { /* not yet in queued mode */ iminternalAddMsg(pri, pMsg, flags); } else { /* we have the queue, so we can simply provide the * message to the queue engine. */ - logmsg(pri, pMsg, flags); + logmsg(pMsg, flags); } -#else - iminternalAddMsg(pri, pMsg, flags); -#endif +finalize_it: + RETiRet; } /* This functions looks at the given message and checks if it matches the @@ -2285,12 +920,17 @@ logmsgInternal(int pri, char *msg, int flags) * 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. */ -int shouldProcessThisMessage(selector_t *f, msg_t *pMsg) +static rsRetVal shouldProcessThisMessage(selector_t *f, msg_t *pMsg, int *bProcessMsg) { + DEFiRet; unsigned short pbMustBeFreed; char *pszPropVal; - int iRet = 0; + int bRet = 0; + vm_t *pVM = NULL; + var_t *pResult = NULL; assert(f != NULL); assert(pMsg != NULL); @@ -2308,14 +948,14 @@ int shouldProcessThisMessage(selector_t *f, msg_t *pMsg) /* not equal, so we are already done... */ dbgprintf("hostname filter '+%s' does not match '%s'\n", rsCStrGetSzStrNoNULL(f->pCSHostnameComp), getHOSTNAME(pMsg)); - return 0; + 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)); - return 0; + FINALIZE; } } @@ -2336,7 +976,7 @@ int shouldProcessThisMessage(selector_t *f, msg_t *pMsg) /* not equal or inverted selection, so we are already done... */ dbgprintf("programname filter '%s' does not match '%s'\n", rsCStrGetSzStrNoNULL(f->pCSProgNameComp), getProgramName(pMsg)); - return 0; + FINALIZE; } } @@ -2346,9 +986,18 @@ int shouldProcessThisMessage(selector_t *f, msg_t *pMsg) /* 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) ) - iRet = 0; + bRet = 0; else - iRet = 1; + 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); @@ -2357,44 +1006,44 @@ int shouldProcessThisMessage(selector_t *f, msg_t *pMsg) switch(f->f_filterData.prop.operation ) { case FIOP_CONTAINS: if(rsCStrLocateInSzStr(f->f_filterData.prop.pCSCompValue, (uchar*) pszPropVal) != -1) - iRet = 1; + bRet = 1; break; case FIOP_ISEQUAL: if(rsCStrSzStrCmp(f->f_filterData.prop.pCSCompValue, (uchar*) pszPropVal, strlen(pszPropVal)) == 0) - iRet = 1; /* process message! */ + bRet = 1; /* process message! */ break; case FIOP_STARTSWITH: if(rsCStrSzStrStartsWithCStr(f->f_filterData.prop.pCSCompValue, (uchar*) pszPropVal, strlen(pszPropVal)) == 0) - iRet = 1; /* process message! */ + bRet = 1; /* process message! */ break; case FIOP_REGEX: if(rsCStrSzStrMatchRegex(f->f_filterData.prop.pCSCompValue, (unsigned char*) pszPropVal) == 0) - iRet = 1; + bRet = 1; break; default: /* here, it handles NOP (for performance reasons) */ assert(f->f_filterData.prop.operation == FIOP_NOP); - iRet = 1; /* as good as any other default ;) */ + bRet = 1; /* as good as any other default ;) */ break; } /* now check if the value must be negated */ if(f->f_filterData.prop.isNegated) - iRet = (iRet == 1) ? 0 : 1; + bRet = (bRet == 1) ? 0 : 1; if(Debug) { - printf("Filter: check for property '%s' (value '%s') ", + dbgprintf("Filter: check for property '%s' (value '%s') ", rsCStrGetSzStrNoNULL(f->f_filterData.prop.pCSPropName), pszPropVal); if(f->f_filterData.prop.isNegated) - printf("NOT "); - printf("%s '%s': %s\n", + dbgprintf("NOT "); + dbgprintf("%s '%s': %s\n", getFIOPName(f->f_filterData.prop.operation), rsCStrGetSzStrNoNULL(f->f_filterData.prop.pCSCompValue), - iRet ? "TRUE" : "FALSE"); + bRet ? "TRUE" : "FALSE"); } /* cleanup */ @@ -2402,112 +1051,15 @@ int shouldProcessThisMessage(selector_t *f, msg_t *pMsg) free(pszPropVal); } - return(iRet); -} - - -/* doEmergencyLoggin() - * ... does exactly do that. It logs messages when the subsystem has not yet - * been initialized. This almost always happens during initial startup or - * during HUPing. -- rgerhards, 2007-07-25 - * rgerhards, 2007-08-03: as of now, this can normally no longer happen. All - * startup messages are now buffered until the system is ready to run. I leave - * this minimal implementation here in in the very remote case that it might - * be needed in the future or due to a program bug. Do *not* excpect this - * code to be called. - */ -static void doEmergencyLogging(msg_t *pMsg) -{ - assert(pMsg != NULL); - fprintf(stderr, "rsyslog: %s\n", pMsg->pszMSG); -} - - -/* call the configured action. Does all necessary housekeeping. - * rgerhards, 2007-08-01 - */ -static rsRetVal callAction(msg_t *pMsg, action_t *pAction) -{ - DEFiRet; - - assert(pMsg != NULL); - assert(pAction != NULL); - - /* Make sure nodbody else modifies/uses this action object. Right now, this - * is important because of "message repeated n times" processing, later it will - * become important when we (possibly) have multiple worker threads. - * rgerhards, 2007-12-11 - */ - LockObj(pAction); - - /* first, we need to check if this is a disabled - * entry. If so, we must not further process it. - * rgerhards 2005-09-26 - * In the future, disabled modules may be re-probed from time - * to time. They are in a perfectly legal state, except that the - * doAction method indicated that it wanted to be disabled - but - * we do not consider this is a solution for eternity... So we - * should check from time to time if affairs have improved. - * rgerhards, 2007-07-24 - */ - if(pAction->bEnabled == 0) { - ABORT_FINALIZE(RS_RET_OK); - } - - if(actionIsSuspended(pAction)) { - CHKiRet(actionTryResume(pAction)); - } - - /* don't output marks to recently written files */ - if ((pMsg->msgFlags & MARK) && (time(NULL) - pAction->f_time) < MarkInterval / 2) { - ABORT_FINALIZE(RS_RET_OK); - } - - /* suppress duplicate messages - */ - if ((pAction->f_ReduceRepeated == 1) && pAction->f_pMsg != NULL && - (pMsg->msgFlags & MARK) == 0 && getMSGLen(pMsg) == getMSGLen(pAction->f_pMsg) && - !strcmp(getMSG(pMsg), getMSG(pAction->f_pMsg)) && - !strcmp(getHOSTNAME(pMsg), getHOSTNAME(pAction->f_pMsg)) && - !strcmp(getPROCID(pMsg), getPROCID(pAction->f_pMsg)) && - !strcmp(getAPPNAME(pMsg), getAPPNAME(pAction->f_pMsg))) { - pAction->f_prevcount++; - dbgprintf("msg repeated %d times, %ld sec of %d.\n", - pAction->f_prevcount, time(NULL) - pAction->f_time, - repeatinterval[pAction->f_repeatcount]); - /* use current message, so we have the new timestamp (means we need to discard previous one) */ - MsgDestruct(pAction->f_pMsg); - pAction->f_pMsg = MsgAddRef(pMsg); - /* If domark would have logged this by now, flush it now (so we don't hold - * isolated messages), but back off so we'll flush less often in the future. - */ - if(time(NULL) > REPEATTIME(pAction)) { - iRet = fprintlog(pAction); - BACKOFF(pAction); - } - } else { - /* new message, save it */ - /* first check if we have a previous message stored - * if so, emit and then discard it first - */ - if(pAction->f_pMsg != NULL) { - if(pAction->f_prevcount > 0) - CHKiRet(fprintlog(pAction)); - /* if we run into trouble (most importantly a suspended - * action), we keep the old message (by virtue of not - * destructing it) and discard the new one (done - * automatically when we return. - */ - MsgDestruct(pAction->f_pMsg); - } - pAction->f_pMsg = MsgAddRef(pMsg); - /* call the output driver */ - iRet = fprintlog(pAction); +finalize_it: + /* destruct in any case, not just on error, but it makes error handling much easier */ + if(pVM != NULL) { + var.Destruct(&pResult); + vm.Destruct(&pVM); } -finalize_it: - UnlockObj(pAction); - return iRet; + *bProcessMsg = bRet; + RETiRet; } @@ -2533,7 +1085,7 @@ DEFFUNC_llExecFunc(processMsgDoActions) ABORT_FINALIZE(RS_RET_OK); } - iRetMod = callAction(pDoActData->pMsg, pAction); + iRetMod = actionCallAction(pAction, pDoActData->pMsg); if(iRetMod == RS_RET_DISCARDMSG) { ABORT_FINALIZE(RS_RET_DISCARDMSG); } else if(iRetMod == RS_RET_SUSPENDED) { @@ -2544,42 +1096,32 @@ DEFFUNC_llExecFunc(processMsgDoActions) } finalize_it: - return iRet; + RETiRet; } /* Process (consume) a received message. Calls the actions configured. - * Can some time later run in its own thread. To aid this, the calling - * parameters should be reduced to just pMsg. - * See comment dated 2005-10-13 in logmsg() on multithreading. * rgerhards, 2005-10-13 */ -static void processMsg(msg_t *pMsg) +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 */ - if (!Initialized) { - doEmergencyLogging(pMsg); - return; - } bContinue = 1; for (f = Files; f != NULL && bContinue ; f = f->f_next) { - /* This is actually the "filter logic". Looks like we need - * to improve it a little for complex selector line conditions. We - * won't do that for now, but at least we now know where - * to look at. - * 2005-09-09 rgerhards - * ok, we are now ready to move to something more advanced. Because - * of this, I am moving the actual decision code to outside this function. - * 2005-09-19 rgerhards - */ - if(!shouldProcessThisMessage(f, pMsg)) { + /* first check the filters... */ + iRet = shouldProcessThisMessage(f, pMsg, &bProcessMsg); + if(!bProcessMsg) { continue; } @@ -2589,244 +1131,31 @@ static void processMsg(msg_t *pMsg) if(llExecFunc(&f->llActList, processMsgDoActions, (void*)&DoActData) == RS_RET_DISCARDMSG) bContinue = 0; } + ENDfunc } -#ifdef USE_PTHREADS -/* This block contains code that is only present when USE_PTHREADS is - * enabled. I plan to move it to some other file, but for the time - * being, I include it here because that saves me from the need to - * do so many external definitons. - * rgerhards, 2005-10-24 - */ - -/* shuts down the worker process. The worker will first finish - * with the message queue. Control returns, when done. - * This function is intended to be called during syslogd shutdown - * AND restart (init()!). - * rgerhards, 2005-10-25 - */ -static void stopWorker(void) -{ - if(bRunningMultithreaded) { - /* we could run single-threaded if there was an error - * during startup. Then, we obviously do not need to - * do anything to stop the worker ;) - */ - dbgprintf("Initiating worker thread shutdown sequence...\n"); - /* We are now done with all messages, so we need to wake up the - * worker thread and then wait for it to finish. - */ - bGlblDone = 1; - /* It's actually not "not empty" below but awaking the worker. The worker - * then finds out that it shall terminate and does so. - */ - pthread_cond_signal(pMsgQueue->notEmpty); - pthread_join(thrdWorker, NULL); - bRunningMultithreaded = 0; - dbgprintf("Worker thread terminated.\n"); - } -} - - -/* starts the worker thread. It must be made sure that the queue is - * already existing and the worker is NOT already running. - * rgerhards 2005-10-25 - */ -static void startWorker(void) -{ - int i; - if(pMsgQueue != NULL) { - bGlblDone = 0; /* we are NOT done (else worker would immediately terminate) */ - i = pthread_create(&thrdWorker, NULL, singleWorker, NULL); - dbgprintf("Worker thread started with state %d.\n", i); - bRunningMultithreaded = 1; - } else { - dbgprintf("message queue not existing, remaining single-threaded.\n"); - } -} - - -static msgQueue *queueInit (void) -{ - msgQueue *q; - - q = (msgQueue *)malloc(sizeof(msgQueue)); - if (q == NULL) return (NULL); - if((q->pbuf = malloc(sizeof(void *) * iMainMsgQueueSize)) == NULL) { - free(q); - return NULL; - } - - q->empty = 1; - q->full = 0; - q->head = 0; - q->tail = 0; - q->mut = (pthread_mutex_t *) malloc (sizeof (pthread_mutex_t)); - pthread_mutex_init (q->mut, NULL); - q->notFull = (pthread_cond_t *) malloc (sizeof (pthread_cond_t)); - pthread_cond_init (q->notFull, NULL); - q->notEmpty = (pthread_cond_t *) malloc (sizeof (pthread_cond_t)); - pthread_cond_init (q->notEmpty, NULL); - - return (q); -} - -static void queueDelete (msgQueue *q) -{ - pthread_mutex_destroy (q->mut); - free (q->mut); - pthread_cond_destroy (q->notFull); - free (q->notFull); - pthread_cond_destroy (q->notEmpty); - free (q->notEmpty); - free(q->pbuf); - free (q); -} - - -/* In queueAdd() and queueDel() we have a potential race condition. If a message - * is dequeued and at the same time a message is enqueued and the queue is either - * full or empty, the full (or empty) indicator may be invalidly updated. HOWEVER, - * this does not cause any real problems. No queue pointers can be wrong. And even - * if one of the flags is set invalidly, that does not pose a real problem. If - * "full" is invalidly set, at mose one message might be lost, if we are already in - * a timeout situation (this is quite acceptable). And if "empty" is accidently set, - * the receiver will not continue the inner loop, but break out of the outer. So no - * harm is done at all. For this reason, I do not yet use a mutex to guard the two - * flags - there would be a notable performance hit with, IMHO, no gain in stability - * or functionality. But anyhow, now it's documented... - * rgerhards, 2007-09-20 - * NOTE: this comment does not really apply - the callers handle the mutex, so it - * *is* guarded. - */ -static void queueAdd (msgQueue *q, void* in) -{ - q->pbuf[q->tail] = in; - q->tail++; - if (q->tail == iMainMsgQueueSize) - q->tail = 0; - if (q->tail == q->head) - q->full = 1; - q->empty = 0; - - return; -} - -static void queueDel (msgQueue *q, msg_t **out) -{ - *out = (msg_t*) q->pbuf[q->head]; - - q->head++; - if (q->head == iMainMsgQueueSize) - q->head = 0; - if (q->head == q->tail) - q->empty = 1; - q->full = 0; - - return; -} - - -/* The worker thread (so far, we have dual-threading, so only one - * worker thread. Having more than one worker requires considerable - * additional code review in regard to thread-safety. - */ -static void *singleWorker() -{ - msgQueue *fifo = pMsgQueue; - msg_t *pMsg; - sigset_t sigSet; - - assert(fifo != NULL); - - sigfillset(&sigSet); - pthread_sigmask(SIG_BLOCK, &sigSet, NULL); - - while(!bGlblDone || !fifo->empty) { - pthread_mutex_lock(fifo->mut); - while (fifo->empty && !bGlblDone) { - dbgprintf("singleWorker: queue EMPTY, waiting for next message.\n"); - pthread_cond_wait (fifo->notEmpty, fifo->mut); - } - if(!fifo->empty) { - /* dequeue element (still protected from mutex) */ - queueDel(fifo, &pMsg); - assert(pMsg != NULL); - pthread_mutex_unlock(fifo->mut); - pthread_cond_signal (fifo->notFull); - /* do actual processing (the lengthy part, runs in parallel) */ - dbgprintf("Lone worker is running...\n"); - processMsg(pMsg); - MsgDestruct(pMsg); - /* If you need a delay for testing, here do a */ - /* sleep(1); */ - } else { /* the mutex must be unlocked in any case (important for termination) */ - pthread_mutex_unlock(fifo->mut); - } - - if(debugging_on && bGlblDone && !fifo->empty) - dbgprintf("Worker does not yet terminate because it still has messages to process.\n"); - } - - dbgprintf("Worker thread terminates\n"); - pthread_exit(0); -} - -/* END threads-related code */ -#endif /* #ifdef USE_PTHREADS */ - - -/* This method enqueues a message into the the message buffer. It also - * the worker thread, so that the message will be processed. If we are - * compiled without PTHREADS support, we simply use this method as - * an alias for processMsg(). - * See comment dated 2005-10-13 in logmsg() on multithreading. - * rgerhards, 2005-10-24 +/* The consumer of dequeued messages. This function is called by the + * queue engine on dequeueing of a message. It runs on a SEPARATE + * THREAD. + * NOTE: Having more than one worker requires guarding of some + * message object structures and potentially others - need to be checked + * before we support multiple worker threads on the message queue. + * Please note: the message object is destructed by the queue itself! */ -#ifndef USE_PTHREADS -#define enqueueMsg(x) processMsg((x)) -#else -static void enqueueMsg(msg_t *pMsg) +static rsRetVal +msgConsumer(void __attribute__((unused)) *notNeeded, void *pUsr) { - int iRet; - msgQueue *fifo = pMsgQueue; - struct timespec t; + DEFiRet; + msg_t *pMsg = (msg_t*) pUsr; assert(pMsg != NULL); - if(bRunningMultithreaded == 0) { - /* multi-threading is not yet initialized, happens e.g. - * during startup and restart. rgerhards, 2005-10-25 - */ - dbgprintf("enqueueMsg: not yet running on multiple threads\n"); - processMsg(pMsg); - } else { - /* "normal" mode, threading initialized */ - pthread_mutex_lock(fifo->mut); - - while (fifo->full) { - dbgprintf("enqueueMsg: queue FULL.\n"); - - clock_gettime (CLOCK_REALTIME, &t); - t.tv_sec += 2; - - if(pthread_cond_timedwait (fifo->notFull, - fifo->mut, &t) != 0) { - dbgprintf("enqueueMsg: cond timeout, dropping message!\n"); - MsgDestruct(pMsg); - goto unlock; - } - } - queueAdd(fifo, pMsg); - unlock: - /* now activate the worker thread */ - pthread_mutex_unlock(fifo->mut); - iRet = pthread_cond_signal(fifo->notEmpty); - dbgprintf("EnqueueMsg signaled condition (%d)\n", iRet); - } + processMsg(pMsg); + msgDestruct(&pMsg); + + RETiRet; } -#endif /* #ifndef USE_PTHREADS */ /* Helper to parseRFCSyslogMsg. This function parses a field up to @@ -2973,14 +1302,14 @@ static int parseRFCSyslogMsg(msg_t *pMsg, int flags) */ /* TIMESTAMP */ - if(srSLMGParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse) == FALSE) { + if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse) == FALSE) { dbgprintf("no TIMESTAMP detected!\n"); bContParse = 0; flags |= ADDDATE; } if (flags & ADDDATE) { - getCurrTime(&(pMsg->tTIMESTAMP)); /* use the current time! */ + datetime.getCurrTime(&(pMsg->tTIMESTAMP)); /* use the current time! */ } /* HOSTNAME */ @@ -3024,6 +1353,8 @@ static int parseRFCSyslogMsg(msg_t *pMsg, int flags) free(pBuf); 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 @@ -3042,7 +1373,7 @@ static int parseLegacySyslogMsg(msg_t *pMsg, int flags) char *p2parse; char *pBuf; char *pWork; - rsCStrObj *pStrB; + cstr_t *pStrB; int iCnt; int bTAGCharDetected; @@ -3050,9 +1381,12 @@ static int parseLegacySyslogMsg(msg_t *pMsg, int flags) assert(pMsg->pszUxTradMsg != NULL); p2parse = (char*) pMsg->pszUxTradMsg; - /* Check to see if msg contains a timestamp + /* Check to see if msg contains a timestamp. We stary trying with a + * high-precision one... */ - if(srSLMGParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), p2parse) == TRUE) + if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse) == TRUE) + /* we are done - parse pointer is moved by ParseTIMESTAMP3339 */; + else if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), p2parse) == TRUE) p2parse += 16; else { flags |= ADDDATE; @@ -3064,7 +1398,7 @@ static int parseLegacySyslogMsg(msg_t *pMsg, int flags) * rgerhards 2004-12-03 */ if(flags & ADDDATE) { - getCurrTime(&(pMsg->tTIMESTAMP)); /* use the current time! */ + datetime.getCurrTime(&(pMsg->tTIMESTAMP)); /* use the current time! */ } /* rgerhards, 2006-03-13: next, we parse the hostname and tag. But we @@ -3074,7 +1408,6 @@ static int parseLegacySyslogMsg(msg_t *pMsg, int flags) * 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 @@ -3132,16 +1465,18 @@ static int parseLegacySyslogMsg(msg_t *pMsg, int flags) MsgSetHOSTNAME(pMsg, getRcvFrom(pMsg)); } - /* now parse TAG - that should be present in message from - * all sources. + /* 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). Finally, we allow only up to 32 characters for - * TAG, as it is specified in RFC 3164. + * (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 */ @@ -3150,20 +1485,15 @@ static int parseLegacySyslogMsg(msg_t *pMsg, int flags) * the records: the code is currently clean, but we could optimize it! */ if(!bTAGCharDetected) { uchar *pszTAG; - if((pStrB = rsCStrConstruct()) == NULL) + if(rsCStrConstruct(&pStrB) != RS_RET_OK) return 1; rsCStrSetAllocIncrement(pStrB, 33); pWork = pBuf; iCnt = 0; - while(*p2parse && *p2parse != ':' && *p2parse != ' ' && iCnt < 32) { + while(*p2parse && *p2parse != ':' && *p2parse != ' ') { rsCStrAppendChar(pStrB, *p2parse++); ++iCnt; } - if (iCnt == 32) { - while(*p2parse && *p2parse != ':' && *p2parse != ' ') { - ++p2parse; - } - } if(*p2parse == ':') { ++p2parse; rsCStrAppendChar(pStrB, ':'); @@ -3208,6 +1538,27 @@ static int parseLegacySyslogMsg(msg_t *pMsg, int flags) } +/* 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 + * 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) +{ + DEFiRet; + + ISOBJ_TYPE_assert(pMsg, msg); + + MsgPrepareEnqueue(pMsg); + queueEnqObj(pMsgQueue, pMsg->flowCtlType, (void*) pMsg); + + RETiRet; +} + + /* * Log a message to the appropriate log files, users, etc. based on * the priority. @@ -3228,17 +1579,15 @@ static int parseLegacySyslogMsg(msg_t *pMsg, int flags) * circumstances given. */ void -logmsg(int pri, msg_t *pMsg, int flags) +logmsg(msg_t *pMsg, int flags) { char *msg; - char PRItext[20]; + BEGINfunc assert(pMsg != NULL); assert(pMsg->pszUxTradMsg != NULL); msg = (char*) pMsg->pszUxTradMsg; - dbgprintf("logmsg: %s, flags %x, from '%s', msg %s\n", - textpri(PRItext, sizeof(PRItext) / sizeof(char), pri), - flags, getRcvFrom(pMsg), msg); + 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. @@ -3249,171 +1598,25 @@ logmsg(int pri, msg_t *pMsg, int flags) dbgprintf("Message has syslog-protocol format.\n"); setProtocolVersion(pMsg, 1); if(parseRFCSyslogMsg(pMsg, flags) == 1) { - MsgDestruct(pMsg); + 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); + msgDestruct(&pMsg); return; } } /* ---------------------- END PARSING ---------------- */ - - /* rgerhards, 2005-10-13: if we consider going multi-threaded, this - * is probably the best point to split between a producer and a consumer - * thread. In general, with the first multi-threaded approach, we should - * NOT try to do more than have a single producer and consumer, at least - * if both are from the current code base. The issue is that this code - * was definitely not written with reentrancy in mind and uses a lot of - * global variables. So it is very dangerous to simply go ahead and multi - * thread it. However, I think there is a clear distinction between - * producer (where data is received) and consumer (where the actions are). - * It should be fairly safe to create a single thread for each and run them - * concurrently, thightly coupled via an in-memory queue. Even with this - * limited multithraeding, benefits are immediate: the lengthy actions - * (database writes!) are de-coupled from the receivers, what should result - * in less likely message loss (loss due to receiver overrun). It also allows - * us to utilize 2-cpu systems, which will soon be common given the current - * advances in multicore CPU hardware. So this is well worth trying. - * Another plus of this two-thread-approach would be that it can easily be configured, - * so if there are compatibility issues with the threading libs, we could simply - * disable it (as a makefile feature). - * There is one important thing to keep in mind when doing this basic - * multithreading. The syslog/tcp message forwarder manipulates a structutre - * that is used by the main thread, which actually sends the data. This - * structure must be guarded by a mutex, else we will have race conditions and - * some very bad things could happen. - * - * Additional consumer threads might be added relatively easy for new receivers, - * e.g. if we decide to move RFC 3195 via liblogging natively into rsyslogd. - * - * To aid this functionality, I am moving the rest of the code (the actual - * consumer) to its own method, now called "processMsg()". - * - * rgerhards, 2005-10-25: as of now, the dual-threading code is now in place. - * It is an optional feature and even when enabled, rsyslogd will run single-threaded - * if it gets any errors during thread creation. - */ + /* now submit the message to the main queue - then we are done */ pMsg->msgFlags = flags; - enqueueMsg(pMsg); -} - - -/* rgerhards 2004-11-09: fprintlog() is the actual driver for - * the output channel. It receives the channel description (f) as - * well as the message and outputs them according to the channel - * semantics. The message is typically already contained in the - * channel save buffer (f->f_prevline). This is not only the case - * when a message was already repeated but also when a new message - * arrived. - * rgerhards 2007-08-01: interface changed to use action_t - * rgerhards, 2007-12-11: please note: THIS METHOD MUST ONLY BE - * CALLED AFTER THE CALLER HAS LOCKED THE pAction OBJECT! We do - * not do this here. Failing to do so results in all kinds of - * "interesting" problems! - */ -rsRetVal -fprintlog(action_t *pAction) -{ - msg_t *pMsgSave; /* to save current message pointer, necessary to restore - it in case it needs to be updated (e.g. repeated msgs) */ - DEFiRet; - int i; - - pMsgSave = NULL; /* indicate message poiner not saved */ - /* first check if this is a regular message or the repeation of - * a previous message. If so, we need to change the message text - * to "last message repeated n times" and then go ahead and write - * it. Please note that we can not modify the message object, because - * that would update it in other selectors as well. As such, we first - * need to create a local copy of the message, which we than can update. - * rgerhards, 2007-07-10 - */ - if(pAction->f_prevcount > 1) { - msg_t *pMsg; - uchar szRepMsg[64]; - snprintf((char*)szRepMsg, sizeof(szRepMsg), "last message repeated %d times", - pAction->f_prevcount); - - if((pMsg = MsgDup(pAction->f_pMsg)) == NULL) { - /* it failed - nothing we can do against it... */ - dbgprintf("Message duplication failed, dropping repeat message.\n"); - return RS_RET_ERR; - /* This return is OK. The finalizer frees strings, which are not - * yet allocated. So we can not use the finalizer. - */ - } - - /* We now need to update the other message properties. - * ... RAWMSG is a problem ... Please note that digital - * signatures inside the message are also invalidated. - */ - getCurrTime(&(pMsg->tRcvdAt)); - getCurrTime(&(pMsg->tTIMESTAMP)); - MsgSetMSG(pMsg, (char*)szRepMsg); - MsgSetRawMsg(pMsg, (char*)szRepMsg); - - pMsgSave = pAction->f_pMsg; /* save message pointer for later restoration */ - pAction->f_pMsg = pMsg; /* use the new msg (pointer will be restored below) */ - } - - dbgprintf("Called fprintlog, logging to %s", modGetStateName(pAction->pMod)); - - time(&pAction->f_time); /* we need this for message repeation processing */ - - /* When we reach this point, we have a valid, non-disabled action. - * So let's execute it. -- rgerhards, 2007-07-24 - */ - /* here we must loop to process all requested strings */ - - for(i = 0 ; i < pAction->iNumTpls ; ++i) { - CHKiRet(tplToString(pAction->ppTpl[i], pAction->f_pMsg, &pAction->ppMsgs[i])); - } - /* call configured action */ - iRet = pAction->pMod->mod.om.doAction(pAction->ppMsgs, pAction->f_pMsg->msgFlags, pAction->pModData); - - if(iRet == RS_RET_DISABLE_ACTION) { - dbgprintf("Action requested to be disabled, done that.\n"); - pAction->bEnabled = 0; /* that's it... */ - } - - if(iRet == RS_RET_SUSPENDED) { - dbgprintf("Action requested to be suspended, done that.\n"); - actionSuspend(pAction); - } - - if(iRet == RS_RET_OK) - pAction->f_prevcount = 0; /* message processed, so we start a new cycle */ - -finalize_it: - /* cleanup */ - for(i = 0 ; i < pAction->iNumTpls ; ++i) { - if(pAction->ppMsgs[i] != NULL) { - free(pAction->ppMsgs[i]); - pAction->ppMsgs[i] = NULL; - } - } - - if(pMsgSave != NULL) { - /* we had saved the original message pointer. That was - * done because we needed to create a temporary one - * (most often for "message repeated n time" handling). If so, - * we need to restore the original one now, so that procesing - * can continue as normal. We also need to discard the temporary - * one, as we do not like memory leaks ;) Please note that the original - * message object will be discarded by our callers, so this is nothing - * of our business. rgerhards, 2007-07-10 - */ - MsgDestruct(pAction->f_pMsg); - pAction->f_pMsg = pMsgSave; /* restore it */ - } - - return iRet; + MsgPrepareEnqueue(pMsg); + queueEnqObj(pMsgQueue, pMsg->flowCtlType, (void*) pMsg); + ENDfunc } @@ -3433,161 +1636,192 @@ reapchild() } -/* helper to domark to flush the individual action links via llExecFunc +/* helper to doFlushRptdMsgs() to flush the individual action links via llExecFunc * rgerhards, 2007-08-02 */ -DEFFUNC_llExecFunc(domarkActions) +DEFFUNC_llExecFunc(flushRptdMsgsActions) { action_t *pAction = (action_t*) pData; assert(pAction != NULL); + BEGINfunc LockObj(pAction); if (pAction->f_prevcount && time(NULL) >= REPEATTIME(pAction)) { dbgprintf("flush %s: repeated %d times, %d sec.\n", - modGetStateName(pAction->pMod), pAction->f_prevcount, + module.GetStateName(pAction->pMod), pAction->f_prevcount, repeatinterval[pAction->f_repeatcount]); - if(actionIsSuspended(pAction) && - (actionTryResume(pAction) != RS_RET_OK)) { - goto finalize_it; - } - fprintlog(pAction); + actionWriteToAction(pAction); BACKOFF(pAction); } - -finalize_it: UnlockObj(pAction); + ENDfunc return RS_RET_OK; /* we ignore errors, we can not do anything either way */ } -/* This method writes mark messages and - some time later - flushes reapeat - * messages. - * This method was initially called by an alarm handler. As such, it could potentially - * have race-conditons. For details, see - * http://lkml.org/lkml/2005/3/26/37 - * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=301511 - * I have now changed it so that the alarm handler only sets a global variable, telling - * the main thread that it must do mark processing. So domark() is now called from the - * main thread itself, which is the only thing to make sure rsyslogd will not do - * strange things. The way it originally was seemed to work because mark occurs very - * seldom. However, the code called was anything else but reentrant, so it was like - * russian roulette. - rgerhards, 2005-10-20 - * rgerhards, 2007-12-11: ... and it still is, if running multithreaded. Because in this - * case we run concurrently to the actions... I have now fixed that by using synchronization - * macros. +/* This method flushes reapeat messages. */ static void -domark(void) +doFlushRptdMsgs(void) { register selector_t *f; - if (MarkInterval > 0) { - MarkSeq += TIMERINTVL; - if (MarkSeq >= MarkInterval) { - logmsgInternal(LOG_INFO, "-- MARK --", ADDDATE|MARK); - MarkSeq = 0; - } - - /* see if we need to flush any "message repeated n times"... - * Note that this interferes with objects running on another thread. - * We are using appropriate locking inside the function to handle that. - */ - for (f = Files; f != NULL ; f = f->f_next) { - llExecFunc(&f->llActList, domarkActions, NULL); - } + /* 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); } } -/* This is the alarm handler setting the global variable for - * domark request. See domark() comments for further details. - * rgerhards, 2005-10-20 - */ -static void -domarkAlarmHdlr() +static void debug_switch() { struct sigaction sigAct; - bRequestDoMark = 1; /* request alarm */ - + if(debugging_on == 0) { + debugging_on = 1; + dbgprintf("Switching debugging_on to true\n"); + } else { + dbgprintf("Switching debugging_on to false\n"); + debugging_on = 0; + } + memset(&sigAct, 0, sizeof (sigAct)); sigemptyset(&sigAct.sa_mask); - sigAct.sa_handler = domarkAlarmHdlr; - sigaction(SIGALRM, &sigAct, NULL); - - (void) alarm(TIMERINTVL); + sigAct.sa_handler = debug_switch; + sigaction(SIGUSR1, &sigAct, NULL); } -static void debug_switch() +void legacyOptsEnq(uchar *line) { - struct sigaction sigAct; + legacyOptsLL_t *pNew; - dbgprintf("Switching debugging_on to %s\n", (debugging_on == 0) ? "true" : "false"); - debugging_on = (debugging_on == 0) ? 1 : 0; - - memset(&sigAct, 0, sizeof (sigAct)); - sigemptyset(&sigAct.sa_mask); - sigAct.sa_handler = debug_switch; - sigaction(SIGUSR1, &sigAct, NULL); + pNew = malloc(sizeof(legacyOptsLL_t)); + if(line == NULL) + pNew->line = NULL; + else + pNew->line = (uchar *) strdup((char *) line); + pNew->next = NULL; + + if(pLegacyOptsLL == NULL) + pLegacyOptsLL = pNew; + else { + legacyOptsLL_t *pThis = pLegacyOptsLL; + + while(pThis->next != NULL) + pThis = pThis->next; + pThis->next = pNew; + } } -/* - * Add a string to error message and send it to logerror() - * The error message is passed to snprintf() and must be - * correctly formatted for it (containing a single %s param). - * rgerhards 2005-09-19 - */ -void logerrorSz(char *type, char *errMsg) +void legacyOptsFree(void) { - char buf[1024]; + legacyOptsLL_t *pThis = pLegacyOptsLL, *pNext; - snprintf(buf, sizeof(buf), type, errMsg); - buf[sizeof(buf)/sizeof(char) - 1] = '\0'; /* just to be on the safe side... */ - logerror(buf); - return; + while(pThis != NULL) { + if(pThis->line != NULL) + free(pThis->line); + pNext = pThis->next; + free(pThis); + pThis = pNext; + } } -/* - * Add an integer to error message and send it to logerror() - * The error message is passed to snprintf() and must be - * correctly formatted for it (containing a single %d param). - * rgerhards 2005-09-19 - */ -void logerrorInt(char *type, int errCode) + +void legacyOptsHook(void) { - char buf[1024]; + legacyOptsLL_t *pThis = pLegacyOptsLL; - snprintf(buf, sizeof(buf), type, errCode); - buf[sizeof(buf)/sizeof(char) - 1] = '\0'; /* just to be on the safe side... */ - logerror(buf); - return; + while(pThis != NULL) { + if(pThis->line != NULL) { + errmsg.LogError(NO_ERRCODE, "Warning: backward compatibility layer added to following " + "directive to rsyslog.conf: %s", pThis->line); + conf.cfsysline(pThis->line); + } + pThis = pThis->next; + } } -/* Print syslogd errors some place. - */ -void logerror(char *type) + +void legacyOptsParseTCP(char ch, char *arg) { - char buf[1024]; - char errStr[1024]; + register int i; + register char *pArg = arg; + static char conflict = '\0'; - dbgprintf("Called logerr, msg: %s\n", type); + if((conflict == 'g' && ch == 't') || (conflict == 't' && ch == 'g')) { + fprintf(stderr, "rsyslog: If you want to use both -g and -t, use directives instead, -%c ignored.\n", ch); + return; + } else + conflict = ch; - if (errno == 0) - snprintf(buf, sizeof(buf), "%s", type); - else { - rs_strerror_r(errno, errStr, sizeof(errStr)); - snprintf(buf, sizeof(buf), "%s: %s", type, errStr); - } - buf[sizeof(buf)/sizeof(char) - 1] = '\0'; /* just to be on the safe side... */ - errno = 0; - logmsgInternal(LOG_SYSLOG|LOG_ERR, buf, ADDDATE); - return; + /* extract port */ + i = 0; + while(isdigit((int) *pArg)) + i = i * 10 + *pArg++ - '0'; + + /* number of sessions */ + if(*pArg == '\0' || *pArg == ',') { + if(ch == 't') + legacyOptsEnq((uchar *) "ModLoad imtcp"); + else if(ch == 'g') + legacyOptsEnq((uchar *) "ModLoad imgssapi"); + + if(i >= 0 && i <= 65535) { + uchar line[30]; + + if(ch == 't') { + snprintf((char *) line, sizeof(line), "InputTCPServerRun %d", i); + } else if(ch == 'g') { + snprintf((char *) line, sizeof(line), "InputGSSServerRun %d", i); + } + legacyOptsEnq(line); + } else { + if(ch == 't') { + fprintf(stderr, "rsyslogd: Invalid TCP listen port %d - changed to 514.\n", i); + legacyOptsEnq((uchar *) "InputTCPServerRun 514"); + } else if(ch == 'g') { + fprintf(stderr, "rsyslogd: Invalid GSS listen port %d - changed to 514.\n", i); + legacyOptsEnq((uchar *) "InputGSSServerRun 514"); + } + } + + if(*pArg == ',') { + ++pArg; + while(isspace((int) *pArg)) + ++pArg; + i = 0; + while(isdigit((int) *pArg)) { + i = i * 10 + *pArg++ - '0'; + } + if(i > 0) { + uchar line[30]; + + snprintf((char *) line, sizeof(line), "InputTCPMaxSessions %d", i); + legacyOptsEnq(line); + } else { + if(ch == 't') { + fprintf(stderr, "rsyslogd: TCP session max configured " + "to %d [-t %s] - changing to 1.\n", i, arg); + legacyOptsEnq((uchar *) "InputTCPMaxSessions 1"); + } else if (ch == 'g') { + fprintf(stderr, "rsyslogd: GSS session max configured " + "to %d [-g %s] - changing to 1.\n", i, arg); + legacyOptsEnq((uchar *) "InputTCPMaxSessions 1"); + } + } + } + } else + fprintf(stderr, "rsyslogd: Invalid -t %s command line option.\n", arg); } + /* doDie() is a signal handler. If called, it sets the bFinished variable * to indicate the program should terminate. However, it does not terminate * it itself, because that causes issues with multi-threading. The actual @@ -3598,27 +1832,66 @@ void logerror(char *type) */ static void doDie(int sig) { - dbgprintf("DoDie called.\n"); + static int iRetries = 0; /* debug aid */ + printf("DoDie called.\n"); + if(iRetries++ == 4) { + printf("DoDie called 5 times - unconditional exit\n"); + abort(); + } bFinished = sig; } +/* This function frees all dynamically allocated memory for program termination. + * It must be called only immediately before exit(). It is primarily an aid + * for memory debuggers, which prevents cluttered outupt. + * rgerhards, 2008-03-20 + */ +static void +freeAllDynMemForTermination(void) +{ + if(pszWorkDir != NULL) + free(pszWorkDir); + if(pszMainMsgQFName != NULL) + free(pszMainMsgQFName); + if(pModDir != NULL) + free(pModDir); +} + + /* die() is called when the program shall end. This typically only occurs - * during sigterm or during the initialization. If you search for places where - * it is called, search for "die", not "die(", because the later will not find - * setting of signal handlers! As die() is intended to shutdown rsyslogd, it is + * during sigterm or during the initialization. + * As die() is intended to shutdown rsyslogd, it is * safe to call exit() here. Just make sure that die() itself is not called * at inapropriate places. As a general rule of thumb, it is a bad idea to add * any calls to die() in new code! * rgerhards, 2005-10-24 */ -static void die(int sig) +static void +die(int sig) { char buf[256]; - int i; + 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 + * a full queue and the inputs still fill up that queue. Depending on the + * scheduling order, we may end up with logmsgInternal being held for a quite + * long time. When the inputs are terminated first, that should not happen + * because the queue is drained in parallel. The situation could only become + * an issue with extremely long running actions in a queue full environment. + * However, such actions are at least considered poorly written, if not + * outright wrong. So we do not care about this very remote problem. + * rgerhards, 2008-01-11 + */ + + /* close the inputs */ + dbgprintf("Terminating input threads...\n"); + thrdTerminateAll(); /* TODO: inputs only, please */ + + /* and THEN send the termination log message (see long comment above) */ if (sig) { - dbgprintf(" exiting on signal %d\n", sig); (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.", @@ -3627,38 +1900,18 @@ static void die(int sig) logmsgInternal(LOG_SYSLOG|LOG_INFO, buf, ADDDATE); } - /* Free ressources and close connections */ - freeSelectors(); - -#ifdef USE_PTHREADS - /* Worker threads are stopped by freeSelectors() */ - queueDelete(pMsgQueue); /* delete fifo here! */ + /* drain queue (if configured so) and stop main queue worker thread pool */ + dbgprintf("Terminating main queue...\n"); + queueDestruct(&pMsgQueue); pMsgQueue = NULL; -#endif - - /* now clean up the listener part */ - /* Close the UNIX sockets. */ - for (i = 0; i < nfunix; i++) - if (funix[i] != -1) - close(funix[i]); -#ifdef SYSLOG_INET - /* Close the UDP inet socket. */ - closeUDPListenSockets(); - /* Close the TCP inet socket. */ - if(sockTCPLstn != NULL && *sockTCPLstn) { - deinit_tcp_listener(); - } -#ifdef USE_GSSAPI - if(bEnableTCP & ALLOWEDMETHOD_GSS) - TCPSessGSSDeinit(); -#endif -#endif - /* Clean-up files. */ - for (i = 0; i < nfunix; i++) - if (funixn[i] && funix[i] != -1) - (void)unlink(funixn[i]); + /* Free ressources and close connections. This includes flushing any remaining + * repeated msgs. + */ + dbgprintf("Terminating outputs...\n"); + freeSelectors(); + 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 @@ -3674,6 +1927,18 @@ static void die(int sig) /* de-init some modules */ modExitIminternal(); + /*dbgPrintAllDebugInfo(); / * this is the last spot where this can be done - below output modules are unloaded! */ + + /* the following line cleans up CfSysLineHandlers that were not based on loadable + * modules. As such, they are not yet cleared. + */ + unregCfSysLineHdlrs(); + + legacyOptsFree(); + + /* 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). @@ -3684,19 +1949,18 @@ static void die(int sig) * 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 */ - modUnloadAndDestructAll(); - - /* the following line cleans up CfSysLineHandlers that were not based on loadable - * modules. As such, they are not yet cleared. - */ - unregCfSysLineHdlrs(); - + module.UnloadAndDestructAll(eMOD_LINK_ALL); - /* clean up auxiliary data */ - if(pModDir != NULL) - free(pModDir); + dbgprintf("Clean shutdown completed, bye\n"); + /* dbgClassExit MUST be the last one, because it de-inits the debug system */ + dbgClassExit(); - dbgprintf("Clean shutdown completed, bye.\n"); + /* free all remaining memory blocks - this is not absolutely necessary, but helps + * us keep memory debugger logs clean and this is in aid in developing. It doesn't + * cost much time, so we do it always. -- rgerhards, 2008-03-20 + */ + freeAllDynMemForTermination(); + /* NO CODE HERE - feeelAllDynMemForTermination() must be the last thing before exit()! */ exit(0); /* "good" exit, this is the terminator function for rsyslog [die()] */ } @@ -3711,344 +1975,6 @@ static void doexit() } -/* parse an allowed sender config line and add the allowed senders - * (if the line is correct). - * rgerhards, 2005-09-27 - */ -static rsRetVal addAllowedSenderLine(char* pName, uchar** ppRestOfConfLine) -{ -#ifdef SYSLOG_INET - struct AllowedSenders **ppRoot; - struct AllowedSenders **ppLast; - rsParsObj *pPars; - rsRetVal iRet; - struct NetAddr *uIP = NULL; - int iBits; -#endif - - assert(pName != NULL); - assert(ppRestOfConfLine != NULL); - assert(*ppRestOfConfLine != NULL); - -#ifndef SYSLOG_INET - errno = 0; - logerror("config file contains allowed sender list, but rsyslogd " - "compiled without Internet support - line ignored"); - return RS_RET_ERR; -#else - if(!strcasecmp(pName, "udp")) { - ppRoot = &pAllowedSenders_UDP; - ppLast = &pLastAllowedSenders_UDP; - } else if(!strcasecmp(pName, "tcp")) { - ppRoot = &pAllowedSenders_TCP; - ppLast = &pLastAllowedSenders_TCP; -#ifdef USE_GSSAPI - } else if(!strcasecmp(pName, "gss")) { - ppRoot = &pAllowedSenders_GSS; - ppLast = &pLastAllowedSenders_GSS; -#endif - } else { - logerrorSz("Invalid protocol '%s' in allowed sender " - "list, line ignored", pName); - return RS_RET_ERR; - } - - /* OK, we now know the protocol and have valid list pointers. - * So let's process the entries. We are using the parse class - * for this. - */ - /* create parser object starting with line string without leading colon */ - if((iRet = rsParsConstructFromSz(&pPars, (uchar*) *ppRestOfConfLine) != RS_RET_OK)) { - logerrorInt("Error %d constructing parser object - ignoring allowed sender list", iRet); - return(iRet); - } - - while(!parsIsAtEndOfParseString(pPars)) { - if(parsPeekAtCharAtParsPtr(pPars) == '#') - break; /* a comment-sign stops processing of line */ - /* now parse a single IP address */ - if((iRet = parsAddrWithBits(pPars, &uIP, &iBits)) != RS_RET_OK) { - logerrorInt("Error %d parsing address in allowed sender" - "list - ignoring.", iRet); - rsParsDestruct(pPars); - return(iRet); - } - if((iRet = AddAllowedSender(ppRoot, ppLast, uIP, iBits)) - != RS_RET_OK) { - if (iRet == RS_RET_NOENTRY) { - logerrorInt("Error %d adding allowed sender entry " - "- ignoring.", iRet); - } else { - logerrorInt("Error %d adding allowed sender entry " - "- terminating, nothing more will be added.", iRet); - rsParsDestruct(pPars); - return(iRet); - } - } - free (uIP); /* copy stored in AllowedSenders list */ - } - - /* cleanup */ - *ppRestOfConfLine += parsGetCurrentPosition(pPars); - return rsParsDestruct(pPars); -#endif /*#ifndef SYSLOG_INET */ -} - - -/* process a directory and include all of its files into - * the current config file. There is no specific order of inclusion, - * files are included in the order they are read from the directory. - * The caller must have make sure that the provided parameter is - * indeed a directory. - * rgerhards, 2007-08-01 - */ -static rsRetVal doIncludeDirectory(uchar *pDirName) -{ - DEFiRet; - int iEntriesDone = 0; - DIR *pDir; - union { - struct dirent d; - char b[offsetof(struct dirent, d_name) + NAME_MAX + 1]; - } u; - struct dirent *res; - size_t iDirNameLen; - size_t iFileNameLen; - uchar szFullFileName[MAXFNAME]; - - assert(pDirName != NULL); - - if((pDir = opendir((char*) pDirName)) == NULL) { - logerror("error opening include directory"); - ABORT_FINALIZE(RS_RET_FOPEN_FAILURE); - } - - /* prepare file name buffer */ - iDirNameLen = strlen((char*) pDirName); - memcpy(szFullFileName, pDirName, iDirNameLen); - - /* now read the directory */ - iEntriesDone = 0; - while(readdir_r(pDir, &u.d, &res) == 0) { - if(res == NULL) - break; /* this also indicates end of directory */ - if(res->d_type != DT_REG) - continue; /* we are not interested in special files */ - if(res->d_name[0] == '.') - continue; /* these files we are also not interested in */ - ++iEntriesDone; - /* construct filename */ - iFileNameLen = strlen(res->d_name); - if (iFileNameLen > NAME_MAX) - iFileNameLen = NAME_MAX; - memcpy(szFullFileName + iDirNameLen, res->d_name, iFileNameLen); - *(szFullFileName + iDirNameLen + iFileNameLen) = '\0'; - dbgprintf("including file '%s'\n", szFullFileName); - processConfFile(szFullFileName); - /* we deliberately ignore the iRet of processConfFile() - this is because - * failure to process one file does not mean all files will fail. By ignoring, - * we retry with the next file, which is the best thing we can do. -- rgerhards, 2007-08-01 - */ - } - - if(iEntriesDone == 0) { - /* I just make it a debug output, because I can think of a lot of cases where it - * makes sense not to have any files. E.g. a system maintainer may place a $Include - * into the config file just in case, when additional modules be installed. When none - * are installed, the directory will be empty, which is fine. -- rgerhards 2007-08-01 - */ - dbgprintf("warning: the include directory contained no files - this may be ok.\n"); - } - -finalize_it: - if(pDir != NULL) - closedir(pDir); - - return iRet; -} - - -/* process a $include config line. That type of line requires - * inclusion of another file. - * rgerhards, 2007-08-01 - */ -static rsRetVal doIncludeLine(uchar **pp, __attribute__((unused)) void* pVal) -{ - DEFiRet; - char pattern[MAXFNAME]; - uchar *cfgFile; - glob_t cfgFiles; - size_t i = 0; - struct stat fileInfo; - - assert(pp != NULL); - assert(*pp != NULL); - - if(getSubString(pp, (char*) pattern, sizeof(pattern) / sizeof(char), ' ') != 0) { - logerror("could not extract group name"); - ABORT_FINALIZE(RS_RET_NOT_FOUND); - } - - /* Use GLOB_MARK to append a trailing slash for directories. - * Required by doIncludeDirectory(). - */ - glob(pattern, GLOB_MARK, NULL, &cfgFiles); - - for(i = 0; i < cfgFiles.gl_pathc; i++) { - cfgFile = (uchar*) cfgFiles.gl_pathv[i]; - - if(stat((char*) cfgFile, &fileInfo) != 0) - continue; /* continue with the next file if we can't stat() the file */ - - if(S_ISREG(fileInfo.st_mode)) { /* config file */ - dbgprintf("requested to include config file '%s'\n", cfgFile); - iRet = processConfFile(cfgFile); - } else if(S_ISDIR(fileInfo.st_mode)) { /* config directory */ - dbgprintf("requested to include directory '%s'\n", cfgFile); - iRet = doIncludeDirectory(cfgFile); - } else { /* TODO: shall we handle symlinks or not? */ - dbgprintf("warning: unable to process IncludeConfig directive '%s'\n", cfgFile); - } - } - - globfree(&cfgFiles); - -finalize_it: - return iRet; -} - - -/* process a $ModLoad config line. - * As of now, it is a dummy, that will later evolve into the - * loader for plug-ins. - * rgerhards, 2007-07-21 - * varmojfekoj added support for dynamically loadable modules on 2007-08-13 - * rgerhards, 2007-09-25: please note that the non-threadsafe function dlerror() is - * called below. This is ok because modules are currently only loaded during - * configuration file processing, which is executed on a single thread. Should we - * change that design at any stage (what is unlikely), we need to find a - * replacement. - */ -static rsRetVal doModLoad(uchar **pp, __attribute__((unused)) void* pVal) -{ - DEFiRet; - uchar szName[512]; - uchar szPath[512]; - uchar errMsg[1024]; - uchar *pModName; - void *pModHdlr, *pModInit; - - assert(pp != NULL); - assert(*pp != NULL); - - if(getSubString(pp, (char*) szName, sizeof(szName) / sizeof(uchar), ' ') != 0) { - logerror("could not extract module name"); - ABORT_FINALIZE(RS_RET_NOT_FOUND); - } - - /* this below is a quick and dirty hack to provide compatibility with the - * $ModLoad MySQL forward compatibility statement. TODO: clean this up - * For the time being, it is clean enough, it just needs to be done - * differently when we have a full design for loadable plug-ins. For the - * time being, we just mangle the names a bit. - * rgerhards, 2007-08-14 - */ - if(!strcmp((char*) szName, "MySQL")) - pModName = (uchar*) "ommysql.so"; - else - pModName = szName; - - dbgprintf("Requested to load module '%s'\n", szName); - - if(*pModName == '/') { - *szPath = '\0'; /* we do not need to append the path - its already in the module name */ - } else { - strncpy((char *) szPath, (pModDir == NULL) ? _PATH_MODDIR : (char*) pModDir, sizeof(szPath)); - } - strncat((char *) szPath, (char *) pModName, sizeof(szPath) - strlen((char*) szPath) - 1); - if(!(pModHdlr = dlopen((char *) szPath, RTLD_NOW))) { - snprintf((char *) errMsg, sizeof(errMsg), "could not load module '%s', dlopen: %s\n", szPath, dlerror()); - errMsg[sizeof(errMsg)/sizeof(uchar) - 1] = '\0'; - logerror((char *) errMsg); - ABORT_FINALIZE(RS_RET_ERR); - } - if(!(pModInit = dlsym(pModHdlr, "modInit"))) { - snprintf((char *) errMsg, sizeof(errMsg), "could not load module '%s', dlsym: %s\n", szPath, dlerror()); - errMsg[sizeof(errMsg)/sizeof(uchar) - 1] = '\0'; - logerror((char *) errMsg); - dlclose(pModHdlr); - ABORT_FINALIZE(RS_RET_ERR); - } - if((iRet = doModInit(pModInit, (uchar*) pModName, pModHdlr)) != RS_RET_OK) - return iRet; - - skipWhiteSpace(pp); /* skip over any whitespace */ - -finalize_it: - return iRet; -} - -/* parse and interpret a $-config line that starts with - * a name (this is common code). It is parsed to the name - * and then the proper sub-function is called to handle - * the actual directive. - * rgerhards 2004-11-17 - * rgerhards 2005-06-21: previously only for templates, now - * generalized. - */ -static rsRetVal doNameLine(uchar **pp, void* pVal) -{ - DEFiRet; - uchar *p; - enum eDirective eDir; - char szName[128]; - - assert(pp != NULL); - p = *pp; - assert(p != NULL); - - eDir = (enum eDirective) pVal; /* this time, it actually is NOT a pointer! */ - - if(getSubString(&p, szName, sizeof(szName) / sizeof(char), ',') != 0) { - logerror("Invalid config line: could not extract name - line ignored"); - ABORT_FINALIZE(RS_RET_NOT_FOUND); - } - if(*p == ',') - ++p; /* comma was eaten */ - - /* we got the name - now we pass name & the rest of the string - * to the subfunction. It makes no sense to do further - * parsing here, as this is in close interaction with the - * respective subsystem. rgerhards 2004-11-17 - */ - - switch(eDir) { - case DIR_TEMPLATE: - tplAddLine(szName, &p); - break; - case DIR_OUTCHANNEL: - ochAddLine(szName, &p); - break; - case DIR_ALLOWEDSENDER: - addAllowedSenderLine(szName, &p); - break; - default:/* we do this to avoid compiler warning - not all - * enum values call this function, so an incomplete list - * is quite ok (but then we should not run into this code, - * so at least we log a debug warning). - */ - dbgprintf("INTERNAL ERROR: doNameLine() called with invalid eDir %d.\n", - eDir); - break; - } - - *pp = p; - -finalize_it: - return iRet; -} - - /* set the action resume interval */ static rsRetVal setActionResumeInterval(void __attribute__((unused)) *pVal, int iNewVal) @@ -4068,49 +1994,6 @@ static rsRetVal setUmask(void __attribute__((unused)) *pVal, int iUmask) } -/* Parse and interpret a system-directive in the config line - * A system directive is one that starts with a "$" sign. It offers - * extended configuration parameters. - * 2004-11-17 rgerhards - */ -rsRetVal cfsysline(uchar *p) -{ - DEFiRet; - uchar szCmd[64]; - uchar errMsg[128]; /* for dynamic error messages */ - - assert(p != NULL); - errno = 0; - if(getSubString(&p, (char*) szCmd, sizeof(szCmd) / sizeof(uchar), ' ') != 0) { - logerror("Invalid $-configline - could not extract command - line ignored\n"); - ABORT_FINALIZE(RS_RET_NOT_FOUND); - } - - /* we now try and see if we can find the command in the registered - * list of cfsysline handlers. -- rgerhards, 2007-07-31 - */ - CHKiRet(processCfSysLineCommand(szCmd, &p)); - - /* now check if we have some extra characters left on the line - that - * should not be the case. Whitespace is OK, but everything else should - * trigger a warning (that may be an indication of undesired behaviour). - * An exception, of course, are comments (starting with '#'). - * rgerhards, 2007-07-04 - */ - skipWhiteSpace(&p); - - if(*p && *p != '#') { /* we have a non-whitespace, so let's complain */ - snprintf((char*) errMsg, sizeof(errMsg)/sizeof(uchar), - "error: extra characters in config line ignored: '%s'", p); - errno = 0; - logerror((char*) errMsg); - } - -finalize_it: - return iRet; -} - - /* 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 @@ -4125,14 +2008,9 @@ DEFFUNC_llExecFunc(freeSelectorsActions) /* flush any pending output */ if(pAction->f_prevcount) { - if(actionIsSuspended(pAction) && - (actionTryResume(pAction) != RS_RET_OK)) { - goto finalize_it; - } - fprintlog(pAction); + actionWriteToAction(pAction); } -finalize_it: return RS_RET_OK; /* never fails ;) */ } @@ -4147,23 +2025,6 @@ static void freeSelectors(void) if(Files != NULL) { dbgprintf("Freeing log structures.\n"); - /* just in case, we flush the emergency log. If error messages occur after - * this stage, we loose them, but that's ok. With multi-threading, this can - * never happen. -- rgerhards, 2007-08-03 - */ - processImInternal(); - - /* we first wait until all messages are processed (stopWorker() does - * that. Then, we go one last time over all actions and flush any - * pending "message repeated n times" messages. We must use this sequence - * because otherwise we would flush at whatever message is currently being - * processed without draining the queue. That would lead to invalid - * results. -- rgerhards, 2007-12-12 - */ -# ifdef USE_PTHREADS - stopWorker(); -# endif - for(f = Files ; f != NULL ; f = f->f_next) { llExecFunc(&f->llActList, freeSelectorsActions, NULL); } @@ -4178,7 +2039,7 @@ static void freeSelectors(void) /* Reflect the deletion of the selectors linked list. */ Files = NULL; - Initialized = 0; + bHaveMainQueue = 0; } } @@ -4191,9 +2052,9 @@ DEFFUNC_llExecFunc(dbgPrintInitInfoAction) { DEFiRet; iRet = actionDbgPrint((action_t*) pData); - printf("\n"); + dbgprintf("\n"); - return iRet; + RETiRet; } /* print debug information as part of init(). This pretty much @@ -4207,258 +2068,143 @@ static void dbgPrintInitInfo(void) int iSelNbr = 1; int i; - printf("\nActive selectors:\n"); + dbgprintf("\nActive selectors:\n"); for (f = Files; f != NULL ; f = f->f_next) { - printf("Selector %d:\n", iSelNbr++); + dbgprintf("Selector %d:\n", iSelNbr++); if(f->pCSProgNameComp != NULL) - printf("tag: '%s'\n", rsCStrGetSzStrNoNULL(f->pCSProgNameComp)); + dbgprintf("tag: '%s'\n", rsCStrGetSzStrNoNULL(f->pCSProgNameComp)); if(f->eHostnameCmpMode != HN_NO_COMP) - printf("hostname: %s '%s'\n", + 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) - printf(" X "); + dbgprintf(" X "); else - printf("%2X ", f->f_filterData.f_pmask[i]); + 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 { - printf("PROPERTY-BASED Filter:\n"); - printf("\tProperty.: '%s'\n", + dbgprintf("PROPERTY-BASED Filter:\n"); + dbgprintf("\tProperty.: '%s'\n", rsCStrGetSzStrNoNULL(f->f_filterData.prop.pCSPropName)); - printf("\tOperation: "); + dbgprintf("\tOperation: "); if(f->f_filterData.prop.isNegated) - printf("NOT "); - printf("'%s'\n", getFIOPName(f->f_filterData.prop.operation)); - printf("\tValue....: '%s'\n", + dbgprintf("NOT "); + dbgprintf("'%s'\n", getFIOPName(f->f_filterData.prop.operation)); + dbgprintf("\tValue....: '%s'\n", rsCStrGetSzStrNoNULL(f->f_filterData.prop.pCSCompValue)); - printf("\tAction...: "); + dbgprintf("\tAction...: "); } - printf("\nActions:\n"); + dbgprintf("\nActions:\n"); llExecFunc(&f->llActList, dbgPrintInitInfoAction, NULL); /* actions */ - printf("\n"); + dbgprintf("\n"); } - printf("\n"); + dbgprintf("\n"); if(bDebugPrintTemplateList) tplPrintList(); if(bDebugPrintModuleList) - modPrintList(); + module.PrintList(); ochPrintList(); if(bDebugPrintCfSysLineHandlerList) dbgPrintCfSysLineHandlers(); -#ifdef SYSLOG_INET - /* now the allowedSender lists: */ - PrintAllowedSenders(1); /* UDP */ - PrintAllowedSenders(2); /* TCP */ -#ifdef USE_GSSAPI - PrintAllowedSenders(3); /* GSS */ -#endif - printf("\n"); -#endif /* #ifdef SYSLOG_INET */ - - printf("Messages with malicious PTR DNS Records are %sdropped.\n", + dbgprintf("Messages with malicious PTR DNS Records are %sdropped.\n", bDropMalPTRMsgs ? "" : "not "); - printf("Control characters are %sreplaced upon reception.\n", + dbgprintf("Control characters are %sreplaced upon reception.\n", bEscapeCCOnRcv? "" : "not "); if(bEscapeCCOnRcv) - printf("Control character escape sequence prefix is '%c'.\n", + dbgprintf("Control character escape sequence prefix is '%c'.\n", cCCEscapeChar); -#ifdef USE_PTHREADS - printf("Main queue size %d messages.\n", iMainMsgQueueSize); -#endif + dbgprintf("Main queue size %d messages.\n", iMainMsgQueueSize); + dbgprintf("Main queue worker threads: %d, Perists every %d updates.\n", + iMainMsgQueueNumWorkers, iMainMsgQPersistUpdCnt); + 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", + iMainMsgQHighWtrMark, iMainMsgQLowWtrMark, iMainMsgQDiscardMark, iMainMsgQDiscardSeverity); + dbgprintf("Main queue save on shutdown %d, max disk space allowed %lld\n", + bMainMsgQSaveOnShutdown, iMainMsgQueMaxDiskSpace); + /* TODO: add + iActionRetryCount = 0; + iActionRetryInterval = 30000; + static int iMainMsgQtoWrkShutdown = 60000; + static int iMainMsgQtoWrkMinMsgs = 100; + static int iMainMsgQbSaveOnShutdown = 1; + iMainMsgQueMaxDiskSpace = 0; + setQPROP(queueSettoWrkShutdown, "$MainMsgQueueTimeoutWorkerThreadShutdown", 5000); + setQPROP(queueSetiMinMsgsPerWrkr, "$MainMsgQueueWorkerThreadMinimumMessages", 100); + setQPROP(queueSetbSaveOnShutdown, "$MainMsgQueueSaveOnShutdown", 1); + */ + dbgprintf("Work Directory: '%s'.\n", pszWorkDir); } -/* process a configuration file - * started with code from init() by rgerhards on 2007-07-31 +/* Start the input modules. This function will probably undergo big changes + * while we implement the input module interface. For now, it does the most + * important thing to get at least my poor initial input modules up and + * running. Almost no config option is taken. + * rgerhards, 2007-12-14 */ -static rsRetVal processConfFile(uchar *pConfFile) +static rsRetVal +startInputModules(void) { DEFiRet; - int iLnNbr = 0; - FILE *cf; - selector_t *fCurr = NULL; - uchar *p; -#ifdef CONT_LINE - uchar cbuf[BUFSIZ]; - uchar *cline; -#else - uchar cline[BUFSIZ]; -#endif - assert(pConfFile != NULL); - - if((cf = fopen((char*)pConfFile, "r")) == NULL) { - ABORT_FINALIZE(RS_RET_FOPEN_FAILURE); - } - - /* Now process the file. - */ -#if CONT_LINE - cline = cbuf; - while (fgets((char*)cline, sizeof(cbuf) - (cline - cbuf), cf) != NULL) { -#else - while (fgets(cline, sizeof(cline), cf) != NULL) { -#endif - ++iLnNbr; - /* drop LF - TODO: make it better, replace fgets(), but its clean as it is */ - if(cline[strlen((char*)cline)-1] == '\n') { - cline[strlen((char*)cline) -1] = '\0'; - } - /* check for end-of-section, comments, strip off trailing - * spaces and newline character. - */ - p = cline; - skipWhiteSpace(&p); - if (*p == '\0' || *p == '#') - continue; - -#if CONT_LINE - strcpy((char*)cline, (char*)p); -#endif - for (p = (uchar*) strchr((char*)cline, '\0'); isspace((int) *--p);); -#if CONT_LINE - if (*p == '\\') { - if ((p - cbuf) > BUFSIZ - 30) { - /* Oops the buffer is full - what now? */ - cline = cbuf; - } else { - *p = 0; - cline = p; - continue; - } - } else - cline = cbuf; -#endif - *++p = '\0'; // TODO: check this + modInfo_t *pMod; - /* we now have the complete line, and are positioned at the first non-whitespace - * character. So let's process it - */ -#if CONT_LINE - if(cfline(cbuf, &fCurr) != RS_RET_OK) { -#else - if(cfline((uchar*)cline, &fCurr) != RS_RET_OK) { -#endif - /* we log a message, but otherwise ignore the error. After all, the next - * line can be correct. -- rgerhards, 2007-08-02 - */ - uchar szErrLoc[MAXFNAME + 64]; - dbgprintf("config line NOT successfully processed\n"); - snprintf((char*)szErrLoc, sizeof(szErrLoc) / sizeof(uchar), - "%s, line %d", pConfFile, iLnNbr); - logerrorSz("the last error occured in %s", (char*)szErrLoc); + /* 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); } + pMod = module.GetNxtType(pMod, eMOD_IN); } - /* we probably have one selector left to be added - so let's do that now */ - CHKiRet(selectorAddList(fCurr)); - - /* close the configuration file */ - (void) fclose(cf); - -finalize_it: - if(iRet != RS_RET_OK) { - char errStr[1024]; - if(fCurr != NULL) - selectorDestruct(fCurr); - - rs_strerror_r(errno, errStr, sizeof(errStr)); - dbgprintf("error %d processing config file '%s'; os error (if any): %s\n", - iRet, pConfFile, errStr); - } - return iRet; + ENDfunc + return RS_RET_OK; /* intentional: we do not care about module errors */ } /* INIT -- Initialize syslogd from configuration table * init() is called at initial startup AND each time syslogd is HUPed */ -static void init(void) +static void +init(void) { DEFiRet; - register int i; -#ifdef CONT_LINE char cbuf[BUFSIZ]; -#else - char cline[BUFSIZ]; -#endif char bufStartUpMsg[512]; - struct servent *sp; struct sigaction sigAct; + thrdTerminateAll(); /* stop all running input threads - TODO: reconsider location! */ + /* initialize some static variables */ pDfltHostnameCmp = NULL; pDfltProgNameCmp = NULL; eDfltHostnameCmpMode = HN_NO_COMP; - Forwarding = 0; - -#ifdef SYSLOG_INET - if (restart) { - if (pAllowedSenders_UDP != NULL) { - clearAllowedSenders (pAllowedSenders_UDP); - pAllowedSenders_UDP = NULL; - } - - if (pAllowedSenders_TCP != NULL) { - clearAllowedSenders (pAllowedSenders_TCP); - pAllowedSenders_TCP = NULL; - } -#ifdef USE_GSSAPI - if (pAllowedSenders_GSS != NULL) { - clearAllowedSenders (pAllowedSenders_GSS); - pAllowedSenders_GSS = NULL; - } -#endif - } - - assert(pAllowedSenders_UDP == NULL && pAllowedSenders_TCP == NULL -#ifdef USE_GSSAPI - && pAllowedSenders_GSS == NULL -#endif - ); -#endif - /* I was told by an IPv6 expert that calling getservbyname() seems to be - * still valid, at least for the use case we have. So I re-enabled that - * code. rgerhards, 2007-07-02 - */ - if(!strcmp(LogPort, "0")) { - /* we shall use the default syslog/udp port, so let's - * look it up. - * NOTE: getservbyname() is not thread-safe, but this is OK as - * it is called only during init, in single-threading mode. - */ - sp = getservbyname("syslog", "udp"); - if (sp == NULL) { - errno = 0; - logerror("Could not find syslog/udp port in /etc/services. " - "Now using IANA-assigned default of 514."); - LogPort = "514"; - } else { - /* we can dynamically allocate memory here and do NOT need - * to care about freeing it because even though init() is - * called on each restart, the LogPort can never again be - * "0". So we will only once run into this part of the code - * here. rgerhards, 2007-07-02 - * We save ourselfs the hassle of dynamic memory management - * for the very same reason. - */ - static char defPort[8]; - snprintf(defPort, sizeof(defPort), "%d", ntohs(sp->s_port)); - LogPort = defPort; - } - } dbgprintf("rsyslog %s.\n", VERSION); dbgprintf("Called init.\n"); + /* delete the message queue, which also flushes all messages left over */ + if(pMsgQueue != NULL) { + dbgprintf("deleting main message queue\n"); + queueDestruct(&pMsgQueue); /* delete pThis here! */ + pMsgQueue = NULL; + } + /* Close all open log files and free log descriptor array. This also frees * all output-modules instance data. */ @@ -4466,19 +2212,11 @@ static void init(void) /* Unload all non-static modules */ dbgprintf("Unloading non-static modules.\n"); - modUnloadAndDestructDynamic(); + module.UnloadAndDestructAll(eMOD_LINK_DYNAMIC_LOADED); dbgprintf("Clearing templates.\n"); tplDeleteNew(); -#ifdef USE_PTHREADS - if(pMsgQueue != NULL) { - dbgprintf("deleting message queue\n"); - queueDelete(pMsgQueue); /* delete fifo here! */ - pMsgQueue = NULL; - } -#endif - /* re-setting values to defaults (where applicable) */ /* TODO: 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 @@ -4487,10 +2225,10 @@ static void init(void) * think about the whole situation when we implement loadable plugins. * rgerhards, 2007-07-31 */ - cfsysline((uchar*)"ResetConfigVariables"); + conf.cfsysline((uchar*)"ResetConfigVariables"); /* open the configuration file */ - if((iRet = processConfFile(ConfFile)) != RS_RET_OK) { + if((iRet = conf.processConfFile(ConfFile)) != RS_RET_OK) { /* 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 @@ -4500,93 +2238,107 @@ static void init(void) selector_t *f = NULL; char szTTYNameBuf[_POSIX_TTY_NAME_MAX+1]; /* +1 for NULL character */ dbgprintf("primary config file could not be opened - using emergency definitions.\n"); - cfline((uchar*)"*.ERR\t" _PATH_CONSOLE, &f); - cfline((uchar*)"*.PANIC\t*", &f); + conf.cfline((uchar*)"*.ERR\t" _PATH_CONSOLE, &f); + conf.cfline((uchar*)"*.PANIC\t*", &f); if(ttyname_r(0, szTTYNameBuf, sizeof(szTTYNameBuf)) == 0) { snprintf(cbuf,sizeof(cbuf), "*.*\t%s", szTTYNameBuf); - cfline((uchar*)cbuf, &f); + conf.cfline((uchar*)cbuf, &f); } selectorAddList(f); } + 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); - pDfltHostnameCmp = NULL; + rsCStrDestruct(&pDfltHostnameCmp); } if(pDfltProgNameCmp != NULL) { - rsCStrDestruct(pDfltProgNameCmp); - pDfltProgNameCmp = NULL; + rsCStrDestruct(&pDfltProgNameCmp); } -#ifdef SYSLOG_UNIXAF - for (i = startIndexUxLocalSockets ; i < nfunix ; i++) { - if (funix[i] != -1) - /* Don't close the socket, preserve it instead - close(funix[i]); - */ - continue; - if ((funix[i] = create_unix_socket(funixn[i])) != -1) - dbgprintf("Opened UNIX socket `%s' (fd %d).\n", funixn[i], funix[i]); + /* some checks */ + if(iMainMsgQueueNumWorkers < 1) { + errmsg.LogError(NO_ERRCODE, "$MainMsgQueueNumWorkers must be at least 1! Set to 1.\n"); + iMainMsgQueueNumWorkers = 1; } -#endif -#ifdef SYSLOG_INET - /* I have moved initializing UDP sockets before the TCP sockets. This ensures - * they are as soon ready for reception as possible. Of course, it is only a - * very small window of exposure, but it doesn't hurt to limit the message - * loss risk to as low as possible - especially if it costs nothing... - * rgerhards, 2007-06-28 - */ - if(Forwarding || AcceptRemote) { - if (finet == NULL) { - if((finet = create_udp_socket()) != NULL) - dbgprintf("Opened %d syslog UDP port(s).\n", *finet); + if(MainMsgQueType == QUEUETYPE_DISK) { + errno = 0; /* for logerror! */ + if(pszWorkDir == NULL) { + errmsg.LogError(NO_ERRCODE, "No $WorkDirectory specified - can not run main message queue in 'disk' mode. " + "Using 'FixedArray' instead.\n"); + MainMsgQueType = QUEUETYPE_FIXED_ARRAY; } - } else { - /* this case can happen during HUP processing. */ - closeUDPListenSockets(); - } - - if (bEnableTCP) { - if(sockTCPLstn == NULL) { - /* even when doing a re-init, we do not shut down and - * re-open the TCP socket. That would break existing TCP - * session, which we do not desire. Should at some time arise - * need to do that, I recommend controlling that via a - * user-selectable option. rgerhards, 2007-06-21 - */ -# ifdef USE_GSSAPI - if(bEnableTCP & ALLOWEDMETHOD_GSS) { - if(TCPSessGSSInit()) { - logerror("GSS-API initialization failed\n"); - bEnableTCP &= ~(ALLOWEDMETHOD_GSS); - } - } - if(bEnableTCP) -# endif - if((sockTCPLstn = create_tcp_socket()) != NULL) { - dbgprintf("Opened %d syslog TCP port(s).\n", *sockTCPLstn); - } + if(pszMainMsgQFName == NULL) { + errmsg.LogError(NO_ERRCODE, "No $MainMsgQueueFileName specified - can not run main message queue in " + "'disk' mode. Using 'FixedArray' instead.\n"); + MainMsgQueType = QUEUETYPE_FIXED_ARRAY; } } -#endif -# ifdef USE_PTHREADS - /* create message queue */ - pMsgQueue = queueInit(); - if(pMsgQueue == NULL) { - errno = 0; - logerror("error: could not create message queue - running single-threaded!\n"); + /* switch the message object to threaded operation, if necessary */ + if(MainMsgQueType == QUEUETYPE_DIRECT || iMainMsgQueueNumWorkers > 1) { + MsgEnableThreadSafety(); } - startWorker(); -# endif - - Initialized = 1; + /* create message queue */ + CHKiRet_Hdlr(queueConstruct(&pMsgQueue, MainMsgQueType, iMainMsgQueueNumWorkers, iMainMsgQueueSize, msgConsumer)) { + /* 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(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(NO_ERRCODE, "Invalid " #directive ", error %d. Ignored, running with default setting", iRet); \ + } + + setQPROP(queueSetMaxFileSize, "$MainMsgQueueFileSize", iMainMsgQueMaxFileSize); + setQPROP(queueSetsizeOnDiskMax, "$MainMsgQueueMaxDiskSpace", iMainMsgQueMaxDiskSpace); + setQPROPstr(queueSetFilePrefix, "$MainMsgQueueFileName", pszMainMsgQFName); + setQPROP(queueSetiPersistUpdCnt, "$MainMsgQueueCheckpointInterval", iMainMsgQPersistUpdCnt); + setQPROP(queueSettoQShutdown, "$MainMsgQueueTimeoutShutdown", iMainMsgQtoQShutdown ); + setQPROP(queueSettoActShutdown, "$MainMsgQueueTimeoutActionCompletion", iMainMsgQtoActShutdown); + setQPROP(queueSettoWrkShutdown, "$MainMsgQueueWorkerTimeoutThreadShutdown", iMainMsgQtoWrkShutdown); + setQPROP(queueSettoEnq, "$MainMsgQueueTimeoutEnqueue", iMainMsgQtoEnq); + setQPROP(queueSetiHighWtrMrk, "$MainMsgQueueHighWaterMark", iMainMsgQHighWtrMark); + setQPROP(queueSetiLowWtrMrk, "$MainMsgQueueLowWaterMark", iMainMsgQLowWtrMark); + setQPROP(queueSetiDiscardMrk, "$MainMsgQueueDiscardMark", iMainMsgQDiscardMark); + setQPROP(queueSetiDiscardSeverity, "$MainMsgQueueDiscardSeverity", iMainMsgQDiscardSeverity); + setQPROP(queueSetiMinMsgsPerWrkr, "$MainMsgQueueWorkerThreadMinimumMessages", iMainMsgQWrkMinMsgs); + setQPROP(queueSetbSaveOnShutdown, "$MainMsgQueueSaveOnShutdown", bMainMsgQSaveOnShutdown); + setQPROP(queueSetiDeqSlowdown, "$MainMsgQueueDequeueSlowdown", iMainMsgQDeqSlowdown); + +# undef setQPROP +# undef setQPROPstr + + /* ... and finally start the queue! */ + CHKiRet_Hdlr(queueStart(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"); + + /* 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 + * shuffled to down here once we have everything in input modules. + * rgerhards, 2007-12-14 + */ + startInputModules(); if(Debug) { dbgPrintInitInfo(); @@ -4597,17 +2349,8 @@ static void init(void) */ snprintf(bufStartUpMsg, sizeof(bufStartUpMsg)/sizeof(char), " [origin software=\"rsyslogd\" " "swVersion=\"" VERSION \ - "\" x-pid=\"%d\" x-info=\"http://www.rsyslog.com\"][x-configInfo udpReception=\"%s\" " \ - "udpPort=\"%s\" tcpReception=\"%s\" tcpPort=\"%s\"]" \ - " restart", - (int) myPid, -#ifdef SYSLOG_INET - AcceptRemote ? "Yes" : "No", LogPort, - bEnableTCP ? "Yes" : "No", TCPLstnPort -#else - "No", "0", "No", "0" -#endif /* #ifdef SYSLOG_INET */ - ); + "\" x-pid=\"%d\" x-info=\"http://www.rsyslog.com\"] restart", + (int) myPid); logmsgInternal(LOG_SYSLOG|LOG_INFO, bufStartUpMsg, ADDDATE); memset(&sigAct, 0, sizeof (sigAct)); @@ -4615,684 +2358,8 @@ static void init(void) sigAct.sa_handler = sighup_handler; sigaction(SIGHUP, &sigAct, NULL); - dbgprintf(" restarted.\n"); -} - - -/* Helper to cfline() and its helpers. Parses a template name - * from an "action" line. Must be called with the Line pointer - * pointing to the first character after the semicolon. - * rgerhards 2004-11-19 - * changed function to work with OMSR. -- rgerhards, 2007-07-27 - * the default template is to be used when no template is specified. - */ -rsRetVal cflineParseTemplateName(uchar** pp, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts, uchar *dfltTplName) -{ - uchar *p; - uchar *tplName; - DEFiRet; - rsCStrObj *pStrB; - - assert(pp != NULL); - assert(*pp != NULL); - assert(pOMSR != NULL); - - p =*pp; - /* a template must follow - search it and complain, if not found - */ - skipWhiteSpace(&p); - if(*p == ';') - ++p; /* eat it */ - else if(*p != '\0' && *p != '#') { - logerror("invalid character in selector line - ';template' expected"); - iRet = RS_RET_ERR; - goto finalize_it; - } - - skipWhiteSpace(&p); /* go to begin of template name */ - - if(*p == '\0') { - /* no template specified, use the default */ - /* TODO: check NULL ptr */ - tplName = (uchar*) strdup((char*)dfltTplName); - } else { - /* template specified, pick it up */ - if((pStrB = rsCStrConstruct()) == NULL) { - glblHadMemShortage = 1; - iRet = RS_RET_OUT_OF_MEMORY; - goto finalize_it; - } - - /* now copy the string */ - while(*p && *p != '#' && !isspace((int) *p)) { - CHKiRet(rsCStrAppendChar(pStrB, *p)); - ++p; - } - CHKiRet(rsCStrFinish(pStrB)); - CHKiRet(rsCStrConvSzStrAndDestruct(pStrB, &tplName, 0)); - } - - iRet = OMSRsetEntry(pOMSR, iEntry, tplName, iTplOpts); - if(iRet != RS_RET_OK) goto finalize_it; - -finalize_it: - *pp = p; - - return iRet; -} - -/* Helper to cfline(). Parses a file name up until the first - * comma and then looks for the template specifier. Tries - * to find that template. - * rgerhards 2004-11-18 - * parameter pFileName must point to a buffer large enough - * to hold the largest possible filename. - * rgerhards, 2007-07-25 - * updated to include OMSR pointer -- rgerhards, 2007-07-27 - */ -rsRetVal cflineParseFileName(uchar* p, uchar *pFileName, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts) -{ - register uchar *pName; - int i; - DEFiRet; - - assert(pOMSR != NULL); - - pName = pFileName; - i = 1; /* we start at 1 so that we reseve space for the '\0'! */ - while(*p && *p != ';' && i < MAXFNAME) { - *pName++ = *p++; - ++i; - } - *pName = '\0'; - - iRet = cflineParseTemplateName(&p, pOMSR, iEntry, iTplOpts, (uchar*) " TradFmt"); - - return iRet; -} - - -/* - * Helper to cfline(). This function takes the filter part of a traditional, PRI - * based line and decodes the PRIs given in the selector line. It processed the - * line up to the beginning of the action part. A pointer to that beginnig is - * passed back to the caller. - * rgerhards 2005-09-15 - */ -static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register selector_t *f) -{ - uchar *p; - register uchar *q; - register int i, i2; - uchar *bp; - int pri; - int singlpri = 0; - int ignorepri = 0; - uchar buf[MAXLINE]; - uchar xbuf[200]; - - assert(pline != NULL); - assert(*pline != NULL); - assert(f != NULL); - - dbgprintf(" - traditional PRI filter\n"); - errno = 0; /* keep strerror_r() stuff out of logerror messages */ - - f->f_filter_type = FILTER_PRI; - /* Note: file structure is pre-initialized to zero because it was - * created with calloc()! - */ - for (i = 0; i <= LOG_NFACILITIES; i++) { - f->f_filterData.f_pmask[i] = TABLE_NOPRI; - } - - /* scan through the list of selectors */ - for (p = *pline; *p && *p != '\t' && *p != ' ';) { - - /* find the end of this facility name list */ - for (q = p; *q && *q != '\t' && *q++ != '.'; ) - continue; - - /* collect priority name */ - for (bp = buf; *q && !strchr("\t ,;", *q); ) - *bp++ = *q++; - *bp = '\0'; - - /* skip cruft */ - while (strchr(",;", *q)) - q++; - - /* decode priority name */ - if ( *buf == '!' ) { - ignorepri = 1; - for (bp=buf; *(bp+1); bp++) - *bp=*(bp+1); - *bp='\0'; - } - else { - ignorepri = 0; - } - if ( *buf == '=' ) - { - singlpri = 1; - pri = decode(&buf[1], PriNames); - } - else { - singlpri = 0; - pri = decode(buf, PriNames); - } - - if (pri < 0) { - snprintf((char*) xbuf, sizeof(xbuf), "unknown priority name \"%s\"", buf); - logerror((char*) xbuf); - return RS_RET_ERR; - } - - /* scan facilities */ - while (*p && !strchr("\t .;", *p)) { - for (bp = buf; *p && !strchr("\t ,;.", *p); ) - *bp++ = *p++; - *bp = '\0'; - if (*buf == '*') { - for (i = 0; i <= LOG_NFACILITIES; i++) { - if ( pri == INTERNAL_NOPRI ) { - if ( ignorepri ) - f->f_filterData.f_pmask[i] = TABLE_ALLPRI; - else - f->f_filterData.f_pmask[i] = TABLE_NOPRI; - } - else if ( singlpri ) { - if ( ignorepri ) - f->f_filterData.f_pmask[i] &= ~(1<<pri); - else - f->f_filterData.f_pmask[i] |= (1<<pri); - } - else - { - if ( pri == TABLE_ALLPRI ) { - if ( ignorepri ) - f->f_filterData.f_pmask[i] = TABLE_NOPRI; - else - f->f_filterData.f_pmask[i] = TABLE_ALLPRI; - } - else - { - if ( ignorepri ) - for (i2= 0; i2 <= pri; ++i2) - f->f_filterData.f_pmask[i] &= ~(1<<i2); - else - for (i2= 0; i2 <= pri; ++i2) - f->f_filterData.f_pmask[i] |= (1<<i2); - } - } - } - } else { - i = decode(buf, FacNames); - if (i < 0) { - - snprintf((char*) xbuf, sizeof(xbuf), "unknown facility name \"%s\"", buf); - logerror((char*) xbuf); - return RS_RET_ERR; - } - - if ( pri == INTERNAL_NOPRI ) { - if ( ignorepri ) - f->f_filterData.f_pmask[i >> 3] = TABLE_ALLPRI; - else - f->f_filterData.f_pmask[i >> 3] = TABLE_NOPRI; - } else if ( singlpri ) { - if ( ignorepri ) - f->f_filterData.f_pmask[i >> 3] &= ~(1<<pri); - else - f->f_filterData.f_pmask[i >> 3] |= (1<<pri); - } else { - if ( pri == TABLE_ALLPRI ) { - if ( ignorepri ) - f->f_filterData.f_pmask[i >> 3] = TABLE_NOPRI; - else - f->f_filterData.f_pmask[i >> 3] = TABLE_ALLPRI; - } else { - if ( ignorepri ) - for (i2= 0; i2 <= pri; ++i2) - f->f_filterData.f_pmask[i >> 3] &= ~(1<<i2); - else - for (i2= 0; i2 <= pri; ++i2) - f->f_filterData.f_pmask[i >> 3] |= (1<<i2); - } - } - } - while (*p == ',' || *p == ' ') - p++; - } - - p = q; - } - - /* skip to action part */ - while (*p == '\t' || *p == ' ') - p++; - - *pline = p; - return RS_RET_OK; -} - - -/* - * Helper to cfline(). This function takes the filter part of a property - * based filter and decodes it. It processes the line up to the beginning - * of the action part. A pointer to that beginnig is passed back to the caller. - * rgerhards 2005-09-15 - */ -static rsRetVal cflineProcessPropFilter(uchar **pline, register selector_t *f) -{ - rsParsObj *pPars; - rsCStrObj *pCSCompOp; - rsRetVal iRet; - int iOffset; /* for compare operations */ - - assert(pline != NULL); - assert(*pline != NULL); - assert(f != NULL); - - dbgprintf(" - property-based filter\n"); - errno = 0; /* keep strerror_r() stuff out of logerror messages */ - - f->f_filter_type = FILTER_PROP; - - /* create parser object starting with line string without leading colon */ - if((iRet = rsParsConstructFromSz(&pPars, (*pline)+1)) != RS_RET_OK) { - logerrorInt("Error %d constructing parser object - ignoring selector", iRet); - return(iRet); - } - - /* read property */ - iRet = parsDelimCStr(pPars, &f->f_filterData.prop.pCSPropName, ',', 1, 1); - if(iRet != RS_RET_OK) { - logerrorInt("error %d parsing filter property - ignoring selector", iRet); - rsParsDestruct(pPars); - return(iRet); - } - - /* read operation */ - iRet = parsDelimCStr(pPars, &pCSCompOp, ',', 1, 1); - if(iRet != RS_RET_OK) { - logerrorInt("error %d compare operation property - ignoring selector", iRet); - rsParsDestruct(pPars); - return(iRet); - } - - /* we now first check if the condition is to be negated. To do so, we first - * must make sure we have at least one char in the param and then check the - * first one. - * rgerhards, 2005-09-26 - */ - if(rsCStrLen(pCSCompOp) > 0) { - if(*rsCStrGetBufBeg(pCSCompOp) == '!') { - f->f_filterData.prop.isNegated = 1; - iOffset = 1; /* ignore '!' */ - } else { - f->f_filterData.prop.isNegated = 0; - iOffset = 0; - } - } else { - f->f_filterData.prop.isNegated = 0; - iOffset = 0; - } - - if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (uchar*) "contains", 8)) { - f->f_filterData.prop.operation = FIOP_CONTAINS; - } else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (uchar*) "isequal", 7)) { - f->f_filterData.prop.operation = FIOP_ISEQUAL; - } else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (uchar*) "startswith", 10)) { - f->f_filterData.prop.operation = FIOP_STARTSWITH; - } else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (unsigned char*) "regex", 5)) { - f->f_filterData.prop.operation = FIOP_REGEX; - } else { - logerrorSz("error: invalid compare operation '%s' - ignoring selector", - (char*) rsCStrGetSzStrNoNULL(pCSCompOp)); - } - rsCStrDestruct (pCSCompOp); /* no longer needed */ - - /* read compare value */ - iRet = parsQuotedCStr(pPars, &f->f_filterData.prop.pCSCompValue); - if(iRet != RS_RET_OK) { - logerrorInt("error %d compare value property - ignoring selector", iRet); - rsParsDestruct(pPars); - return(iRet); - } - - /* skip to action part */ - if((iRet = parsSkipWhitespace(pPars)) != RS_RET_OK) { - logerrorInt("error %d skipping to action part - ignoring selector", iRet); - rsParsDestruct(pPars); - return(iRet); - } - - /* cleanup */ - *pline = *pline + rsParsGetParsePointer(pPars) + 1; - /* we are adding one for the skipped initial ":" */ - - return rsParsDestruct(pPars); -} - - -/* - * Helper to cfline(). This function interprets a BSD host selector line - * from the config file ("+/-hostname"). It stores it for further reference. - * rgerhards 2005-10-19 - */ -static rsRetVal cflineProcessHostSelector(uchar **pline) -{ - rsRetVal iRet; - - assert(pline != NULL); - assert(*pline != NULL); - assert(**pline == '-' || **pline == '+'); - - dbgprintf(" - host selector line\n"); - - /* check include/exclude setting */ - if(**pline == '+') { - eDfltHostnameCmpMode = HN_COMP_MATCH; - } else { /* we do not check for '-', it must be, else we wouldn't be here */ - eDfltHostnameCmpMode = HN_COMP_NOMATCH; - } - (*pline)++; /* eat + or - */ - - /* the below is somewhat of a quick hack, but it is efficient (this is - * why it is in here. "+*" resets the tag selector with BSD syslog. We mimic - * this, too. As it is easy to check that condition, we do not fire up a - * parser process, just make sure we do not address beyond our space. - * Order of conditions in the if-statement is vital! rgerhards 2005-10-18 - */ - if(**pline != '\0' && **pline == '*' && *(*pline+1) == '\0') { - dbgprintf("resetting BSD-like hostname filter\n"); - eDfltHostnameCmpMode = HN_NO_COMP; - if(pDfltHostnameCmp != NULL) { - if((iRet = rsCStrSetSzStr(pDfltHostnameCmp, NULL)) != RS_RET_OK) - return(iRet); - } - } else { - dbgprintf("setting BSD-like hostname filter to '%s'\n", *pline); - if(pDfltHostnameCmp == NULL) { - /* create string for parser */ - if((iRet = rsCStrConstructFromszStr(&pDfltHostnameCmp, *pline)) != RS_RET_OK) - return(iRet); - } else { /* string objects exists, just update... */ - if((iRet = rsCStrSetSzStr(pDfltHostnameCmp, *pline)) != RS_RET_OK) - return(iRet); - } - } - return RS_RET_OK; -} - - -/* - * Helper to cfline(). This function interprets a BSD tag selector line - * from the config file ("!tagname"). It stores it for further reference. - * rgerhards 2005-10-18 - */ -static rsRetVal cflineProcessTagSelector(uchar **pline) -{ - rsRetVal iRet; - - assert(pline != NULL); - assert(*pline != NULL); - assert(**pline == '!'); - - dbgprintf(" - programname selector line\n"); - - (*pline)++; /* eat '!' */ - - /* the below is somewhat of a quick hack, but it is efficient (this is - * why it is in here. "!*" resets the tag selector with BSD syslog. We mimic - * this, too. As it is easy to check that condition, we do not fire up a - * parser process, just make sure we do not address beyond our space. - * Order of conditions in the if-statement is vital! rgerhards 2005-10-18 - */ - if(**pline != '\0' && **pline == '*' && *(*pline+1) == '\0') { - dbgprintf("resetting programname filter\n"); - if(pDfltProgNameCmp != NULL) { - if((iRet = rsCStrSetSzStr(pDfltProgNameCmp, NULL)) != RS_RET_OK) - return(iRet); - } - } else { - dbgprintf("setting programname filter to '%s'\n", *pline); - if(pDfltProgNameCmp == NULL) { - /* create string for parser */ - if((iRet = rsCStrConstructFromszStr(&pDfltProgNameCmp, *pline)) != RS_RET_OK) - return(iRet); - } else { /* string objects exists, just update... */ - if((iRet = rsCStrSetSzStr(pDfltProgNameCmp, *pline)) != RS_RET_OK) - return(iRet); - } - } - return RS_RET_OK; -} - - -/* add an Action to the current selector - * The pOMSR is freed, as it is not needed after this function. - * Note: this function pulls global data that specifies action config state. - * rgerhards, 2007-07-27 - */ -rsRetVal addAction(action_t **ppAction, modInfo_t *pMod, void *pModData, omodStringRequest_t *pOMSR, int bSuspended) -{ - DEFiRet; - int i; - int iTplOpts; - uchar *pTplName; - action_t *pAction; - char errMsg[512]; - - assert(ppAction != NULL); - assert(pMod != NULL); - assert(pOMSR != NULL); - dbgprintf("Module %s processed this config line.\n", modGetName(pMod)); - - CHKiRet(actionConstruct(&pAction)); /* create action object first */ - pAction->pMod = pMod; - pAction->pModData = pModData; - pAction->bExecWhenPrevSusp = bActExecWhenPrevSusp; - - /* check if we can obtain the template pointers - TODO: move to separat function? */ - pAction->iNumTpls = OMSRgetEntryCount(pOMSR); - assert(pAction->iNumTpls >= 0); /* only debug check because this "can not happen" */ - /* please note: iNumTpls may validly be zero. This is the case if the module - * does not request any templates. This sounds unlikely, but an actual example is - * the discard action, which does not require a string. -- rgerhards, 2007-07-30 - */ - if(pAction->iNumTpls > 0) { - /* we first need to create the template pointer array */ - if((pAction->ppTpl = calloc(pAction->iNumTpls, sizeof(struct template *))) == NULL) { - glblHadMemShortage = 1; - ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); - } - /* and now the array for doAction() message pointers */ - if((pAction->ppMsgs = calloc(pAction->iNumTpls, sizeof(uchar *))) == NULL) { - glblHadMemShortage = 1; - ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); - } - } - - for(i = 0 ; i < pAction->iNumTpls ; ++i) { - CHKiRet(OMSRgetEntry(pOMSR, i, &pTplName, &iTplOpts)); - /* Ok, we got everything, so it now is time to look up the - * template (Hint: templates MUST be defined before they are - * used!) - */ - if((pAction->ppTpl[i] = tplFind((char*)pTplName, strlen((char*)pTplName))) == NULL) { - snprintf(errMsg, sizeof(errMsg) / sizeof(char), - " Could not find template '%s' - action disabled\n", - pTplName); - errno = 0; - logerror(errMsg); - ABORT_FINALIZE(RS_RET_NOT_FOUND); - } - /* check required template options */ - if( (iTplOpts & OMSR_RQD_TPL_OPT_SQL) - && (pAction->ppTpl[i]->optFormatForSQL == 0)) { - errno = 0; - logerror("Action disabled. To use this action, you have to specify " - "the SQL or stdSQL option in your template!\n"); - ABORT_FINALIZE(RS_RET_RQD_TPLOPT_MISSING); - } - - dbgprintf("template: '%s' assigned\n", pTplName); - } - - pAction->pMod = pMod; - pAction->pModData = pModData; - /* now check if the module is compatible with select features */ - if(pMod->isCompatibleWithFeature(sFEATURERepeatedMsgReduction) == RS_RET_OK) - pAction->f_ReduceRepeated = bReduceRepeatMsgs; - else { - dbgprintf("module is incompatible with RepeatedMsgReduction - turned off\n"); - pAction->f_ReduceRepeated = 0; - } - pAction->bEnabled = 1; /* action is enabled */ - - if(bSuspended) - actionSuspend(pAction); - - *ppAction = pAction; /* finally store the action pointer */ - -finalize_it: - if(iRet == RS_RET_OK) - iRet = OMSRdestruct(pOMSR); - else { - /* do not overwrite error state! */ - OMSRdestruct(pOMSR); - if(pAction != NULL) - actionDestruct(pAction); - } - - return iRet; -} - - -/* read the filter part of a configuration line and store the filter - * in the supplied selector_t - * rgerhards, 2007-08-01 - */ -static rsRetVal cflineDoFilter(uchar **pp, selector_t *f) -{ - DEFiRet; - - assert(pp != NULL); - assert(f != NULL); - - /* check which filter we need to pull... */ - switch(**pp) { - case ':': - iRet = cflineProcessPropFilter(pp, f); - break; - default: - iRet = cflineProcessTradPRIFilter(pp, f); - break; - } - - /* we now check if there are some global (BSD-style) filter conditions - * and, if so, we copy them over. rgerhards, 2005-10-18 - */ - if(pDfltProgNameCmp != NULL) - if((iRet = rsCStrConstructFromCStr(&(f->pCSProgNameComp), pDfltProgNameCmp)) != RS_RET_OK) - return(iRet); - - if(eDfltHostnameCmpMode != HN_NO_COMP) { - f->eHostnameCmpMode = eDfltHostnameCmpMode; - if((iRet = rsCStrConstructFromCStr(&(f->pCSHostnameComp), pDfltHostnameCmp)) != RS_RET_OK) - return(iRet); - } - - return iRet; -} - - -/* process the action part of a selector line - * rgerhards, 2007-08-01 - */ -static rsRetVal cflineDoAction(uchar **p, action_t **ppAction) -{ - DEFiRet; - modInfo_t *pMod; - omodStringRequest_t *pOMSR; - action_t *pAction; - void *pModData; - - assert(p != NULL); - assert(ppAction != NULL); - - /* loop through all modules and see if one picks up the line */ - pMod = omodGetNxt(NULL); - while(pMod != NULL) { - pOMSR = NULL; - iRet = pMod->mod.om.parseSelectorAct(p, &pModData, &pOMSR); - dbgprintf("tried selector action for %s: %d\n", modGetName(pMod), iRet); - if(iRet == RS_RET_OK || iRet == RS_RET_SUSPENDED) { - if((iRet = addAction(&pAction, pMod, pModData, pOMSR, (iRet == RS_RET_SUSPENDED)? 1 : 0)) == RS_RET_OK) { - /* now check if the module is compatible with select features */ - if(pMod->isCompatibleWithFeature(sFEATURERepeatedMsgReduction) == RS_RET_OK) - pAction->f_ReduceRepeated = bReduceRepeatMsgs; - else { - dbgprintf("module is incompatible with RepeatedMsgReduction - turned off\n"); - pAction->f_ReduceRepeated = 0; - } - pAction->bEnabled = 1; /* action is enabled */ - } - break; - } - else if(iRet != RS_RET_CONFLINE_UNPROCESSED) { - /* In this case, the module would have handled the config - * line, but some error occured while doing so. This error should - * already by reported by the module. We do not try any other - * modules on this line, because we found the right one. - * rgerhards, 2007-07-24 - */ - dbgprintf("error %d parsing config line\n", (int) iRet); - break; - } - pMod = omodGetNxt(pMod); - } - - *ppAction = pAction; - return iRet; -} - - -/* helper to selectorAddListCheckActions() - * This is the fucntion to be executed by llExecFunc - */ -DEFFUNC_llExecFunc(selectorAddListCheckActionsChecker) -{ - DEFiRet; - action_t *pAction = (action_t *) pData; - - assert(pAction != NULL); - - if(pAction->pMod->needUDPSocket(pAction->pModData) == RS_RET_TRUE) { - Forwarding++; - } - - return iRet; -} - -/* loop through a list of actions and perform necessary checks and - * housekeeping. This function must only be called when the owning - * selector_t looks valid and is not likely to be discarded. However, - * if we do not return RS_RET_OK, the caller MUST discard the - * owning selector_t. -- rgerhards, 2007-08-02 -*/ -static rsRetVal selectorAddListCheckActions(selector_t *f) -{ - DEFiRet; - - assert(f != NULL); - - CHKiRet(llExecFunc(&f->llActList, selectorAddListCheckActionsChecker, NULL)); - -finalize_it: - return iRet; + dbgprintf(" (re)started.\n"); + ENDfunc } @@ -5305,7 +2372,8 @@ finalize_it: * selector is NULL, which means we do not need to care about it at * all. -- rgerhards, 2007-08-01 */ -static rsRetVal selectorAddList(selector_t *f) +rsRetVal +selectorAddList(selector_t *f) { DEFiRet; int iActionCnt; @@ -5315,14 +2383,9 @@ static rsRetVal selectorAddList(selector_t *f) if(f != NULL) { CHKiRet(llGetNumElts(&f->llActList, &iActionCnt)); if(iActionCnt == 0) { - logerror("warning: selector line without actions will be discarded"); + errmsg.LogError(NO_ERRCODE, "warning: selector line without actions will be discarded"); selectorDestruct(f); } else { - if((iRet = selectorAddListCheckActions(f)) != RS_RET_OK) { - logerror("selector line will be discarded due to error in action(s)"); - selectorDestruct(f); - goto finalize_it; - } /* 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 @@ -5343,162 +2406,36 @@ static rsRetVal selectorAddList(selector_t *f) } finalize_it: - return iRet; + RETiRet; } -/* Process a configuration file line in traditional "filter selector" format +/* set the main message queue mode + * rgerhards, 2008-01-03 */ -static rsRetVal cflineClassic(uchar *p, selector_t **pfCurr) +static rsRetVal setMainMsgQueType(void __attribute__((unused)) *pVal, uchar *pszType) { DEFiRet; - action_t *pAction; - selector_t *fCurr; - - assert(pfCurr != NULL); - fCurr = *pfCurr; - - /* lines starting with '&' have no new filters and just add - * new actions to the currently processed selector. - */ - if(*p == '&') { - ++p; /* eat '&' */ - skipWhiteSpace(&p); /* on to command */ + if (!strcasecmp((char *) pszType, "fixedarray")) { + MainMsgQueType = QUEUETYPE_FIXED_ARRAY; + 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"); + } else if (!strcasecmp((char *) pszType, "disk")) { + MainMsgQueType = QUEUETYPE_DISK; + 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"); } else { - /* we are finished with the current selector. So we now need to check - * if it has any actions associated and, if so, link it to the linked - * list. If it has nothing associated with it, we can simply discard - * it. In any case, we create a fresh selector for our new filter. - * We have one special case during initialization: then, the current - * selector is NULL, which means we do not need to care about it at - * all. -- rgerhards, 2007-08-01 - */ - CHKiRet(selectorAddList(fCurr)); - CHKiRet(selectorConstruct(&fCurr)); /* create "fresh" selector */ - CHKiRet(cflineDoFilter(&p, fCurr)); /* pull filters */ - } - - CHKiRet(cflineDoAction(&p, &pAction)); - CHKiRet(llAppend(&fCurr->llActList, NULL, (void*) pAction)); - -finalize_it: - *pfCurr = fCurr; - return iRet; -} - - -/* process a configuration line - * I re-did this functon because it was desperately time to do so - * rgerhards, 2007-08-01 - */ -static rsRetVal cfline(uchar *line, selector_t **pfCurr) -{ - DEFiRet; - - assert(line != NULL); - - dbgprintf("cfline: '%s'\n", line); - - /* check type of line and call respective processing */ - switch(*line) { - case '!': - iRet = cflineProcessTagSelector(&line); - break; - case '+': - case '-': - iRet = cflineProcessHostSelector(&line); - break; - case '$': - ++line; /* eat '$' */ - iRet = cfsysline(line); - break; - default: - iRet = cflineClassic(line, pfCurr); - break; - } - - return iRet; -} - - -/* Decode a symbolic name to a numeric value - */ -int decode(uchar *name, struct code *codetab) -{ - register struct code *c; - register uchar *p; - uchar buf[80]; - - assert(name != NULL); - assert(codetab != NULL); - - dbgprintf("symbolic name: %s", name); - if (isdigit((int) *name)) - { - dbgprintf("\n"); - return (atoi((char*) name)); + errmsg.LogError(NO_ERRCODE, "unknown mainmessagequeuetype parameter: %s", (char *) pszType); + iRet = RS_RET_INVALID_PARAMS; } - strncpy((char*) buf, (char*) name, 79); - for (p = buf; *p; p++) - if (isupper((int) *p)) - *p = tolower((int) *p); - for (c = codetab; c->c_name; c++) - if (!strcmp((char*) buf, (char*) c->c_name)) - { - dbgprintf(" ==> %d\n", c->c_val); - return (c->c_val); - } - return (-1); -} - -extern void dbgprintf(char *fmt, ...) __attribute__((format(printf,1, 2))); -void dbgprintf(char *fmt, ...) -{ -# ifdef USE_PTHREADS - static int bWasNL = FALSE; -# endif - va_list ap; - - if ( !(Debug && debugging_on) ) - return; - -# ifdef USE_PTHREADS - /* The bWasNL handler does not really work. It works if no thread - * switching occurs during non-NL messages. Else, things are messed - * up. Anyhow, it works well enough to provide useful help during - * getting this up and running. It is questionable if the extra effort - * is worth fixing it, giving the limited appliability. - * rgerhards, 2005-10-25 - * I have decided that it is not worth fixing it - especially as it works - * pretty well. - * rgerhards, 2007-06-15 - */ - if(bWasNL) { - fprintf(stdout, "%8.8d: ", (unsigned int) pthread_self()); - } - bWasNL = (*(fmt + strlen(fmt) - 1) == '\n') ? TRUE : FALSE; -# endif - va_start(ap, fmt); - vfprintf(stdout, fmt, ap); - va_end(ap); - - fflush(stdout); - return; -} - + free(pszType); /* no longer needed */ -char *rs_strerror_r(int errnum, char *buf, size_t buflen) { -#ifdef STRERROR_R_CHAR_P - char *p = strerror_r(errnum, buf, buflen); - if (p != buf) { - strncpy(buf, p, buflen); - buf[buflen - 1] = '\0'; - } -#else - strerror_r(errnum, buf, buflen); -#endif - return buf; + RETiRet; } @@ -5538,6 +2475,10 @@ void sighup_handler() * \param DstSize Maximum numbers of characters to store. * \param cSep Separator char. * \ret int Returns 0 if no error occured. + * + * rgerhards, 2008-02-12: some notes are due... I will once again fix this function, this time + * so that it treats ' ' as a request for whitespace. But in general, the function and its callers + * should be changed over time, this is not really very good code... */ int getSubString(uchar **ppSrc, char *pDst, size_t DstSize, char cSep) { @@ -5564,48 +2505,6 @@ int getSubString(uchar **ppSrc, char *pDst, size_t DstSize, char cSep) } -/* print out which socket we are listening on. This is only - * a debug aid. rgerhards, 2007-07-02 - */ -static void debugListenInfo(int fd, char *type) -{ - char *szFamily; - int port; - struct sockaddr sa; - struct sockaddr_in *ipv4; - struct sockaddr_in6 *ipv6; - socklen_t saLen = sizeof(sa); - - if(getsockname(fd, &sa, &saLen) == 0) { - switch(sa.sa_family) { - case PF_INET: - szFamily = "IPv4"; - ipv4 = (struct sockaddr_in*) &sa; - port = ntohs(ipv4->sin_port); - break; - case PF_INET6: - szFamily = "IPv6"; - ipv6 = (struct sockaddr_in6*) &sa; - port = ntohs(ipv6->sin6_port); - break; - default: - szFamily = "other"; - port = -1; - break; - } - dbgprintf("Listening on %s syslogd socket %d (%s/port %d).\n", - type, fd, szFamily, port); - return; - } - - /* we can not obtain peer info. We are just providing - * debug info, so this is no reason to break the program - * or do any serious error reporting. - */ - dbgprintf("Listening on syslogd socket %d - could not obtain peer info.\n", fd); -} - - /* this function pulls all internal messages from the buffer * and puts them into the processing engine. * We can only do limited error handling, as this would not @@ -5619,459 +2518,68 @@ static void processImInternal(void) msg_t *pMsg; while(iminternalRemoveMsg(&iPri, &pMsg, &iFlags) == RS_RET_OK) { - logmsg(iPri, pMsg, iFlags); + logmsg(pMsg, iFlags); } } -/* helper function for mainloop(). This is used to add all module - * writeFDsfor Select via llExecFunc(). - * rgerhards, 2007-08-02 - */ -typedef struct selectHelperWriteFDSInfo_s { /* struct for pParam */ - fd_set *pWritefds; - int *pMaxfds; -} selectHelperWriteFDSInfo_t; -DEFFUNC_llExecFunc(mainloopAddModWriteFDSforSelect) -{ - DEFiRet; - action_t *pAction = (action_t*) pData; - selectHelperWriteFDSInfo_t *pState = (selectHelperWriteFDSInfo_t*) pParam; - short fdMod; - - assert(pAction != NULL); - assert(pState != NULL); - - if(pAction->pMod->getWriteFDForSelect(pAction->pModData, &fdMod) == RS_RET_OK) { - FD_SET(fdMod, pState->pWritefds); - if(fdMod > *pState->pMaxfds) - *pState->pMaxfds = fdMod; - } - - return iRet; -} - - -/* helper function for mainloop(). This is used to call module action - * handlers after select if a fd is writable. - * HINT: when we change to the new threading model, this function - * is probably no longer needed. - * rgerhards, 2007-08-02 - */ -DEFFUNC_llExecFunc(mainloopCallWithWritableFDsActions) -{ - DEFiRet; - action_t *pAction = (action_t*) pData; - selectHelperWriteFDSInfo_t *pState = (selectHelperWriteFDSInfo_t*) pParam; - short fdMod; - - assert(pAction != NULL); - assert(pState != NULL); - - if(pAction->pMod->getWriteFDForSelect(pAction->pModData, &fdMod) == RS_RET_OK) { - if(FD_ISSET(fdMod, pState->pWritefds)) { - if((iRet = pAction->pMod->onSelectReadyWrite(pAction->pModData)) - != RS_RET_OK) { - dbgprintf("error %d from onSelectReadyWrite() - continuing\n", iRet); - } - if(--(pState->pMaxfds) == 0) { - ABORT_FINALIZE(RS_RET_FINISHED); /* all processed, nothing left to do */ - } - } - } - -finalize_it: - return iRet; -} - - -/* process the select() selector array after the successful select. - * processing is completed as soon as all selectors needing attention - * are processed. - * rgerhards, 2007-08-08 - */ -static rsRetVal processSelectAfter(int maxfds, int nfds, fd_set *pReadfds, fd_set *pWritefds) -{ - DEFiRet; - rsRetVal iRetLL; - int i; - int fd; - char line[MAXLINE +1]; - selectHelperWriteFDSInfo_t writeFDSInfo; -#ifdef SYSLOG_INET - selector_t *f; - struct sockaddr_storage frominet; - socklen_t socklen; - uchar fromHost[NI_MAXHOST]; - uchar fromHostFQDN[NI_MAXHOST]; - int iTCPSess; - ssize_t l; -#endif /* #ifdef SYSLOG_INET */ - - /* the following macro is used to decrement the number of to-be-probed - * fds and abort this function when we are done with all. - */ -# define FDPROCESSED() if(--nfds == 0) { ABORT_FINALIZE(RS_RET_OK); } - - if (nfds < 0) { - if (errno != EINTR) - logerror("select"); - dbgprintf("Select interrupted.\n"); - ABORT_FINALIZE(RS_RET_OK); /* we are done in any case */ - } - - if(debugging_on) { - dbgprintf("\nSuccessful select, descriptor count = %d, Activity on: ", nfds); - for (i = 0; i <= maxfds; ++i) - if ( FD_ISSET(i, pReadfds) ) - dbgprintf("%d ", i); - dbgprintf(("\n")); - } - -#ifdef SYSLOG_INET - /* Now check the TCP send sockets. So far, we only see if they become - * writable and then change their internal status. No real async - * writing is currently done. This code will be replaced once liblogging - * is used, thus we try not to focus too much on it. - * - * IMPORTANT: With the current code, the writefds must be checked first, - * because the readfds might have messages to be forwarded, which - * rely on the status setting that is done here! - * rgerhards 2005-07-20 - * - * liblogging implementation will not happen as anticipated above. So - * this code here will stay for quite a while. - * rgerhards, 2006-12-07 - */ - writeFDSInfo.pWritefds = pWritefds; - writeFDSInfo.pMaxfds = &nfds; - for(f = Files; f != NULL ; f = f->f_next) { - iRetLL = llExecFunc(&f->llActList, mainloopCallWithWritableFDsActions, &writeFDSInfo); - if(iRetLL == RS_RET_FINISHED) { - ABORT_FINALIZE(RS_RET_OK); /* we are done in this case */ - } - } -#endif /* #ifdef SYSLOG_INET */ -#ifdef SYSLOG_UNIXAF - for (i = 0; i < nfunix; i++) { - if ((fd = funix[i]) != -1 && FD_ISSET(fd, pReadfds)) { - int iRcvd; - iRcvd = recv(fd, line, MAXLINE - 1, 0); - dbgprintf("Message from UNIX socket: #%d\n", fd); - if (iRcvd > 0) { - printchopped(LocalHostName, line, iRcvd, fd, funixParseHost[i]); - } else if (iRcvd < 0 && errno != EINTR) { - char errStr[1024]; - rs_strerror_r(errno, errStr, sizeof(errStr)); - dbgprintf("UNIX socket error: %d = %s.\n", \ - errno, errStr); - logerror("recvfrom UNIX"); - } - FDPROCESSED(); - } - } -#endif - -#ifdef SYSLOG_INET - if (finet != NULL && AcceptRemote) { - for (i = 0; i < *finet; i++) { - if (FD_ISSET(finet[i+1], pReadfds)) { - socklen = sizeof(frominet); - memset(line, 0xff, sizeof(line)); // TODO: I think we need this for debug only - remove after bug hunt - l = recvfrom(finet[i+1], line, MAXLINE - 1, 0, - (struct sockaddr *)&frominet, &socklen); - if (l > 0) { - if(cvthname(&frominet, fromHost, fromHostFQDN) == RS_RET_OK) { - dbgprintf("Message from inetd socket: #%d, host: %s\n", - finet[i+1], fromHost); - /* 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 - */ - if(isAllowedSender(pAllowedSenders_UDP, - (struct sockaddr *)&frominet, (char*)fromHostFQDN)) { - printchopped((char*)fromHost, line, l, finet[i+1], 1); - } else { - dbgprintf("%s is not an allowed sender\n", (char*)fromHostFQDN); - if(option_DisallowWarning) { - logerrorSz("UDP message from disallowed sender %s discarded", - (char*)fromHost); - } - } - } - } else if (l < 0 && errno != EINTR && errno != EAGAIN) { - char errStr[1024]; - rs_strerror_r(errno, errStr, sizeof(errStr)); - dbgprintf("INET socket error: %d = %s.\n", errno, errStr); - logerror("recvfrom inet"); - /* should be harmless */ - sleep(1); - } - FDPROCESSED(); - } - } - } - - if(sockTCPLstn != NULL && *sockTCPLstn) { - for (i = 0; i < *sockTCPLstn; i++) { - if (FD_ISSET(sockTCPLstn[i+1], pReadfds)) { - dbgprintf("New connect on TCP inetd socket: #%d\n", sockTCPLstn[i+1]); -# ifdef USE_GSSAPI - if(bEnableTCP & ALLOWEDMETHOD_GSS) - TCPSessGSSAccept(sockTCPLstn[i+1]); - else -# endif - TCPSessAccept(sockTCPLstn[i+1]); - FDPROCESSED(); - } - } - - /* now check the sessions */ - iTCPSess = TCPSessGetNxtSess(-1); - while(iTCPSess != -1) { - int fdSess; - int state; - fdSess = pTCPSessions[iTCPSess].sock; - if(FD_ISSET(fdSess, pReadfds)) { - char buf[MAXLINE]; - dbgprintf("tcp session socket with new data: #%d\n", fdSess); - - /* Receive message */ -# ifdef USE_GSSAPI - int allowedMethods = pTCPSessions[iTCPSess].allowedMethods; - if(allowedMethods & ALLOWEDMETHOD_GSS) - state = TCPSessGSSRecv(iTCPSess, buf, sizeof(buf)); - else -# endif - state = recv(fdSess, buf, sizeof(buf), 0); - if(state == 0) { -# ifdef USE_GSSAPI - if(allowedMethods & ALLOWEDMETHOD_GSS) - TCPSessGSSClose(iTCPSess); - else { -# endif - /* process any incomplete frames left over */ - TCPSessPrepareClose(iTCPSess); - /* Session closed */ - TCPSessClose(iTCPSess); -# ifdef USE_GSSAPI - } -# endif - } else if(state == -1) { - logerrorInt("TCP session %d will be closed, error ignored\n", - fdSess); -# ifdef USE_GSSAPI - if(allowedMethods & ALLOWEDMETHOD_GSS) - TCPSessGSSClose(iTCPSess); - else -# endif - TCPSessClose(iTCPSess); - } else { - /* valid data received, process it! */ - if(TCPSessDataRcvd(iTCPSess, buf, state) == 0) { - /* in this case, something went awfully wrong. - * We are instructed to terminate the session. - */ - logerrorInt("Tearing down TCP Session %d - see " - "previous messages for reason(s)\n", - iTCPSess); -# ifdef USE_GSSAPI - if(allowedMethods & ALLOWEDMETHOD_GSS) - TCPSessGSSClose(iTCPSess); - else -# endif - TCPSessClose(iTCPSess); - } - } - FDPROCESSED(); - } - iTCPSess = TCPSessGetNxtSess(iTCPSess); - } - } - -#endif -finalize_it: - return iRet; -} - - /* This is the main processing loop. It is called after successful initialization. * When it returns, the syslogd terminates. + * Its sole function is to provide some housekeeping things. The real work is done + * by the other threads spawned. */ -static void mainloop(void) +static void +mainloop(void) { - fd_set readfds; - int i; - int maxfds; - int nfds; - int errnoSave; -#ifdef SYSLOG_INET - selectHelperWriteFDSInfo_t writeFDSInfo; - fd_set writefds; - selector_t *f; - int iTCPSess; -#endif /* #ifdef SYSLOG_INET */ -#ifdef BSD -#ifdef USE_PTHREADS struct timeval tvSelectTimeout; -#endif -#endif + BEGINfunc while(!bFinished){ - errno = 0; - maxfds = 0; - FD_ZERO (&readfds); - /* first check if we have any internal messages queued and spit them out */ + /* TODO: do we need this any longer? I doubt it, but let's care about it + * later -- rgerhards, 2007-12-21 + */ processImInternal(); -#ifdef SYSLOG_UNIXAF - /* Add the Unix Domain Sockets to the list of read - * descriptors. - * rgerhards 2005-08-01: we must now check if there are - * any local sockets to listen to at all. If the -o option - * is given without -a, we do not need to listen at all.. - */ - /* Copy master connections */ - for (i = startIndexUxLocalSockets; i < nfunix; i++) { - if (funix[i] != -1) { - FD_SET(funix[i], &readfds); - if (funix[i]>maxfds) maxfds=funix[i]; - } - } -#endif -#ifdef SYSLOG_INET - /* Add the UDP listen sockets to the list of read descriptors. - */ - if(finet != NULL && AcceptRemote) { - for (i = 0; i < *finet; i++) { - if (finet[i+1] != -1) { - if(Debug) - debugListenInfo(finet[i+1], "UDP"); - FD_SET(finet[i+1], &readfds); - if(finet[i+1]>maxfds) maxfds=finet[i+1]; - } - } - } - - /* Add the TCP listen sockets to the list of read descriptors. - */ - if(sockTCPLstn != NULL && *sockTCPLstn) { - for (i = 0; i < *sockTCPLstn; i++) { - /* The if() below is theoretically not needed, but I leave it in - * so that a socket may become unsuable during execution. That - * feature is not yet supported by the current code base. - */ - if (sockTCPLstn[i+1] != -1) { - if(Debug) - debugListenInfo(sockTCPLstn[i+1], "TCP"); - FD_SET(sockTCPLstn[i+1], &readfds); - if(sockTCPLstn[i+1]>maxfds) maxfds=sockTCPLstn[i+1]; - } - } - /* do the sessions */ - iTCPSess = TCPSessGetNxtSess(-1); - while(iTCPSess != -1) { - int fdSess; - fdSess = pTCPSessions[iTCPSess].sock; - dbgprintf("Adding TCP Session %d\n", fdSess); - FD_SET(fdSess, &readfds); - if (fdSess>maxfds) maxfds=fdSess; - /* now get next... */ - iTCPSess = TCPSessGetNxtSess(iTCPSess); - } - } - - /* TODO: activate the code below only if we actually need to check - * for outstanding writefds. - */ - if(1) { - /* Now add the TCP output sockets to the writefds set. This implementation - * is not optimal (performance-wise) and it should be replaced with something - * better in the longer term. I've not yet done this, as this code is - * scheduled to be replaced after the liblogging integration. - * rgerhards 2005-07-20 - */ - FD_ZERO(&writefds); - writeFDSInfo.pWritefds = &writefds; - writeFDSInfo.pMaxfds = &maxfds; - for (f = Files; f != NULL ; f = f->f_next) { - llExecFunc(&f->llActList, mainloopAddModWriteFDSforSelect, &writeFDSInfo); - } - } -#endif - - if ( debugging_on ) { - dbgprintf("----------------------------------------\n"); - dbgprintf("Calling select, active file descriptors (max %d): ", maxfds); - for (nfds= 0; nfds <= maxfds; ++nfds) - if ( FD_ISSET(nfds, &readfds) ) - dbgprintf("%d ", nfds); - dbgprintf("\n"); - } - -#define MAIN_SELECT_TIMEVAL NULL -#ifdef BSD -#ifdef USE_PTHREADS - /* There seems to be a problem with BSD and threads. When running on - * multiple threads, a signal will not cause the select call to be - * interrrupted. I am not sure if this is by design or an bug (some - * information on the web let's me think it is a bug), but that really - * does not matter. The issue with our code is that we will not gain - * control when rsyslogd is terminated or huped. What I am doing now is - * make the select call timeout after 10 seconds, so that we can check - * the condition then. Obviously, this causes some sluggish behaviour and - * also the loss of some (very few) cpu cycles. Both, I think, are - * absolutely acceptable. - * rgerhards, 2005-10-26 - * TODO: I got some information: this seems to be expected signal() behaviour - * we should investigate the use of sigaction() (see klogd.c for an sample). - * rgerhards, 2007-06-22 - * rgerhards, 2007-09-11: code has been converted to sigaction() now. We need - * to re-check on BSD, I think the issue is now solved. - */ - tvSelectTimeout.tv_sec = 10; + /* this is now just a wait */ + tvSelectTimeout.tv_sec = TIMERINTVL; tvSelectTimeout.tv_usec = 0; -# undef MAIN_SELECT_TIMEVAL -# define MAIN_SELECT_TIMEVAL &tvSelectTimeout -#endif -#endif -#ifdef SYSLOG_INET -#define MAIN_SELECT_WRITEFDS (fd_set *) &writefds -#else -#define MAIN_SELECT_WRITEFDS NULL -#endif - nfds = select(maxfds+1, (fd_set *) &readfds, MAIN_SELECT_WRITEFDS, - (fd_set *) NULL, MAIN_SELECT_TIMEVAL); - errnoSave = errno; /* save errno for later reference */ - - if(bRequestDoMark) { - domark(); - bRequestDoMark = 0; - /* We do not use continue, because domark() is carried out - * only when something else happened. - */ - } + select(1, NULL, NULL, NULL, &tvSelectTimeout); + if(bFinished) + break; /* exit as quickly as possible - see long comment below */ + + /* If we received a HUP signal, we call doFlushRptdMsgs() a bit early. This + * doesn't matter, because doFlushRptdMsgs() checks timestamps. What may happen, + * however, is that the too-early call may lead to a bit too-late output + * of "last message repeated n times" messages. But that is quite acceptable. + * rgerhards, 2007-12-21 + * ... and just to explain, we flush here because that is exactly what the mainloop + * shall do - provide a periodic interval in which not-yet-flushed messages will + * be flushed. Be careful, there is a potential race condition: doFlushRptdMsgs() + * needs to aquire a lock on the action objects. If, however, long-running consumers + * cause the main queue worker threads to lock them for a long time, we may receive + * a starvation condition, resulting in the mainloop being held on lock for an extended + * period of time. That, in turn, could lead to unresponsiveness to termination + * requests. It is especially important that the bFinished flag is checked before + * doFlushRptdMsgs() is called (I know because I ran into that situation). I am + * not yet sure if the remaining probability window of a termination-related + * problem is large enough to justify changing the code - I would consider it + * extremely unlikely that the problem ever occurs in practice. Fixing it would + * require not only a lot of effort but would cost considerable performance. So + * for the time being, I think the remaining risk can be accepted. + * rgerhards, 2008-01-10 + */ + doFlushRptdMsgs(); + if(restart) { dbgprintf("\nReceived SIGHUP, reloading rsyslogd.\n"); - /* worker thread is stopped as part of init() */ + /* main queue is stopped as part of init() */ init(); restart = 0; continue; } - if (nfds == 0) { - dbgprintf("No select activity.\n"); - continue; - } - - errno = errnoSave; /* restore errno to state right after select (which is what we need) -- rgerhards, 2008-02-11 */ - processSelectAfter(maxfds, nfds, &readfds, MAIN_SELECT_WRITEFDS); - -#undef MAIN_SELECT_TIMEVAL -#undef MAIN_SELECT_WRITEFDS } + ENDfunc } /* If user is not root, prints warnings or even exits @@ -6081,6 +2589,11 @@ static void mainloop(void) */ 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) { @@ -6105,6 +2618,7 @@ static void checkPermissions() } #endif } +#endif } @@ -6115,16 +2629,20 @@ static rsRetVal loadBuildInModules(void) { DEFiRet; - if((iRet = doModInit(modInitFile, (uchar*) "builtin-file", NULL)) != RS_RET_OK) - return iRet; + if((iRet = module.doModInit(modInitFile, (uchar*) "builtin-file", NULL)) != RS_RET_OK) { + RETiRet; + } #ifdef SYSLOG_INET - if((iRet = doModInit(modInitFwd, (uchar*) "builtin-fwd", NULL)) != RS_RET_OK) - return iRet; + if((iRet = module.doModInit(modInitFwd, (uchar*) "builtin-fwd", NULL)) != RS_RET_OK) { + RETiRet; + } #endif - if((iRet = doModInit(modInitShell, (uchar*) "builtin-shell", NULL)) != RS_RET_OK) - return iRet; - if((iRet = doModInit(modInitDiscard, (uchar*) "builtin-discard", NULL)) != RS_RET_OK) - return iRet; + if((iRet = module.doModInit(modInitShell, (uchar*) "builtin-shell", NULL)) != RS_RET_OK) { + RETiRet; + } + if((iRet = module.doModInit(modInitDiscard, (uchar*) "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. @@ -6135,8 +2653,8 @@ static rsRetVal loadBuildInModules(void) * User names now must begin with: * [a-zA-Z0-9_.] */ - if((iRet = doModInit(modInitUsrMsg, (uchar*) "builtin-usrmsg", NULL)) != RS_RET_OK) - return iRet; + if((iRet = module.doModInit(modInitUsrMsg, (uchar*) "builtin-usrmsg", NULL)) != RS_RET_OK) + RETiRet; /* 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 @@ -6145,9 +2663,26 @@ 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 */ -#ifdef USE_PTHREADS + CHKiRet(regCfSysLineHdlr((uchar *)"workdirectory", 0, eCmdHdlrGetWord, NULL, &pszWorkDir, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"actionresumeretrycount", 0, eCmdHdlrInt, NULL, &glbliActionResumeRetryCount, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuefilename", 0, eCmdHdlrGetWord, NULL, &pszMainMsgQFName, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuesize", 0, eCmdHdlrInt, NULL, &iMainMsgQueueSize, NULL)); -#endif + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuehighwatermark", 0, eCmdHdlrInt, NULL, &iMainMsgQHighWtrMark, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuelowwatermark", 0, eCmdHdlrInt, NULL, &iMainMsgQLowWtrMark, NULL)); + 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 *)"mainmsgqueuetype", 0, eCmdHdlrGetWord, setMainMsgQueType, NULL, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueueworkerthreads", 0, eCmdHdlrInt, NULL, &iMainMsgQueueNumWorkers, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuetimeoutshutdown", 0, eCmdHdlrInt, NULL, &iMainMsgQtoQShutdown, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuetimeoutactioncompletion", 0, eCmdHdlrInt, NULL, &iMainMsgQtoActShutdown, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuetimeoutenqueue", 0, eCmdHdlrInt, NULL, &iMainMsgQtoEnq, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueueworketimeoutrthreadshutdown", 0, eCmdHdlrInt, NULL, &iMainMsgQtoWrkShutdown, NULL)); + 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 *)"mainmsgqueuemaxdiskspace", 0, eCmdHdlrSize, NULL, &iMainMsgQueMaxDiskSpace, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuesaveonshutdown", 0, eCmdHdlrBinary, NULL, &bMainMsgQSaveOnShutdown, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"repeatedmsgreduction", 0, eCmdHdlrBinary, NULL, &bReduceRepeatMsgs, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"actionexeconlywhenpreviousissuspended", 0, eCmdHdlrBinary, NULL, &bActExecWhenPrevSusp, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"actionresumeinterval", 0, eCmdHdlrInt, setActionResumeInterval, NULL, NULL)); @@ -6155,11 +2690,11 @@ static rsRetVal loadBuildInModules(void) CHKiRet(regCfSysLineHdlr((uchar *)"escapecontrolcharactersonreceive", 0, eCmdHdlrBinary, NULL, &bEscapeCCOnRcv, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"dropmsgswithmaliciousdnsptrrecords", 0, eCmdHdlrBinary, NULL, &bDropMalPTRMsgs, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"droptrailinglfonreception", 0, eCmdHdlrBinary, NULL, &bDropTrailingLF, NULL)); - CHKiRet(regCfSysLineHdlr((uchar *)"template", 0, eCmdHdlrCustomHandler, doNameLine, (void*)DIR_TEMPLATE, NULL)); - CHKiRet(regCfSysLineHdlr((uchar *)"outchannel", 0, eCmdHdlrCustomHandler, doNameLine, (void*)DIR_OUTCHANNEL, NULL)); - CHKiRet(regCfSysLineHdlr((uchar *)"allowedsender", 0, eCmdHdlrCustomHandler, doNameLine, (void*)DIR_ALLOWEDSENDER, NULL)); - CHKiRet(regCfSysLineHdlr((uchar *)"modload", 0, eCmdHdlrCustomHandler, doModLoad, NULL, NULL)); - CHKiRet(regCfSysLineHdlr((uchar *)"includeconfig", 0, eCmdHdlrCustomHandler, doIncludeLine, NULL, 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)); + CHKiRet(regCfSysLineHdlr((uchar *)"modload", 0, eCmdHdlrCustomHandler, conf.doModLoad, NULL, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"includeconfig", 0, eCmdHdlrCustomHandler, conf.doIncludeLine, NULL, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"umask", 0, eCmdHdlrFileCreateMode, setUmask, NULL, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"debugprinttemplatelist", 0, eCmdHdlrBinary, NULL, &bDebugPrintTemplateList, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"debugprintmodulelist", 0, eCmdHdlrBinary, NULL, &bDebugPrintModuleList, NULL)); @@ -6167,12 +2702,14 @@ static rsRetVal loadBuildInModules(void) NULL, &bDebugPrintCfSysLineHandlerList, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"moddir", 0, eCmdHdlrGetWord, NULL, &pModDir, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, NULL)); -#if defined(SYSLOG_INET) && defined(USE_GSSAPI) - CHKiRet(regCfSysLineHdlr((uchar *)"gsslistenservicename", 0, eCmdHdlrGetWord, NULL, &gss_listen_service_name, NULL)); -#endif + + /* 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: - return iRet; + RETiRet; } @@ -6182,11 +2719,6 @@ static void printVersion(void) { printf("rsyslogd %s, ", VERSION); printf("compiled with:\n"); -#ifdef USE_PTHREADS - printf("\tFEATURE_PTHREADS (dual-threading):\tYes\n"); -#else - printf("\tFEATURE_PTHREADS (dual-threading):\tNo\n"); -#endif #ifdef FEATURE_REGEXP printf("\tFEATURE_REGEXP:\t\t\t\tYes\n"); #else @@ -6202,21 +2734,21 @@ static void printVersion(void) #else printf("\tFEATURE_NETZIP (message compression):\tNo\n"); #endif -#ifdef SYSLOG_INET - printf("\tSYSLOG_INET (Internet/remote support):\tYes\n"); -#else - printf("\tSYSLOG_INET (Internet/remote support):\tNo\n"); -#endif #if defined(SYSLOG_INET) && defined(USE_GSSAPI) - printf("\tFEATURE_GSSAPI (GSSAPI Kerberos 5 support):\tYes\n"); + printf("\tGSSAPI Kerberos 5 support:\t\tYes\n"); #else - printf("\tFEATURE_GSSAPI (GSSAPI Kerberos 5 support):\tNo\n"); + printf("\tGSSAPI Kerberos 5 support:\t\tNo\n"); #endif #ifndef NDEBUG printf("\tFEATURE_DEBUG (debug build, slow code):\tYes\n"); #else printf("\tFEATURE_DEBUG (debug build, slow code):\tNo\n"); #endif +#ifdef RTINST + printf("\tRuntime Instrumentation (slow code):\tYes\n"); +#else + printf("\tRuntime Instrumentation (slow code):\tNo\n"); +#endif printf("\nSee http://www.rsyslog.com for more information.\n"); } @@ -6227,9 +2759,10 @@ static void printVersion(void) */ static void mainThread() { - DEFiRet; + BEGINfunc uchar *pTmp; +#if 0 // code moved back to main() /* doing some core initializations */ if((iRet = modInitIminternal()) != RS_RET_OK) { fprintf(stderr, "fatal error: could not initialize errbuf object (error code %d).\n", @@ -6242,21 +2775,25 @@ static void mainThread() iRet); exit(1); /* "good" exit, leaving at init for fatal error */ } +#endif /* Note: signals MUST be processed by the thread this code is running in. The reason * is that we need to interrupt the select() system call. -- rgerhards, 2007-10-17 */ - /* initialize the default templates - * we use template names with a SP in front - these - * can NOT be generated via the configuration file - */ - pTmp = template_TraditionalFormat; - tplAddLine(" TradFmt", &pTmp); + /* initialize the build-in templates */ + pTmp = template_SyslogProtocol23Format; + tplAddLine("RSYSLOG_SyslogProtocol23Format", &pTmp); + pTmp = template_FileFormat; /* new format for files with high-precision stamp */ + tplAddLine("RSYSLOG_FileFormat", &pTmp); + pTmp = template_TraditionalFileFormat; + tplAddLine("RSYSLOG_TraditionalFileFormat", &pTmp); pTmp = template_WallFmt; tplAddLine(" WallFmt", &pTmp); - pTmp = template_StdFwdFmt; - tplAddLine(" StdFwdFmt", &pTmp); + pTmp = template_ForwardFormat; + tplAddLine("RSYSLOG_ForwardFormat", &pTmp); + pTmp = template_TraditionalForwardFormat; + tplAddLine("RSYSLOG_TraditionalForwardFormat", &pTmp); pTmp = template_StdUsrMsgFmt; tplAddLine(" StdUsrMsgFmt", &pTmp); pTmp = template_StdDBFmt; @@ -6283,17 +2820,135 @@ static void mainThread() */ mainloop(); + ENDfunc +} - /* do any de-init's that need to be done AFTER this comment */ - die(bFinished); + +/* Method to initialize all global classes. + * rgerhards, 2008-01-04 + */ +static rsRetVal +InitGlobalClasses(void) +{ + DEFiRet; + + CHKiRet(objClassInit(NULL)); /* *THIS* *MUST* always be the first class initilizer being called! */ + CHKiRet(objGetObjInterface(&obj)); /* this provides the root pointer for all other queries */ + /* the following classes were intialized by objClassInit() */ + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + CHKiRet(objUse(module, CORE_COMPONENT)); + + /* initialize and use classes. We must be very careful with the order of events. Some + * classes use others and if we do not initialize them in the right order, we may end + * up with an invalid call. The most important thing that can happen is that an error + pErrObj = "var"; + CHKiRet(objUse(var, CORE_COMPONENT)); + * is detected and needs to be logged, wich in turn requires a broader number of classes + * to be available. The solution is that we take care in the order of calls AND use a + * class immediately after it is initialized. And, of course, we load those classes + * first that we use ourselfs... -- rgerhards, 2008-03-07 + */ + CHKiRet(datetimeClassInit(NULL)); + CHKiRet(objUse(datetime, CORE_COMPONENT)); + CHKiRet(msgClassInit(NULL)); + CHKiRet(strmClassInit(NULL)); + CHKiRet(wtiClassInit(NULL)); + CHKiRet(wtpClassInit(NULL)); + CHKiRet(queueClassInit(NULL)); + CHKiRet(vmstkClassInit(NULL)); + CHKiRet(sysvarClassInit(NULL)); + CHKiRet(vmClassInit(NULL)); + CHKiRet(objUse(vm, CORE_COMPONENT)); + CHKiRet(vmopClassInit(NULL)); + CHKiRet(vmprgClassInit(NULL)); + CHKiRet(ctok_tokenClassInit(NULL)); + CHKiRet(ctokClassInit(NULL)); + CHKiRet(exprClassInit(NULL)); + CHKiRet(objUse(expr, CORE_COMPONENT)); + CHKiRet(confClassInit(NULL)); + CHKiRet(objUse(conf, CORE_COMPONENT)); + + /* dummy "classes" */ + CHKiRet(actionClassInit()); + CHKiRet(templateInit()); + CHKiRet(strInit()); + + /* TODO: the dependency on net shall go away! -- rgerhards, 2008-03-07 */ + CHKiRet(objUse(net, LM_NET_FILENAME)); + +finalize_it: + RETiRet; +} + + +/* Method to exit all global classes. We do not do any error checking here, + * because that wouldn't help us at all. So better try to deinit blindly + * as much as succeeds (which usually means everything will). We just must + * be careful to do the de-init in the opposite order of the init, because + * of the dependencies. However, its not as important this time, because + * we have reference counting. + * rgerhards, 2008-03-10 + */ +static rsRetVal +GlobalClassExit(void) +{ + DEFiRet; + + /* first, release everything we used ourself */ + objRelease(net, LM_NET_FILENAME);/* TODO: the dependency on net shall go away! -- rgerhards, 2008-03-07 */ + objRelease(conf, CORE_COMPONENT); + objRelease(expr, CORE_COMPONENT); + objRelease(vm, CORE_COMPONENT); + objRelease(datetime, CORE_COMPONENT); + + /* TODO: implement the rest of the deinit */ + confClassExit(); +#if 0 + CHKiRet(datetimeClassInit(NULL)); + CHKiRet(msgClassInit(NULL)); + CHKiRet(strmClassInit(NULL)); + CHKiRet(wtiClassInit(NULL)); + CHKiRet(wtpClassInit(NULL)); + CHKiRet(queueClassInit(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 */ +dbgprintf("pre strExit()\n"); + strExit(); +dbgprintf("post strExit()\n"); + + +#if 0 + CHKiRet(objGetObjInterface(&obj)); /* this provides the root pointer for all other queries */ + /* the following classes were intialized by objClassInit() */ + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + CHKiRet(objUse(module, CORE_COMPONENT)); +#endif + objClassExit(); /* *THIS* *MUST/SHOULD?* always be the first class initilizer being called (except debug)! */ + + RETiRet; } + /* This is the main entry point into rsyslogd. Over time, we should try to * modularize it a bit more... */ -int main(int argc, char **argv) +int realMain(int argc, char **argv) { + DEFiRet; + register int i; register char *p; int num_fds; @@ -6302,28 +2957,74 @@ int main(int argc, char **argv) extern int optind; extern char *optarg; struct sigaction sigAct; -#if 0 /* see comment for #if 0 below (towards end of function) */ - pthread_t thrdMain; - sigset_t sigSet; -#endif + int bIsFirstOption = 1; + int bEOptionWasGiven = 0; + int bImUxSockLoaded = 0; /* already generated a $ModLoad imuxsock? */ + uchar legacyConfLine[80]; -#ifdef MTRACE - mtrace(); /* this is a debug aid for leak detection - either remove - * or put in conditional compilation. 2005-01-18 RGerhards */ -#endif + gethostname(LocalHostName, sizeof(LocalHostName)); + if ( (p = strchr(LocalHostName, '.')) ) { + *p++ = '\0'; + LocalDomain = p; + } + else + { + LocalDomain = ""; + + /* It's not clearly defined whether gethostname() + * should return the simple hostname or the fqdn. A + * good piece of software should be aware of both and + * we want to distribute good software. Joey + * + * Good software also always checks its return values... + * If syslogd starts up before DNS is up & /etc/hosts + * doesn't have LocalHostName listed, gethostbyname will + * return NULL. + */ + /* TODO: gethostbyname() is not thread-safe, but replacing it is + * not urgent as we do not run on multiple threads here. rgerhards, 2007-09-25 + */ + hent = gethostbyname(LocalHostName); + if(hent) { + snprintf(LocalHostName, sizeof(LocalHostName), "%s", hent->h_name); + + if ( (p = strchr(LocalHostName, '.')) ) + { + *p++ = '\0'; + LocalDomain = p; + } + } + } + + /* Convert to lower case to recognize the correct domain laterly + */ + for (p = (char *)LocalDomain; *p ; p++) + if (isupper((int) *p)) + *p = (char)tolower((int)*p); + + CHKiRet(InitGlobalClasses()); + + /* doing some core initializations */ + if((iRet = modInitIminternal()) != RS_RET_OK) { + fprintf(stderr, "fatal error: could not initialize errbuf object (error code %d).\n", + iRet); + exit(1); /* "good" exit, leaving at init for fatal error */ + } + + if((iRet = loadBuildInModules()) != RS_RET_OK) { + fprintf(stderr, "fatal error: could not activate built-in modules. Error code %d.\n", + iRet); + exit(1); /* "good" exit, leaving at init for fatal error */ + } ppid = getpid(); if(chdir ("/") != 0) fprintf(stderr, "Can not do 'cd /' - still trying to run\n"); - for (i = 1; i < MAXFUNIX; i++) { - funixn[i] = ""; - funix[i] = -1; - } /* END core initializations */ - while ((ch = getopt(argc, argv, "46Aa:c:dehi:f:g:l:m:nop:qQr::s:t:u:vwx")) != EOF) { + while ((ch = getopt(argc, argv, "46Ac:dehi:f:g:l:m:M:nqQr::s:t:u:vwx")) != EOF) { switch((char)ch) { case '4': family = PF_INET; @@ -6334,98 +3035,132 @@ int main(int argc, char **argv) case 'A': send_to_all++; break; - case 'a': - if (nfunix < MAXFUNIX) - if(*optarg == ':') { - funixParseHost[nfunix] = 1; - funixn[nfunix++] = optarg+1; + case 'a': + if(iCompatibilityMode < 3) { + if(!bImUxSockLoaded) { + legacyOptsEnq((uchar *) "ModLoad imuxsock"); + bImUxSockLoaded = 1; } - else { - funixParseHost[nfunix] = 0; - funixn[nfunix++] = optarg; - } - else - fprintf(stderr, "rsyslogd: Out of descriptors, ignoring %s\n", optarg); - break; - case 'c': /* forward-compatibility: sets mode in v3+ */ - fprintf(stderr, "-c option not yet supported, reserved for future use\n"); + snprintf((char *) legacyConfLine, sizeof(legacyConfLine), "addunixlistensocket %s", optarg); + legacyOptsEnq(legacyConfLine); + } else { + fprintf(stderr, "error -a is no longer supported, use module imuxsock instead"); + } + break; + case 'c': /* compatibility mode */ + if(!bIsFirstOption) { + fprintf(stderr, "-c option MUST be specified as the first option - aborting...\n"); + usage(); + exit(1); + } + iCompatibilityMode = atoi(optarg); break; case 'd': /* debug */ Debug = 1; break; case 'e': /* log every message (no repeat message supression) */ - logEveryMsg = 1; + fprintf(stderr, "note: -e option is no longer supported, every message is now logged by default\n"); + bEOptionWasGiven = 1; break; case 'f': /* configuration file */ ConfFile = (uchar*) optarg; break; case 'g': /* enable tcp gssapi logging */ #if defined(SYSLOG_INET) && defined(USE_GSSAPI) - if (!bEnableTCP) - configureTCPListen(optarg); - bEnableTCP |= ALLOWEDMETHOD_GSS; + if(iCompatibilityMode < 3) { + legacyOptsParseTCP(ch, optarg); + } else + fprintf(stderr, "-g option only supported in compatibility modes 0 to 2 - ignored\n"); #else fprintf(stderr, "rsyslogd: -g not valid - not compiled with gssapi support"); #endif break; case 'h': - NoHops = 0; + if(iCompatibilityMode < 3) { + errmsg.LogError(NO_ERRCODE, "WARNING: -h option is no longer supported - ignored"); + } else { + usage(); /* for v3 and above, it simply is an error */ + } break; case 'i': /* pid file name */ PidFile = optarg; break; case 'l': if (LocalHosts) { - fprintf (stderr, "rsyslogd: Only one -l argument allowed," \ - "the first one is taken.\n"); + fprintf (stderr, "rsyslogd: Only one -l argument allowed, the first one is taken.\n"); } else { LocalHosts = crunch_list(optarg); } break; case 'm': /* mark interval */ - MarkInterval = atoi(optarg) * 60; + if(iCompatibilityMode < 3) { + MarkInterval = atoi(optarg) * 60; + } else + fprintf(stderr, + "-m option only supported in compatibility modes 0 to 2 - ignored\n"); + break; + case 'M': /* default module load path */ + module.SetModDir((uchar*)optarg); break; case 'n': /* don't fork */ NoFork = 1; break; - case 'o': /* omit local logging (/dev/log) */ - startIndexUxLocalSockets = 1; - break; - case 'p': /* path to regular log socket */ - funixn[0] = optarg; - break; + case 'o': + if(iCompatibilityMode < 3) { + if(!bImUxSockLoaded) { + legacyOptsEnq((uchar *) "ModLoad imuxsock"); + bImUxSockLoaded = 1; + } + legacyOptsEnq((uchar *) "OmitLocaLogging"); + } else { + fprintf(stderr, "error -o is no longer supported, use module imuxsock instead"); + } + break; + case 'p': + if(iCompatibilityMode < 3) { + if(!bImUxSockLoaded) { + legacyOptsEnq((uchar *) "ModLoad imuxsock"); + bImUxSockLoaded = 1; + } + snprintf((char *) legacyConfLine, sizeof(legacyConfLine), "SystemLogSocketName %s", optarg); + legacyOptsEnq(legacyConfLine); + } else { + fprintf(stderr, "error -p is no longer supported, use module imuxsock instead"); + } case 'q': /* add hostname if DNS resolving has failed */ - ACLAddHostnameOnFail = 1; + *net.pACLAddHostnameOnFail = 1; break; case 'Q': /* dont resolve hostnames in ACL to IPs */ - ACLDontResolve = 1; + *net.pACLDontResolve = 1; break; case 'r': /* accept remote messages */ #ifdef SYSLOG_INET - AcceptRemote = 1; - if(optarg == NULL) - LogPort = "0"; - else - LogPort = optarg; + if(iCompatibilityMode < 3) { + legacyOptsEnq((uchar *) "ModLoad imudp"); + snprintf((char *) legacyConfLine, sizeof(legacyConfLine), "UDPServerRun %s", optarg); + legacyOptsEnq(legacyConfLine); + } else + fprintf(stderr, + "-r option only supported in compatibility modes 0 to 2 - ignored\n"); #else - fprintf(stderr, "rsyslogd: -r not valid - not compiled with network support"); + fprintf(stderr, "rsyslogd: -r not valid - not compiled with network support\n"); #endif break; case 's': if (StripDomains) { - fprintf (stderr, "rsyslogd: Only one -s argument allowed," \ - "the first one is taken.\n"); + fprintf (stderr, "rsyslogd: Only one -s argument allowed, the first one is taken.\n"); } else { StripDomains = crunch_list(optarg); } break; case 't': /* enable tcp logging */ #ifdef SYSLOG_INET - if (!bEnableTCP) - configureTCPListen(optarg); - bEnableTCP |= ALLOWEDMETHOD_TCP; + if(iCompatibilityMode < 3) { + legacyOptsParseTCP(ch, optarg); + } else + fprintf(stderr, "-t option only supported in compatibility modes 0 to 2 - ignored\n"); #else - fprintf(stderr, "rsyslogd: -t not valid - not compiled with network support"); + fprintf(stderr, "rsyslogd: -t not valid - not compiled with network support\n"); #endif break; case 'u': /* misc user settings */ @@ -6445,12 +3180,39 @@ int main(int argc, char **argv) default: usage(); } + bIsFirstOption = 0; /* we already saw an option character */ } if ((argc -= optind)) usage(); + /* TODO: this should go away at a reasonable stage of v3 development. + * rgerhards, 2007-12-19 + */ + if(iCompatibilityMode < 3) { + errmsg.LogError(NO_ERRCODE, "WARNING: rsyslogd is running in compatibility mode. Automatically " + "generated config directives may interfer with your rsyslog.conf settings. " + "We suggest upgrading your config and adding -c3 as the first " + "rsyslogd option."); + if(MarkInterval > 0) { + legacyOptsEnq((uchar *) "ModLoad immark"); + snprintf((char *) legacyConfLine, sizeof(legacyConfLine), "MarkMessagePeriod %d", MarkInterval); + legacyOptsEnq(legacyConfLine); + } + if(!bImUxSockLoaded) { + legacyOptsEnq((uchar *) "ModLoad imuxsock"); + } + } + + if(bEOptionWasGiven && iCompatibilityMode < 3) { + errmsg.LogError(NO_ERRCODE, "WARNING: \"message repeated n times\" feature MUST be turned on in " + "rsyslog.conf - CURRENTLY EVERY MESSAGE WILL BE LOGGED. Visit " + "http://www.rsyslog.com/rptdmsgreduction to learn " + "more and cast your vote if you want us to keep this feature."); + } + checkPermissions(); + thrdInit(); if ( !(Debug || NoFork) ) { @@ -6491,70 +3253,32 @@ int main(int argc, char **argv) else debugging_on = 1; + dbgprintf("Compatibility Mode: %d\n", iCompatibilityMode); + /* tuck my process id away */ - if ( !Debug ) + dbgprintf("Writing pidfile %s.\n", PidFile); + if (!check_pid(PidFile)) { - dbgprintf("Writing pidfile.\n"); - if (!check_pid(PidFile)) - { - if (!write_pid(PidFile)) - { - fputs("Can't write pid.\n", stderr); - exit(1); /* exit during startup - questionable */ - } - } - else + if (!write_pid(PidFile)) { - fputs("Pidfile (and pid) already exist.\n", stderr); + fputs("Can't write pid.\n", stderr); exit(1); /* exit during startup - questionable */ } - } /* if ( !Debug ) */ - myPid = getpid(); /* save our pid for further testing (also used for messages) */ - - - gethostname(LocalHostName, sizeof(LocalHostName)); - if ( (p = strchr(LocalHostName, '.')) ) { - *p++ = '\0'; - LocalDomain = p; } else { - LocalDomain = ""; - - /* It's not clearly defined whether gethostname() - * should return the simple hostname or the fqdn. A - * good piece of software should be aware of both and - * we want to distribute good software. Joey - * - * Good software also always checks its return values... - * If syslogd starts up before DNS is up & /etc/hosts - * doesn't have LocalHostName listed, gethostbyname will - * return NULL. - */ - /* TODO: gethostbyname() is not thread-safe, but replacing it is - * not urgent as we do not run on multiple threads here. rgerhards, 2007-09-25 - */ - hent = gethostbyname(LocalHostName); - if(hent) { - snprintf(LocalHostName, sizeof(LocalHostName), "%s", hent->h_name); - - if ( (p = strchr(LocalHostName, '.')) ) - { - *p++ = '\0'; - LocalDomain = p; - } - } + fputs("Pidfile (and pid) already exist.\n", stderr); + exit(1); /* exit during startup - questionable */ } - - /* Convert to lower case to recognize the correct domain laterly - */ - for (p = (char *)LocalDomain; *p ; p++) - if (isupper((int) *p)) - *p = (char)tolower((int)*p); + myPid = getpid(); /* save our pid for further testing (also used for messages) */ memset(&sigAct, 0, sizeof (sigAct)); sigemptyset(&sigAct.sa_mask); + sigAct.sa_handler = sigsegvHdlr; + sigaction(SIGSEGV, &sigAct, NULL); + sigAct.sa_handler = sigsegvHdlr; + sigaction(SIGABRT, &sigAct, NULL); sigAct.sa_handler = doDie; sigaction(SIGTERM, &sigAct, NULL); sigAct.sa_handler = Debug ? doDie : SIG_IGN; @@ -6562,44 +3286,39 @@ int main(int argc, char **argv) sigaction(SIGQUIT, &sigAct, NULL); sigAct.sa_handler = reapchild; sigaction(SIGCHLD, &sigAct, NULL); - sigAct.sa_handler = domarkAlarmHdlr; - sigaction(SIGALRM, &sigAct, NULL); sigAct.sa_handler = Debug ? debug_switch : SIG_IGN; sigaction(SIGUSR1, &sigAct, NULL); sigAct.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sigAct, NULL); sigaction(SIGXFSZ, &sigAct, NULL); /* do not abort if 2gig file limit is hit */ - (void) alarm(TIMERINTVL); mainThread(); -#if 0 - /* This commented-out code was once used to spawn a separate thread - * for the mainThread(). This was initially done to solve a problem that not - * really existed. Thus the code is now commented out. I do not remove it yet, - * because there may be use for it in the not too distant future. If it is - * still commented out in a year's time, that's a good indication it should - * be removed! -- rgerhards, 2007-10-17 - */ - i = pthread_create(&thrdMain, NULL, mainThread, NULL); - dbgprintf("\"main\" thread started with state %d.\n", i); + /* do any de-init's that need to be done AFTER this comment */ - /* we block all signals - they will be processed by the "main"-thread. This most - * closely resembles previous behaviour. TODO: think about optimizing it, some - * signals may better be delivered here. rgerhards, 2007-10-08 - */ - sigfillset(&sigSet); - pthread_sigmask(SIG_BLOCK, &sigSet, NULL); + die(bFinished); - /* see comment in mainThread on why we start thread and then immediately - * do a blocking wait on it - it makese sense... ;) rgerhards, 2007-10-08 - */ - pthread_join(thrdMain, NULL); -#endif + thrdExit(); + +finalize_it: + if(iRet != RS_RET_OK) + fprintf(stderr, "rsyslogd run failed with error %d.\n", iRet); + ENDfunc return 0; } -/* vi:set ai: +/* This is the main entry point into rsyslogd. This must be a function in its own + * right in order to intialize the debug system in a portable way (otherwise we would + * need to have a statement before variable definitions. + * rgerhards, 20080-01-28 + */ +int main(int argc, char **argv) +{ + dbgClassInit(); + return realMain(argc, argv); +} + +/* vim:set ai: */ |