diff options
-rw-r--r-- | ChangeLog | 17 | ||||
-rw-r--r-- | configure.ac | 6 | ||||
-rw-r--r-- | doc/debug.html | 5 | ||||
-rw-r--r-- | doc/impstats.html | 100 | ||||
-rw-r--r-- | doc/omfile.html | 4 | ||||
-rw-r--r-- | doc/omfwd.html | 2 | ||||
-rw-r--r-- | doc/omudpspoof.html | 173 | ||||
-rw-r--r-- | plugins/impstats/impstats.c | 76 | ||||
-rw-r--r-- | plugins/imudp/imudp.c | 2 | ||||
-rw-r--r-- | plugins/omudpspoof/omudpspoof.c | 312 | ||||
-rw-r--r-- | runtime/debug.c | 22 | ||||
-rw-r--r-- | runtime/debug.h | 1 | ||||
-rw-r--r-- | runtime/rsyslog.h | 2 | ||||
-rw-r--r-- | runtime/wtp.c | 1 | ||||
-rw-r--r-- | tests/tcpflood.c | 12 | ||||
-rw-r--r-- | threads.c | 4 | ||||
-rw-r--r-- | tools/omfile.c | 1 |
17 files changed, 586 insertions, 154 deletions
@@ -1,4 +1,21 @@ ---------------------------------------------------------------------------- +Version 7.2.x (?really?) [v7-stable] 2013-??-?? +- impstats: added ability to write stats records to local file + and avoid going through the syslog log stream. syslog logging can now + also be turned off (see doc for details). +- omudpspoof: add support for new config system +- omudpspoof: add support for packets larger than 1472 bytes + On Ethernet, they need to be transmitted in multiple fragments. While + it is known that fragmentation can cause issues, it is the best choice + to be made in that case. Also improved debug output. +- bugfix: omudpspoof failed depending on the execution environment + The v7 engine closes fds, and closed some of libnet's fds as well, what + lead to problems (unfortunately, at least some libnet versions do not + report a proper error state but still "success"...). The order of libnet + calls has been adjusted to by in sync with what the core engine does. +- added capability to output thread-id-to-function debug info + This is a useful debug aid, but nothing of concern for regular users. +---------------------------------------------------------------------------- Version 7.2.6 [v7-stable] 2013-01-?? - slightly improved config parser error messages when invalid escapes happen - bugfix: omelasticsearch failed when authentication data was provided diff --git a/configure.ac b/configure.ac index 71a3fc19..b26ef6f4 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],[7.2.5],[rsyslog@lists.adiscon.com]) +AC_INIT([rsyslog],[7.2.5.up2],[rsyslog@lists.adiscon.com]) AM_INIT_AUTOMAKE m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) @@ -114,7 +114,7 @@ AC_TYPE_SIGNAL AC_FUNC_STAT AC_FUNC_STRERROR_R AC_FUNC_VPRINTF -AC_CHECK_FUNCS([flock basename alarm clock_gettime getifaddrs gethostbyname gethostname gettimeofday localtime_r memset mkdir regcomp select setid socket strcasecmp strchr strdup strerror strndup strnlen strrchr strstr strtol strtoul uname ttyname_r getline malloc_trim prctl epoll_create epoll_create1 fdatasync lseek64]) +AC_CHECK_FUNCS([flock basename alarm clock_gettime getifaddrs gethostbyname gethostname gettimeofday localtime_r memset mkdir regcomp select setid socket strcasecmp strchr strdup strerror strndup strnlen strrchr strstr strtol strtoul uname ttyname_r getline malloc_trim prctl epoll_create epoll_create1 fdatasync syscall lseek64]) # the check below is probably ugly. If someone knows how to do it in a better way, please # let me know! -- rgerhards, 2010-10-06 @@ -146,7 +146,7 @@ RS_ATOMIC_OPERATIONS RS_ATOMIC_OPERATIONS_64BIT # fall back to POSIX sems for atomic operations (cpu expensive) -AC_CHECK_HEADERS([semaphore.h]) +AC_CHECK_HEADERS([semaphore.h sys/syscall.h]) # Additional module directories diff --git a/doc/debug.html b/doc/debug.html index 6aeb7975..8b104d80 100644 --- a/doc/debug.html +++ b/doc/debug.html @@ -49,6 +49,11 @@ FileTrace=vm.c FileTrace=expr.c"</li> <li><b>Debug</b> - if present, turns on the debug system and enables debug output <li><b>DebugOnDemand</b> - if present, turns on the debug system but does not enable debug output itself. You need to send SIGUSR1 to turn it on when desired. +<li><b>OutputTidToStderr</b> - if present, makes rsyslog output information about +the thread id (tid) of newly create processesto stderr. Note that not necessarily +all new threads are reported (depends on the code, e.g. of plugins). This is +only available under Linux. This usually does NOT work when privileges have +been dropped (that's not a bug, but the way it is). <li><b>help</b> - display a very short list of commands - hopefully a life saver if you can't access the documentation...</li> </ul> </ul> diff --git a/doc/impstats.html b/doc/impstats.html index 64b04a30..8db9c6f6 100644 --- a/doc/impstats.html +++ b/doc/impstats.html @@ -16,26 +16,66 @@ availabilty and format of counters may change and is not yet stable (so be prepared to change your trending scripts when you upgrade to a newer rsyslog version). <p>The set of available counters will be output as a set of syslog messages. This output is periodic, with the interval being configurable (default is 5 minutes). -Be sure that your configuration records the counter messages (default is syslog.info). +Be sure that your configuration records the counter messages (default is syslog.=info). +Besides logging to the regular syslog stream, the module can also be configured to +write statistics data into a (local) file. <p>Note that loading this module has impact on rsyslog performance. Depending on settings, this impact may be noticable (for high-load environments). <p>The rsyslog website has an updated overview of available <a href="http://rsyslog.com/rsyslog-statistic-counter/">rsyslog statistic counters</a>. </p> -<p><b>Configuration Directives</b>:</p> +<p><b>Module Confguration Parameters</b>:</p> +<p>This module supports module parameters, only. <ul> -<li>$PStatInterval <Seconds><br> -Sets the interval, in <b>seconds</b> at which messages are generated. Please note that the -actual interval may be a bit longer. We do not try to be precise and so the interval is -actually a sleep period which is entered after generating all messages. So the actual -interval is what is configured here plus the actual time required to generate messages. -In general, the difference should not really matter. -<li>$PStatFacility <numerical facility><br> -The numerical syslog facility code to be used for generated messages. Default -is 5 (syslog).This is useful for filtering messages.</li> -<li>$PStatSeverity <numerical severity><br> -The numerical syslog severity code to be used for generated messages. Default -is 6 (info).This is useful for filtering messages.</li> + <li><strong>interval </strong>[seconds] (default 300 [5minutes])<br> + Sets the interval, in <b>seconds</b> at which messages are generated. Please note that the + actual interval may be a bit longer. We do not try to be precise and so the interval is + actually a sleep period which is entered after generating all messages. So the actual + interval is what is configured here plus the actual time required to generate messages. + In general, the difference should not really matter. + <br></li> + <li><strong>facility </strong>[templateName]<br> + The numerical syslog facility code to be used for generated messages. Default + is 5 (syslog). This is useful for filtering messages. + <br></li> + <li><strong>severity </strong>[templateName]<br> + The numerical syslog severity code to be used for generated messages. Default + is 6 (info).This is useful for filtering messages. + <br></li> + <li><strong>format </strong>[json/cee/<b>legacy</b>](rsyslog v6.3.8+ only)<br> + Specifies the format of emitted stats messages. The default of "legacy" is + compatible with pre v6-rsyslog. The other options provide support for + structured formats (note the "cee" is actually "project lumberack" logging). + <br></li> + <li><strong>log.syslog </strong>[<b>on</b>/off] - available since 7.3.6<br> + This is a boolean setting specifying if data should be sent + to the usual syslog stream. This is useful if custom formatting + or more elaborate processing is desired. However, output is placed + under the same restrictions as regular syslog data, especially in + regard to the queue position (stats data may sit for an extended + period of time in queues if they are full).<br></li> + <li><strong>log.file </strong>[file name] - available since 7.3.6<br> + If specified, statistics data is written the specified file. For + robustness, this should be a local file. The file format cannot be + customized, it consists of a date header, followed by a colon, + followed by the actual statistics record, all on one line. Only + very limited error handling is done, so if things go wrong stats + records will probably be lost. Logging to file an be a useful + alternative if for some reasons (e.g. full queues) the regular + syslog stream method shall not be used solely. Note that turning + on file logging does NOT turn of syslog logging. If that is desired + log.syslog="off" must be explicitely set. + <br></li> + +</ul> +<p><b>Legacx Configuration Directives</b>:</p> +A limited set of parameters can also be set via the legacy configuration +syntax. Note that this is intended as an upward compatibilit layer, so +newer features are intentionally <b>not</b> available via legacy directives. +<ul> +<li>$PStatInterval <Seconds> - same as the "interval" parameter. +<li>$PStatFacility <numerical facility> - same as the "facility" parameter. +<li>$PStatSeverity <numerical severity> - same as the "severity" parameter. <li>$PStatJSON <on/<b>off</b>> (rsyslog v6.3.8+ only)<br> If set to on, stats messages are emitted as structured cee-enhanced syslog. If set to off, legacy format is used (which is compatible with pre v6-rsyslog). @@ -45,23 +85,45 @@ set to off, legacy format is used (which is compatible with pre v6-rsyslog). <ul> <li>This module MUST be loaded right at the top of rsyslog.conf, otherwise stats may not get turned on in all places.</li> -<li>experimental code</li> </ul> -<p><b>Sample:</b></p> +<p><b>Samples:</b></p> <p>This activates the module and records messages to /var/log/rsyslog-stats in 10 minute intervals:<br> </p> -<textarea rows="8" cols="60">$ModLoad impstats +<textarea rows="5" cols="60">module(load="impstats" interval="600" severity="7") + +# to actually gather the data: +syslog.=debug /var/log/rsyslog-stats +</textarea> +<p><b>Legacy Sample:</b></p> +<p>This activates the module and records messages to /var/log/rsyslog-stats in 10 minute intervals:</p> +<textarea rows="6" cols="60">$ModLoad impstats $PStatInterval 600 $PStatSeverity 7 -syslog.debug /var/log/rsyslog-stats +syslog.=debug /var/log/rsyslog-stats </textarea> +<p>In the next sample, the default interval of 5 minutes is used. However, this time +stats data is NOT emitted to the syslog stream but to a local file instead. +<p> +<textarea rows="3" cols="70">module(load="impstats" interval="600" severity="7" + log.syslog="off" /* need to turn log stream logging off! */ + log.file="/path/to/local/stats.log") +</textarea> +<p>And finally, we log to both the regular syslog log stream as well as a file. +Within the log stream, we forward the data records to another server: +<p> +<textarea rows="4" cols="70">module(load="impstats" interval="600" severity="7" + log.file="/path/to/local/stats.log") + +syslog.=debug @central.example.net +</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 © 2010 by <a href="http://www.gerhards.net/rainer">Rainer +Copyright © 2013 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> diff --git a/doc/omfile.html b/doc/omfile.html index 23ecc034..d17b643b 100644 --- a/doc/omfile.html +++ b/doc/omfile.html @@ -13,14 +13,14 @@ <p>The omfile plug-in provides the core functionality of writing messages to files residing inside the local file system (which may actually be remote if methods like NFS are used). Both files named with static names as well files with names based on message content are supported by this module. It is a built-in module that does not need to be loaded. </p> <p> </p> -<p><b>Global Configuration Directives</b>:</p> +<p><b>Module Confguration Parameters</b>:</p> <ul> <li><strong>Template </strong>[templateName]<br> sets a new default template for file actions.<br></li> </ul> <p> </p> -<p><b>Action specific Configuration Directives</b>:</p> +<p><b>Action Confguration Parameters</b>:</p> <ul> <li><strong>DynaFileCacheSize </strong>(not mandatory, default will be used)<br> Defines a template to be used for the output. <br></li><br> diff --git a/doc/omfwd.html b/doc/omfwd.html index 5599ae39..fb9145e1 100644 --- a/doc/omfwd.html +++ b/doc/omfwd.html @@ -16,7 +16,7 @@ <p><b>Global Configuration Directives</b>:</p> <ul> <li><strong>Template </strong>[templateName]<br> - sets a new default template for file actions.<br></li> + sets a non-standard default template for this module.<br></li> </ul> <p> </p> diff --git a/doc/omudpspoof.html b/doc/omudpspoof.html index df14bbe1..930412c8 100644 --- a/doc/omudpspoof.html +++ b/doc/omudpspoof.html @@ -7,46 +7,161 @@ <h1>UDP spoofing output module (omudpspoof)</h1> <p><b>Module Name: omstdout</b></p> -<p><b>Author: </b>David Lang <david@lang.hm> and Rainer Gerhards -<rgerhards@adiscon.com></p> -<p><b>Available Since</b>: 5.1.3</p> +<p><b>Authors: </b>Rainer Gerhards <rgerhards@adiscon.com> +and David Lang <david@lang.hm> +</p> +<p><b>Available Since</b>: 5.1.3 / v7 config since 7.2.5</p> <p><b>Description</b>:</p> <p>This module is similar to the regular UDP forwarder, but permits to spoof the sender address. Also, it enables to circle through a number of source ports. -<p><b>Configuration Directives</b>:</p> +<p><b>Important:</b> This module requires root priveleges for its low-level +socket access. As such, the <b>module will not work if rsyslog is configured to +drop privileges</b>. + +<p><b>load() Parameters</b>:</p> +<ul> + <li><strong>Template </strong>[templateName]<br> + sets a non-standard default template for this module.<br></li> + +</ul> +<p> </p> +<p><b>action() parameters</b>:</p> +<ul> + <li><strong>Target </strong>string<br> + Name or IP-Address of the system that shall receive messages. Any resolvable name is fine. <br></li><br> + + <li><strong>Port </strong>[Integer, Default 514]<br> + Name or numerical value of port to use when connecting to target. <br></li><br> + + <li><b>Template</b>[Word]<br> + Template to use as message text. + <br></li><br> + + <li><strong>SourceTemplate </strong>[Word]<br> + This is the name of the template that contains a + numerical IP address that is to be used as the source system IP address. + While it may often be a constant value, it can be generated as usual via the + property replacer, as long as it is a valid IPv4 address. If not specified, the + build-in default template RSYSLOG_omudpspoofDfltSourceTpl is used. This template is defined + as follows:<br> + template(name="RSYSLOG_omudpspoofDfltSourceTpl" type="string" string="%fromhost-ip%")<br> + So in essence, the default template spoofs the address of the system the message + was received from. This is considered the most important use case. + <br></li><br> + + <li><b>SourcePortStart</b>[Word]<br> + Specifies the start value for circeling the source ports. Must be less than or + equal to the end value. Default is 32000. + <br></li><br> + + <li><b>SourcePortEnd</b>[Word]<br> + Specifies the ending value for circeling the source ports. Must be less than or + equal to the start value. Default is 42000. + <br></li><br> + + <li><b>mtu</b>[Integer, default 1500]<br> + Maximum MTU supported by the network. Default respects Ethernet and must + usually not be adjusted. Setting a too-high MTU can lead to message loss, + too low to excess message fragmentation. Change only if you really know what + you are doing. This is always given in number of bytes. + <br></li><br> +</ul> +<p><b>pre-v7 Configuration Directives</b>:</p> <ul> -<li><b>$ActionOMOMUDPSpoofSourceNameTemplate</b> <templatename><br> -This is the name of the template that contains a -numerical IP address that is to be used as the source system IP address. -While it may often be a constant value, it can be generated as usual via the -property replacer, as long as it is a valid IPv4 address. If not specified, the -build-in default template RSYSLOG_omudpspoofDfltSourceTpl is used. This template is defined -as follows:<br> -$template RSYSLOG_omudpspoofDfltSourceTpl,"%fromhost-ip%"<br> -So in essence, the default template spoofs the address of the system the message -was received from. This is considered the most important use case. -<li><b>$ActionOMUDPSpoofTargetHost</b> <hostname><br> -Host that the messages shall be sent to. -<li><b>$ActionOMUDPSpoofTargetPort</b> <port><br> -Remote port that the messages shall be sent to. -<li><b>$ActionOMUDPSpoofDefaultTemplate</b> <templatename><br> -This setting instructs omudpspoof to use a template different from the -default template for all of its actions that do not have a template specified -explicitely. -<li><b>$ActionOMUDPSpoofSourcePortStart</b> <number><br> -Specifies the start value for circeling the source ports. Must be less than or -equal to the end value. Default is 32000. -<li><b>$ActionOMUDPSpoofSourcePortEnd</b> <number><br> -Specifies the ending value for circeling the source ports. Must be less than or -equal to the start value. Default is 42000. +<li><b>$ActionOMOMUDPSpoofSourceNameTemplate</b> <templatename> +- equivalent to the "sourceTemplate" parameter. +<li><b>$ActionOMUDPSpoofTargetHost</b> <hostname> - equivalent to the "target" parameter. +<li><b>$ActionOMUDPSpoofTargetPort</b> <port> - equivalent to the "target" parameter. +<li><b>$ActionOMUDPSpoofDefaultTemplate</b> <templatename> +- equivalent to the "template" load() parameter. +<li><b>$ActionOMUDPSpoofSourcePortStart</b> <number> +- equivalent to the "SourcePortStart" parameter. +<li><b>$ActionOMUDPSpoofSourcePortEnd</b> <number> +- equivalent to the "SourcePortEnd" parameter. </ul> <b>Caveats/Known Bugs:</b> <ul> <li><b>IPv6</b> is currently not supported. If you need this capability, please let us know via the rsyslog mailing list. +<li>Versions shipped prior to rsyslog 7.2.5 do not support message sizes over 1472 bytes (more +pricesely: over the network-supported MTU). Starting with 7.2.5, those messages will be +fragmented, up to a total upper limit of 64K (induced by UDP). Message sizes over +64K will be truncated. For older versions, messages over 1472 may be totally discarded +or truncated, depending on version and environment. </ul> -<p><b>Sample:</b></p> + +<p><b>Config Samples</b></p> +<p>The following sample forwards all syslog messages in standard form to the +remote server server.example.com. The original sender's address is used. We do not +care about the source port. This example is considered the typical use case for +omudpspoof. +</p> +<textarea rows="3" cols="80">module(load="omudpspoof") +action(type="omudpspoof" target="server.example.com") +</textarea> + +<p>The following sample forwards all syslog messages in unmodified form to the +remote server server.example.com. The sender address 192.0.2.1 with fixed +source port 514 is used. +</p> +<textarea rows="7" cols="80">module(load="omudpspoof") +template(name="spoofaddr" type="string" string="192.0.2.1") +template(name="spooftemplate" type="string" string="%rawmsg%") +action(type="omudpspoof" target="server.example.com" + sourcetemplate="spoofaddr" template="spooftemplate" + sourceport.start="514" sourceport.end="514) +</textarea> +<p>The following sample is exatly like the previous, but it specifies a larger size +MTU. If, for example, the envrionment supports Jumbo Ethernet frames, increasing the +MTU is useful as it reduces packet fragmentation, which most often is the source of +problems. Note that setting the MTU to a value larger than the local-attached network +supports will lead to send errors and loss of message. So use with care! +</p> +<textarea rows="8" cols="80">module(load="omudpspoof") +template(name="spoofaddr" type="string" string="192.0.2.1") +template(name="spooftemplate" type="string" string="%rawmsg%") +action(type="omudpspoof" target="server.example.com" + sourcetemplate="spoofaddr" template="spooftemplate" + sourceport.start="514" sourceport.end="514 + mtu="8000") +</textarea> +<p>Of course, the action can be combined with any type of filter, for +example a tradition PRI filter:</p> +<textarea rows="8" cols="80">module(load="omudpspoof") +template(name="spoofaddr" type="string" string="192.0.2.1") +template(name="spooftemplate" type="string" string="%rawmsg%") +local0.* action(type="omudpspoof" target="server.example.com" + sourcetemplate="spoofaddr" template="spooftemplate" + sourceport.start="514" sourceport.end="514 + mtu="8000") +</textarea> +<p>... or any complex expression-based filter:</p> +<textarea rows="8" cols="80">module(load="omudpspoof") +template(name="spoofaddr" type="string" string="192.0.2.1") +template(name="spooftemplate" type="string" string="%rawmsg%") +if prifilt("local0.*") and $msg contains "error" then + action(type="omudpspoof" target="server.example.com" + sourcetemplate="spoofaddr" template="spooftemplate" + sourceport.start="514" sourceport.end="514 + mtu="8000") +</textarea> +<p>and of course it can also be combined with as many other actions +as one likes:</p> +<textarea rows="11" cols="80">module(load="omudpspoof") +template(name="spoofaddr" type="string" string="192.0.2.1") +template(name="spooftemplate" type="string" string="%rawmsg%") +if prifilt("local0.*") and $msg contains "error" then { + action(type="omudpspoof" target="server.example.com" + sourcetemplate="spoofaddr" template="spooftemplate" + sourceport.start="514" sourceport.end="514 + mtu="8000") + action(type="omfile" file="/var/log/somelog") + stop # or whatever... +} +</textarea> + +<p><b>Legacy Sample (pre-v7):</b></p> <p>The following sample forwards all syslog messages in standard form to the remote server server.example.com. The original sender's address is used. We do not care about the source port. This example is considered the typical use case for diff --git a/plugins/impstats/impstats.c b/plugins/impstats/impstats.c index 62599969..571d734e 100644 --- a/plugins/impstats/impstats.c +++ b/plugins/impstats/impstats.c @@ -27,6 +27,8 @@ #include <signal.h> #include <string.h> #include <pthread.h> +#include <fcntl.h> +#include <sys/uio.h> #include "dirty.h" #include "cfsysline.h" #include "module-template.h" @@ -63,11 +65,14 @@ typedef struct configSettings_s { } configSettings_t; struct modConfData_s { - rsconf_t *pConf; /* our overall config object */ + rsconf_t *pConf; /* our overall config object */ int iStatsInterval; int iFacility; int iSeverity; + int logfd; /* fd if logging to file, or -1 if closed */ statsFmtType_t statsFmt; + sbool bLogToSyslog; + char *logfile; sbool configSetViaV2Method; }; static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */ @@ -82,6 +87,8 @@ static struct cnfparamdescr modpdescr[] = { { "interval", eCmdHdlrInt, 0 }, { "facility", eCmdHdlrInt, 0 }, { "severity", eCmdHdlrInt, 0 }, + { "log.syslog", eCmdHdlrBinary, 0 }, + { "log.file", eCmdHdlrGetWord, 0 }, { "format", eCmdHdlrGetWord, 0 } }; static struct cnfparamblk modpblk = @@ -120,7 +127,7 @@ initConfigSettings(void) /* actually submit a message to the rsyslog core */ -static inline rsRetVal +static inline void doSubmitMsg(uchar *line) { msg_t *pMsg; @@ -139,10 +146,53 @@ doSubmitMsg(uchar *line) pMsg->msgFlags = 0; submitMsg(pMsg); + DBGPRINTF("impstats: submit [%d,%d] msg '%s'\n", runModConf->iFacility, + runModConf->iSeverity, line); finalize_it: - RETiRet; + return; +} + + +/* log stats message to file; limited error handling done */ +static inline void +doLogToFile(cstr_t *cstr) +{ + struct iovec iov[4]; + ssize_t nwritten; + ssize_t nexpect; + time_t t; + char timebuf[32]; + + if(cstrLen(cstr) == 0) + goto done; + if(runModConf->logfd == -1) { + runModConf->logfd = open(runModConf->logfile, O_WRONLY|O_CREAT|O_APPEND|O_CLOEXEC, S_IRUSR|S_IWUSR); + if(runModConf->logfd == -1) { + dbgprintf("error opening stats file %s\n", runModConf->logfile); + goto done; + } + } + time(&t); + iov[0].iov_base = ctime_r(&t, timebuf); + iov[0].iov_len = nexpect = strlen(iov[0].iov_base) - 1; /* -1: strip \n */ + iov[1].iov_base = ": "; + iov[1].iov_len = 2; + nexpect += 2; + iov[2].iov_base = rsCStrGetSzStrNoNULL(cstr); + iov[2].iov_len = (size_t) cstrLen(cstr); + nexpect += cstrLen(cstr); + iov[3].iov_base = "\n"; + iov[3].iov_len = 1; + nexpect++; + nwritten = writev(runModConf->logfd, iov, 4); + + if(nwritten != nexpect) { + dbgprintf("error writing stats file %s, nwritten %lld, expected %lld\n", + runModConf->logfile, (long long) nwritten, (long long) nexpect); + } +done: return; } @@ -153,7 +203,10 @@ static rsRetVal doStatsLine(void __attribute__((unused)) *usrptr, cstr_t *cstr) { DEFiRet; - doSubmitMsg(rsCStrGetSzStrNoNULL(cstr)); + if(runModConf->bLogToSyslog) + doSubmitMsg(rsCStrGetSzStrNoNULL(cstr)); + if(runModConf->logfile != NULL) + doLogToFile(cstr); RETiRet; } @@ -178,6 +231,9 @@ CODESTARTbeginCnfLoad loadModConf->iFacility = DEFAULT_FACILITY; loadModConf->iSeverity = DEFAULT_SEVERITY; loadModConf->statsFmt = statsFmt_Legacy; + loadModConf->logfd = -1; + loadModConf->logfile = NULL; + loadModConf->bLogToSyslog = 1; bLegacyCnfModGlobalsPermitted = 1; /* init legacy config vars */ initConfigSettings(); @@ -210,6 +266,10 @@ CODESTARTsetModCnf loadModConf->iFacility = (int) pvals[i].val.d.n; } else if(!strcmp(modpblk.descr[i].name, "severity")) { loadModConf->iSeverity = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "log.syslog")) { + loadModConf->bLogToSyslog = (sbool) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "log.file")) { + loadModConf->logfile = es_str2cstr(pvals[i].val.d.estr, NULL); } else if(!strcmp(modpblk.descr[i].name, "format")) { mode = es_str2cstr(pvals[i].val.d.estr, NULL); if(!strcasecmp(mode, "json")) { @@ -270,7 +330,9 @@ BEGINactivateCnf rsRetVal localRet; CODESTARTactivateCnf runModConf = pModConf; - DBGPRINTF("impstats: stats interval %d seconds\n", runModConf->iStatsInterval); + DBGPRINTF("impstats: stats interval %d seconds, logToSyslog %d, logFile %s\n", + runModConf->iStatsInterval, runModConf->bLogToSyslog, + runModConf->logfile == NULL ? "deactivated" : (char*)runModConf->logfile); localRet = statsobj.EnableStats(); if(localRet != RS_RET_OK) { errmsg.LogError(0, localRet, "impstats: error enabling statistics gathering"); @@ -282,6 +344,9 @@ ENDactivateCnf BEGINfreeCnf CODESTARTfreeCnf + if(runModConf->logfd != -1) + close(runModConf->logfd); + free(runModConf->logfile); ENDfreeCnf @@ -297,6 +362,7 @@ CODESTARTrunInput if(glbl.GetGlobalInputTermState() == 1) break; /* terminate input! */ + DBGPRINTF("impstats: woke up, generating messages\n"); generateStatsMsgs(); } ENDrunInput diff --git a/plugins/imudp/imudp.c b/plugins/imudp/imudp.c index 782d7bee..9ec1dbbc 100644 --- a/plugins/imudp/imudp.c +++ b/plugins/imudp/imudp.c @@ -367,7 +367,7 @@ processSocket(thrdInfo_t *pThrd, struct lstn_s *lstn, struct sockaddr_storage *f *pbIsPermitted = 1; /* no check -> everything permitted */ } - DBGPRINTF("recv(%d,%d),acl:%d,msg:%s\n", lstn->sock, (int) lenRcvBuf, *pbIsPermitted, pRcvBuf); + DBGPRINTF("imudp:recv(%d,%d),acl:%d,msg:%s\n", lstn->sock, (int) lenRcvBuf, *pbIsPermitted, pRcvBuf); if(*pbIsPermitted != 0) { if((runModConf->iTimeRequery == 0) || (iNbrTimeUsed++ % runModConf->iTimeRequery) == 0) { diff --git a/plugins/omudpspoof/omudpspoof.c b/plugins/omudpspoof/omudpspoof.c index a45d49fa..4f37fd8c 100644 --- a/plugins/omudpspoof/omudpspoof.c +++ b/plugins/omudpspoof/omudpspoof.c @@ -5,7 +5,7 @@ * This file builds on UDP spoofing code contributed by * David Lang <david@lang.hm>. I then created a "real" rsyslog module * out of that code and omfwd. I decided to make it a separate module because - * omfwd already mixes up too many things (TCP & UDP & a differnt modes, + * omfwd already mixes up too many things (TCP & UDP & a different modes, * this has historic reasons), it would not be a good idea to also add * spoofing to it. And, looking at the requirements, there is little in * common between omfwd and this module. @@ -93,14 +93,19 @@ DEFobjCurrIf(glbl) DEFobjCurrIf(net) typedef struct _instanceData { + uchar *tplName; /* name of assigned template */ uchar *host; uchar *port; + uchar *sourceTpl; + int mtu; int *pSockArray; /* sockets to use for UDP */ - int compressionLevel; /* 0 - no compression, else level for zlib */ struct addrinfo *f_addr; u_short sourcePort; u_short sourcePortStart; /* for sorce port iteration */ u_short sourcePortEnd; + int bReportLibnetInitErr; /* help prevent multiple error messages on init err */ + libnet_t *libnet_handle; + char errbuf[LIBNET_ERRBUF_SIZE]; } instanceData; #define DFLT_SOURCE_PORT_START 32000 @@ -116,6 +121,22 @@ typedef struct configSettings_s { } configSettings_t; static configSettings_t cs; +/* action (instance) parameters */ +static struct cnfparamdescr actpdescr[] = { + { "target", eCmdHdlrGetWord, 1 }, + { "port", eCmdHdlrGetWord, 0 }, + { "sourcetemplate", eCmdHdlrGetWord, 0 }, + { "sourceport.start", eCmdHdlrInt, 0 }, + { "sourceport.end", eCmdHdlrInt, 0 }, + { "mtu", eCmdHdlrInt, 0 }, + { "template", eCmdHdlrGetWord, 0 } +}; +static struct cnfparamblk actpblk = + { CNFPARAMBLK_VERSION, + sizeof(actpdescr)/sizeof(struct cnfparamdescr), + actpdescr + }; + /* module-global parameters */ static struct cnfparamdescr modpdescr[] = { { "template", eCmdHdlrGetWord, 0 }, @@ -148,8 +169,6 @@ ENDinitConfVars /* add some variables needed for libnet */ -libnet_t *libnet_handle; -char errbuf[LIBNET_ERRBUF_SIZE]; pthread_mutex_t mutLibnet; /* forward definitions */ @@ -291,6 +310,9 @@ ENDfreeCnf BEGINcreateInstance CODESTARTcreateInstance + pData->libnet_handle = NULL; + pData->mtu = 1500; + pData->bReportLibnetInitErr = 1; ENDcreateInstance @@ -305,8 +327,12 @@ BEGINfreeInstance CODESTARTfreeInstance /* final cleanup */ closeUDPSockets(pData); + free(pData->tplName); free(pData->port); free(pData->host); + free(pData->sourceTpl); + if(pData->libnet_handle != NULL) + libnet_destroy(pData->libnet_handle); ENDfreeInstance @@ -331,16 +357,20 @@ UDPSend(instanceData *pData, uchar *pszSourcename, char *msg, size_t len) libnet_ptag_t ip, ipo; libnet_ptag_t udp; sbool bNeedUnlock = 0; + /* hdrOffs = fragmentation flags + offset (in bytes) + * divided by 8 */ + unsigned msgOffs, hdrOffs; + unsigned maxPktLen, pktLen; DEFiRet; if(pData->pSockArray == NULL) { CHKiRet(doTryResume(pData)); } - if(len > 1472) { - DBGPRINTF("omudpspoof: msg with length %d truncated to 1472 bytes: '%.768s'\n", + if(len > 65528) { + DBGPRINTF("omudpspoof: msg with length %d truncated to 64k: '%.768s'\n", len, msg); - len = 1472; + len = 65528; } ip = ipo = udp = 0; @@ -355,26 +385,41 @@ UDPSend(instanceData *pData, uchar *pszSourcename, char *msg, size_t len) bNeedUnlock = 1; for (r = pData->f_addr; r && bSendSuccess == RSFALSE ; r = r->ai_next) { tempaddr = (struct sockaddr_in *)r->ai_addr; - libnet_clear_packet(libnet_handle); + /* Getting max payload size (must be multiple of 8) */ + maxPktLen = (pData->mtu - LIBNET_IPV4_H) & ~0x07; + msgOffs = 0; + /* We're doing (payload size - UDP header size) and not + * checking if it's a multiple of 8 because we know the + * header is 8 bytes long */ + if(len > (maxPktLen - LIBNET_UDP_H) ) { + hdrOffs = IP_MF; + pktLen = maxPktLen - LIBNET_UDP_H; + } else { + hdrOffs = 0; + pktLen = len; + } + DBGPRINTF("omudpspoof: stage 1: MF:%d, hdrOffs %d, pktLen %d\n", + (hdrOffs & IP_MF) >> 13, (hdrOffs & 0x1FFF) << 3, pktLen); + libnet_clear_packet(pData->libnet_handle); /* note: libnet does need ports in host order NOT in network byte order! -- rgerhards, 2009-11-12 */ udp = libnet_build_udp( ntohs(pData->sourcePort),/* source port */ ntohs(tempaddr->sin_port),/* destination port */ - LIBNET_UDP_H + len, /* packet length */ + pktLen+LIBNET_UDP_H, /* packet length */ 0, /* checksum */ (u_char*)msg, /* payload */ - len, /* payload size */ - libnet_handle, /* libnet handle */ + pktLen, /* payload size */ + pData->libnet_handle, /* libnet handle */ udp); /* libnet id */ if (udp == -1) { - DBGPRINTF("omudpspoof: can't build UDP header: %s\n", libnet_geterror(libnet_handle)); + DBGPRINTF("omudpspoof: can't build UDP header: %s\n", libnet_geterror(pData->libnet_handle)); } ip = libnet_build_ipv4( - LIBNET_IPV4_H + len + LIBNET_UDP_H, /* length */ + LIBNET_IPV4_H+LIBNET_UDP_H+pktLen, /* length */ 0, /* TOS */ 242, /* IP ID */ - 0, /* IP Frag */ + hdrOffs, /* IP Frag */ 64, /* TTL */ IPPROTO_UDP, /* protocol */ 0, /* checksum */ @@ -382,31 +427,87 @@ UDPSend(instanceData *pData, uchar *pszSourcename, char *msg, size_t len) tempaddr->sin_addr.s_addr, NULL, /* payload */ 0, /* payload size */ - libnet_handle, /* libnet handle */ + pData->libnet_handle, /* libnet handle */ ip); /* libnet id */ if (ip == -1) { - DBGPRINTF("omudpspoof: can't build IP header: %s\n", libnet_geterror(libnet_handle)); + DBGPRINTF("omudpspoof: can't build IP header: %s\n", libnet_geterror(pData->libnet_handle)); } /* Write it to the wire. */ - lsent = libnet_write(libnet_handle); - if(lsent != LIBNET_IPV4_H+LIBNET_UDP_H+len) { - DBGPRINTF("omudpspoof: write error len %d, sent %d: %s\n", - LIBNET_IPV4_H+LIBNET_UDP_H+len, lsent, libnet_geterror(libnet_handle)); + lsent = libnet_write(pData->libnet_handle); + dbgprintf("DDDD: omudpspoof stage 1 return state %d (expected %d), fd %d\n", lsent, + (int) (LIBNET_IPV4_H+LIBNET_UDP_H+pktLen), pData->libnet_handle->fd); + if(lsent != (int) (LIBNET_IPV4_H+LIBNET_UDP_H+pktLen)) { + /* note: access to fd is a libnet internal. If a newer version of libnet does + * not expose that member, we should simply remove it. However, while it is there + * it is useful for consolidating with strace output. + */ + DBGPRINTF("omudpspoof: write error (total len %d): pktLen %d, sent %d, fd %d: %s\n", + len, LIBNET_IPV4_H+LIBNET_UDP_H+pktLen, lsent, pData->libnet_handle->fd, + libnet_geterror(pData->libnet_handle)); if(lsent != -1) { bSendSuccess = RSTRUE; } } else { bSendSuccess = RSTRUE; } - } - /* finished looping */ - if(bSendSuccess == RSFALSE) { - DBGPRINTF("omudpspoof: error sending message, suspending\n"); - iRet = RS_RET_SUSPENDED; + msgOffs += pktLen; + + /* We need to get rid of the UDP header to build the other fragments */ + libnet_clear_packet(pData->libnet_handle); + ip = LIBNET_PTAG_INITIALIZER; + while(len > msgOffs ) { /* loop until all payload is sent */ + /* check if there will be more fragments */ + if((len - msgOffs) > maxPktLen) { + /* In IP's eyes, the UDP header in the first packet + * needs to be in the offset, so we add its size to + * the payload offset here */ + hdrOffs = IP_MF + (msgOffs + LIBNET_UDP_H)/8; + pktLen = maxPktLen; + } else { + /* See above */ + hdrOffs = (msgOffs + LIBNET_UDP_H)/8; + pktLen = len - msgOffs; + } + DBGPRINTF("omudpspoof: stage 2: MF:%d, hdrOffs %d, pktLen %d\n", + (hdrOffs & IP_MF) >> 13, (hdrOffs & 0x1FFF) << 3, pktLen); + ip = libnet_build_ipv4( + LIBNET_IPV4_H + pktLen, /* length */ + 0, /* TOS */ + 242, /* IP ID */ + hdrOffs, /* IP Frag */ + 64, /* TTL */ + IPPROTO_UDP, /* protocol */ + 0, /* checksum */ + source_ip.sin_addr.s_addr, + tempaddr->sin_addr.s_addr, + (u_int8_t*)(msg+msgOffs), /* payload */ + pktLen, /* payload size */ + pData->libnet_handle, /* libnet handle */ + ip); /* libnet id */ + if (ip == -1) { + DBGPRINTF("omudpspoof: can't build IP fragment header: %s\n", libnet_geterror(pData->libnet_handle)); + } + /* Write it to the wire. */ + lsent = libnet_write(pData->libnet_handle); + dbgprintf("DDDD: omudpspoof stage 1 return state %d (expected %d)\n", lsent, (int) (LIBNET_IPV4_H+pktLen)); + if(lsent != (int) (LIBNET_IPV4_H+pktLen)) { + DBGPRINTF("omudpspoof: fragment write error len %d, sent %d: %s\n", + LIBNET_IPV4_H+LIBNET_UDP_H+len, lsent, libnet_geterror(pData->libnet_handle)); + bSendSuccess = RSFALSE; + continue; + } + msgOffs += pktLen; + } } finalize_it: + if(iRet != RS_RET_OK) { + if(pData->libnet_handle != NULL) { + libnet_destroy(pData->libnet_handle); + pData->libnet_handle = NULL; + } + } if(bNeedUnlock) { d_pthread_mutex_unlock(&mutLibnet); } @@ -427,8 +528,32 @@ static rsRetVal doTryResume(instanceData *pData) if(pData->pSockArray != NULL) FINALIZE; + if(pData->host == NULL) + ABORT_FINALIZE(RS_RET_DISABLE_ACTION); + + if(pData->libnet_handle == NULL) { + /* Initialize the libnet library. Root priviledges are required. + * this initializes a IPv4 socket to use for forging UDP packets. + */ + pData->libnet_handle = libnet_init( + LIBNET_RAW4, /* injection type */ + NULL, /* network interface */ + pData->errbuf); /* errbuf */ + + if(pData->libnet_handle == NULL) { + if(pData->bReportLibnetInitErr) { + errmsg.LogError(0, RS_RET_ERR_LIBNET_INIT, "omudpsoof: error " + "initializing libnet - are you running as root?"); + pData->bReportLibnetInitErr = 0; + } + ABORT_FINALIZE(RS_RET_ERR_LIBNET_INIT); + } + } + DBGPRINTF("omudpspoof: libnit_init() ok\n"); + pData->bReportLibnetInitErr = 1; + /* The remote address is not yet known and needs to be obtained */ - DBGPRINTF(" %s\n", pData->host); + DBGPRINTF("omudpspoof trying resume for '%s'\n", pData->host); memset(&hints, 0, sizeof(hints)); /* port must be numeric, because config file syntax requires this */ hints.ai_flags = AI_NUMERICSERV; @@ -449,7 +574,8 @@ finalize_it: freeaddrinfo(pData->f_addr); pData->f_addr = NULL; } - iRet = RS_RET_SUSPENDED; + if(iRet != RS_RET_DISABLE_ACTION) + iRet = RS_RET_SUSPENDED; } RETiRet; @@ -463,69 +589,95 @@ ENDtryResume BEGINdoAction char *psz; /* temporary buffering */ - register unsigned l; + unsigned l; int iMaxLine; CODESTARTdoAction CHKiRet(doTryResume(pData)); - iMaxLine = glbl.GetMaxLine(); - - //TODO: enable THIS one! DBGPRINTF(" %s:%s/omudpspoof, src '%s', msg strt '%.256s'\n", pData->host, - DBGPRINTF(" %s:%s/omudpspoof, src '%s', msg strt '%s'\n", pData->host, + DBGPRINTF(" %s:%s/omudpspoof, src '%s', msg strt '%.256s'\n", pData->host, getFwdPt(pData), ppString[1], ppString[0]); + iMaxLine = glbl.GetMaxLine(); psz = (char*) ppString[0]; l = strlen((char*) psz); if((int) l > iMaxLine) l = iMaxLine; -# ifdef USE_NETZIP - /* Check if we should compress and, if so, do it. We also - * check if the message is large enough to justify compression. - * The smaller the message, the less likely is a gain in compression. - * To save CPU cycles, we do not try to compress very small messages. - * What "very small" means needs to be configured. Currently, it is - * hard-coded but this may be changed to a config parameter. - * rgerhards, 2006-11-30 - */ - if(pData->compressionLevel && (l > CONF_MIN_SIZE_FOR_COMPRESS)) { - Bytef *out; - uLongf destLen = iMaxLine + iMaxLine/100 +12; /* recommended value from zlib doc */ - uLong srcLen = l; - int ret; - /* TODO: optimize malloc sequence? -- rgerhards, 2008-09-02 */ - CHKmalloc(out = (Bytef*) MALLOC(destLen)); - out[0] = 'z'; - out[1] = '\0'; - ret = compress2((Bytef*) out+1, &destLen, (Bytef*) psz, - srcLen, pData->compressionLevel); - DBGPRINTF("Compressing message, length was %d now %d, return state %d.\n", - l, (int) destLen, ret); - if(ret != Z_OK) { - /* if we fail, we complain, but only in debug mode - * Otherwise, we are silent. In any case, we ignore the - * failed compression and just sent the uncompressed - * data, which is still valid. So this is probably the - * best course of action. - * rgerhards, 2006-11-30 - */ - DBGPRINTF("Compression failed, sending uncompressed message\n"); - } else if(destLen+1 < l) { - /* only use compression if there is a gain in using it! */ - DBGPRINTF("there is gain in compression, so we do it\n"); - psz = (char*) out; - l = destLen + 1; /* take care for the "z" at message start! */ - } - ++destLen; - } -# endif - CHKiRet(UDPSend(pData, ppString[1], psz, l)); finalize_it: ENDdoAction +static inline void +setInstParamDefaults(instanceData *pData) +{ + pData->tplName = NULL; + pData->sourcePortStart = DFLT_SOURCE_PORT_START; + pData->sourcePortEnd = DFLT_SOURCE_PORT_END; + pData->host = NULL; + pData->port = NULL; + pData->sourceTpl = (uchar*) strdup("RSYSLOG_omudpspoofDfltSourceTpl"); + pData->mtu = 1500; +} + +BEGINnewActInst + struct cnfparamvals *pvals; + uchar *tplToUse; + int i; +CODESTARTnewActInst + DBGPRINTF("newActInst (omudpspoof)\n"); + + pvals = nvlstGetParams(lst, &actpblk, NULL); + if(pvals == NULL) { + errmsg.LogError(0, RS_RET_MISSING_CNFPARAMS, "omudpspoof: mandatory " + "parameters missing"); + ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); + } + + if(Debug) { + dbgprintf("action param blk in omudpspoof:\n"); + cnfparamsPrint(&actpblk, pvals); + } + + CHKiRet(createInstance(&pData)); + setInstParamDefaults(pData); + + for(i = 0 ; i < actpblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(actpblk.descr[i].name, "target")) { + pData->host = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "port")) { + pData->port = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "sourcetemplate")) { + free(pData->sourceTpl); + pData->sourceTpl = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "sourceport.start")) { + pData->sourcePortStart = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "sourceport.end")) { + pData->sourcePortEnd = pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "mtu")) { + pData->mtu = pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "template")) { + pData->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else { + DBGPRINTF("omudpspoof: program error, non-handled " + "param '%s'\n", actpblk.descr[i].name); + } + } + CODE_STD_STRING_REQUESTnewActInst(2) + pData->sourcePort = pData->sourcePortStart; + + tplToUse = ustrdup((pData->tplName == NULL) ? getDfltTpl() : pData->tplName); + CHKiRet(OMSRsetEntry(*ppOMSR, 0, tplToUse, OMSR_NO_RQD_TPL_OPTS)); + CHKiRet(OMSRsetEntry(*ppOMSR, 1, ustrdup(pData->sourceTpl), OMSR_NO_RQD_TPL_OPTS)); + +CODE_STD_FINALIZERnewActInst + cnfparamvalsDestruct(pvals, &actpblk); +ENDnewActInst + + BEGINparseSelectorAct uchar *sourceTpl; CODESTARTparseSelectorAct @@ -583,7 +735,6 @@ freeConfigVars(void) BEGINmodExit CODESTARTmodExit /* destroy the libnet state needed for forged UDP sources */ - libnet_destroy(libnet_handle); pthread_mutex_destroy(&mutLibnet); /* release what we no longer need */ objRelease(errmsg, CORE_COMPONENT); @@ -596,6 +747,7 @@ ENDmodExit BEGINqueryEtryPt CODESTARTqueryEtryPt CODEqueryEtryPt_STD_OMOD_QUERIES +CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES CODEqueryEtryPt_STD_CONF2_QUERIES CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES ENDqueryEtryPt @@ -623,18 +775,6 @@ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(errmsg, CORE_COMPONENT)); CHKiRet(objUse(net,LM_NET_FILENAME)); - /* Initialize the libnet library. Root priviledges are required. - * this initializes a IPv4 socket to use for forging UDP packets. - */ - libnet_handle = libnet_init( - LIBNET_RAW4, /* injection type */ - NULL, /* network interface */ - errbuf); /* errbuf */ - - if(libnet_handle == NULL) { - errmsg.LogError(0, NO_ERRCODE, "Error initializing libnet, can not continue "); - ABORT_FINALIZE(RS_RET_ERR_LIBNET_INIT); - } pthread_mutex_init(&mutLibnet, NULL); CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspoofdefaulttemplate", 0, eCmdHdlrGetWord, setLegacyDfltTpl, NULL, NULL)); diff --git a/runtime/debug.c b/runtime/debug.c index 1d306dbd..fa39e7fe 100644 --- a/runtime/debug.c +++ b/runtime/debug.c @@ -44,6 +44,9 @@ #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> +#ifdef HAVE_SYS_SYSCALL_H +# include <sys/syscall.h> +#endif #if _POSIX_TIMERS <= 0 #include <sys/time.h> #endif @@ -66,6 +69,7 @@ static int bPrintMutexAction = 0; /* shall mutex calls be printed to the debug l static int bPrintTime = 1; /* print a timestamp together with debug message */ static int bPrintAllDebugOnExit = 0; static int bAbortTrace = 1; /* print a trace after SIGABRT or SIGSEGV */ +static int bOutputTidToStderr = 0;/* output TID to stderr on thread creation */ static char *pszAltDbgFileName = NULL; /* if set, debug output is *also* sent to here */ static int altdbg = -1; /* and the handle for alternate debug output */ int stddbg = 1; /* the handle for regular debug output, set to stdout if not forking, -1 otherwise */ @@ -293,6 +297,21 @@ static inline void dbgFuncDBRemoveMutexLock(dbgFuncDB_t *pFuncDB, pthread_mutex_ /* ------------------------- END FuncDB utility functions ------------------------- */ +/* output the current thread ID to "relevant" places + * (what "relevant" means is determinded by various ways) + */ +void +dbgOutputTID(char* name) +{ +# ifdef HAVE_SYSCALL + if(bOutputTidToStderr) + fprintf(stderr, "thread tid %u, name '%s'\n", + (unsigned)syscall(SYS_gettid), name); + DBGPRINTF("thread created, tid %u, name '%s'\n", + (unsigned)syscall(SYS_gettid), name); +# endif +} + /* ########################################################################### * IMPORTANT NOTE * Mutex instrumentation reduces the code's concurrency and thus affects its @@ -1325,6 +1344,7 @@ dbgGetRuntimeOptions(void) "PrintAllDebugInfoOnExit (not yet implemented)\n" "NoLogTimestamp\n" "Nostdoout\n" + "OutputTidToStderr\n" "filetrace=file (may be provided multiple times)\n" "DebugOnDemand - enables debugging on USR1, but does not turn on output\n" "\nSee debug.html in your doc set or http://www.rsyslog.com for details\n"); @@ -1358,6 +1378,8 @@ dbgGetRuntimeOptions(void) stddbg = -1; } else if(!strcasecmp((char*)optname, "noaborttrace")) { bAbortTrace = 0; + } else if(!strcasecmp((char*)optname, "outputtidtostderr")) { + bOutputTidToStderr = 1; } else if(!strcasecmp((char*)optname, "filetrace")) { if(*optval == '\0') { fprintf(stderr, "rsyslogd " VERSION " error: logfile debug option requires filename, " diff --git a/runtime/debug.h b/runtime/debug.h index 5bd26bd8..f802e8c1 100644 --- a/runtime/debug.h +++ b/runtime/debug.h @@ -104,6 +104,7 @@ void dbgSetExecLocation(int iStackPtr, int line); void dbgSetThrdName(uchar *pszName); void dbgPrintAllDebugInfo(void); void *dbgmalloc(size_t size); +void dbgOutputTID(char* name); /* macros */ #ifdef DEBUGLESS diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index 07d58d68..60f0d1a7 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -322,7 +322,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_ERR_DOOR = -2147, /**< some problems with handling the Solaris door functionality */ RS_RET_NO_SRCNAME_TPL = -2150, /**< sourcename template was not specified where one was needed (omudpspoof spoof addr) */ RS_RET_HOST_NOT_SPECIFIED = -2151, /**< (target) host was not specified where it was needed */ - RS_RET_ERR_LIBNET_INIT = -2152, /**< error initializing libnet */ + RS_RET_ERR_LIBNET_INIT = -2152, /**< error initializing libnet, e.g. because not running as root */ RS_RET_FORCE_TERM = -2153, /**< thread was forced to terminate by bShallShutdown, a state, not an error */ RS_RET_RULES_QUEUE_EXISTS = -2154,/**< we were instructed to create a new ruleset queue, but one already exists */ RS_RET_NO_CURR_RULESET = -2155,/**< no current ruleset exists (but one is required) */ diff --git a/runtime/wtp.c b/runtime/wtp.c index a53a9888..f8d3588b 100644 --- a/runtime/wtp.c +++ b/runtime/wtp.c @@ -383,6 +383,7 @@ wtpWorker(void *arg) /* the arg is actually a wti object, even though we are in } # endif + dbgOutputTID((char*)thrdName); pthread_cleanup_push(wtpWrkrExecCancelCleanup, pWti); wtiWorker(pWti); pthread_cleanup_pop(0); diff --git a/tests/tcpflood.c b/tests/tcpflood.c index 4a3199c8..b3cef2e0 100644 --- a/tests/tcpflood.c +++ b/tests/tcpflood.c @@ -258,7 +258,7 @@ int openConnections(void) return setupUDP(); if(bShowProgress) - write(1, " open connections", sizeof(" open connections")-1); + if(write(1, " open connections", sizeof(" open connections")-1)){} # ifdef ENABLE_GNUTLS sessArray = calloc(numConnections, sizeof(gnutls_session_t)); # endif @@ -278,7 +278,7 @@ int openConnections(void) } if(bShowProgress) { lenMsg = sprintf(msgBuf, "\r%5.5d open connections\n", i); - write(1, msgBuf, lenMsg); + if(write(1, msgBuf, lenMsg)) {} } return 0; @@ -303,12 +303,12 @@ void closeConnections(void) return; if(bShowProgress) - write(1, " close connections", sizeof(" close connections")-1); + if(write(1, " close connections", sizeof(" close connections")-1)){} for(i = 0 ; i < numConnections ; ++i) { if(i % 10 == 0) { if(bShowProgress) { lenMsg = sprintf(msgBuf, "\r%5.5d", i); - write(1, msgBuf, lenMsg); + if(write(1, msgBuf, lenMsg)){} } } if(sockArray[i] != -1) { @@ -325,7 +325,7 @@ void closeConnections(void) } if(bShowProgress) { lenMsg = sprintf(msgBuf, "\r%5.5d close connections\n", i); - write(1, msgBuf, lenMsg); + if(write(1, msgBuf, lenMsg)){} } } @@ -347,7 +347,7 @@ genMsg(char *buf, size_t maxBuf, int *pLenBuf, struct instdata *inst) /* get message from file */ do { done = 1; - *pLenBuf = fread(buf, 1, 1024, dataFP); + *pLenBuf = fread(buf, 1, MAX_EXTRADATA_LEN + 1024, dataFP); if(*pLenBuf == 0) { if(--numFileIterations > 0) { rewind(dataFP); @@ -183,9 +183,11 @@ static void* thrdStarter(void *arg) assert(pThis != NULL); assert(pThis->pUsrThrdMain != NULL); + ustrncpy(thrdName+3, pThis->name, 20); + dbgOutputTID((char*)thrdName); + # if HAVE_PRCTL && defined PR_SET_NAME /* set thread name - we ignore if the call fails, has no harsh consequences... */ - ustrncpy(thrdName+3, pThis->name, 20); if(prctl(PR_SET_NAME, thrdName, 0, 0, 0) != 0) { DBGPRINTF("prctl failed, not setting thread name for '%s'\n", pThis->name); } else { diff --git a/tools/omfile.c b/tools/omfile.c index 5b0bfb46..451a258b 100644 --- a/tools/omfile.c +++ b/tools/omfile.c @@ -852,6 +852,7 @@ ENDendTransaction BEGINdoAction CODESTARTdoAction DBGPRINTF("file to log to: %s\n", pData->f_fname); + DBGPRINTF("omfile: start of data: '%.128s'\n", ppString[0]); CHKiRet(writeFile(ppString, iMsgOpts, pData)); if(!bCoreSupportsBatching && pData->bFlushOnTXEnd) { CHKiRet(strm.Flush(pData->pStrm)); |