summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog26
-rw-r--r--Makefile.am4
-rw-r--r--action.c32
-rw-r--r--action.h4
-rw-r--r--conf.c4
-rw-r--r--configure.ac56
-rw-r--r--doc/features.html10
-rw-r--r--doc/ommail.html123
-rw-r--r--doc/property_replacer.html46
-rw-r--r--doc/queues.html12
-rw-r--r--doc/rsyslog_conf.html79
-rw-r--r--doc/rsyslog_ng_comparison.html24
-rw-r--r--doc/status.html2
-rw-r--r--glbl.h1
-rw-r--r--modules.c11
-rw-r--r--msg.c42
-rw-r--r--obj.c7
-rw-r--r--parse.c5
-rw-r--r--parse.h2
-rw-r--r--plugins/ommail/Makefile.am6
-rw-r--r--plugins/ommail/ommail.c630
-rw-r--r--queue.c135
-rw-r--r--queue.h15
-rw-r--r--rsyslog.conf2
-rw-r--r--rsyslog.conf.52
-rw-r--r--rsyslog.h4
-rw-r--r--rsyslogd.82
-rw-r--r--stringbuf.h1
-rw-r--r--syslogd.c315
-rw-r--r--syslogd.h1
-rw-r--r--tcpsrv.c26
-rw-r--r--template.c4
-rw-r--r--wti.c8
-rw-r--r--wtp.c1
-rw-r--r--wtp.h2
35 files changed, 1402 insertions, 242 deletions
diff --git a/ChangeLog b/ChangeLog
index fe0fbdd3..d8566e45 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,30 @@
---------------------------------------------------------------------------
+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
+---------------------------------------------------------------------------
Version 3.15.1 (rgerhards), 2008-04-??
- bugfix: some messages were emited without hostname
- disabled atomic operations for the time being because they introduce some
diff --git a/Makefile.am b/Makefile.am
index f97687c1..6aea7600 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -217,3 +217,7 @@ endif
if ENABLE_IMFILE
SUBDIRS += plugins/imfile
endif
+
+if ENABLE_MAIL
+SUBDIRS += plugins/ommail
+endif
diff --git a/action.c b/action.c
index 99ae8b32..39c37b5b 100644
--- a/action.c
+++ b/action.c
@@ -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:
*/
diff --git a/action.h b/action.h
index 1fa05c15..99108aab 100644
--- a/action.h
+++ b/action.h
@@ -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? */
diff --git a/conf.c b/conf.c
index f87ab992..dac46970 100644
--- a/conf.c
+++ b/conf.c
@@ -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 c58c2173..a938eae2 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.1],[rsyslog@lists.adiscon.com])
+AC_INIT([rsyslog],[3.17.0],[rsyslog@lists.adiscon.com])
AM_INIT_AUTOMAKE
AC_CONFIG_SRCDIR([syslogd.c])
AC_CONFIG_HEADERS([config.h])
@@ -413,6 +413,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 +491,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 +515,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 +611,7 @@ AC_CONFIG_FILES([Makefile \
plugins/ompgsql/Makefile \
plugins/omrelp/Makefile \
plugins/omlibdbi/Makefile \
+ plugins/ommail/Makefile \
plugins/omsnmp/Makefile])
AC_OUTPUT
@@ -576,6 +626,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 +635,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
new file mode 100644
index 00000000..74fab739
--- /dev/null
+++ b/doc/ommail.html
@@ -0,0 +1,123 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><title>mail output module - sending syslog messages via mail</title>
+
+</head>
+<body>
+<h1>Mail Output Module (ommail)</h1>
+<p><b>Module Name:&nbsp;&nbsp;&nbsp; ommail</b></p>
+<p><b>Author: </b>Rainer Gerhards
+&lt;rgerhards@adiscon.com&gt;</p>
+<p><b>Description</b>:</p>
+<p>This module supports sending syslog messages via mail. Each
+syslog message is sent via its own mail. Obviously, you will want to
+apply rigorous filtering, otherwise your mailbox (and mail server) will
+be heavily spammed. The ommail plugin is primarily meant for alerting
+users. As such, it is assume that mails will only be sent in an
+extremely limited number of cases.</p>
+<p>Please note that ommail is especially well-suited to work in
+tandem with <a href="imfile.html">imfile</a> to
+watch files for the occurence of specific things to be alerted on. So
+its scope is far broader than forwarding syslog messages to mail
+recipients.</p>
+Ommail uses two templates, one for the mail body and one for the
+subject line. If neither is provided, a quite meaningless subject line
+is used and the mail body will be a syslog message just as if it were
+written to a file. It is expected that the users customizes both
+messages. In an effort to support cell phones (including SMS gateways),
+there is an option to turn off the body part at all. This is considered
+to be useful to send a short alert to a pager-like device.<br>
+<br>
+It is highly recommended to use the&nbsp; "<span style="font-weight: bold;">$ActionExecOnlyOnceEveryInterval
+&lt;seconds&gt;</span>" directive to limit the amount of
+mails that potentially be generated. With it, mails are sent at most in
+a &lt;seconds&gt; interval. This may be your life safer. And
+remember that an hour has 3,600 seconds, so if you would like to
+receive mails at most once every two hours, include a
+"$ActionExecOnlyOnceEveryInterval 7200" immediately before the ommail
+action. Messages sent more frequently are simpy discarded.<span style="font-weight: bold;"></span>
+<p><b>Configuration Directives</b>:</p>
+<ul>
+<li><span style="font-weight: bold;">$ActionMailSMTPServer</span><br>
+Name or IP address of the SMTP server to be used. Must currently be
+set. The default is 127.0.0.1, the SMTP server on the local machine.
+Obviously it is not good to expect one to be present on each machine,
+so this value should be specified.<br>
+</li>
+<li><span style="font-weight: bold;">$ActionMailSMTPPort</span><br>
+Port number or name of the SMTP port to be used. The default is 25, the
+standard SMTP port.</li>
+<li><span style="font-weight: bold;">$ActionMailFrom</span><br>
+The email address used as the senders address. There is no default.</li>
+<li><span style="font-weight: bold;">$ActionMailTo</span><br>
+The recipients email address. There is no default.</li>
+<li><span style="font-weight: bold;">$ActionMailSubject</span><br>
+The name of the <span style="font-weight: bold;">template</span>
+to be used as the mail subject. If this is not specified, a more or
+less meaningless mail subject is generated (we don't tell you the exact
+text because that can change - if you want to have something specific,
+configure it!).</li>
+<li><span style="font-weight: bold;">$ActionMailEnableBody</span><br>
+Setting this to "off" permits to exclude the actual message body. This
+may be useful for pager-like devices or cell phone SMS messages. The
+default is "on", which is appropriate for allmost all cases. Turn it
+off only if you know exactly what you do!</li>
+</ul>
+<b>Caveats/Known Bugs:</b>
+<p>The current ommail implementation supports <span style="font-weight: bold;">SMTP-direct mode</span>
+only. In that mode, the plugin talks to the mail server via SMTP
+protocol. No other process is involved. This mode offers best
+reliability as it is not depending on any external entity except the
+mail server. Mail server downtime is acceptable if the action is put
+onto its own action queue, so that it may wait for the SMTP server to
+come back online. However, the module implements only the bare SMTP
+essentials. Most importantly, it does not provide any authentication
+capabilities. So your mail server must be configured to accept incoming
+mail from ommail without any authentication needs (this may be change
+in the future as need arises, but you may also be referred to
+sendmail-mode).</p>
+<p>In theory, ommail should also offer a mode where it uses the
+sendmail utility to send its mail (<span style="font-weight: bold;">sendmail-mode</span>).
+This is somewhat less reliable (because we depend on an entity we do
+not have close control over - sendmail). It also requires dramatically
+more system ressources, as we need to load the external process (but
+that should be no problem given the expected infrequent number of calls
+into this plugin). The big advantage of sendmail mode is that it
+supports all the bells and whistles of a full-blown SMTP implementation
+and may even work for local delivery without a SMTP server being
+present. Sendmail mode will be implemented as need arises. So if you
+need it, please drop us a line (I nobody does, sendmail mode will
+probably never be implemented).</p>
+<p><b>Sample:</b></p>
+<p>The following sample alerts the operator if the string "hard
+disk fatal failure" is present inside a syslog message. The mail server
+at mail.example.net is used and the subject shall be "disk problem on
+&lt;hostname&gt;". Note how \r\n is included inside the body
+text
+to create line breaks. A message is sent at most once every 6 hours,
+any other messages are silently discarded (or, to be precise, not being
+forwarded - they are still being processed by the rest of the
+configuration file).<br>
+</p>
+<textarea rows="15" cols="80">$ModLoad ommail
+$ActionMailSMTPServer mail.example.net
+$ActionMailFrom rsyslog@example.net
+$ActionMailTo operator@example.net
+$template mailSubject,"disk problem on %hostname%"
+$template mailBody,"RSYSLOG Alert\r\nmsg='%msg%'"
+$ActionMailSubject mailSubject
+# make sure we receive a mail only once in six
+# hours (21,600 seconds ;))
+$ActionExecOnlyOnceEveryInterval 21600
+# the if ... then ... mailbody mus be on one line!
+if $msg contains 'hard disk fatal failure' then :ommail:;mailBody
+</textarea>
+<p>[<a href="rsyslog_conf.html">rsyslog.conf overview</a>]
+[<a href="manual.html">manual index</a>] [<a href="http://www.rsyslog.com/">rsyslog site</a>]</p>
+<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
+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
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>$&lt;object&gt;QueueDequeueTimeBegin &lt;hour&gt;</i>" and&nbsp;"<i>$&lt;object&gt;QueueDequeueTimeEnd &lt;hour&gt;</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>
--&nbsp; input module for text files</li><li><a href="imrelp.html">imrelp</a> - RELP input module</li>
+-&nbsp; 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 &lt;seconds&gt; -
+execute action only if the last execute is at last
+&lt;seconds&gt; 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 &lt;number&gt;</li>
<li>$ActionQueueDequeueSlowdown &lt;number&gt; [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
&lt;number&gt;, default 100</li>
-<li><a href="rsconf1_actionresumeinterval.html">$ActionResumeInterval</a></li><li>$ActionResumeRetryCount &lt;number&gt; [default 0,
+<li><a href="rsconf1_actionresumeinterval.html">$ActionResumeInterval</a></li>
+<li>$ActionResumeRetryCount &lt;number&gt; [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 &lt;name&gt; (directory for spool
-and other work files)</li><li>$UDPServerAddress &lt;IP&gt; (imudp) -- local IP
+and other work files)</li>
+<li>$UDPServerAddress &lt;IP&gt; (imudp) -- local IP
address (or name) the UDP listens should bind to</li>
<li>$UDPServerRun &lt;port&gt; (imudp) -- former
-r&lt;port&gt; 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&nbsp;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&nbsp;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..5ab6ea05 100644
--- a/doc/status.html
+++ b/doc/status.html
@@ -2,7 +2,7 @@
<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-07.</p>
<h2>Current Releases</h2>
<p><b>development:</b> 3.15.0 -
diff --git a/glbl.h b/glbl.h
index fb55fb76..6d08ddd5 100644
--- a/glbl.h
+++ b/glbl.h
@@ -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)
diff --git a/modules.c b/modules.c
index 86ee64a5..623a2f10 100644
--- a/modules.c
+++ b/modules.c
@@ -783,6 +783,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)
diff --git a/msg.c b/msg.c
index 7c020a61..9a12d572 100644
--- a/msg.c
+++ b/msg.c
@@ -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**";
}
diff --git a/obj.c b/obj.c
index 2f16669a..3485803d 100644
--- a/obj.c
+++ b/obj.c
@@ -1073,7 +1073,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) {
@@ -1113,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) {
@@ -1137,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 */
@@ -1170,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 */
}
diff --git a/parse.c b/parse.c
index bca6457d..5239b540 100644
--- a/parse.c
+++ b/parse.c
@@ -235,11 +235,12 @@ 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;
@@ -256,7 +257,7 @@ rsRetVal parsDelimCStr(rsParsObj *pThis, cstr_t **ppCStr, char cDelim, int bTrim
while(pThis->iCurrPos < rsCStrLen(pThis->pCStr)
&& *pC != cDelim) {
- if((iRet = rsCStrAppendChar(pCStr, *pC)) != RS_RET_OK) {
+ if((iRet = rsCStrAppendChar(pCStr, bConvLower ? tolower(*pC) : *pC)) != RS_RET_OK) {
rsCStrDestruct(&pCStr);
FINALIZE;
}
diff --git a/parse.h b/parse.h
index 483c109b..b7ac950d 100644
--- a/parse.h
+++ b/parse.h
@@ -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/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/queue.c b/queue.c
index ed720c55..0828cc1d 100644
--- a/queue.c
+++ b/queue.c
@@ -1,16 +1,16 @@
/* queue.c
-*
-* This file implements the queue object and its several queueing methods.
-*
-* File begun on 2008-01-03 by RGerhards
-*
-* There is some in-depth documentation available in doc/dev_queue.html
-* (and in the web doc set on http://www.rsyslog.com/doc). Be sure to read it
-* if you are getting aquainted to the object.
-*
-* Copyright 2008 Rainer Gerhards and Adiscon GmbH.
-*
-* This file is part of rsyslog.
+ *
+ * This file implements the queue object and its several queueing methods.
+ *
+ * File begun on 2008-01-03 by RGerhards
+ *
+ * There is some in-depth documentation available in doc/dev_queue.html
+ * (and in the web doc set on http://www.rsyslog.com/doc). Be sure to read it
+ * if you are getting aquainted to the object.
+ *
+ * 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
@@ -38,6 +38,7 @@
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h> /* required for HP UX */
+#include <time.h>
#include <errno.h>
#include "rsyslog.h"
@@ -55,6 +56,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);
@@ -271,6 +273,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) {
@@ -1267,6 +1271,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;
@@ -1411,12 +1416,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);
}
@@ -1450,6 +1453,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
@@ -1690,6 +1791,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));
@@ -2151,6 +2253,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);
@@ -2213,6 +2317,5 @@ BEGINObjClassInit(queue, 1, OBJ_IS_CORE_MODULE)
OBJSetMethodHandler(objMethod_SETPROPERTY, queueSetProperty);
ENDObjClassInit(queue)
-/*
- * vi:set ai:
+/* vi:set ai:
*/
diff --git a/queue.h b/queue.h
index bc09fbd8..7dfeb226 100644
--- a/queue.h
+++ b/queue.h
@@ -82,9 +82,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
@@ -173,6 +184,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
diff --git a/rsyslog.h b/rsyslog.h
index 9fe162d5..93b5c149 100644
--- a/rsyslog.h
+++ b/rsyslog.h
@@ -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) */
diff --git a/rsyslogd.8 b/rsyslogd.8
index 0125d589..2aa911d9 100644
--- a/rsyslogd.8
+++ b/rsyslogd.8
@@ -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 aa31884e..3475b8f6 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);
diff --git a/syslogd.c b/syslogd.c
index 803e5239..b56c2f39 100644
--- a/syslogd.c
+++ b/syslogd.c
@@ -301,7 +301,9 @@ 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 */
@@ -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;
@@ -2196,8 +2201,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) {
@@ -2320,6 +2324,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
@@ -2684,8 +2690,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));
@@ -2763,21 +2772,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
*/
@@ -2802,7 +2796,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");
@@ -2819,6 +2812,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
@@ -2970,6 +2964,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...
@@ -2989,15 +3049,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];
+ /* 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, "46Ac:dehi:f:g:l:m:M:nqQr::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
+ */
gethostname(LocalHostName, sizeof(LocalHostName));
- if ( (p = strchr(LocalHostName, '.')) ) {
+ if((p = strchr(LocalHostName, '.'))) {
*p++ = '\0';
LocalDomain = p;
- }
- else
- {
+ } else {
LocalDomain = "";
/* It's not clearly defined whether gethostname()
@@ -3025,21 +3195,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);
@@ -3052,14 +3212,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 - we now come back to carrying out command line options*/
- /* END core initializations */
-
- while ((ch = getopt(argc, argv, "46Ac:dehi:f:g:l:m:M:nqQr::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;
@@ -3076,39 +3232,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) {
@@ -3118,25 +3255,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;
@@ -3157,7 +3291,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");
@@ -3169,42 +3303,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;
@@ -3215,15 +3337,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. "
@@ -3249,7 +3368,7 @@ int realMain(int argc, char **argv)
checkPermissions();
thrdInit();
- if ( !(Debug || NoFork) )
+ if( !(Debug || NoFork) )
{
dbgprintf("Checking pidfile.\n");
if (!check_pid(PidFile))
@@ -3288,8 +3407,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))
diff --git a/syslogd.h b/syslogd.h
index 4eefd325..cbb4bb05 100644
--- a/syslogd.h
+++ b/syslogd.h
@@ -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,
diff --git a/tcpsrv.c b/tcpsrv.c
index adbe26e0..ae5f3371 100644
--- a/tcpsrv.c
+++ b/tcpsrv.c
@@ -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
*/
diff --git a/template.c b/template.c
index 75c9ce78..0eea4572 100644
--- a/template.c
+++ b/template.c
@@ -385,7 +385,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 +509,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*/
diff --git a/wti.c b/wti.c
index 2386dee9..c9184b36 100644
--- a/wti.c
+++ b/wti.c
@@ -370,6 +370,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);
diff --git a/wtp.c b/wtp.c
index 1c2ea30f..d6192bc0 100644
--- a/wtp.c
+++ b/wtp.c
@@ -545,6 +545,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));
diff --git a/wtp.h b/wtp.h
index 6100fe52..e47f9fe6 100644
--- a/wtp.h
+++ b/wtp.h
@@ -68,6 +68,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);
@@ -99,6 +100,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));