diff options
-rw-r--r-- | ChangeLog | 42 | ||||
-rw-r--r-- | Makefile.am | 4 | ||||
-rw-r--r-- | action.c | 32 | ||||
-rw-r--r-- | action.h | 4 | ||||
-rw-r--r-- | conf.c | 4 | ||||
-rw-r--r-- | configure.ac | 72 | ||||
-rw-r--r-- | doc/features.html | 10 | ||||
-rw-r--r-- | doc/ommail.html | 9 | ||||
-rw-r--r-- | doc/property_replacer.html | 46 | ||||
-rw-r--r-- | doc/queues.html | 12 | ||||
-rw-r--r-- | doc/rsyslog_conf.html | 79 | ||||
-rw-r--r-- | doc/rsyslog_ng_comparison.html | 24 | ||||
-rw-r--r-- | doc/status.html | 14 | ||||
-rw-r--r-- | expr.c | 2 | ||||
-rw-r--r-- | glbl.h | 1 | ||||
-rw-r--r-- | modules.c | 11 | ||||
-rw-r--r-- | msg.c | 42 | ||||
-rw-r--r-- | net.c | 39 | ||||
-rw-r--r-- | net.h | 3 | ||||
-rw-r--r-- | obj-types.h | 2 | ||||
-rw-r--r-- | obj.c | 10 | ||||
-rw-r--r-- | obj.h | 4 | ||||
-rw-r--r-- | omfwd.c | 12 | ||||
-rw-r--r-- | outchannel.c | 51 | ||||
-rw-r--r-- | parse.c | 58 | ||||
-rw-r--r-- | parse.h | 2 | ||||
-rw-r--r-- | plugins/imfile/imfile.c | 2 | ||||
-rw-r--r-- | plugins/imklog/imklog.c | 2 | ||||
-rw-r--r-- | plugins/imuxsock/imuxsock.c | 2 | ||||
-rw-r--r-- | plugins/omgssapi/omgssapi.c | 17 | ||||
-rw-r--r-- | plugins/ommail/Makefile.am | 6 | ||||
-rw-r--r-- | plugins/ommail/ommail.c | 630 | ||||
-rw-r--r-- | plugins/omrelp/omrelp.c | 12 | ||||
-rw-r--r-- | queue.c | 111 | ||||
-rw-r--r-- | queue.h | 15 | ||||
-rw-r--r-- | rsyslog.conf | 2 | ||||
-rw-r--r-- | rsyslog.conf.5 | 2 | ||||
-rw-r--r-- | rsyslog.h | 4 | ||||
-rw-r--r-- | rsyslogd.8 | 2 | ||||
-rw-r--r-- | stringbuf.h | 1 | ||||
-rw-r--r-- | syslogd.c | 345 | ||||
-rw-r--r-- | syslogd.h | 3 | ||||
-rw-r--r-- | tcpsrv.c | 26 | ||||
-rw-r--r-- | template.c | 52 | ||||
-rw-r--r-- | template.h | 2 | ||||
-rw-r--r-- | wti.c | 8 | ||||
-rw-r--r-- | wtp.c | 1 | ||||
-rw-r--r-- | wtp.h | 2 |
48 files changed, 1479 insertions, 357 deletions
@@ -1,4 +1,46 @@ --------------------------------------------------------------------------- +Version 3.17.1 (rgerhards), 2008-04-?? +- removed dependency on MAXHOSTNAMELEN as much as it made sense. + GNU/Hurd does not define it (because it has no limit), and we have taken + care for cases where it is undefined now. However, some very few places + remain where IMHO it currently is not worth fixing the code. If it is + not defined, we have used a generous value of 1K, which is above IETF + RFC's on hostname length at all. The memory consumption is no issue, as + there are only a handful of this buffers allocated *per run* -- that's + also the main reason why we consider it not worth to be fixed any further. +- enhanced legacy syslog parser to handle slightly malformed messages + (with a space in front of the timestamp) - at least HP procurve is + known to do that and I won't outrule that others also do it. The + change looks quite unintrusive and so we added it to the parser. +--------------------------------------------------------------------------- +Version 3.17.0 (rgerhards), 2008-04-08 +- added native ability to send mail messages +- removed no longer needed file relptuil.c/.h +- added $ActionExecOnlyOnceEveryInterval config directive +- bugfix: memory leaks in script engine +- bugfix: zero-length strings were not supported in object + deserializer +- properties are now case-insensitive everywhere (script, filters, + templates) +- added the capability to specify a processing (actually dequeue) + timeframe with queues - so things can be configured to be done + at off-peak hours +- 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. If you need large tags in the output, you need + to provide custom templates. +- changed command line processing. -v, -M, -c options are now parsed + and processed before all other options. Inter-option dependencies + have been relieved. Among others, permits to specify intial module + load path via -M only (not the environment) which makes it much + easier to work with non-standard module library locations. Thanks + to varmojfekoj for suggesting this change. Matches bugzilla bug 55. +- bugfix: some messages were emited without hostname +Plus a number of bugfixes that were applied to v3-stable and beta +branches (not mentioned here in detail). +--------------------------------------------------------------------------- Version 3.15.2 (rgerhards), 2008-04-?? --------------------------------------------------------------------------- Version 3.15.1 (rgerhards), 2008-04-11 diff --git a/Makefile.am b/Makefile.am index 659acfb2..d0341819 100644 --- a/Makefile.am +++ b/Makefile.am @@ -218,3 +218,7 @@ endif if ENABLE_IMFILE SUBDIRS += plugins/imfile endif + +if ENABLE_MAIL +SUBDIRS += plugins/ommail +endif @@ -74,8 +74,10 @@ static int iActionQtoEnq = 2000; /* timeout for queue enque */ static int iActionQtoWrkShutdown = 60000; /* timeout for worker thread shutdown */ static int iActionQWrkMinMsgs = 100; /* minimum messages per worker needed to start a new one */ static int bActionQSaveOnShutdown = 1; /* save queue on shutdown (when DA enabled)? */ -static int iActionQueueDeqSlowdown = 0; /* dequeue slowdown (simple rate limiting) */ static int64 iActionQueMaxDiskSpace = 0; /* max disk space allocated 0 ==> unlimited */ +static int iActionQueueDeqSlowdown = 0; /* dequeue slowdown (simple rate limiting) */ +static int iActionQueueDeqtWinFromHr = 0; /* hour begin of time frame when queue is to be dequeued */ +static int iActionQueueDeqtWinToHr = 25; /* hour begin of time frame when queue is to be dequeued */ /* the counter below counts actions created. It is used to obtain unique IDs for the action. They * should not be relied on for any long-term activity (e.g. disk queue names!), but they are nice @@ -113,8 +115,10 @@ actionResetQueueParams(void) iActionQtoWrkShutdown = 60000; /* timeout for worker thread shutdown */ iActionQWrkMinMsgs = 100; /* minimum messages per worker needed to start a new one */ bActionQSaveOnShutdown = 1; /* save queue on shutdown (when DA enabled)? */ - iActionQueueDeqSlowdown = 0; iActionQueMaxDiskSpace = 0; + iActionQueueDeqSlowdown = 0; + iActionQueueDeqtWinFromHr = 0; + iActionQueueDeqtWinToHr = 25; /* 25 disables time windowed dequeuing */ glbliActionResumeRetryCount = 0; /* I guess it is smart to reset this one, too */ @@ -237,7 +241,9 @@ actionConstructFinalize(action_t *pThis) setQPROP(queueSetiDiscardSeverity, "$ActionQueueDiscardSeverity", iActionQDiscardSeverity); setQPROP(queueSetiMinMsgsPerWrkr, "$ActionQueueWorkerThreadMinimumMessages", iActionQWrkMinMsgs); setQPROP(queueSetbSaveOnShutdown, "$ActionQueueSaveOnShutdown", bActionQSaveOnShutdown); - setQPROP(queueSetiDeqSlowdown, "$ActionQueueDequeueSlowdown", iActionQueueDeqSlowdown); + setQPROP(queueSetiDeqSlowdown, "$ActionQueueDequeueSlowdown", iActionQueueDeqSlowdown); + setQPROP(queueSetiDeqtWinFromHr, "$ActionQueueDequeueTimeBegin", iActionQueueDeqtWinFromHr); + setQPROP(queueSetiDeqtWinToHr, "$ActionQueueDequeueTimeEnd", iActionQueueDeqtWinToHr); # undef setQPROP # undef setQPROPstr @@ -498,6 +504,7 @@ actionWriteToAction(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) */ + time_t now; DEFiRet; pMsgSave = NULL; /* indicate message poiner not saved */ @@ -536,7 +543,20 @@ actionWriteToAction(action_t *pAction) dbgprintf("Called action, logging to %s", module.GetStateName(pAction->pMod)); - time(&pAction->f_time); /* we need this for message repeation processing */ + time(&now); /* we need this for message repeation processing AND $ActionExecOnlyOnceEveryInterval */ + /* now check if we need to drop the message because otherwise the action would be too + * frequently called. -- rgerhards, 2008-04-08 + */ + if(pAction->f_time != 0 && pAction->iSecsExecOnceInterval + pAction->tLastExec > now) { + /* in this case we need to discard the message - its not yet time to exec the action */ + dbgprintf("action not yet ready again to be executed, onceInterval %d, tCurr %d, tNext %d\n", + (int) pAction->iSecsExecOnceInterval, (int) now, + (int) (pAction->iSecsExecOnceInterval + pAction->tLastExec)); + FINALIZE; + } + + pAction->tLastExec = now; /* we need this OnceInterval */ + pAction->f_time = now; /* we need this for message repeation processing */ /* When we reach this point, we have a valid, non-disabled action. * So let's enqueue our message for execution. -- rgerhards, 2007-07-24 @@ -680,6 +700,8 @@ actionAddCfSysLineHdrl(void) CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuemaxfilesize", 0, eCmdHdlrSize, NULL, &iActionQueMaxFileSize, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuesaveonshutdown", 0, eCmdHdlrBinary, NULL, &bActionQSaveOnShutdown, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuedequeueslowdown", 0, eCmdHdlrInt, NULL, &iActionQueueDeqSlowdown, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuedequeuetimebegin", 0, eCmdHdlrInt, NULL, &iActionQueueDeqtWinFromHr, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuedequeuetimeend", 0, eCmdHdlrInt, NULL, &iActionQueueDeqtWinToHr, NULL)); finalize_it: RETiRet; @@ -710,6 +732,7 @@ addAction(action_t **ppAction, modInfo_t *pMod, void *pModData, omodStringReques pAction->pMod = pMod; pAction->pModData = pModData; pAction->bExecWhenPrevSusp = bActExecWhenPrevSusp; + pAction->iSecsExecOnceInterval = iActExecOnceInterval; /* check if we can obtain the template pointers - TODO: move to separate function? */ pAction->iNumTpls = OMSRgetEntryCount(pOMSR); @@ -800,6 +823,5 @@ finalize_it: RETiRet; } - /* vi:set ai: */ @@ -39,8 +39,10 @@ extern int glbliActionResumeRetryCount; /* the following struct defines the action object data structure */ struct action_s { - time_t f_time; /* time this was last written */ + time_t f_time; /* used for "message repeated n times" - be careful, old, old code */ + time_t tLastExec; /* time this action was last executed */ int bExecWhenPrevSusp;/* execute only when previous action is suspended? */ + int iSecsExecOnceInterval; /* if non-zero, minimum seconds to wait until action is executed again */ short bEnabled; /* is the related action enabled (1) or disabled (0)? */ short bSuspended; /* is the related action temporarily suspended? */ time_t ttResumeRtry; /* when is it time to retry the resume? */ @@ -816,7 +816,7 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register selector_t *f) } /* read property */ - iRet = parsDelimCStr(pPars, &f->f_filterData.prop.pCSPropName, ',', 1, 1); + iRet = parsDelimCStr(pPars, &f->f_filterData.prop.pCSPropName, ',', 1, 1, 1); if(iRet != RS_RET_OK) { errmsg.LogError(NO_ERRCODE, "error %d parsing filter property - ignoring selector", iRet); rsParsDestruct(pPars); @@ -824,7 +824,7 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register selector_t *f) } /* read operation */ - iRet = parsDelimCStr(pPars, &pCSCompOp, ',', 1, 1); + iRet = parsDelimCStr(pPars, &pCSCompOp, ',', 1, 1, 1); if(iRet != RS_RET_OK) { errmsg.LogError(NO_ERRCODE, "error %d compare operation property - ignoring selector", iRet); rsParsDestruct(pPars); diff --git a/configure.ac b/configure.ac index 4d5bec51..4f3459c3 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) -AC_INIT([rsyslog],[3.15.2],[rsyslog@lists.adiscon.com]) +AC_INIT([rsyslog],[3.17.1],[rsyslog@lists.adiscon.com]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([syslogd.c]) AC_CONFIG_HEADERS([config.h]) @@ -83,6 +83,22 @@ AC_FUNC_STRERROR_R AC_FUNC_VPRINTF AC_CHECK_FUNCS([flock basename alarm clock_gettime gethostbyname gethostname gettimeofday localtime_r memset mkdir regcomp select setid socket strcasecmp strchr strdup strerror strndup strnlen strrchr strstr strtol strtoul uname ttyname_r]) +# Check for MAXHOSTNAMELEN +AC_MSG_CHECKING(for MAXHOSTNAMELEN) +AC_TRY_COMPILE([ + #include <sys/param.h> + ], [ + return MAXHOSTNAMELEN; + ] + , + AC_MSG_RESULT(yes) + , + # note: we use 1024 here, which should be far more than needed by any system. If that's too low, we simply + # life with the need to change it. Most of the code doesn't need it anyways, but there are a few places + # where it actually is needed and it makes no sense to change them. + AC_DEFINE(MAXHOSTNAMELEN, 1024, [Define with a value if your <sys/param.h> does not define MAXHOSTNAMELEN]) + AC_MSG_RESULT(no; defined as 64) +) # Large file support AC_ARG_ENABLE(largefile, @@ -413,6 +429,42 @@ AC_SUBST(libdbi_cflags) AC_SUBST(libdbi_libs) +# openssl support +AC_ARG_ENABLE(openssl, + [AS_HELP_STRING([--enable-openssl],[Enable openssl support @<:@default=yes@:>@])], + [case "${enableval}" in + yes) enable_openssl="yes" ;; + no) enable_openssl="no" ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-openssl) ;; + esac], + [enable_openssl=no] +) +if test "x$enable_openssl" = "xyes"; then + AC_CHECK_HEADERS( + [openssl/ssl.h],, + [AC_MSG_FAILURE([openssl is missing])] + ) + AC_CHECK_LIB( + [crypto], + [CRYPTO_new_ex_data], + [openssl_cflags="" + openssl_libs="-lcrypto" + ], + [AC_MSG_FAILURE([library 'crypto' is missing (needed for openssl)])] + ) + AC_CHECK_LIB( + [ssl], + [SSL_library_init], + [ openssl_libs+="-lssl" + ], + [AC_MSG_FAILURE([library 'ssl' is missing (needed for openssl)])] + ) +fi +AM_CONDITIONAL(ENABLE_OPENSSL, test x$enable_openssl = xyes) +AC_SUBST(openssl_cflags) +AC_SUBST(openssl_libs) + + # SNMP support AC_ARG_ENABLE(snmp, [AS_HELP_STRING([--enable-snmp],[Enable SNMP support @<:@default=no@:>@])], @@ -455,6 +507,19 @@ AC_ARG_ENABLE(rsyslogd, AM_CONDITIONAL(ENABLE_RSYSLOGD, test x$enable_rsyslogd = xyes) +# Mail support (so far we do not need a library, but we need to turn this on and off) +AC_ARG_ENABLE(mail, + [AS_HELP_STRING([--enable-mail],[Enable mail support @<:@default=no@:>@])], + [case "${enableval}" in + yes) enable_mail="yes" ;; + no) enable_mail="no" ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-mail) ;; + esac], + [enable_mail=no] +) +AM_CONDITIONAL(ENABLE_MAIL, test x$enable_mail = xyes) + + # RELP support AC_ARG_ENABLE(relp, [AS_HELP_STRING([--enable-relp],[Enable RELP support @<:@default=no@:>@])], @@ -466,7 +531,7 @@ AC_ARG_ENABLE(relp, [enable_relp=no] ) if test "x$enable_relp" = "xyes"; then - PKG_CHECK_MODULES(RELP, relp) + PKG_CHECK_MODULES(RELP, relp >= 0.1.1) fi AM_CONDITIONAL(ENABLE_RELP, test x$enable_relp = xyes) AC_SUBST(RELP_CFLAGS) @@ -562,6 +627,7 @@ AC_CONFIG_FILES([Makefile \ plugins/ompgsql/Makefile \ plugins/omrelp/Makefile \ plugins/omlibdbi/Makefile \ + plugins/ommail/Makefile \ plugins/omsnmp/Makefile]) AC_OUTPUT @@ -576,6 +642,7 @@ echo "MySql support enabled: $enable_mysql" echo "libdbi support enabled: $enable_libdbi" echo "PostgreSQL support enabled: $enable_pgsql" echo "SNMP support enabled: $enable_snmp" +echo "Mail support enabled: $enable_mail" echo "RELP support enabled: $enable_relp" echo "file input module enabled: $enable_imfile" echo "input template module will be compiled: $enable_imtemplate" @@ -584,6 +651,7 @@ echo "Networking support enabled: $enable_inet" echo "Enable GSSAPI Kerberos 5 support: $want_gssapi_krb5" echo "Debug mode enabled: $enable_debug" echo "Runtime Instrumentation enabled: $enable_rtinst" +echo "openssl enabled: $enable_openssl" echo "valgrind support settings enabled: $enable_valgrind" echo "rsyslogd will be built: $enable_rsyslogd" diff --git a/doc/features.html b/doc/features.html index 9573030e..13fc34c6 100644 --- a/doc/features.html +++ b/doc/features.html @@ -1,7 +1,5 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html><head><title>rsyslog features</title> - -</head> +<html><head><title>rsyslog features</title></head> <body> <h1>RSyslog - Features</h1> <p><b>This page lists both current features as well as @@ -25,19 +23,19 @@ to MySQL databases</a></li> <li> native support for writing to Postgres databases</li> <li>direct support for Firebird/Interbase, OpenTDS (MS SQL, Sybase), SQLLite, Ingres, Oracle, and mSQL via libdbi, -a database abstraction layer (almost as good as native)</li> +a database abstraction layer (almost as good as native)</li><li>native support for <a href="ommail.html">sending mail messages</a> (first seen in 3.17.0)</li> <li>support for (plain) tcp based syslog - much better reliability</li> <li>support for sending and receiving compressed syslog messages</li> <li>support for on-demand on-disk spooling of messages that can not be processed fast enough (a great feature for <a href="rsyslog_high_database_rate.html">writing massive -amounts of syslog messages to a database</a>)</li> +amounts of syslog messages to a database</a>)</li><li>support for selectively <a href="http://wiki.rsyslog.com/index.php/OffPeakHours">processing messages only during specific timeframes</a> and spooling them to disk otherwise</li> <li>ability to monitor text files and convert their contents into syslog messages (one per line)</li> <li>ability to configure backup syslog/database servers - if the primary fails, control is switched to a prioritized list of backups</li> <li>support for receiving messages via reliable <a href="http://www.monitorware.com/Common/en/glossary/rfc3195.php"> -RFC 3195</a> delivery</li> +RFC 3195</a> delivery (a bit clumpsy to build right now...)</li> <li>ability to generate file names and directories (log targets) dynamically, based on many different properties</li> <li>control of log output format, including ability to present diff --git a/doc/ommail.html b/doc/ommail.html index b6b7c2ad..ba06a366 100644 --- a/doc/ommail.html +++ b/doc/ommail.html @@ -110,9 +110,8 @@ $ActionMailSubject mailSubject $ActionExecOnlyOnceEveryInterval 21600 # the if ... then ... mailBody mus be on one line! if $msg contains 'hard disk fatal failure' then :ommail:;mailBody -</textarea><br> -<br> -A more advanced example plus a discussion on using the email feature +</textarea> +<p>A more advanced example plus a discussion on using the email feature inside a reliable system can be found in Rainer's blogpost "<a style="font-style: italic;" href="http://rgerhards.blogspot.com/2008/04/why-is-native-email-capability.html">Why is native email capability an advantage for a syslogd?</a>" @@ -121,8 +120,8 @@ is native email capability an advantage for a syslogd?</a>" <p><font size="2">This documentation is part of the <a href="http://www.rsyslog.com/">rsyslog</a> project.<br> -Copyright © 2008 by <a href="http://www.gerhards.net/rainer">Rainer +Copyright © 2008 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and <a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL version 3 or higher.</font></p> -</body></html>
\ No newline at end of file +</body></html> diff --git a/doc/property_replacer.html b/doc/property_replacer.html index 3484acf2..a2efaede 100644 --- a/doc/property_replacer.html +++ b/doc/property_replacer.html @@ -1,7 +1,5 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html><head><title>The Rsyslogd Property Replacer</title> - -</head> +<html><head><title>The Rsyslogd Property Replacer</title></head> <body> <h1>The Property Replacer</h1> <p><b>The property replacer is a core component in @@ -17,7 +15,7 @@ modified by the property replacer. The full syntax is as follows:</p> <blockquote><b><code>%propname:fromChar:toChar:options%</code></b></blockquote> <h2>Available Properties</h2> <p><b><code>propname</code></b> is the -name of the property to access. It is case-sensitive. +name of the property to access. It is case-insensitive (prior to 3.17.0, they were case-senstive). Currently supported are:</p> <table> <tbody> @@ -31,11 +29,11 @@ Currently supported are:</p> socket. Should be useful for debugging.</td> </tr> <tr> -<td><b>UxTradMsg</b></td> +<td><b>uxtradmsg</b></td> <td>will disappear soon - do NOT use!</td> </tr> <tr> -<td><b>HOSTNAME</b></td> +<td><b>hostname</b></td> <td>hostname from the message</td> </tr> <tr> @@ -43,7 +41,7 @@ socket. Should be useful for debugging.</td> <td>alias for HOSTNAME</td> </tr> <tr> -<td><b>FROMHOST</b></td> +<td><b>fromhost</b></td> <td>hostname of the system the message was received from (in a relay chain, this is the system immediately in front of us and not necessarily the original sender)</td> @@ -59,16 +57,16 @@ BSD syslogd. For example, when TAG is "named[12345]", programname is "named".</td> </tr> <tr> -<td><b>PRI</b></td> +<td><b>pri</b></td> <td>PRI part of the message - undecoded (single value)</td> </tr> <tr> -<td><b>PRI-text</b></td> +<td><b>pri-text</b></td> <td>the PRI part of the message in a textual form (e.g. "syslog.info")</td> </tr> <tr> -<td><b>IUT</b></td> +<td><span style="font-weight: bold;">iut</span></td> <td>the monitorware InfoUnitType - used when talking to a <a href="http://www.monitorware.com">MonitorWare</a> backend (also for <a href="http://www.phplogcon.org/">phpLogCon</a>)</td> @@ -110,67 +108,67 @@ what was provided in the message (in most cases, only seconds)</td> </tr> <tr> -<td><b>TIMESTAMP</b></td> +<td><b>timestamp</b></td> <td>alias for timereported</td> </tr> <tr> -<td><b>PROTOCOL-VERSION</b></td> +<td><b>protocol-version</b></td> <td>The contents of the PROTCOL-VERSION field from IETF draft draft-ietf-syslog-protcol</td> </tr> <tr> -<td><b>STRUCTURED-DATA</b></td> +<td><b>structured-data</b></td> <td>The contents of the STRUCTURED-DATA field from IETF draft draft-ietf-syslog-protocol</td> </tr> <tr> -<td><b>APP-NAME</b></td> +<td><b>app-name</b></td> <td>The contents of the APP-NAME field from IETF draft draft-ietf-syslog-protocol</td> </tr> <tr> -<td><b>PROCID</b></td> +<td><b>procid</b></td> <td>The contents of the PROCID field from IETF draft draft-ietf-syslog-protocol</td> </tr> <tr> -<td height="24"><b>MSGID</b></td> +<td height="24"><b>msgid</b></td> <td height="24">The contents of the MSGID field from IETF draft draft-ietf-syslog-protocol</td> </tr> <tr> -<td><b>$NOW</b></td> +<td><b>$now</b></td> <td>The current date stamp in the format YYYY-MM-DD</td> </tr> <tr> -<td><b>$YEAR</b></td> +<td><b>$year</b></td> <td>The current year (4-digit)</td> </tr> <tr> -<td><b>$MONTH</b></td> +<td><b>$month</b></td> <td>The current month (2-digit)</td> </tr> <tr> -<td><b>$DAY</b></td> +<td><b>$day</b></td> <td>The current day of the month (2-digit)</td> </tr> <tr> -<td><b>$HOUR</b></td> +<td><b>$hour</b></td> <td>The current hour in military (24 hour) time (2-digit)</td> </tr> <tr> -<td><b>$HHOUR</b></td> +<td><b>$hhour</b></td> <td>The current half hour we are in. From minute 0 to 29, this is always 0 while from 30 to 59 it is always 1.</td> </tr> <tr> -<td><b>$QHOUR</b></td> +<td><b>$qhour</b></td> <td>The current quarter hour we are in. Much like $HHOUR, but values range from 0 to 3 (for the four quater hours that are in each hour)</td> </tr> <tr> -<td><b>$MINUTE</b></td> +<td><b>$minute</b></td> <td>The current minute (2-digit)</td> </tr> </tbody> diff --git a/doc/queues.html b/doc/queues.html index 80641d8c..a2074d36 100644 --- a/doc/queues.html +++ b/doc/queues.html @@ -288,7 +288,17 @@ directive allows to specify how long (in microseconds) dequeueing should be delayed. While simple, it still is powerful. For example, using a DequeueSlowdown delay of 1,000 microseconds on a UDP send action ensures that no more than 1,000 messages can be sent within a second (actually less, as there is -also some time needed for the processing itself). </p> +also some time needed for the processing itself).</p><h2>Processing Timeframes</h2><p>Queues +can be set to dequeue (process) messages only during certain +timeframes. This is useful if you, for example, would like to transfer +the bulk of messages only during off-peak hours, e.g. when you have +only limited bandwidth on the network path the the central server.</p><p>Currently, +only a single timeframe is supported and, even worse, it can only be +specified by the hour. It is not hard to extend rsyslog's capabilities +in this regard - it was just not requested so far. So if you need more +fine-grained control, let us know and we'll probably implement it. +There are two configuration directives, both should be used together or +results are unpredictable:" <i>$<object>QueueDequeueTimeBegin <hour></i>" and "<i>$<object>QueueDequeueTimeEnd <hour></i>". The hour parameter must be specified in 24-hour format (so 10pm is 22). A use case for this parameter can be found in the <a href="http://wiki.rsyslog.com/index.php/OffPeakHours">rsyslog wiki</a>. </p> <h2>Terminating Queues</h2> <p>Terminating a process sounds easy, but can be complex. <span style="font-size: 12pt; line-height: 115%; font-family: 'Times New Roman',serif;" lang="EN-US"> diff --git a/doc/rsyslog_conf.html b/doc/rsyslog_conf.html index 2a0f0c60..8967f671 100644 --- a/doc/rsyslog_conf.html +++ b/doc/rsyslog_conf.html @@ -1,5 +1,7 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html><head><title>rsyslog.conf file</title></head> +<html><head><title>rsyslog.conf file</title> + +</head> <body> <h1>rsyslog.conf configuration file</h1> <p><b>This document is currently being enhanced. Please @@ -26,22 +28,30 @@ number of modules. Here is the entry point to their documentation and what they do (list is currently not complete)</p> <ul> <li><a href="omsnmp.html">omsnmp</a> - SNMP -trap output module</li><li><a href="omrelp.html">omrelp</a> - RELP output module</li> +trap output module</li> +<li><a href="omrelp.html">omrelp</a> - RELP +output module</li> <li>omgss - output module for GSS-enabled syslog</li> <li>ommysql - output module for MySQL</li> <li>ompgsql - output module for PostgreSQL</li> <li><a href="omlibdbi.html">omlibdbi</a> - generic database output module (Firebird/Interbase, MS SQL, Sybase, SQLLite, Ingres, Oracle, mSQL)</li> +<li><a href="ommail.html">ommail</a> - +permits rsyslog to alert folks by mail if something important happens</li> <li><a href="imfile.html">imfile</a> -- input module for text files</li><li><a href="imrelp.html">imrelp</a> - RELP input module</li> +- input module for text files</li> +<li><a href="imrelp.html">imrelp</a> - RELP +input module</li> <li>imudp - udp syslog message input</li> <li><a href="imtcp.html">imtcp</a> - input plugin for plain tcp syslog</li> <li><a href="imgssapi.html">imgssapi</a> - input plugin for plain tcp and GSS-enable syslog</li> <li>immark - support for mark messages</li> -<li>imklog - kernel logging</li><li><a href="imuxsock.html">imuxsock</a> - unix sockets, including the system log socket</li> +<li>imklog - kernel logging</li> +<li><a href="imuxsock.html">imuxsock</a> - +unix sockets, including the system log socket</li> </ul> <p>Please note that each module provides configuration directives, which are NOT necessarily being listed below. Also @@ -64,7 +74,19 @@ unstable...). So you have been warned ;)</p> many parameter settings modify queue parameters. If in doubt, use the default, it is usually well-chosen and applicable in most cases.</p> <ul> -<li><a href="rsconf1_actionexeconlywhenpreviousissuspended.html">$ActionExecOnlyWhenPreviousIsSuspended</a></li><li>$ActionFileDefaultTemplate [templateName] - sets a new default template for file actions</li><li>$ActionFileEnableSync [on/<span style="font-weight: bold;">off</span>] - enables file syncing capability of omfile</li><li>$ActionForwardDefaultTemplate [templateName] - sets a new default template for UDP and plain TCP forwarding action</li><li>$ActionGSSForwardDefaultTemplate [templateName] - sets a new default template for GSS-API forwarding action</li> +<li><a href="rsconf1_actionexeconlywhenpreviousissuspended.html">$ActionExecOnlyWhenPreviousIsSuspended</a></li> +<li>$ActionExecOnlyOnceEveryInterval <seconds> - +execute action only if the last execute is at last +<seconds> seconds in the past (more info in <a href="ommail.html">ommail</a>, +but may be used with any action)</li> +<li>$ActionFileDefaultTemplate [templateName] - sets a new +default template for file actions</li> +<li>$ActionFileEnableSync [on/<span style="font-weight: bold;">off</span>] - enables file +syncing capability of omfile</li> +<li>$ActionForwardDefaultTemplate [templateName] - sets a new +default template for UDP and plain TCP forwarding action</li> +<li>$ActionGSSForwardDefaultTemplate [templateName] - sets a +new default template for GSS-API forwarding action</li> <li>$ActionQueueCheckpointInterval <number></li> <li>$ActionQueueDequeueSlowdown <number> [number is timeout in <i> micro</i>seconds (1000000us is 1sec!), @@ -98,7 +120,8 @@ default 60000 (1 minute)]</li> worker threads, default 1, recommended 1</li> <li>$ActionQueueWorkerThreadMinumumMessages <number>, default 100</li> -<li><a href="rsconf1_actionresumeinterval.html">$ActionResumeInterval</a></li><li>$ActionResumeRetryCount <number> [default 0, +<li><a href="rsconf1_actionresumeinterval.html">$ActionResumeInterval</a></li> +<li>$ActionResumeRetryCount <number> [default 0, -1 means eternal]</li> <li><a href="rsconf1_allowedsender.html">$AllowedSender</a></li> <li><a href="rsconf1_controlcharacterescapeprefix.html">$ControlCharacterEscapePrefix</a></li> @@ -166,11 +189,11 @@ worker threads, default 1, recommended 1</li> (immark)</li> <li><a href="rsconf1_moddir.html">$ModDir</a></li> <li><a href="rsconf1_modload.html">$ModLoad</a></li> - <li><a href="rsconf1_repeatedmsgreduction.html">$RepeatedMsgReduction</a></li> <li><a href="rsconf1_resetconfigvariables.html">$ResetConfigVariables</a></li> <li>$WorkDirectory <name> (directory for spool -and other work files)</li><li>$UDPServerAddress <IP> (imudp) -- local IP +and other work files)</li> +<li>$UDPServerAddress <IP> (imudp) -- local IP address (or name) the UDP listens should bind to</li> <li>$UDPServerRun <port> (imudp) -- former -r<port> option, default 514, start UDP server on this @@ -302,18 +325,29 @@ template:</p> DynFile,"/var/log/system-%HOSTNAME%.log"</code></blockquote> <p>This template can then be used when defining an output selector line. It will result in something like -"/var/log/system-localhost.log"</p><p>Template +"/var/log/system-localhost.log"</p> +<p>Template names beginning with "RSYSLOG_" are reserved for rsyslog use. Do NOT use them if, otherwise you may receive a conflict in the future (and quite unpredictable behaviour). There is a small set of pre-defined -templates that you can use without the need to define it:</p><ul><li><span style="font-weight: bold;">RSYSLOG_TraditionalFileFormat</span> - the "old style" default log file format with low-precision timestamps</li><li><span style="font-weight: bold;">RSYSLOG_FileFormat</span> - a modern-style logfile format similar to TraditionalFileFormat, buth with high-precision timestamps and timezone information</li><li><span style="font-weight: bold;">RSYSLOG_TraditionalForwardFormat</span> +templates that you can use without the need to define it:</p> +<ul> +<li><span style="font-weight: bold;">RSYSLOG_TraditionalFileFormat</span> +- the "old style" default log file format with low-precision timestamps</li> +<li><span style="font-weight: bold;">RSYSLOG_FileFormat</span> +- a modern-style logfile format similar to TraditionalFileFormat, buth +with high-precision timestamps and timezone information</li> +<li><span style="font-weight: bold;">RSYSLOG_TraditionalForwardFormat</span> - the traditional forwarding format with low-precision timestamps. Most -useful if you send messages to other syslogd's or rsyslogd below -version 3.12.5.</li><li><span style="font-weight: bold;">RSYSLOG_ForwardFormat</span> +useful if you send messages to other syslogd's or rsyslogd +below +version 3.12.5.</li> +<li><span style="font-weight: bold;">RSYSLOG_ForwardFormat</span> - a new high-precision forwarding format very similar to the traditional one, but with high-precision timestamps and timezone information. Recommended to be used when sending messages to rsyslog -3.12.5 or above.</li><li><span style="font-weight: bold;">RSYSLOG_SyslogProtocol23Format</span> +3.12.5 or above.</li> +<li><span style="font-weight: bold;">RSYSLOG_SyslogProtocol23Format</span> - the format specified in IETF's internet-draft ietf-syslog-protocol-23, which is assumed to be come the new syslog standard RFC. This format includes several improvements. The rsyslog @@ -321,7 +355,8 @@ message parser understands this format, so you can use it together with all relatively recent versions of rsyslog. Other syslogd's may get hopelessly confused if receiving that format, so check before you use it. Note that the format is unlikely to change when the final RFC comes -out, but this may happen.</li></ul> +out, but this may happen.</li> +</ul> <h2>Output Channels</h2> <p>Output Channels are a new concept first introduced in rsyslog 0.9.0. <b>As of this writing, it is most likely that they will @@ -524,7 +559,8 @@ once they are implemented, it can make very much sense </tr> <tr> <td>regex</td> -<td>Compares the property against the provided POSIX regular +<td>Compares the property against the provided POSIX +regular expression.</td> </tr> </tbody> @@ -567,10 +603,12 @@ code), this can be done easily by:</p> "ID-4711". Please note that the comparison is case-sensitive, so it would not match if "id-4711" would be contained in the message.</p> <p><code><b>:msg, regex, "fatal .* error"</b></code></p> -<p>This filter uses a POSIX regular expression. It matches when the +<p>This filter uses a POSIX regular expression. It matches when +the string contains the words "fatal" and "error" with anything in between (e.g. "fatal net error" and "fatal lib error" but not "fatal error" as -two spaces are required by the regular expression!).</p><p>Getting property-based filters right can sometimes be +two spaces are required by the regular expression!).</p> +<p>Getting property-based filters right can sometimes be challenging. In order to help you do it with as minimal effort as possible, rsyslogd spits out debug information for all property-based filters during their evaluation. To enable this, run rsyslogd in @@ -635,9 +673,12 @@ startswith 'DEVNAME' and <span style="font-weight: bold;">not</span> ($msg contains 'error1' or $msg contains 'error0') then /var/log/somelog<br> </code> -<br>If you would like to do case-insensitive comparisons, use +<br> +If you would like to do case-insensitive comparisons, use "contains_i" instead of "contains" and "startswith_i" instead of -"startswith".<br><br>Note that regular expressions are currently NOT +"startswith".<br> +<br> +Note that regular expressions are currently NOT supported in expression-based filters. These will be added later when function support is added to the expression engine (the reason is that regular expressions will be a separate loadable module, which requires diff --git a/doc/rsyslog_ng_comparison.html b/doc/rsyslog_ng_comparison.html index 2a1d15bd..28413337 100644 --- a/doc/rsyslog_ng_comparison.html +++ b/doc/rsyslog_ng_comparison.html @@ -1,12 +1,11 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html><head> -<meta content="de" http-equiv="Content-Language"><title>rsyslog vs. syslog-ng - a comparison</title> +<html><head><title>rsyslog vs. syslog-ng - a comparison</title> </head> <body> <h1>rsyslog vs. syslog-ng</h1> <p><small><i>Written by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> -(2008-02-28)</i></small></p> +(2008-04-08)</i></small></p> <p>We have often been asked about a comparison sheet between rsyslog and syslog-ng. Unfortunately, I do not know much about syslog-ng, I did not even use it once. Also, there seems to be no @@ -122,7 +121,9 @@ based framing on syslog/tcp connections</td> <td valign="top">yes</td> </tr> <tr> -<td valign="top">syslog over RELP<br>this is a truely reliable solution (plain tcp syslog can lose messages!)</td> +<td valign="top">syslog over RELP<br> +truly reliable message delivery (<a href="http://rgerhards.blogspot.com/2008/04/on-unreliability-of-plain-tcp-syslog.html">Why +is plain tcp syslog not reliable?</a>)</td> <td valign="top">yes</td> <td valign="top">no</td> </tr> @@ -337,6 +338,15 @@ be placed on different disk</td> <td valign="top">no</td> </tr> <tr> +<td valign="top">ability to process spooled +messages only during a configured timeframe (e.g. process messages only +during off-peak hours, during peak hours they are enqueued only)</td> +<td valign="top"><a href="http://wiki.rsyslog.com/index.php/OffPeakHours">yes</a><br> +(can independently be configured for the main queue and each action +queue)</td> +<td valign="top">no</td> +</tr> +<tr> <td valign="top">ability to configure backup syslog/database servers </td> <td valign="top">yes</td> @@ -416,6 +426,10 @@ including ability to present channel and priority as visible log data</td> <td valign="top">yes</td> <td valign="top">not sure...</td> </tr> +<tr><td valign="top">native ability to send mail messages</td> +<td valign="top">yes (<a href="ommail.html">ommail</a>, introduced in 3.17.0)</td> +<td valign="top">not sure...</td> +</tr> <tr> <td valign="top">good timestamp format control; at a minimum, ISO 8601/RFC 3339 second-resolution UTC zone</td> @@ -564,6 +578,6 @@ feature sheet. I have not yet been able to fully work through it. In the mean time, you may want to read it in parallel. It is available at <a href="http://www.balabit.com/network-security/syslog-ng/features/detailed/">Balabit's site</a>.</p> -<p>This document is current as of 2008-02-28 and definitely +<p>This document is current as of 2008-04-08 and definitely incomplete (I did not yet manage to complete it!).</p> </body></html>
\ No newline at end of file diff --git a/doc/status.html b/doc/status.html index d7111a50..98bb4f90 100644 --- a/doc/status.html +++ b/doc/status.html @@ -2,19 +2,23 @@ <html><head><title>rsyslog status page</title></head> <body> <h2>rsyslog status page</h2> -<p>This page reflects the status as of 2008-04-04.</p> +<p>This page reflects the status as of 2008-04-09.</p> <h2>Current Releases</h2> -<p><b>development:</b> 3.15.0 - +<p><b>development:</b> 3.17.0 - +<a href="http://www.rsyslog.com/Article207.phtml">change log</a> - +<a href="http://www.rsyslog.com/Downloads-index-req-getit-lid-95.phtml">download</a> + +<br><b>beta:</b> 3.15.0 - <a href="http://www.rsyslog.com/Article203.phtml">change log</a> - <a href="http://www.rsyslog.com/Downloads-index-req-getit-lid-93.phtml">download</a></p> -<p><b>v3 stable:</b> 3.14.0 - <a href="http://www.rsyslog.com/Article205.phtml">change log</a> - -<a href="http://www.rsyslog.com/Downloads-index-req-getit-lid-94.phtml">download</a> +<p><b>v3 stable:</b> 3.14.2 - <a href="http://www.rsyslog.com/Article209.phtml">change log</a> - +<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-96.phtml">download</a> <br><b>v2 stable:</b> 2.0.4 - <a href="http://www.rsyslog.com/Article197.phtml">change log</a> - <a href="http://www.rsyslog.com/Downloads-index-req-getit-lid-90.phtml">download</a> -<br>v0 and v1 are depricated and no longer supported. If you absolutely do not like to +<br>v0 and v1 are deprecated and no longer supported. If you absolutely do not like to upgrade, you may consider purchasing a <a href="professional_support.html">commercial rsyslog support package</a>. Just let us point out that it is really not a good idea to still run a v0 version. @@ -336,7 +336,7 @@ ENDobjConstruct(expr) /* ConstructionFinalizer * rgerhards, 2008-01-09 */ -rsRetVal exprConstructFinalize(expr_t *pThis) +rsRetVal exprConstructFinalize(expr_t __attribute__((unused)) *pThis) { DEFiRet; @@ -33,6 +33,7 @@ #define glblGetIOBufSize() 4096 /* size of the IO buffer, e.g. for strm class */ +extern uchar *glblModPath; /* module load path */ extern uchar *pszWorkDir; #define glblGetWorkDir() (pszWorkDir == NULL ? (uchar*) "" : pszWorkDir) @@ -780,6 +780,17 @@ BEGINAbstractObjClassInit(module, 1, OBJ_IS_CORE_MODULE) /* class, version - CHA SetModDir(pModPath); } + /* now check if another module path was set via the command line (-M) + * if so, that overrides the environment. Please note that we must use + * a global setting here because the command line parser can NOT call + * into the module object, because it is not initialized at that point. So + * instead a global setting is changed and we pick it up as soon as we + * initialize -- rgerhards, 2008-04-04 + */ + if(glblModPath != NULL) { + SetModDir(glblModPath); + } + /* request objects we use */ CHKiRet(objUse(errmsg, CORE_COMPONENT)); ENDObjClassInit(module) @@ -1604,20 +1604,17 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, pRes = getMSG(pMsg); } else if(!strcmp((char*) pName, "rawmsg")) { pRes = getRawMsg(pMsg); - } else if(!strcmp((char*) pName, "UxTradMsg")) { + } else if(!strcmp((char*) pName, "uxtradmsg")) { pRes = getUxTradMsg(pMsg); - } else if( !strcmp((char*) pName, "FROMHOST") - || !strcmp((char*) pName, "fromhost")) { + } else if(!strcmp((char*) pName, "fromhost")) { pRes = getRcvFrom(pMsg); - } else if(!strcmp((char*) pName, "source") - || !strcmp((char*) pName, "hostname") - || !strcmp((char*) pName, "HOSTNAME")) { + } else if(!strcmp((char*) pName, "source") || !strcmp((char*) pName, "hostname")) { pRes = getHOSTNAME(pMsg); } else if(!strcmp((char*) pName, "syslogtag")) { pRes = getTAG(pMsg); - } else if(!strcmp((char*) pName, "PRI")) { + } else if(!strcmp((char*) pName, "pri")) { pRes = getPRI(pMsg); - } else if(!strcmp((char*) pName, "PRI-text")) { + } else if(!strcmp((char*) pName, "pri-text")) { pBuf = malloc(20 * sizeof(char)); if(pBuf == NULL) { *pbMustBeFreed = 0; @@ -1639,57 +1636,57 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, } else if(!strcmp((char*) pName, "timegenerated")) { pRes = getTimeGenerated(pMsg, pTpe->data.field.eDateFormat); } else if(!strcmp((char*) pName, "timereported") - || !strcmp((char*) pName, "TIMESTAMP")) { + || !strcmp((char*) pName, "timestamp")) { pRes = getTimeReported(pMsg, pTpe->data.field.eDateFormat); } else if(!strcmp((char*) pName, "programname")) { pRes = getProgramName(pMsg); - } else if(!strcmp((char*) pName, "PROTOCOL-VERSION")) { + } else if(!strcmp((char*) pName, "protocol-version")) { pRes = getProtocolVersionString(pMsg); - } else if(!strcmp((char*) pName, "STRUCTURED-DATA")) { + } else if(!strcmp((char*) pName, "structured-data")) { pRes = getStructuredData(pMsg); - } else if(!strcmp((char*) pName, "APP-NAME")) { + } else if(!strcmp((char*) pName, "app-name")) { pRes = getAPPNAME(pMsg); - } else if(!strcmp((char*) pName, "PROCID")) { + } else if(!strcmp((char*) pName, "procid")) { pRes = getPROCID(pMsg); - } else if(!strcmp((char*) pName, "MSGID")) { + } else if(!strcmp((char*) pName, "msgid")) { pRes = getMSGID(pMsg); /* here start system properties (those, that do not relate to the message itself */ - } else if(!strcmp((char*) pName, "$NOW")) { + } else if(!strcmp((char*) pName, "$now")) { if((pRes = (char*) getNOW(NOW_NOW)) == NULL) { return "***OUT OF MEMORY***"; } else *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ - } else if(!strcmp((char*) pName, "$YEAR")) { + } else if(!strcmp((char*) pName, "$year")) { if((pRes = (char*) getNOW(NOW_YEAR)) == NULL) { return "***OUT OF MEMORY***"; } else *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ - } else if(!strcmp((char*) pName, "$MONTH")) { + } else if(!strcmp((char*) pName, "$month")) { if((pRes = (char*) getNOW(NOW_MONTH)) == NULL) { return "***OUT OF MEMORY***"; } else *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ - } else if(!strcmp((char*) pName, "$DAY")) { + } else if(!strcmp((char*) pName, "$day")) { if((pRes = (char*) getNOW(NOW_DAY)) == NULL) { return "***OUT OF MEMORY***"; } else *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ - } else if(!strcmp((char*) pName, "$HOUR")) { + } else if(!strcmp((char*) pName, "$hour")) { if((pRes = (char*) getNOW(NOW_HOUR)) == NULL) { return "***OUT OF MEMORY***"; } else *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ - } else if(!strcmp((char*) pName, "$HHOUR")) { + } else if(!strcmp((char*) pName, "$hhour")) { if((pRes = (char*) getNOW(NOW_HHOUR)) == NULL) { return "***OUT OF MEMORY***"; } else *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ - } else if(!strcmp((char*) pName, "$QHOUR")) { + } else if(!strcmp((char*) pName, "$qhour")) { if((pRes = (char*) getNOW(NOW_QHOUR)) == NULL) { return "***OUT OF MEMORY***"; } else *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ - } else if(!strcmp((char*) pName, "$MINUTE")) { + } else if(!strcmp((char*) pName, "$minute")) { if((pRes = (char*) getNOW(NOW_MINUTE)) == NULL) { return "***OUT OF MEMORY***"; } else @@ -1698,6 +1695,7 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, /* there is no point in continuing, we may even otherwise render the * error message unreadable. rgerhards, 2007-07-10 */ + dbgprintf("invalid property name: '%s'\n", pName); return "**INVALID PROPERTY NAME**"; } @@ -852,6 +852,44 @@ finalize_it: } +/* get the name of the local host. A pointer to a character pointer is passed + * in, which on exit points to the local hostname. This buffer is dynamically + * allocated and must be free()ed by the caller. If the functions returns an + * error, the pointer is NULL. This function is based on GNU/Hurd's localhostname + * function. + * rgerhards, 20080-04-10 + */ +static rsRetVal +getLocalHostname(uchar **ppName) +{ + DEFiRet; + uchar *buf = NULL; + size_t buf_len = 0; + + assert(ppName != NULL); + + do { + if(buf == NULL) { + buf_len = 128; /* Initial guess */ + CHKmalloc(buf = malloc(buf_len)); + } else { + buf_len += buf_len; + CHKmalloc(buf = realloc (buf, buf_len)); + } + } while((gethostname((char*)buf, buf_len) == 0 && !memchr (buf, '\0', buf_len)) || errno == ENAMETOOLONG); + + *ppName = buf; + buf = NULL; + +finalize_it: + if(iRet != RS_RET_OK) { + if(buf != NULL) + free(buf); + } + RETiRet; +} + + /* closes the UDP listen sockets (if they exist) and frees * all dynamically assigned memory. */ @@ -1047,6 +1085,7 @@ CODESTARTobjQueryInterface(net) pIf->closeUDPListenSockets = closeUDPListenSockets; pIf->isAllowedSender = isAllowedSender; pIf->should_use_so_bsdcompat = should_use_so_bsdcompat; + pIf->getLocalHostname = getLocalHostname; finalize_it: ENDobjQueryInterface(net) @@ -97,6 +97,7 @@ BEGINinterface(net) /* name must also be changed in ENDinterface macro! */ int *(*create_udp_socket)(uchar *hostname, uchar *LogPort, int bIsServer); void (*closeUDPListenSockets)(int *finet); int (*isAllowedSender)(struct AllowedSenders *pAllowRoot, struct sockaddr *pFrom, const char *pszFromHost); + rsRetVal (*getLocalHostname)(uchar**); int (*should_use_so_bsdcompat)(void); /* data memebers - these should go away over time... TODO */ int *pACLAddHostnameOnFail; /* add hostname to acl when DNS resolving has failed */ @@ -105,7 +106,7 @@ BEGINinterface(net) /* name must also be changed in ENDinterface macro! */ struct AllowedSenders *pAllowedSenders_TCP; struct AllowedSenders *pAllowedSenders_GSS; ENDinterface(net) -#define netCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ +#define netCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */ /* prototypes */ PROTOTYPEObj(net); diff --git a/obj-types.h b/obj-types.h index afbe1a8b..4cd45153 100644 --- a/obj-types.h +++ b/obj-types.h @@ -210,7 +210,7 @@ rsRetVal objName##ClassExit(void) \ #define CODESTARTObjClassExit(objName) #define ENDObjClassExit(objName) \ - iRet = obj.UnregisterObj((uchar*)#objName, pObjInfoOBJ); \ + iRet = obj.UnregisterObj((uchar*)#objName); \ RETiRet; \ } @@ -1074,7 +1074,7 @@ RegisterObj(uchar *pszObjName, objInfo_t *pInfo) if(i >= OBJ_NUM_IDS) ABORT_FINALIZE(RS_RET_OBJ_REGISTRY_OUT_OF_SPACE); arrObjInfo[i] = pInfo; - dbgprintf("object '%s' successfully registered with index %d, qIF %p\n", pszObjName, i, pInfo->QueryIF); + /* DEV debug only: dbgprintf("object '%s' successfully registered with index %d, qIF %p\n", pszObjName, i, pInfo->QueryIF); */ finalize_it: if(iRet != RS_RET_OK) { @@ -1090,14 +1090,13 @@ finalize_it: * rgerhards, 2008-03-10 */ static rsRetVal -UnregisterObj(uchar *pszObjName, objInfo_t *pInfo) +UnregisterObj(uchar *pszObjName) { DEFiRet; int bFound; int i; assert(pszObjName != NULL); - assert(pInfo != NULL); bFound = 0; i = 0; @@ -1114,7 +1113,7 @@ UnregisterObj(uchar *pszObjName, objInfo_t *pInfo) ABORT_FINALIZE(RS_RET_OBJ_NOT_REGISTERED); InfoDestruct(&arrObjInfo[i]); - dbgprintf("object '%s' successfully unregistered with index %d\n", pszObjName, i); + /* DEV debug only: dbgprintf("object '%s' successfully unregistered with index %d\n", pszObjName, i); */ finalize_it: if(iRet != RS_RET_OK) { @@ -1138,7 +1137,7 @@ UseObj(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf) objInfo_t *pObjInfo; - dbgprintf("source file %s requests object '%s', ifIsLoaded %d\n", srcFile, pObjName, pIf->ifIsLoaded); + /* DEV debug only: dbgprintf("source file %s requests object '%s', ifIsLoaded %d\n", srcFile, pObjName, pIf->ifIsLoaded); */ if(pIf->ifIsLoaded == 1) { ABORT_FINALIZE(RS_RET_OK); /* we are already set */ @@ -1171,7 +1170,6 @@ UseObj(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf) } /* if we reach this point, we have a valid pObjInfo */ - //if(pObjInfo->pModInfo != NULL) { /* NULL means core module */ if(pObjFile != NULL) { /* NULL means core module */ module.Use(srcFile, pObjInfo->pModInfo); /* increase refcount */ } @@ -104,13 +104,13 @@ BEGINinterface(obj) /* name must also be changed in ENDinterface macro! */ rsRetVal (*SerializeProp)(strm_t *pStrm, uchar *pszPropName, propType_t propType, void *pUsr); rsRetVal (*EndSerialize)(strm_t *pStrm); rsRetVal (*RegisterObj)(uchar *pszObjName, objInfo_t *pInfo); - rsRetVal (*UnregisterObj)(uchar *pszObjName, objInfo_t *pInfo); + rsRetVal (*UnregisterObj)(uchar *pszObjName); rsRetVal (*Deserialize)(void *ppObj, uchar *pszTypeExpected, strm_t *pStrm, rsRetVal (*fFixup)(obj_t*,void*), void *pUsr); rsRetVal (*DeserializePropBag)(obj_t *pObj, strm_t *pStrm); rsRetVal (*SetName)(obj_t *pThis, uchar *pszName); uchar * (*GetName)(obj_t *pThis); ENDinterface(obj) -#define objCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ +#define objCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */ /* prototypes */ @@ -71,7 +71,7 @@ DEFobjCurrIf(net) DEFobjCurrIf(tcpclt) typedef struct _instanceData { - char f_hname[MAXHOSTNAMELEN+1]; + char *f_hname; short sock; /* file descriptor */ int *pSockArray; /* sockets to use for UDP */ enum { /* TODO: we shoud revisit these definitions */ @@ -145,6 +145,9 @@ CODESTARTfreeInstance tcpclt.Destruct(&pData->pTCPClt); } + if(pData->f_hname != NULL) + free(pData->f_hname); + ENDfreeInstance @@ -540,10 +543,11 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) /* TODO: make this if go away! */ if(*p == ';') { *p = '\0'; /* trick to obtain hostname (later)! */ - strcpy(pData->f_hname, (char*) q); + CHKmalloc(pData->f_hname = strdup((char*) q)); *p = ';'; - } else - strcpy(pData->f_hname, (char*) q); + } else { + CHKmalloc(pData->f_hname = strdup((char*) q)); + } /* process template */ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, diff --git a/outchannel.c b/outchannel.c index 394371f0..d013ea08 100644 --- a/outchannel.c +++ b/outchannel.c @@ -91,12 +91,12 @@ static void skip_Comma(char **pp) /* helper to ochAddLine. Parses a comma-delimited field * The field is delimited by SP or comma. Leading whitespace * is "eaten" and does not become part of the field content. - * returns: 0 - ok, 1 - failure */ -static int get_Field(uchar **pp, uchar **pField) +static rsRetVal get_Field(uchar **pp, uchar **pField) { + DEFiRet; register uchar *p; - cstr_t *pStrB; + cstr_t *pStrB = NULL; assert(pp != NULL); assert(*pp != NULL); @@ -105,21 +105,25 @@ static int get_Field(uchar **pp, uchar **pField) skip_Comma((char**)pp); p = *pp; - if(rsCStrConstruct(&pStrB) != RS_RET_OK) - return 1; + CHKiRet(rsCStrConstruct(&pStrB)); rsCStrSetAllocIncrement(pStrB, 32); /* copy the field */ while(*p && *p != ' ' && *p != ',') { - rsCStrAppendChar(pStrB, *p++); + CHKiRet(rsCStrAppendChar(pStrB, *p++)); } *pp = p; - rsCStrFinish(pStrB); - if(rsCStrConvSzStrAndDestruct(pStrB, pField, 0) != RS_RET_OK) - return 1; + CHKiRet(rsCStrFinish(pStrB)); + CHKiRet(rsCStrConvSzStrAndDestruct(pStrB, pField, 0)); - return 0; +finalize_it: + if(iRet != RS_RET_OK) { + if(pStrB != NULL) + rsCStrDestruct(&pStrB); + } + + RETiRet; } @@ -156,12 +160,12 @@ static int get_off_t(uchar **pp, off_t *pOff_t) * current position to the end of line and returns it * to the caller. Leading white space is removed, but * not trailing. - * returns: 0 - ok, 1 - failure */ -static inline int get_restOfLine(uchar **pp, uchar **pBuf) +static inline rsRetVal get_restOfLine(uchar **pp, uchar **pBuf) { + DEFiRet; register uchar *p; - cstr_t *pStrB; + cstr_t *pStrB = NULL; assert(pp != NULL); assert(*pp != NULL); @@ -170,21 +174,25 @@ static inline int get_restOfLine(uchar **pp, uchar **pBuf) skip_Comma((char**)pp); p = *pp; - if(rsCStrConstruct(&pStrB) != RS_RET_OK) - return 1; + CHKiRet(rsCStrConstruct(&pStrB)); rsCStrSetAllocIncrement(pStrB, 32); /* copy the field */ while(*p) { - rsCStrAppendChar(pStrB, *p++); + CHKiRet(rsCStrAppendChar(pStrB, *p++)); } *pp = p; - rsCStrFinish(pStrB); - if(rsCStrConvSzStrAndDestruct(pStrB, pBuf, 0) != RS_RET_OK) - return 1; + CHKiRet(rsCStrFinish(pStrB)); + CHKiRet(rsCStrConvSzStrAndDestruct(pStrB, pBuf, 0)); - return 0; +finalize_it: + if(iRet != RS_RET_OK) { + if(pStrB != NULL) + rsCStrDestruct(&pStrB); + } + + RETiRet; } @@ -291,6 +299,5 @@ void ochPrintList(void) pOch = pOch->pNext; /* done, go next */ } } -/* - * vi:set ai: +/* vi:set ai: */ @@ -235,15 +235,16 @@ rsRetVal parsSkipWhitespace(rsParsObj *pThis) * 0 means "no", 1 "yes" * - bTrimLeading * - bTrimTrailing + * - bConvLower - convert string to lower case? * * Output: * ppCStr Pointer to the parsed string - must be freed by caller! */ -rsRetVal parsDelimCStr(rsParsObj *pThis, cstr_t **ppCStr, char cDelim, int bTrimLeading, int bTrimTrailing) +rsRetVal parsDelimCStr(rsParsObj *pThis, cstr_t **ppCStr, char cDelim, int bTrimLeading, int bTrimTrailing, int bConvLower) { DEFiRet; register unsigned char *pC; - cstr_t *pCStr; + cstr_t *pCStr = NULL; rsCHECKVALIDOBJECT(pThis, OIDrsPars); @@ -254,12 +255,8 @@ rsRetVal parsDelimCStr(rsParsObj *pThis, cstr_t **ppCStr, char cDelim, int bTrim pC = rsCStrGetBufBeg(pThis->pCStr) + pThis->iCurrPos; - while(pThis->iCurrPos < rsCStrLen(pThis->pCStr) - && *pC != cDelim) { - if((iRet = rsCStrAppendChar(pCStr, *pC)) != RS_RET_OK) { - rsCStrDestruct(&pCStr); - FINALIZE; - } + while(pThis->iCurrPos < rsCStrLen(pThis->pCStr) && *pC != cDelim) { + CHKiRet(rsCStrAppendChar(pCStr, bConvLower ? tolower(*pC) : *pC)); ++pThis->iCurrPos; ++pC; } @@ -271,22 +268,21 @@ rsRetVal parsDelimCStr(rsParsObj *pThis, cstr_t **ppCStr, char cDelim, int bTrim /* We got the string, now take it and see if we need to * remove anything at its end. */ - if((iRet = rsCStrFinish(pCStr)) != RS_RET_OK) { - rsCStrDestruct (&pCStr); - FINALIZE; - } + CHKiRet(rsCStrFinish(pCStr)); if(bTrimTrailing) { - if((iRet = rsCStrTrimTrailingWhiteSpace(pCStr)) - != RS_RET_OK) { - rsCStrDestruct (&pCStr); - FINALIZE; - } + CHKiRet(rsCStrTrimTrailingWhiteSpace(pCStr)); } /* done! */ *ppCStr = pCStr; + finalize_it: + if(iRet != RS_RET_OK) { + if(pCStr != NULL) + rsCStrDestruct(&pCStr); + } + RETiRet; } @@ -308,13 +304,12 @@ finalize_it: rsRetVal parsQuotedCStr(rsParsObj *pThis, cstr_t **ppCStr) { register unsigned char *pC; - cstr_t *pCStr; + cstr_t *pCStr = NULL; DEFiRet; rsCHECKVALIDOBJECT(pThis, OIDrsPars); - if((iRet = parsSkipAfterChar(pThis, '"')) != RS_RET_OK) - FINALIZE; + CHKiRet(parsSkipAfterChar(pThis, '"')); pC = rsCStrGetBufBeg(pThis->pCStr) + pThis->iCurrPos; /* OK, we most probably can obtain a value... */ @@ -331,16 +326,10 @@ rsRetVal parsQuotedCStr(rsParsObj *pThis, cstr_t **ppCStr) * to the output buffer (but do not rely on this, * we might later introduce other things, like \007! */ - if((iRet = rsCStrAppendChar(pCStr, *pC)) != RS_RET_OK) { - rsCStrDestruct(&pCStr); - FINALIZE; - } + CHKiRet(rsCStrAppendChar(pCStr, *pC)); } } else { /* regular character */ - if((iRet = rsCStrAppendChar(pCStr, *pC)) != RS_RET_OK) { - rsCStrDestruct (&pCStr); - FINALIZE; - } + CHKiRet(rsCStrAppendChar(pCStr, *pC)); } ++pThis->iCurrPos; ++pC; @@ -350,19 +339,22 @@ rsRetVal parsQuotedCStr(rsParsObj *pThis, cstr_t **ppCStr) ++pThis->iCurrPos; /* 'eat' trailing quote */ } else { /* error - improperly quoted string! */ - rsCStrDestruct (&pCStr); + rsCStrDestruct(&pCStr); ABORT_FINALIZE(RS_RET_MISSING_TRAIL_QUOTE); } /* We got the string, let's finish it... */ - if((iRet = rsCStrFinish(pCStr)) != RS_RET_OK) { - rsCStrDestruct (&pCStr); - FINALIZE; - } + CHKiRet(rsCStrFinish(pCStr)); /* done! */ *ppCStr = pCStr; + finalize_it: + if(iRet != RS_RET_OK) { + if(pCStr != NULL) + rsCStrDestruct(&pCStr); + } + RETiRet; } @@ -91,7 +91,7 @@ rsRetVal parsSkipWhitespace(rsParsObj *pThis); * Output: * ppCStr Pointer to the parsed string */ -rsRetVal parsDelimCStr(rsParsObj *pThis, cstr_t **ppCStr, char cDelim, int bTrimLeading, int bTrimTrailing); +rsRetVal parsDelimCStr(rsParsObj *pThis, cstr_t **ppCStr, char cDelim, int bTrimLeading, int bTrimTrailing, int bConvLower); rsRetVal parsSkipAfterChar(rsParsObj *pThis, char c); rsRetVal parsQuotedCStr(rsParsObj *pThis, cstr_t **ppCStr); diff --git a/plugins/imfile/imfile.c b/plugins/imfile/imfile.c index 162cab9f..61b50d06 100644 --- a/plugins/imfile/imfile.c +++ b/plugins/imfile/imfile.c @@ -95,7 +95,7 @@ static rsRetVal enqLine(fileInfo_t *pInfo, cstr_t *cstrLine) MsgSetUxTradMsg(pMsg, (char*)rsCStrGetSzStr(cstrLine)); MsgSetRawMsg(pMsg, (char*)rsCStrGetSzStr(cstrLine)); MsgSetMSG(pMsg, (char*)rsCStrGetSzStr(cstrLine)); - MsgSetHOSTNAME(pMsg, LocalHostName); + MsgSetHOSTNAME(pMsg, (char*)LocalHostName); MsgSetTAG(pMsg, (char*)pInfo->pszTag); pMsg->iFacility = LOG_FAC(pInfo->iFacility); pMsg->iSeverity = LOG_PRI(pInfo->iSeverity); diff --git a/plugins/imklog/imklog.c b/plugins/imklog/imklog.c index bfea8c6f..65a05229 100644 --- a/plugins/imklog/imklog.c +++ b/plugins/imklog/imklog.c @@ -133,7 +133,7 @@ static rsRetVal writeSyslogV(int iPRI, const char *szFmt, va_list va) /* here we must create our message object and supply it to the message queue */ - CHKiRet(parseAndSubmitMessage(LocalHostName, msgBuf, strlen(msgBuf), MSG_DONT_PARSE_HOSTNAME, NOFLAG, eFLOWCTL_LIGHT_DELAY)); + CHKiRet(parseAndSubmitMessage((char*)LocalHostName, msgBuf, strlen(msgBuf), MSG_DONT_PARSE_HOSTNAME, NOFLAG, eFLOWCTL_LIGHT_DELAY)); finalize_it: RETiRet; diff --git a/plugins/imuxsock/imuxsock.c b/plugins/imuxsock/imuxsock.c index 3cdcbf0a..f8798039 100644 --- a/plugins/imuxsock/imuxsock.c +++ b/plugins/imuxsock/imuxsock.c @@ -181,7 +181,7 @@ static rsRetVal readSocket(int fd, int bParseHost, int flags) iRcvd = recv(fd, line, MAXLINE - 1, 0); dbgprintf("Message from UNIX socket: #%d\n", fd); if (iRcvd > 0) { - parseAndSubmitMessage(LocalHostName, line, iRcvd, bParseHost, flags, eFLOWCTL_LIGHT_DELAY); + parseAndSubmitMessage((char*)LocalHostName, line, iRcvd, bParseHost, flags, eFLOWCTL_LIGHT_DELAY); } else if (iRcvd < 0 && errno != EINTR) { char errStr[1024]; rs_strerror_r(errno, errStr, sizeof(errStr)); diff --git a/plugins/omgssapi/omgssapi.c b/plugins/omgssapi/omgssapi.c index 9a7e8ab9..8c6a2006 100644 --- a/plugins/omgssapi/omgssapi.c +++ b/plugins/omgssapi/omgssapi.c @@ -4,7 +4,7 @@ * NOTE: read comments in module-template.h to understand how this file * works! * - * Copyright 2007 Rainer Gerhards and Adiscon GmbH. + * Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH. * * This file is part of rsyslog. * @@ -79,7 +79,7 @@ DEFobjCurrIf(gssutil) DEFobjCurrIf(tcpclt) typedef struct _instanceData { - char f_hname[MAXHOSTNAMELEN+1]; + char *f_hname; short sock; /* file descriptor */ enum { /* TODO: we shoud revisit these definitions */ eDestFORW, @@ -162,6 +162,9 @@ CODESTARTfreeInstance tcpclt.Destruct(&pData->pTCPClt); if(pData->sock >= 0) close(pData->sock); + + if(pData->f_hname != NULL) + free(pData->f_hname); ENDfreeInstance @@ -592,10 +595,11 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) /* TODO: make this if go away! */ if(*p == ';') { *p = '\0'; /* trick to obtain hostname (later)! */ - strcpy(pData->f_hname, (char*) q); + CHKmalloc(pData->f_hname = strdup((char*) q)); *p = ';'; - } else - strcpy(pData->f_hname, (char*) q); + } else { + CHKmalloc(pData->f_hname = strdup((char*) q)); + } /* process template */ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, @@ -701,6 +705,5 @@ CODEmodInit_QueryRegCFSLineHdlr ENDmodInit #endif /* #ifdef USE_GSSAPI */ -/* - * vi:set ai: +/* vi:set ai: */ diff --git a/plugins/ommail/Makefile.am b/plugins/ommail/Makefile.am new file mode 100644 index 00000000..7e9f5f13 --- /dev/null +++ b/plugins/ommail/Makefile.am @@ -0,0 +1,6 @@ +pkglib_LTLIBRARIES = ommail.la + +ommail_la_SOURCES = ommail.c +ommail_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) +ommail_la_LDFLAGS = -module -avoid-version +ommail_la_LIBADD = diff --git a/plugins/ommail/ommail.c b/plugins/ommail/ommail.c new file mode 100644 index 00000000..218c73c9 --- /dev/null +++ b/plugins/ommail/ommail.c @@ -0,0 +1,630 @@ +/* ommail.c + * + * This is an implementation of a mail sending output module. So far, we + * only support direct SMTP, that is talking to a SMTP server. In the long + * term, support for using sendmail should also be implemented. Please note + * that the SMTP protocol implementation is a very bare one. We support + * RFC821/822 messages, without any authentication and any other nice + * features (no MIME, no nothing). It is assumed that proper firewalling + * and/or STMP server configuration is used together with this module. + * + * NOTE: read comments in module-template.h to understand how this file + * works! + * + * File begun on 2008-04-04 by RGerhards + * + * Copyright 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of rsyslog. + * + * Rsyslog is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Rsyslog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Rsyslog. If not, see <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" +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <unistd.h> +#include <errno.h> +#include <netdb.h> +#include <time.h> +#include <sys/socket.h> +#include "syslogd.h" +#include "syslogd-types.h" +#include "srUtils.h" +#include "cfsysline.h" +#include "module-template.h" +#include "errmsg.h" + +MODULE_TYPE_OUTPUT + +/* internal structures + */ +DEF_OMOD_STATIC_DATA +DEFobjCurrIf(errmsg) + +static uchar *pszSrv = NULL; +static uchar *pszSrvPort = NULL; +static uchar *pszFrom = NULL; +static uchar *pszTo = NULL; +static uchar *pszSubject = NULL; +static int bEnableBody = 1; /* should a mail body be generated? (set to 0 eg for SMS gateways) */ + +typedef struct _instanceData { + int iMode; /* 0 - smtp, 1 - sendmail */ + int bHaveSubject; /* is a subject configured? (if so, it is the second string provided by rsyslog core) */ + int bEnableBody; /* is a body configured? (if so, it is the second string provided by rsyslog core) */ + union { + struct { + uchar *pszSrv; + uchar *pszSrvPort; + uchar *pszFrom; + uchar *pszTo; + char RcvBuf[1024]; /* buffer for receiving server responses */ + size_t lenRcvBuf; + size_t iRcvBuf; /* current index into the rcvBuf (buf empty if iRcvBuf == lenRcvBuf) */ + int sock; /* socket to this server (most important when we do multiple msgs per mail) */ + } smtp; + } md; /* mode-specific data */ +} instanceData; + + +BEGINcreateInstance +CODESTARTcreateInstance +ENDcreateInstance + + +BEGINisCompatibleWithFeature +CODESTARTisCompatibleWithFeature + if(eFeat == sFEATURERepeatedMsgReduction) + iRet = RS_RET_OK; +ENDisCompatibleWithFeature + + +BEGINfreeInstance +CODESTARTfreeInstance + if(pData->iMode == 0) { + if(pData->md.smtp.pszSrv != NULL) + free(pData->md.smtp.pszSrv); + if(pData->md.smtp.pszSrvPort != NULL) + free(pData->md.smtp.pszSrvPort); + if(pData->md.smtp.pszFrom != NULL) + free(pData->md.smtp.pszFrom); + if(pData->md.smtp.pszTo != NULL) + free(pData->md.smtp.pszTo); + } +ENDfreeInstance + + +BEGINdbgPrintInstInfo +CODESTARTdbgPrintInstInfo + printf("mail"); /* TODO: extend! */ +ENDdbgPrintInstInfo + + +/* TCP support code, should probably be moved to net.c or some place else... -- rgerhards, 2008-04-04 */ + +/* "receive" a character from the remote server. A single character + * is returned. Returns RS_RET_NO_MORE_DATA if the server has closed + * the connection and RS_RET_IO_ERROR if something goes wrong. This + * is a blocking read. + * rgerhards, 2008-04-04 + */ +static rsRetVal +getRcvChar(instanceData *pData, char *pC) +{ + DEFiRet; + ssize_t lenBuf; + assert(pData != NULL); + + if(pData->md.smtp.iRcvBuf == pData->md.smtp.lenRcvBuf) { /* buffer empty? */ + /* yes, we need to read the next server response */ + do { + lenBuf = recv(pData->md.smtp.sock, pData->md.smtp.RcvBuf, sizeof(pData->md.smtp.RcvBuf), 0); + if(lenBuf == 0) { + ABORT_FINALIZE(RS_RET_NO_MORE_DATA); + } else if(lenBuf < 0) { + if(errno != EAGAIN) { + ABORT_FINALIZE(RS_RET_IO_ERROR); + } + } else { + /* good read */ + pData->md.smtp.iRcvBuf = 0; + pData->md.smtp.lenRcvBuf = lenBuf; + } + + } while(lenBuf < 1); + } + + /* when we reach this point, we have a non-empty buffer */ + *pC = pData->md.smtp.RcvBuf[pData->md.smtp.iRcvBuf++]; + +finalize_it: + RETiRet; +} + + +/* close the mail server connection + * rgerhards, 2008-04-08 + */ +static rsRetVal +serverDisconnect(instanceData *pData) +{ + DEFiRet; + assert(pData != NULL); + + if(pData->md.smtp.sock != -1) { + close(pData->md.smtp.sock); + pData->md.smtp.sock = -1; + } + + RETiRet; +} + + +/* open a connection to the mail server + * rgerhards, 2008-04-04 + */ +static rsRetVal +serverConnect(instanceData *pData) +{ + struct addrinfo *res = NULL; + struct addrinfo hints; + char *smtpPort; + char *smtpSrv; + char errStr[1024]; + + DEFiRet; + assert(pData != NULL); + + if(pData->md.smtp.pszSrv == NULL) + smtpSrv = "127.0.0.1"; + else + smtpSrv = (char*)pData->md.smtp.pszSrv; + + if(pData->md.smtp.pszSrvPort == NULL) + smtpPort = "25"; + else + smtpPort = (char*)pData->md.smtp.pszSrvPort; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* TODO: make configurable! */ + hints.ai_socktype = SOCK_STREAM; + if(getaddrinfo(smtpSrv, smtpPort, &hints, &res) != 0) { + dbgprintf("error %d in getaddrinfo\n", errno); + ABORT_FINALIZE(RS_RET_IO_ERROR); + } + + if((pData->md.smtp.sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) { + dbgprintf("couldn't create send socket, reason %s", rs_strerror_r(errno, errStr, sizeof(errStr))); + ABORT_FINALIZE(RS_RET_IO_ERROR); + } + + if(connect(pData->md.smtp.sock, res->ai_addr, res->ai_addrlen) != 0) { + dbgprintf("create tcp connection failed, reason %s", rs_strerror_r(errno, errStr, sizeof(errStr))); + ABORT_FINALIZE(RS_RET_IO_ERROR); + } + +finalize_it: + if(res != NULL) + freeaddrinfo(res); + + if(iRet != RS_RET_OK) { + if(pData->md.smtp.sock != -1) { + close(pData->md.smtp.sock); + pData->md.smtp.sock = -1; + } + } + + RETiRet; +} + + +/* send text to the server, blocking send */ +static rsRetVal +Send(int sock, char *msg, size_t len) +{ + DEFiRet; + size_t offsBuf = 0; + ssize_t lenSend; + + assert(msg != NULL); + + if(len == 0) /* it's valid, but does not make much sense ;) */ + FINALIZE; + + do { + lenSend = send(sock, msg + offsBuf, len - offsBuf, 0); + if(lenSend == -1) { + if(errno != EAGAIN) { + dbgprintf("message not (tcp)send, errno %d", errno); + ABORT_FINALIZE(RS_RET_TCP_SEND_ERROR); + } + } else if(lenSend != (ssize_t) len) { + offsBuf += len; /* on to next round... */ + } else { + FINALIZE; + } + } while(1); + +finalize_it: + RETiRet; +} + + +/* send body text to the server, blocking send + * The body is special in that we must escape a leading dot inside a line + */ +static rsRetVal +bodySend(instanceData *pData, char *msg, size_t len) +{ + DEFiRet; + char szBuf[2048]; + size_t iSrc; + size_t iBuf = 0; + int bHadCR = 0; + int bInStartOfLine = 1; + + assert(pData != NULL); + assert(msg != NULL); + + for(iSrc = 0 ; iSrc < len ; ++iSrc) { + if(iBuf >= sizeof(szBuf) - 1) { /* one is reserved for our extra dot */ + CHKiRet(Send(pData->md.smtp.sock, szBuf, iBuf)); + iBuf = 0; + } + szBuf[iBuf++] = msg[iSrc]; + switch(msg[iSrc]) { + case '\r': + bHadCR = 1; + break; + case '\n': + if(bHadCR) + bInStartOfLine = 1; + bHadCR = 0; + break; + case '.': + if(bInStartOfLine) + szBuf[iBuf++] = '.'; /* space is always reserved for this! */ + /*FALLTHROUGH*/ + default: + bInStartOfLine = 0; + bHadCR = 0; + break; + } + } + + if(iBuf > 0) { /* incomplete buffer to send (the *usual* case)? */ + CHKiRet(Send(pData->md.smtp.sock, szBuf, iBuf)); + } + +finalize_it: + RETiRet; +} + + +/* read response line from server + */ +static rsRetVal +readResponseLn(instanceData *pData, char *pLn, size_t lenLn) +{ + DEFiRet; + size_t i = 0; + char c; + + assert(pData != NULL); + assert(pLn != NULL); + + do { + CHKiRet(getRcvChar(pData, &c)); + if(c == '\n') + break; + if(i < (lenLn - 1)) /* if line is too long, we simply discard the rest */ + pLn[i++] = c; + } while(1); + pLn[i] = '\0'; + dbgprintf("smtp server response: %s\n", pLn); /* do not remove, this is helpful in troubleshooting SMTP probs! */ + +finalize_it: + RETiRet; +} + + +/* read numerical response code from server and compare it to requried response code. + * If they two don't match, return RS_RET_SMTP_ERROR. + * rgerhards, 2008-04-07 + */ +static rsRetVal +readResponse(instanceData *pData, int *piState, int iExpected) +{ + DEFiRet; + int bCont; + char buf[128]; + + assert(pData != NULL); + assert(piState != NULL); + + bCont = 1; + do { + CHKiRet(readResponseLn(pData, buf, sizeof(buf))); + /* note: the code below is not 100% clean as we may have received less than 4 characters. + * However, as we have a fixed size this will not create a vulnerability. An error will + * also most likely be generated, so it is quite acceptable IMHO -- rgerhards, 2008-04-08 + */ + if(buf[3] != '-') { /* last or only response line? */ + bCont = 0; + *piState = buf[0] - '0'; + *piState = *piState * 10 + buf[1] - '0'; + *piState = *piState * 10 + buf[2] - '0'; + if(*piState != iExpected) + ABORT_FINALIZE(RS_RET_SMTP_ERROR); + } + } while(bCont); + +finalize_it: + RETiRet; +} + + +/* create a timestamp suitable for use with the Date: SMTP body header + * rgerhards, 2008-04-08 + */ +static void +mkSMTPTimestamp(uchar *pszBuf, size_t lenBuf) +{ + time_t tCurr; + struct tm tmCurr; + static const char szDay[][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + static const char szMonth[][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + time(&tCurr); + gmtime_r(&tCurr, &tmCurr); + snprintf((char*)pszBuf, lenBuf, "Date: %s, %2d %s %4d %2d:%02d:%02d UT\r\n", szDay[tmCurr.tm_wday], tmCurr.tm_mday, + szMonth[tmCurr.tm_mon], 1900 + tmCurr.tm_year, tmCurr.tm_hour, tmCurr.tm_min, tmCurr.tm_sec); +} + + +/* send a message via SMTP + * rgerhards, 2008-04-04 + */ +static rsRetVal +sendSMTP(instanceData *pData, uchar *body, uchar *subject) +{ + DEFiRet; + int iState; /* SMTP state */ + uchar szDateBuf[64]; + + assert(pData != NULL); + + CHKiRet(serverConnect(pData)); + CHKiRet(readResponse(pData, &iState, 220)); + + CHKiRet(Send(pData->md.smtp.sock, "HELO ", 5)); + CHKiRet(Send(pData->md.smtp.sock, (char*)LocalHostName, strlen((char*)LocalHostName))); + CHKiRet(Send(pData->md.smtp.sock, "\r\n", sizeof("\r\n") - 1)); + CHKiRet(readResponse(pData, &iState, 250)); + + CHKiRet(Send(pData->md.smtp.sock, "MAIL FROM: <", sizeof("MAIL FROM: <") - 1)); + CHKiRet(Send(pData->md.smtp.sock, (char*)pData->md.smtp.pszFrom, strlen((char*)pData->md.smtp.pszFrom))); + CHKiRet(Send(pData->md.smtp.sock, ">\r\n", sizeof(">\r\n") - 1)); + CHKiRet(readResponse(pData, &iState, 250)); + + CHKiRet(Send(pData->md.smtp.sock, "RCPT TO: <", sizeof("RCPT TO: <") - 1)); + CHKiRet(Send(pData->md.smtp.sock, (char*)pData->md.smtp.pszTo, strlen((char*)pData->md.smtp.pszTo))); + CHKiRet(Send(pData->md.smtp.sock, ">\r\n", sizeof(">\r\n") - 1)); + CHKiRet(readResponse(pData, &iState, 250)); + + CHKiRet(Send(pData->md.smtp.sock, "DATA\r\n", sizeof("DATA\r\n") - 1)); + CHKiRet(readResponse(pData, &iState, 354)); + + /* now come the data part */ + /* header */ + mkSMTPTimestamp(szDateBuf, sizeof(szDateBuf)); + CHKiRet(Send(pData->md.smtp.sock, (char*)szDateBuf, strlen((char*)szDateBuf))); + + CHKiRet(Send(pData->md.smtp.sock, "From: <", sizeof("From: <") - 1)); + CHKiRet(Send(pData->md.smtp.sock, (char*)pData->md.smtp.pszFrom, strlen((char*)pData->md.smtp.pszFrom))); + CHKiRet(Send(pData->md.smtp.sock, ">\r\n", sizeof(">\r\n") - 1)); + + CHKiRet(Send(pData->md.smtp.sock, "To: <", sizeof("To: <") - 1)); + CHKiRet(Send(pData->md.smtp.sock, (char*)pData->md.smtp.pszTo, strlen((char*)pData->md.smtp.pszTo))); + CHKiRet(Send(pData->md.smtp.sock, ">\r\n", sizeof(">\r\n") - 1)); + + CHKiRet(Send(pData->md.smtp.sock, "Subject: ", sizeof("Subject: ") - 1)); + CHKiRet(Send(pData->md.smtp.sock, (char*)subject, strlen((char*)subject))); + CHKiRet(Send(pData->md.smtp.sock, "\r\n", sizeof("\r\n") - 1)); + + CHKiRet(Send(pData->md.smtp.sock, "X-Mailer: rsyslog-immail\r\n", sizeof("x-mailer: rsyslog-immail\r\n") - 1)); + + CHKiRet(Send(pData->md.smtp.sock, "\r\n", sizeof("\r\n") - 1)); /* indicate end of header */ + + /* body */ + if(pData->bEnableBody) + CHKiRet(bodySend(pData, (char*)body, strlen((char*) body))); + + /* end of data, back to envelope transaction */ + CHKiRet(Send(pData->md.smtp.sock, "\r\n.\r\n", sizeof("\r\n.\r\n") - 1)); + CHKiRet(readResponse(pData, &iState, 250)); + + CHKiRet(Send(pData->md.smtp.sock, "QUIT\r\n", sizeof("QUIT\r\n") - 1)); + CHKiRet(readResponse(pData, &iState, 221)); + + /* we are finished, a new connection is created for each request, so let's close it now */ + CHKiRet(serverDisconnect(pData)); + +finalize_it: + RETiRet; +} + + +/* in tryResume we check if we can connect to the server in question. If that is OK, + * we close the connection without doing any actual SMTP transaction. It will be + * reopened during the actual send process. This may not be the best way to do it if + * there is a problem inside the SMTP transaction. However, we can't find that out without + * actually initiating something, and that would be bad. The logic here helps us + * correctly recover from an unreachable/down mail server, which is probably the majority + * of problem cases. For SMTP transaction problems, we will do lots of retries, but if it + * is a temporary problem, it will be fixed anyhow. So I consider this implementation to + * be clean enough, especially as I think other approaches have other weaknesses. + * rgerhards, 2008-04-08 + */ +BEGINtryResume +CODESTARTtryResume + CHKiRet(serverConnect(pData)); + CHKiRet(serverDisconnect(pData)); /* if we fail, we will never reach this line */ +finalize_it: + if(iRet == RS_RET_IO_ERROR) + iRet = RS_RET_SUSPENDED; +ENDtryResume + + +BEGINdoAction +CODESTARTdoAction + dbgprintf(" Mail\n"); + + /* forward */ + if(pData->bHaveSubject) + iRet = sendSMTP(pData, ppString[0], ppString[1]); + else + iRet = sendSMTP(pData, ppString[0], (uchar*)"message from rsyslog"); + + if(iRet != RS_RET_OK) { + /* error! */ + dbgprintf("error sending mail, suspending\n"); + iRet = RS_RET_SUSPENDED; + } +ENDdoAction + + +BEGINparseSelectorAct +CODESTARTparseSelectorAct + if(!strncmp((char*) p, ":ommail:", sizeof(":ommail:") - 1)) { + p += sizeof(":ommail:") - 1; /* eat indicator sequence (-1 because of '\0'!) */ + } else { + ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED); + } + + /* ok, if we reach this point, we have something for us */ + if((iRet = createInstance(&pData)) != RS_RET_OK) + FINALIZE; + + /* TODO: check strdup() result */ + + if(pszFrom == NULL) { + errmsg.LogError(NO_ERRCODE, "no sender address given - specify $ActionMailFrom"); + ABORT_FINALIZE(RS_RET_MAIL_NO_FROM); + } + if(pszTo == NULL) { + errmsg.LogError(NO_ERRCODE, "no recipient address given - specify $ActionMailTo"); + ABORT_FINALIZE(RS_RET_MAIL_NO_TO); + } + + pData->md.smtp.pszFrom = (uchar*) strdup((char*)pszFrom); + pData->md.smtp.pszTo = (uchar*) strdup((char*)pszTo); + + if(pszSubject == NULL) { + /* if no subject is configured, we need just one template string */ + CODE_STD_STRING_REQUESTparseSelectorAct(1) + } else { + CODE_STD_STRING_REQUESTparseSelectorAct(2) + pData->bHaveSubject = 1; + CHKiRet(OMSRsetEntry(*ppOMSR, 1, (uchar*)strdup((char*) pszSubject), OMSR_NO_RQD_TPL_OPTS)); + } + if(pszSrv != NULL) + pData->md.smtp.pszSrv = (uchar*) strdup((char*)pszSrv); + if(pszSrvPort != NULL) + pData->md.smtp.pszSrvPort = (uchar*) strdup((char*)pszSrvPort); + pData->bEnableBody = bEnableBody; + + /* process template */ + CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar*) "RSYSLOG_FileFormat")); +CODE_STD_FINALIZERparseSelectorAct +ENDparseSelectorAct + + +/* Free string config variables and reset them to NULL (not necessarily the default!) */ +static rsRetVal freeConfigVariables(void) +{ + DEFiRet; + + if(pszSrv != NULL) { + free(pszSrv); + pszSrv = NULL; + } + if(pszSrvPort != NULL) { + free(pszSrvPort); + pszSrvPort = NULL; + } + if(pszFrom != NULL) { + free(pszFrom); + pszFrom = NULL; + } + if(pszTo != NULL) { + free(pszTo); + pszTo = NULL; + } + + RETiRet; +} + + +BEGINmodExit +CODESTARTmodExit + /* cleanup our allocations */ + freeConfigVariables(); + + /* release what we no longer need */ + objRelease(errmsg, CORE_COMPONENT); +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_OMOD_QUERIES +ENDqueryEtryPt + + +/* Reset config variables for this module to default values. + */ +static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal) +{ + DEFiRet; + bEnableBody = 1; + iRet = freeConfigVariables(); + RETiRet; +} + + +BEGINmodInit() +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr + /* tell which objects we need */ + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + + CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionmailsmtpserver", 0, eCmdHdlrGetWord, NULL, &pszSrv, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionmailsmtpport", 0, eCmdHdlrGetWord, NULL, &pszSrvPort, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionmailfrom", 0, eCmdHdlrGetWord, NULL, &pszFrom, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionmailto", 0, eCmdHdlrGetWord, NULL, &pszTo, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionmailsubject", 0, eCmdHdlrGetWord, NULL, &pszSubject, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionmailenablebody", 0, eCmdHdlrBinary, NULL, &bEnableBody, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr( (uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); +ENDmodInit + +/* vim:set ai: + */ diff --git a/plugins/omrelp/omrelp.c b/plugins/omrelp/omrelp.c index 39d25812..04571682 100644 --- a/plugins/omrelp/omrelp.c +++ b/plugins/omrelp/omrelp.c @@ -53,7 +53,7 @@ DEFobjCurrIf(errmsg) static relpEngine_t *pRelpEngine; /* our relp engine */ typedef struct _instanceData { - char f_hname[MAXHOSTNAMELEN+1]; + char *f_hname; int compressionLevel; /* 0 - no compression, else level for zlib */ char *port; int bInitialConnect; /* is this the initial connection request of our module? (0-no, 1-yes) */ @@ -98,6 +98,9 @@ CODESTARTfreeInstance if(pData->pRelpClt != NULL) relpEngineCltDestruct(pRelpEngine, &pData->pRelpClt); + if(pData->f_hname != NULL) + free(pData->f_hname); + ENDfreeInstance @@ -284,10 +287,11 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) /* TODO: make this if go away! */ if(*p == ';') { *p = '\0'; /* trick to obtain hostname (later)! */ - strcpy(pData->f_hname, (char*) q); + CHKmalloc(pData->f_hname = strdup((char*) q)); *p = ';'; - } else - strcpy(pData->f_hname, (char*) q); + } else { + CHKmalloc(pData->f_hname = strdup((char*) q)); + } /* process template */ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar*) "RSYSLOG_ForwardFormat")); @@ -39,6 +39,7 @@ #include <fcntl.h> #include <unistd.h> #include <sys/stat.h> /* required for HP UX */ +#include <time.h> #include <errno.h> #include "rsyslog.h" @@ -56,6 +57,7 @@ DEFobjStaticHelpers /* forward-definitions */ rsRetVal queueChkPersist(queue_t *pThis); static rsRetVal queueSetEnqOnly(queue_t *pThis, int bEnqOnly, int bLockMutex); +static rsRetVal queueRateLimiter(queue_t *pThis); static int queueChkStopWrkrDA(queue_t *pThis); static int queueIsIdleDA(queue_t *pThis); static rsRetVal queueConsumerDA(queue_t *pThis, wti_t *pWti, int iCancelStateSave); @@ -272,6 +274,8 @@ queueStartDA(queue_t *pThis) CHKiRet(queueSettoActShutdown(pThis->pqDA, pThis->toActShutdown)); CHKiRet(queueSettoEnq(pThis->pqDA, pThis->toEnq)); CHKiRet(queueSetEnqOnly(pThis->pqDA, pThis->bDAEnqOnly, MUTEX_ALREADY_LOCKED)); + CHKiRet(queueSetiDeqtWinFromHr(pThis->pqDA, pThis->iDeqtWinFromHr)); + CHKiRet(queueSetiDeqtWinToHr(pThis->pqDA, pThis->iDeqtWinToHr)); CHKiRet(queueSetiHighWtrMrk(pThis->pqDA, 0)); CHKiRet(queueSetiDiscardMrk(pThis->pqDA, 0)); if(pThis->toQShutdown == 0) { @@ -1268,6 +1272,7 @@ rsRetVal queueConstruct(queue_t **ppThis, queueType_t qType, int iWorkerThreads, pThis->iMaxQueueSize = iMaxQueueSize; pThis->pConsumer = pConsumer; pThis->iNumWorkerThreads = iWorkerThreads; + pThis->iDeqtWinToHr = 25; /* disable time-windowed dequeuing by default */ pThis->pszFilePrefix = NULL; pThis->qType = qType; @@ -1412,12 +1417,10 @@ queueDequeueConsumable(queue_t *pThis, wti_t *pWti, int iCancelStateSave) * on the nail [exact value]) -- rgerhards, 2008-03-14 */ if(iQueueSize < pThis->iFullDlyMrk) { -dbgoprint((obj_t*) pThis, "queue size %d below FullDlyMrk %d\n", iQueueSize, pThis->iFullDlyMrk); pthread_cond_broadcast(&pThis->belowFullDlyWtrMrk); } if(iQueueSize < pThis->iLightDlyMrk) { -dbgoprint((obj_t*) pThis, "queue size %d below LightDlyMrk %d\n", iQueueSize, pThis->iLightDlyMrk); pthread_cond_broadcast(&pThis->belowLightDlyWtrMrk); } @@ -1451,6 +1454,104 @@ finalize_it: } +/* The rate limiter + * + * Here we may wait if a dequeue time window is defined or if we are + * rate-limited. TODO: If we do so, we should also look into the + * way new worker threads are spawned. Obviously, it doesn't make much + * sense to spawn additional worker threads when none of them can do any + * processing. However, it is deemed acceptable to allow this for an initial + * implementation of the timeframe/rate limiting feature. + * Please also note that these feature could also be implemented at the action + * level. However, that would limit them to be used together with actions. We have + * taken the broader approach, moving it right into the queue. This is even + * necessary if we want to prevent spawning of multiple unnecessary worker + * threads as described above. -- rgerhards, 2008-04-02 + * + * + * time window: tCurr is current time; tFrom is start time, tTo is end time (in mil 24h format). + * We may have tFrom = 4, tTo = 10 --> run from 4 to 10 hrs. nice and happy + * we may also have tFrom= 22, tTo = 4 -> run from 10pm to 4am, which is actually two + * windows: 0-4; 22-23:59 + * so when to run? Let's assume we have 3am + * + * if(tTo < tFrom) { + * if(tCurr < tTo [3 < 4] || tCurr > tFrom [3 > 22]) + * do work + * else + * sleep for tFrom - tCurr "hours" [22 - 5 --> 17] + * } else { + * if(tCurr >= tFrom [3 >= 4] && tCurr < tTo [3 < 10]) + * do work + * else + * sleep for tTo - tCurr "hours" [4 - 3 --> 1] + * } + * + * Bottom line: we need to check which type of window we have and need to adjust our + * logic accordingly. Of course, sleep calculations need to be done up to the minute, + * but you get the idea from the code above. + */ +static rsRetVal +queueRateLimiter(queue_t *pThis) +{ + DEFiRet; + int iDelay; + int iHrCurr; + time_t tCurr; + struct tm m; + + ISOBJ_TYPE_assert(pThis, queue); + + dbgoprint((obj_t*) pThis, "entering rate limiter\n"); + + iDelay = 0; + if(pThis->iDeqtWinToHr != 25) { /* 25 means disabled */ + /* time calls are expensive, so only do them when needed */ + time(&tCurr); + localtime_r(&tCurr, &m); + iHrCurr = m.tm_hour; + + if(pThis->iDeqtWinToHr < pThis->iDeqtWinFromHr) { + if(iHrCurr < pThis->iDeqtWinToHr || iHrCurr > pThis->iDeqtWinFromHr) { + ; /* do not delay */ + } else { + iDelay = (pThis->iDeqtWinFromHr - iHrCurr) * 3600; + /* this time, we are already into the next hour, so we need + * to subtract our current minute and seconds. + */ + iDelay -= m.tm_min * 60; + iDelay -= m.tm_sec; + } + } else { + if(iHrCurr >= pThis->iDeqtWinFromHr && iHrCurr < pThis->iDeqtWinToHr) { + ; /* do not delay */ + } else { + if(iHrCurr < pThis->iDeqtWinFromHr) { + iDelay = (pThis->iDeqtWinFromHr - iHrCurr - 1) * 3600; /* -1 as we are already in the hour */ + iDelay += (60 - m.tm_min) * 60; + iDelay += 60 - m.tm_sec; + } else { + iDelay = (24 - iHrCurr + pThis->iDeqtWinFromHr) * 3600; + /* this time, we are already into the next hour, so we need + * to subtract our current minute and seconds. + */ + iDelay -= m.tm_min * 60; + iDelay -= m.tm_sec; + } + } + } + } + + if(iDelay > 0) { + dbgoprint((obj_t*) pThis, "outside dequeue time window, delaying %d seconds\n", iDelay); + srSleep(iDelay, 0); + } + + RETiRet; +} + + + /* This is the queue consumer in the regular (non-DA) case. It is * protected by the queue mutex, but MUST release it as soon as possible. * rgerhards, 2008-01-21 @@ -1691,6 +1792,7 @@ rsRetVal queueStart(queue_t *pThis) /* this is the ConstructionFinalizer */ lenBuf = snprintf((char*)pszBuf, sizeof(pszBuf), "%s:Reg", obj.GetName((obj_t*) pThis)); CHKiRet(wtpConstruct (&pThis->pWtpReg)); CHKiRet(wtpSetDbgHdr (pThis->pWtpReg, pszBuf, lenBuf)); + CHKiRet(wtpSetpfRateLimiter (pThis->pWtpReg, (rsRetVal (*)(void *pUsr)) queueRateLimiter)); CHKiRet(wtpSetpfChkStopWrkr (pThis->pWtpReg, (rsRetVal (*)(void *pUsr, int)) queueChkStopWrkrReg)); CHKiRet(wtpSetpfIsIdle (pThis->pWtpReg, (rsRetVal (*)(void *pUsr, int)) queueIsIdleReg)); CHKiRet(wtpSetpfDoWork (pThis->pWtpReg, (rsRetVal (*)(void *pUsr, void *pWti, int)) queueConsumerReg)); @@ -2152,6 +2254,8 @@ finalize_it: /* some simple object access methods */ DEFpropSetMeth(queue, iPersistUpdCnt, int); +DEFpropSetMeth(queue, iDeqtWinFromHr, int); +DEFpropSetMeth(queue, iDeqtWinToHr, int); DEFpropSetMeth(queue, toQShutdown, long); DEFpropSetMeth(queue, toActShutdown, long); DEFpropSetMeth(queue, toWrkShutdown, long); @@ -2214,6 +2318,5 @@ BEGINObjClassInit(queue, 1, OBJ_IS_CORE_MODULE) OBJSetMethodHandler(objMethod_SETPROPERTY, queueSetProperty); ENDObjClassInit(queue) -/* - * vi:set ai: +/* vi:set ai: */ @@ -83,9 +83,20 @@ typedef struct queue_s { int toActShutdown; /* timeout for long-running action shutdown in ms */ int toWrkShutdown; /* timeout for idle workers in ms, -1 means indefinite (0 is immediate) */ int toEnq; /* enqueue timeout */ - /* rate limiting settings (will be expanded */ + /* rate limiting settings (will be expanded) */ int iDeqSlowdown; /* slow down dequeue by specified nbr of microseconds */ /* end rate limiting */ + /* dequeue time window settings (may also be expanded) */ + int iDeqtWinFromHr; /* begin of dequeue time window (hour only) */ + int iDeqtWinToHr; /* end of dequeue time window (hour only), set to 25 to disable deq window! */ + /* note that begin and end have specific semantics. It is a big difference if we have + * begin 4, end 22 or begin 22, end 4. In the later case, dequeuing will run from 10p, + * throughout the night and stop at 4 in the morning. In the first case, it will start + * at 4am, run throughout the day, and stop at 10 in the evening! So far, not logic is + * applied to detect user configuration errors (and tell me how should we detect what + * the user really wanted...). -- rgerhards, 2008-04-02 + */ + /* ane dequeue time window */ rsRetVal (*pConsumer)(void *,void*); /* user-supplied consumer function for dequeued messages */ /* calling interface for pConsumer: arg1 is the global user pointer from this structure, arg2 is the * user pointer that was dequeued (actual sample: for actions, arg1 is the pAction and arg2 is pointer @@ -174,6 +185,8 @@ rsRetVal queueConstruct(queue_t **ppThis, queueType_t qType, int iWorkerThreads, int iMaxQueueSize, rsRetVal (*pConsumer)(void*,void*)); PROTOTYPEObjClassInit(queue); PROTOTYPEpropSetMeth(queue, iPersistUpdCnt, int); +PROTOTYPEpropSetMeth(queue, iDeqtWinFromHr, int); +PROTOTYPEpropSetMeth(queue, iDeqtWinToHr, int); PROTOTYPEpropSetMeth(queue, toQShutdown, long); PROTOTYPEpropSetMeth(queue, toActShutdown, long); PROTOTYPEpropSetMeth(queue, toWrkShutdown, long); diff --git a/rsyslog.conf b/rsyslog.conf index 2ff7d271..9a91823c 100644 --- a/rsyslog.conf +++ b/rsyslog.conf @@ -41,7 +41,7 @@ local7.* /var/log/boot.log #$ActionQueueMaxDiskSpace 1g # 1gb space limit (use as much as possible) #$ActionQueueSaveOnShutdown on # save messages to disk on shutdown #$ActionQueueType LinkedList # run asynchronously -#$ActionResumeRetryCount -1 # infinety retries if host is down +#$ActionResumeRetryCount -1 # infinite retries if host is down # remote host is: name/ip:port, e.g. 192.168.0.1:514, port optional #*.* @@remote-host diff --git a/rsyslog.conf.5 b/rsyslog.conf.5 index 4fa98ef2..1c47f535 100644 --- a/rsyslog.conf.5 +++ b/rsyslog.conf.5 @@ -17,7 +17,7 @@ .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. .\" -.TH RSYSLOG.CONF 5 "07 April 2008" "Version 3.15.1" "Linux System Administration" +.TH RSYSLOG.CONF 5 "07 April 2008" "Version 3.17.0" "Linux System Administration" .SH NAME rsyslog.conf \- rsyslogd(8) configuration file .SH DESCRIPTION @@ -167,6 +167,10 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_MODULE_LOAD_ERR_DLOPEN = -2066, /**< module could not be loaded - problem in dlopen() */ RS_RET_MODULE_LOAD_ERR_NO_INIT = -2067, /**< module could not be loaded - init() missing */ RS_RET_MODULE_LOAD_ERR_INIT_FAILED = -2068, /**< module could not be loaded - init() failed */ + RS_RET_NO_SOCKET = -2069, /**< socket could not be obtained or was not provided */ + RS_RET_SMTP_ERROR = -2070, /**< error during SMTP transation */ + RS_RET_MAIL_NO_TO = -2071, /**< recipient for mail destination is missing */ + RS_RET_MAIL_NO_FROM = -2072, /**< sender for mail destination is missing */ /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ @@ -1,7 +1,7 @@ .\" Copyright 2004-2008 Rainer Gerhards and Adiscon for the rsyslog modifications .\" May be distributed under the GNU General Public License .\" -.TH RSYSLOGD 8 "02 April 2008" "Version 3.14.0" "Linux System Administration" +.TH RSYSLOGD 8 "07 April 2008" "Version 3.17.0" "Linux System Administration" .SH NAME rsyslogd \- reliable and extended syslogd .SH SYNOPSIS diff --git a/stringbuf.h b/stringbuf.h index 0d617699..e44e86e1 100644 --- a/stringbuf.h +++ b/stringbuf.h @@ -121,6 +121,7 @@ void rsCStrSetAllocIncrement(cstr_t *pThis, int iNewIncrement); rsRetVal rsCStrAppendInt(cstr_t *pThis, long i); +rsRetVal strExit(void); /* TODO: remove once we have a real object interface! */ uchar* rsCStrGetSzStr(cstr_t *pThis); uchar* rsCStrGetSzStrNoNULL(cstr_t *pThis); rsRetVal rsCStrSetSzStr(cstr_t *pThis, uchar *pszNew); @@ -301,10 +301,12 @@ static uchar cCCEscapeChar = '\\';/* character to be used to start an escape seq static int bEscapeCCOnRcv = 1; /* escape control characters on reception: 0 - no, 1 - yes */ int bReduceRepeatMsgs; /* reduce repeated message - 0 - no, 1 - yes */ int bActExecWhenPrevSusp; /* execute action only when previous one was suspended? */ +int iActExecOnceInterval = 0; /* execute action once every nn seconds */ uchar *pszWorkDir = NULL;/* name of rsyslog's spool directory (without trailing slash) */ +uchar *glblModPath = NULL; /* module load path - only used during initial init, only settable via -M command line option */ /* end global config file state variables */ -char LocalHostName[MAXHOSTNAMELEN+1];/* our hostname - read-only after startup */ +uchar *LocalHostName;/* our hostname - read-only after startup */ char *LocalDomain; /* our local domain name - read-only after startup */ int MarkInterval = 20 * 60; /* interval between marks in seconds - read-only after startup */ int family = PF_UNSPEC; /* protocol family (IPv4, IPv6 or both), set via cmdline */ @@ -338,8 +340,10 @@ 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 */ +static int bMainMsgQSaveOnShutdown = 1; /* save queue on shutdown (when DA enabled)? */ +static int iMainMsgQueueDeqtWinFromHr = 0; /* hour begin of time frame when queue is to be dequeued */ +static int iMainMsgQueueDeqtWinToHr = 25; /* hour begin of time frame when queue is to be dequeued */ /* support for simple textual representation of FIOP names @@ -376,6 +380,7 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a { cCCEscapeChar = '#'; bActExecWhenPrevSusp = 0; + iActExecOnceInterval = 0; bDebugPrintTemplateList = 1; bDebugPrintCfSysLineHandlerList = 1; bDebugPrintModuleList = 1; @@ -892,8 +897,8 @@ logmsgInternal(int pri, char *msg, int flags) CHKiRet(msgConstruct(&pMsg)); MsgSetUxTradMsg(pMsg, msg); MsgSetRawMsg(pMsg, msg); - MsgSetHOSTNAME(pMsg, LocalHostName); - MsgSetRcvFrom(pMsg, LocalHostName); + MsgSetHOSTNAME(pMsg, (char*)LocalHostName); + MsgSetRcvFrom(pMsg, (char*)LocalHostName); MsgSetTAG(pMsg, "rsyslogd:"); pMsg->iFacility = LOG_FAC(pri); pMsg->iSeverity = LOG_PRI(pri); @@ -1385,11 +1390,18 @@ static int parseLegacySyslogMsg(msg_t *pMsg, int flags) /* Check to see if msg contains a timestamp. We stary trying with a * high-precision one... */ - if(datetime.ParseTIMESTAMP3339(&(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) + } else if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), p2parse) == TRUE) { p2parse += 16; - else { + } else if(*p2parse == ' ') { /* try to see if it is slighly malformed - HP procurve seems to do that sometimes */ + if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), p2parse+1) == TRUE) { + /* indeed, we got it! */ + p2parse += 17; + } else { + flags |= ADDDATE; + } + } else { flags |= ADDDATE; } @@ -1858,6 +1870,8 @@ freeAllDynMemForTermination(void) free(pszMainMsgQFName); if(pModDir != NULL) free(pModDir); + if(LocalHostName != NULL) + free(LocalHostName); } @@ -2197,8 +2211,7 @@ init(void) pDfltProgNameCmp = NULL; eDfltHostnameCmpMode = HN_NO_COMP; - dbgprintf("rsyslog %s.\n", VERSION); - dbgprintf("Called init.\n"); + dbgprintf("rsyslog %s - called init()\n", VERSION); /* delete the message queue, which also flushes all messages left over */ if(pMsgQueue != NULL) { @@ -2321,6 +2334,8 @@ init(void) setQPROP(queueSetiMinMsgsPerWrkr, "$MainMsgQueueWorkerThreadMinimumMessages", iMainMsgQWrkMinMsgs); setQPROP(queueSetbSaveOnShutdown, "$MainMsgQueueSaveOnShutdown", bMainMsgQSaveOnShutdown); setQPROP(queueSetiDeqSlowdown, "$MainMsgQueueDequeueSlowdown", iMainMsgQDeqSlowdown); + setQPROP(queueSetiDeqtWinFromHr, "$MainMsgQueueDequeueTimeBegin", iMainMsgQueueDeqtWinFromHr); + setQPROP(queueSetiDeqtWinToHr, "$MainMsgQueueDequeueTimeEnd", iMainMsgQueueDeqtWinToHr); # undef setQPROP # undef setQPROPstr @@ -2685,8 +2700,11 @@ static rsRetVal loadBuildInModules(void) 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 *)"mainmsgqueuedequeuetimebegin", 0, eCmdHdlrInt, NULL, &iMainMsgQueueDeqtWinFromHr, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuedequeuetimeend", 0, eCmdHdlrInt, NULL, &iMainMsgQueueDeqtWinToHr, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"repeatedmsgreduction", 0, eCmdHdlrBinary, NULL, &bReduceRepeatMsgs, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"actionexeconlywhenpreviousissuspended", 0, eCmdHdlrBinary, NULL, &bActExecWhenPrevSusp, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"actionexeconlyonceeveryinterval", 0, eCmdHdlrInt, NULL, &iActExecOnceInterval, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"actionresumeinterval", 0, eCmdHdlrInt, setActionResumeInterval, NULL, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"controlcharacterescapeprefix", 0, eCmdHdlrGetChar, NULL, &cCCEscapeChar, NULL)); CHKiRet(regCfSysLineHdlr((uchar *)"escapecontrolcharactersonreceive", 0, eCmdHdlrBinary, NULL, &bEscapeCCOnRcv, NULL)); @@ -2764,21 +2782,6 @@ static void mainThread() 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", - 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 */ - } -#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 */ @@ -2803,7 +2806,6 @@ static void mainThread() pTmp = template_StdPgSQLFmt; tplLastStaticInit(tplAddLine(" StdPgSQLFmt", &pTmp)); - dbgprintf("Starting.\n"); init(); if(Debug) { dbgprintf("Debugging enabled, SIGUSR1 to turn off debugging.\n"); @@ -2820,6 +2822,7 @@ static void mainThread() * do the init() and then restart things. * rgerhards, 2005-10-24 */ + dbgprintf("initialization completed, transitioning to regular run mode\n"); mainloop(); ENDfunc @@ -2971,6 +2974,72 @@ GlobalClassExit(void) } +/* some support for command line option parsing. Any non-trivial options must be + * buffered until the complete command line has been parsed. This is necessary to + * prevent dependencies between the options. That, in turn, means we need to have + * something that is capable of buffering options and there values. The follwing + * functions handle that. + * rgerhards, 2008-04-04 + */ +typedef struct bufOpt { + struct bufOpt *pNext; + char optchar; + char *arg; +} bufOpt_t; +static bufOpt_t *bufOptRoot = NULL; +static bufOpt_t *bufOptLast = NULL; + +/* add option buffer */ +static rsRetVal +bufOptAdd(char opt, char *arg) +{ + DEFiRet; + bufOpt_t *pBuf; + + if((pBuf = malloc(sizeof(bufOpt_t))) == NULL) + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + + pBuf->optchar = opt; + pBuf->arg = arg; + pBuf->pNext = NULL; + + if(bufOptLast == NULL) { + bufOptRoot = pBuf; /* then there is also no root! */ + } else { + bufOptLast->pNext = pBuf; + } + bufOptLast = pBuf; + +finalize_it: + RETiRet; +} + + + +/* remove option buffer from top of list, return values and destruct buffer itself. + * returns RS_RET_END_OF_LINKEDLIST when no more options are present. + * (we use int *opt instead of char *opt to keep consistent with getopt()) + */ +static rsRetVal +bufOptRemove(int *opt, char **arg) +{ + DEFiRet; + bufOpt_t *pBuf; + + if(bufOptRoot == NULL) + ABORT_FINALIZE(RS_RET_END_OF_LINKEDLIST); + pBuf = bufOptRoot; + + *opt = pBuf->optchar; + *arg = pBuf->arg; + + bufOptRoot = pBuf->pNext; + free(pBuf); + +finalize_it: + RETiRet; +} + /* This is the main entry point into rsyslogd. Over time, we should try to * modularize it a bit more... @@ -2990,15 +3059,125 @@ int realMain(int argc, char **argv) int bIsFirstOption = 1; int bEOptionWasGiven = 0; int bImUxSockLoaded = 0; /* already generated a $ModLoad imuxsock? */ + char *arg; /* for command line option processing */ uchar legacyConfLine[80]; - gethostname(LocalHostName, sizeof(LocalHostName)); - if ( (p = strchr(LocalHostName, '.')) ) { + /* first, parse the command line options. We do not carry out any actual work, just + * see what we should do. This relieves us from certain anomalies and we can process + * the parameters down below in the correct order. For example, we must know the + * value of -M before we can do the init, but at the same time we need to have + * the base classes init before we can process most of the options. Now, with the + * split of functionality, this is no longer a problem. Thanks to varmofekoj for + * suggesting this algo. + * Note: where we just need to set some flags and can do so without knowledge + * of other options, we do this during the inital option processing. With later + * versions (if a dependency on -c option is introduced), we must move that code + * to other places, but I think it is quite appropriate and saves code to do this + * only when actually neeeded. + * rgerhards, 2008-04-04 + */ + while ((ch = getopt(argc, argv, "46aAc:def:g:hi:l:m:M:nopqQr::s:t:u:vwx")) != EOF) { + switch((char)ch) { + case '4': + case '6': + case 'A': + case 'a': + case 'f': /* configuration file */ + case 'h': + case 'i': /* pid file name */ + case 'l': + case 'm': /* mark interval */ + case 'n': /* don't fork */ + case 'o': + case 'p': + case 'q': /* add hostname if DNS resolving has failed */ + case 'Q': /* dont resolve hostnames in ACL to IPs */ + case 's': + case 'u': /* misc user settings */ + case 'w': /* disable disallowed host warnigs */ + case 'x': /* disable dns for remote messages */ + CHKiRet(bufOptAdd(ch, optarg)); + 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 - must be handled now, so that debug is active during init! */ + Debug = 1; + break; + case 'e': /* log every message (no repeat message supression) */ + fprintf(stderr, "note: -e option is no longer supported, every message is now logged by default\n"); + bEOptionWasGiven = 1; + break; + case 'g': /* enable tcp gssapi logging */ +#if defined(SYSLOG_INET) && defined(USE_GSSAPI) + CHKiRet(bufOptAdd('g', optarg)); +#else + fprintf(stderr, "rsyslogd: -g not valid - not compiled with gssapi support"); +#endif + break; + case 'M': /* default module load path -- this MUST be carried out immediately! */ + glblModPath = (uchar*) optarg; + break; + case 'r': /* accept remote messages */ +#ifdef SYSLOG_INET + CHKiRet(bufOptAdd(ch, optarg)); +#else + fprintf(stderr, "rsyslogd: -r not valid - not compiled with network support\n"); +#endif + break; + case 't': /* enable tcp logging */ +#ifdef SYSLOG_INET + CHKiRet(bufOptAdd(ch, optarg)); +#else + fprintf(stderr, "rsyslogd: -t not valid - not compiled with network support\n"); +#endif + break; + case 'v': /* MUST be carried out immediately! */ + printVersion(); + exit(0); /* exit for -v option - so this is a "good one" */ + case '?': + default: + usage(); + } + bIsFirstOption = 0; /* we already saw an option character */ + } + + if ((argc -= optind)) + usage(); + + dbgprintf("rsyslogd %s startup, compatibility mode %d, module path '%s'\n", + VERSION, iCompatibilityMode, glblModPath == NULL ? "" : (char*)glblModPath); + + /* we are done with the initial option parsing and processing. Now we init the system. */ + + ppid = getpid(); + + if(chdir ("/") != 0) + fprintf(stderr, "Can not do 'cd /' - still trying to run\n"); + + CHKiRet_Hdlr(InitGlobalClasses()) { + fprintf(stderr, "rsyslogd initializiation failed - global classes could not be initialized.\n" + "Did you do a \"make install\"?\n" + "Suggested action: run rsyslogd with -d -n options to see what exactly " + "fails.\n"); + FINALIZE; + } + + /* doing some core initializations */ + + /* get our host and domain names - we need to do this early as we may emit + * error log messages, which need the correct hostname. -- rgerhards, 2008-04-04 + */ + net.getLocalHostname(&LocalHostName); + if((p = strchr((char*)LocalHostName, '.'))) { *p++ = '\0'; LocalDomain = p; - } - else - { + } else { LocalDomain = ""; /* It's not clearly defined whether gethostname() @@ -3014,11 +3193,12 @@ int realMain(int argc, char **argv) /* 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); + hent = gethostbyname((char*)LocalHostName); if(hent) { - snprintf(LocalHostName, sizeof(LocalHostName), "%s", hent->h_name); + free(LocalHostName); + CHKmalloc(LocalHostName = (uchar*)strdup(hent->h_name)); - if ( (p = strchr(LocalHostName, '.')) ) + if((p = strchr((char*)LocalHostName, '.'))) { *p++ = '\0'; LocalDomain = p; @@ -3026,21 +3206,11 @@ int realMain(int argc, char **argv) } } - /* 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); + /* Convert to lower case to recognize the correct domain laterly */ + for (p = (char *)LocalDomain ; *p ; p++) + *p = (char)tolower((int)*p); - CHKiRet_Hdlr(InitGlobalClasses()) { - fprintf(stderr, "rsyslogd initializiation failed - global classes could not be initialized.\n" - "Did you do a \"make install\"?\n" - "Suggested action: run rsyslogd with -d -n options to see what exactly " - "fails.\n"); - FINALIZE; - } - - /* doing some core initializations */ + /* initialize the objects */ if((iRet = modInitIminternal()) != RS_RET_OK) { fprintf(stderr, "fatal error: could not initialize errbuf object (error code %d).\n", iRet); @@ -3053,14 +3223,10 @@ int realMain(int argc, char **argv) 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"); - - /* END core initializations */ + /* END core initializations - we now come back to carrying out command line options*/ - while ((ch = getopt(argc, argv, "46aAc:def:g:hi:l:m:M:nopqQr::s:t:u:vwx")) != EOF) { + while((iRet = bufOptRemove(&ch, &arg)) == RS_RET_OK) { + dbgprintf("deque option %c, optarg '%s'\n", ch, arg); switch((char)ch) { case '4': family = PF_INET; @@ -3077,39 +3243,20 @@ int realMain(int argc, char **argv) legacyOptsEnq((uchar *) "ModLoad imuxsock"); bImUxSockLoaded = 1; } - snprintf((char *) legacyConfLine, sizeof(legacyConfLine), "addunixlistensocket %s", optarg); + snprintf((char *) legacyConfLine, sizeof(legacyConfLine), "addunixlistensocket %s", arg); 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) */ - 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; + ConfFile = (uchar*) arg; break; case 'g': /* enable tcp gssapi logging */ -#if defined(SYSLOG_INET) && defined(USE_GSSAPI) if(iCompatibilityMode < 3) { - legacyOptsParseTCP(ch, optarg); + legacyOptsParseTCP(ch, arg); } 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': if(iCompatibilityMode < 3) { @@ -3119,25 +3266,22 @@ int realMain(int argc, char **argv) } break; case 'i': /* pid file name */ - PidFile = optarg; + PidFile = arg; break; case 'l': if (LocalHosts) { fprintf (stderr, "rsyslogd: Only one -l argument allowed, the first one is taken.\n"); } else { - LocalHosts = crunch_list(optarg); + LocalHosts = crunch_list(arg); } break; case 'm': /* mark interval */ if(iCompatibilityMode < 3) { - MarkInterval = atoi(optarg) * 60; + MarkInterval = atoi(arg) * 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; @@ -3158,7 +3302,7 @@ int realMain(int argc, char **argv) legacyOptsEnq((uchar *) "ModLoad imuxsock"); bImUxSockLoaded = 1; } - snprintf((char *) legacyConfLine, sizeof(legacyConfLine), "SystemLogSocketName %s", optarg); + snprintf((char *) legacyConfLine, sizeof(legacyConfLine), "SystemLogSocketName %s", arg); legacyOptsEnq(legacyConfLine); } else { fprintf(stderr, "error -p is no longer supported, use module imuxsock instead"); @@ -3170,42 +3314,30 @@ int realMain(int argc, char **argv) *net.pACLDontResolve = 1; break; case 'r': /* accept remote messages */ -#ifdef SYSLOG_INET if(iCompatibilityMode < 3) { legacyOptsEnq((uchar *) "ModLoad imudp"); - snprintf((char *) legacyConfLine, sizeof(legacyConfLine), "UDPServerRun %s", optarg); + snprintf((char *) legacyConfLine, sizeof(legacyConfLine), "UDPServerRun %s", arg); 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\n"); -#endif + fprintf(stderr, "-r option only supported in compatibility modes 0 to 2 - ignored\n"); break; case 's': if (StripDomains) { fprintf (stderr, "rsyslogd: Only one -s argument allowed, the first one is taken.\n"); } else { - StripDomains = crunch_list(optarg); + StripDomains = crunch_list(arg); } break; case 't': /* enable tcp logging */ -#ifdef SYSLOG_INET if(iCompatibilityMode < 3) { - legacyOptsParseTCP(ch, optarg); + legacyOptsParseTCP(ch, arg); } 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\n"); -#endif break; case 'u': /* misc user settings */ - if(atoi(optarg) == 1) + if(atoi(arg) == 1) bParseHOSTNAMEandTAG = 0; break; - case 'v': - printVersion(); - exit(0); /* exit for -v option - so this is a "good one" */ case 'w': /* disable disallowed host warnigs */ option_DisallowWarning = 0; break; @@ -3216,15 +3348,12 @@ int realMain(int argc, char **argv) default: usage(); } - bIsFirstOption = 0; /* we already saw an option character */ } - if ((argc -= optind)) - usage(); + if(iRet != RS_RET_END_OF_LINKEDLIST) + FINALIZE; - /* TODO: this should go away at a reasonable stage of v3 development. - * rgerhards, 2007-12-19 - */ + /* process compatibility mode settings */ 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. " @@ -3250,7 +3379,7 @@ int realMain(int argc, char **argv) checkPermissions(); thrdInit(); - if ( !(Debug || NoFork) ) + if( !(Debug || NoFork) ) { dbgprintf("Checking pidfile.\n"); if (!check_pid(PidFile)) @@ -3289,8 +3418,6 @@ int realMain(int argc, char **argv) else debugging_on = 1; - dbgprintf("Compatibility Mode: %d\n", iCompatibilityMode); - /* tuck my process id away */ dbgprintf("Writing pidfile %s.\n", PidFile); if (!check_pid(PidFile)) @@ -134,7 +134,7 @@ rsRetVal logmsgInternal(int pri, char *msg, int flags); void logmsg(msg_t *pMsg, int flags); rsRetVal submitMsg(msg_t *pMsg); extern int glblHadMemShortage; /* indicates if we had memory shortage some time during the run */ -extern char LocalHostName[]; +extern uchar *LocalHostName; extern int family; extern int NoHops; extern int send_to_all; @@ -149,6 +149,7 @@ extern char ctty[]; extern int MarkInterval; extern int bReduceRepeatMsgs; extern int bActExecWhenPrevSusp; +extern int iActExecOnceInterval; /* Intervals at which we flush out "message repeated" messages, * in seconds after previous message is logged. After each flush, @@ -141,32 +141,6 @@ configureTCPListen(tcpsrv_t *pThis, char *cOptarg) } -#if 0 // I think this is no longer needed -static void -configureTCPListenSessMax(char *cOptarg) -{ - register int i; - register char *pArg = cOptarg; - - assert(cOptarg != NULL); - - /* number of sessions */ - i = 0; - while(isdigit((int) *pArg)) { - i = i * 10 + *pArg++ - '0'; - } - - if(i > 0) - pThis->iSessMax = i; - else { - /* too small, need to adjust */ - errmsg.LogError(NO_ERRCODE, "TCP session max configured to %s - changing to 1.\n", cOptarg); - pThis->iSessMax = 1; - } -} -#endif - - /* Initialize the session table * returns 0 if OK, somewhat else otherwise */ @@ -188,11 +188,13 @@ static void doSQLEmergencyEscape(register uchar *p, int escapeMode) * new parameter escapeMode is 0 - standard sql, 1 - "smart" engines * 2005-09-22 rgerhards */ -void doSQLEscape(uchar **pp, size_t *pLen, unsigned short *pbMustBeFreed, int escapeMode) +rsRetVal +doSQLEscape(uchar **pp, size_t *pLen, unsigned short *pbMustBeFreed, int escapeMode) { + DEFiRet; uchar *p; int iLen; - cstr_t *pStrB; + cstr_t *pStrB = NULL; uchar *pszGenerated; assert(pp != NULL); @@ -210,44 +212,25 @@ void doSQLEscape(uchar **pp, size_t *pLen, unsigned short *pbMustBeFreed, int es /* when we get out of the loop, we are either at the * string terminator or the first \'. */ if(*p == '\0') - return; /* nothing to do in this case! */ + FINALIZE; /* nothing to do in this case! */ p = *pp; iLen = *pLen; - if(rsCStrConstruct(&pStrB) != RS_RET_OK) { - /* oops - no mem ... Do emergency... */ - doSQLEmergencyEscape(p, escapeMode); - return; - } + CHKiRet(rsCStrConstruct(&pStrB)); while(*p) { if(*p == '\'') { - if(rsCStrAppendChar(pStrB, (escapeMode == 0) ? '\'' : '\\') != RS_RET_OK) { - doSQLEmergencyEscape(*pp, escapeMode); - rsCStrDestruct(&pStrB); - return; - } + CHKiRet(rsCStrAppendChar(pStrB, (escapeMode == 0) ? '\'' : '\\')); iLen++; /* reflect the extra character */ } else if((escapeMode == 1) && (*p == '\\')) { - if(rsCStrAppendChar(pStrB, '\\') != RS_RET_OK) { - doSQLEmergencyEscape(*pp, escapeMode); - rsCStrDestruct(&pStrB); - return; - } + CHKiRet(rsCStrAppendChar(pStrB, '\\')); iLen++; /* reflect the extra character */ } - if(rsCStrAppendChar(pStrB, *p) != RS_RET_OK) { - doSQLEmergencyEscape(*pp, escapeMode); - rsCStrDestruct(&pStrB); - return; - } + CHKiRet(rsCStrAppendChar(pStrB, *p)); ++p; } - rsCStrFinish(pStrB); - if(rsCStrConvSzStrAndDestruct(pStrB, &pszGenerated, 0) != RS_RET_OK) { - doSQLEmergencyEscape(*pp, escapeMode); - return; - } + CHKiRet(rsCStrFinish(pStrB)); + CHKiRet(rsCStrConvSzStrAndDestruct(pStrB, &pszGenerated, 0)); if(*pbMustBeFreed) free(*pp); /* discard previous value */ @@ -255,6 +238,15 @@ void doSQLEscape(uchar **pp, size_t *pLen, unsigned short *pbMustBeFreed, int es *pp = pszGenerated; *pLen = iLen; *pbMustBeFreed = 1; + +finalize_it: + if(iRet != RS_RET_OK) { + doSQLEmergencyEscape(*pp, escapeMode); + if(pStrB != NULL) + rsCStrDestruct(&pStrB); + } + + RETiRet; } @@ -385,7 +377,6 @@ static int do_Constant(unsigned char **pp, struct template *pTpl) if((pTpe = tpeConstruct(pTpl)) == NULL) { /* OK, we are out of luck. Let's invalidate the * entry and that's it. - * TODO: add panic message once we have a mechanism for this */ pTpe->eEntryType = UNDEFINED; return 1; @@ -510,7 +501,8 @@ static int do_Parameter(unsigned char **pp, struct template *pTpl) pTpe->eEntryType = FIELD; while(*p && *p != '%' && *p != ':') { - rsCStrAppendChar(pStrB, *p++); + rsCStrAppendChar(pStrB, tolower(*p)); + ++p; /* do NOT do this in tolower()! */ } /* got the name*/ @@ -109,7 +109,7 @@ void tplLastStaticInit(struct template *tpl); * rgerhards, 2007-08-06 */ rsRetVal tplToString(struct template *pTpl, msg_t *pMsg, uchar** ppSz); -void doSQLEscape(uchar **pp, size_t *pLen, unsigned short *pbMustBeFreed, int escapeMode); +rsRetVal doSQLEscape(uchar **pp, size_t *pLen, unsigned short *pbMustBeFreed, int escapeMode); rsRetVal templateInit(); @@ -371,6 +371,14 @@ wtiWorker(wti_t *pThis) pthread_yield(); /* see big comment in function header */ # endif + /* if we have a rate-limiter set for this worker pool, let's call it. Please + * keep in mind that the rate-limiter may hold us for an extended period + * of time. -- rgerhards, 2008-04-02 + */ + if(pWtp->pfRateLimiter != NULL) { + pWtp->pfRateLimiter(pWtp->pUsr); + } + wtpSetInactivityGuard(pThis->pWtp, 0, LOCK_MUTEX); /* must be set before usr mutex is locked! */ BEGIN_MTX_PROTECTED_OPERATIONS(pWtp->pmutUsr, LOCK_MUTEX); @@ -546,6 +546,7 @@ DEFpropSetMeth(wtp, pUsr, void*); DEFpropSetMethPTR(wtp, pmutUsr, pthread_mutex_t); DEFpropSetMethPTR(wtp, pcondBusy, pthread_cond_t); DEFpropSetMethFP(wtp, pfChkStopWrkr, rsRetVal(*pVal)(void*, int)); +DEFpropSetMethFP(wtp, pfRateLimiter, rsRetVal(*pVal)(void*)); DEFpropSetMethFP(wtp, pfIsIdle, rsRetVal(*pVal)(void*, int)); DEFpropSetMethFP(wtp, pfDoWork, rsRetVal(*pVal)(void*, void*, int)); DEFpropSetMethFP(wtp, pfOnIdle, rsRetVal(*pVal)(void*, int)); @@ -69,6 +69,7 @@ typedef struct wtp_s { pthread_mutex_t *pmutUsr; pthread_cond_t *pcondBusy; /* condition the user will signal "busy again, keep runing" on (awakes worker) */ rsRetVal (*pfChkStopWrkr)(void *pUsr, int); + rsRetVal (*pfRateLimiter)(void *pUsr); rsRetVal (*pfIsIdle)(void *pUsr, int); rsRetVal (*pfDoWork)(void *pUsr, void *pWti, int); rsRetVal (*pfOnIdle)(void *pUsr, int); @@ -100,6 +101,7 @@ rsRetVal wtpShutdownAll(wtp_t *pThis, wtpState_t tShutdownCmd, struct timespec * int wtpGetCurNumWrkr(wtp_t *pThis, int bLockMutex); PROTOTYPEObjClassInit(wtp); PROTOTYPEpropSetMethFP(wtp, pfChkStopWrkr, rsRetVal(*pVal)(void*, int)); +PROTOTYPEpropSetMethFP(wtp, pfRateLimiter, rsRetVal(*pVal)(void*)); PROTOTYPEpropSetMethFP(wtp, pfIsIdle, rsRetVal(*pVal)(void*, int)); PROTOTYPEpropSetMethFP(wtp, pfDoWork, rsRetVal(*pVal)(void*, void*, int)); PROTOTYPEpropSetMethFP(wtp, pfOnIdle, rsRetVal(*pVal)(void*, int)); |