summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRainer Gerhards <rgerhards@adiscon.com>2012-08-20 12:58:48 +0200
committerRainer Gerhards <rgerhards@adiscon.com>2012-08-20 12:58:48 +0200
commit0f52727043736b1e411bc60309ed18d41a4f1234 (patch)
tree97fd6c1defe93abc97717c4ae3d598166f7ad402
parent7c1ad1c77cc3884931bd2c9e2d2f45db892a15a0 (diff)
parent4a24d8e1b63a27d8c20f2d3d614fcdc2bcb52a01 (diff)
downloadrsyslog-0f52727043736b1e411bc60309ed18d41a4f1234.zip
rsyslog-0f52727043736b1e411bc60309ed18d41a4f1234.tar.gz
rsyslog-0f52727043736b1e411bc60309ed18d41a4f1234.tar.xz
Merge branch 'beta' into v6-stable
-rw-r--r--.gitignore1
-rw-r--r--ChangeLog422
-rw-r--r--Makefile.am33
-rw-r--r--action.c393
-rw-r--r--action.h15
-rw-r--r--configure.ac192
-rw-r--r--dirty.h5
-rw-r--r--doc/design.tex74
-rw-r--r--doc/imfile.html11
-rw-r--r--doc/imklog.html5
-rw-r--r--doc/impstats.html8
-rw-r--r--doc/imptcp.html26
-rw-r--r--doc/imrelp.html9
-rw-r--r--doc/imtcp.html9
-rw-r--r--doc/imuxsock.html46
-rw-r--r--doc/manual.html19
-rw-r--r--doc/mmsnmptrapd.html9
-rw-r--r--doc/omlibdbi.html56
-rw-r--r--doc/ommysql.html46
-rw-r--r--doc/pmlastmsg.html7
-rw-r--r--doc/property_replacer.html66
-rw-r--r--doc/rsconf1_omfileforcechown.html5
-rw-r--r--doc/rsyslog_conf_global.html18
-rw-r--r--doc/rsyslog_conf_templates.html4
-rw-r--r--doc/rsyslog_ng_comparison.html235
-rw-r--r--doc/v6compatibility.html131
-rw-r--r--doc/version_naming.html4
-rw-r--r--grammar/Makefile.am19
-rw-r--r--grammar/conf-fmt145
-rw-r--r--grammar/debian.conf132
-rw-r--r--grammar/debian.new165
-rw-r--r--grammar/grammar.y186
-rw-r--r--grammar/lexer.l316
-rw-r--r--grammar/makefile.stand-alone14
-rw-r--r--grammar/mini.samp33
-rw-r--r--grammar/parserif.h23
-rw-r--r--grammar/rainerscript.c1618
-rw-r--r--grammar/rainerscript.h255
-rw-r--r--grammar/samp11
-rw-r--r--grammar/testdriver.c109
-rw-r--r--outchannel.c19
-rw-r--r--parse.c9
-rw-r--r--parse.h2
-rw-r--r--plugins/im3195/im3195.c35
-rw-r--r--plugins/imdiag/imdiag.c35
-rw-r--r--plugins/imfile/imfile.c31
-rw-r--r--plugins/imgssapi/imgssapi.c34
-rw-r--r--plugins/imklog/Makefile.am3
-rw-r--r--plugins/imklog/bsd.c256
-rw-r--r--plugins/imklog/imklog.c236
-rw-r--r--plugins/imklog/imklog.h38
-rw-r--r--plugins/imklog/ksym.c832
-rw-r--r--plugins/imklog/ksym_mod.c485
-rw-r--r--plugins/imklog/ksyms.h37
-rw-r--r--plugins/imklog/linux.c542
-rw-r--r--plugins/imklog/module.h35
-rw-r--r--plugins/imklog/solaris.c68
-rw-r--r--plugins/immark/immark.c66
-rw-r--r--plugins/impstats/impstats.c90
-rw-r--r--plugins/imptcp/imptcp.c452
-rw-r--r--plugins/imrelp/imrelp.c195
-rw-r--r--plugins/imsolaris/imsolaris.c30
-rw-r--r--plugins/imtcp/imtcp.c298
-rw-r--r--plugins/imtemplate/Makefile.am6
-rw-r--r--plugins/imtemplate/imtemplate.c436
-rw-r--r--plugins/imttcp/imttcp.c10
-rw-r--r--plugins/imudp/imudp.c619
-rw-r--r--plugins/imuxsock/imuxsock.c697
-rw-r--r--plugins/mmaudit/Makefile.am8
-rw-r--r--plugins/mmaudit/mmaudit.c390
-rw-r--r--plugins/mmjsonparse/Makefile.am8
-rw-r--r--plugins/mmjsonparse/mmjsonparse.c250
-rw-r--r--plugins/mmnormalize/mmnormalize.c9
-rw-r--r--plugins/mmsnmptrapd/mmsnmptrapd.c8
-rw-r--r--plugins/omdbalerting/Makefile.am8
-rw-r--r--plugins/omdbalerting/omdbalerting.c145
-rw-r--r--plugins/omelasticsearch/Makefile.am8
-rw-r--r--plugins/omelasticsearch/omelasticsearch.c741
-rw-r--r--plugins/omgssapi/omgssapi.c12
-rw-r--r--plugins/omhdfs/omhdfs.c1
-rw-r--r--plugins/omhiredis/COPYING674
-rw-r--r--plugins/omhiredis/COPYING_LESSER165
-rw-r--r--plugins/omhiredis/Makefile.am7
-rw-r--r--plugins/omhiredis/README29
-rw-r--r--plugins/omhiredis/omhiredis.c239
-rw-r--r--plugins/omlibdbi/omlibdbi.c89
-rw-r--r--plugins/ommail/ommail.c6
-rw-r--r--plugins/ommongodb/Makefile.am8
-rw-r--r--plugins/ommongodb/README10
-rw-r--r--plugins/ommongodb/ommongodb.c470
-rw-r--r--plugins/ommysql/ommysql.c102
-rw-r--r--plugins/omoracle/omoracle.c1
-rw-r--r--plugins/ompgsql/ompgsql.c6
-rw-r--r--plugins/omprog/omprog.c71
-rw-r--r--plugins/omrelp/omrelp.c6
-rw-r--r--plugins/omruleset/omruleset.c8
-rw-r--r--plugins/omsnmp/omsnmp.c251
-rw-r--r--plugins/omstdout/omstdout.c6
-rw-r--r--plugins/omtemplate/Makefile.am8
-rw-r--r--plugins/omtemplate/omtemplate.c233
-rw-r--r--plugins/omtesting/omtesting.c6
-rw-r--r--plugins/omudpspoof/omudpspoof.c8
-rw-r--r--plugins/omuxsock/omuxsock.c5
-rw-r--r--plugins/pmaixforwardedfrom/pmaixforwardedfrom.c1
-rw-r--r--plugins/pmcisconames/pmcisconames.c1
-rw-r--r--plugins/pmlastmsg/pmlastmsg.c1
-rw-r--r--plugins/pmrfc3164sd/pmrfc3164sd.c1
-rw-r--r--plugins/pmsnare/pmsnare.c1
-rw-r--r--plugins/sm_cust_bindcdr/sm_cust_bindcdr.c1
-rw-r--r--runtime/Makefile.am29
-rw-r--r--runtime/apc.c402
-rw-r--r--runtime/apc.h56
-rw-r--r--runtime/cfsysline.c117
-rw-r--r--runtime/conf.c685
-rw-r--r--runtime/conf.h21
-rw-r--r--runtime/ctok.c627
-rw-r--r--runtime/ctok.h54
-rw-r--r--runtime/ctok_token.c127
-rw-r--r--runtime/ctok_token.h86
-rw-r--r--runtime/datetime.c200
-rw-r--r--runtime/datetime.h8
-rw-r--r--runtime/dnscache.c355
-rw-r--r--runtime/dnscache.h29
-rw-r--r--runtime/errmsg.c5
-rw-r--r--runtime/expr.c480
-rw-r--r--runtime/expr.h55
-rw-r--r--runtime/glbl.c214
-rw-r--r--runtime/glbl.h39
-rw-r--r--runtime/im-helper.h66
-rw-r--r--runtime/module-template.h271
-rw-r--r--runtime/modules.c354
-rw-r--r--runtime/modules.h43
-rw-r--r--runtime/msg.c507
-rw-r--r--runtime/msg.h6
-rw-r--r--runtime/net.c161
-rw-r--r--runtime/net.h6
-rw-r--r--runtime/nsd_gtls.c8
-rw-r--r--runtime/nsd_ptcp.c46
-rw-r--r--runtime/obj.c41
-rw-r--r--runtime/objomsr.c6
-rw-r--r--runtime/parser.c11
-rw-r--r--runtime/queue.c363
-rw-r--r--runtime/queue.h8
-rw-r--r--runtime/rsconf.c1354
-rw-r--r--runtime/rsconf.h182
-rw-r--r--runtime/rsyslog.c26
-rw-r--r--runtime/rsyslog.h28
-rw-r--r--runtime/rule.c34
-rw-r--r--runtime/rule.h4
-rw-r--r--runtime/ruleset.c135
-rw-r--r--runtime/ruleset.h36
-rw-r--r--runtime/statsobj.c62
-rw-r--r--runtime/statsobj.h14
-rw-r--r--runtime/stream.h3
-rw-r--r--runtime/strmsrv.c2
-rw-r--r--runtime/sync.c55
-rw-r--r--runtime/sync.h48
-rw-r--r--runtime/sysvar.c202
-rw-r--r--runtime/sysvar.h47
-rw-r--r--runtime/typedefs.h17
-rw-r--r--runtime/var.c326
-rw-r--r--runtime/var.h11
-rw-r--r--runtime/vm.c869
-rw-r--r--runtime/vm.h66
-rw-r--r--runtime/vmop.c305
-rw-r--r--runtime/vmop.h127
-rw-r--r--runtime/vmprg.c234
-rw-r--r--runtime/vmprg.h67
-rw-r--r--runtime/vmstk.c232
-rw-r--r--runtime/vmstk.h54
-rw-r--r--runtime/wti.c5
-rw-r--r--tcps_sess.c6
-rw-r--r--tcps_sess.h3
-rw-r--r--tcpsrv.c112
-rw-r--r--tcpsrv.h12
-rw-r--r--template.c254
-rw-r--r--template.h35
-rw-r--r--tests/Makefile.am42
-rwxr-xr-xtests/cfg.sh4
-rwxr-xr-xtests/diag.sh2
-rwxr-xr-xtests/imtcp-tls-basic-vg.sh15
-rwxr-xr-xtests/imtcp_conndrop.sh1
-rwxr-xr-xtests/imtcp_conndrop_tls-vg.sh17
-rwxr-xr-xtests/imtcp_conndrop_tls.sh16
-rwxr-xr-xtests/imuxsock_ccmiddle_root.sh3
-rwxr-xr-xtests/imuxsock_logger_root.sh3
-rwxr-xr-xtests/imuxsock_traillf_root.sh3
-rw-r--r--tests/inputfilegen.c1
-rwxr-xr-xtests/manytcp-too-few-tls.sh2
-rwxr-xr-xtests/mysql-basic-cnf6.sh13
-rw-r--r--tests/rscript.c1
-rw-r--r--tests/rt-init.c1
-rwxr-xr-xtests/sndrcv_udp.sh3
-rw-r--r--tests/tcpflood.c6
-rw-r--r--tests/testsuites/imfile-basic.conf1
-rw-r--r--tests/testsuites/imtcp_conndrop.conf1
-rw-r--r--tests/testsuites/mysql-basic-cnf6.conf7
-rwxr-xr-xtests/validation-run.sh3
-rw-r--r--threads.c34
-rw-r--r--threads.h3
-rw-r--r--tools/Makefile.am10
-rw-r--r--tools/omdiscard.c11
-rw-r--r--tools/omfile.c246
-rw-r--r--tools/omfwd.c333
-rw-r--r--tools/ompipe.c109
-rw-r--r--tools/omshell.c19
-rw-r--r--tools/omusrmsg.c172
-rw-r--r--tools/pmrfc3164.c2
-rw-r--r--tools/pmrfc5424.c4
-rw-r--r--tools/syslogd.c1154
-rw-r--r--tools/syslogd.h1
211 files changed, 15449 insertions, 11720 deletions
diff --git a/.gitignore b/.gitignore
index 55f069e..b24a066 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,6 +26,7 @@ missing
compile
rsyslogd
rsyslog.service
+ylwrap
*.orig
rg.conf*
*.swp
diff --git a/ChangeLog b/ChangeLog
index fd2bd3e..b112a63 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,224 @@
---------------------------------------------------------------------------
+Version 6.4.0 [V6-STABLE] 2012-08-20
+- THIS IS THE FIRST VERSION OF THE 6.4.x STABLE BRANCH
+ It includes all enhancements made in 6.3.x plus what is written in the
+ ChangeLog below.
+- omelasticsearch: support for parameters parent & dynparent added
+- bugfix: imtcp aborted when more than 2 connections were used.
+ Incremented pthread stack size to 4MB for imtcp, imptcp and imttcp
+ closes: http://bugzilla.adiscon.com/show_bug.cgi?id=342
+- bugfix: imptcp aborted when $InputPTCPServerBindRuleset was used
+- bugfix: problem with cutting first 16 characters from message with
+ bAnnotate
+ Thanks to Milan Bartos for the patch.
+---------------------------------------------------------------------------
+Version 6.3.12 [BETA] 2012-07-02
+- support for elasticsearch via omelasticsearch added
+ Note that this module has been tested quite well by a number of folks,
+ and this is why we merge in new functionality in a late beta stage.
+ Even if problems would exist, only users of omelasticsearch would
+ experience them, making it a pretty safe addition.
+- bugfix: $ActionName was not properly honored
+ Thanks to Abby Edwards for alerting us
+---------------------------------------------------------------------------
+Version 6.3.11 [BETA] 2012-06-18
+- bugfix: expression-based filters with AND/OR could segfault
+ due to a problem with boolean shortcut operations. From the user's
+ perspective, the segfault is almost non-deterministic (it occurs when
+ a shortcut is used).
+ Thanks to Lars Peterson for providing the initial bug report and his
+ support in solving it.
+- bugfix: "last message repeated n times" message was missing hostname
+ Thanks to Zdenek Salvet for finding this bug and to Bodik for reporting
+---------------------------------------------------------------------------
+Version 6.3.10 [BETA] 2012-06-04
+- bugfix: delayble source could block action queue, even if there was
+ a disk queue associated with it. The root cause of this problem was
+ that it makes no sense to delay messages once they arrive in the
+ action queue - the "input" that is being held in that case is the main
+ queue worker, what makes no sense.
+ Thanks to Marcin for alerting us on this problem and providing
+ instructions to reproduce it.
+- bugfix: invalid free in imptcp could lead to abort during startup
+- bugfix: if debug message could end up in log file when forking
+ if rsyslog was set to auto-background (thus fork, the default) and debug
+ mode to stdout was enabled, debug messages ended up in the first log file
+ opened. Currently, stdout logging is completely disabled in forking mode
+ (but writing to the debug log file is still possible). This is a change
+ in behaviour, which is under review. If it causes problems to you,
+ please let us know.
+ Thanks to Tomas Heinrich for the patch.
+- bugfix: --enable-smcustbindcdr configure directive did not work
+ closes: http://bugzilla.adiscon.com/show_bug.cgi?id=330
+ Thanks to Ultrabug for the patch.
+- bugfix: made rsyslog compile when libestr ist not installed in /usr
+ Thanks to Miloslav Trmač for providing patches and suggestions
+---------------------------------------------------------------------------
+Version 6.3.9 [BETA] 2012-05-22
+- bugfix: imtcp could cause hang during reception
+ this also applied to other users of core file tcpsrv.c, but imtcp was
+ by far the most prominent and widely-used, the rest rather exotic
+ (like imdiag)
+- added capability to specify substrings for field extraction mode
+- added the "jsonf" property replacer option (and fieldname)
+- bugfix: omudpspoof did not work correctly if no spoof hostname was
+ configured
+- bugfix: property replacer option "json" could lead to content loss
+ message was truncated if escaping was necessary
+- bugfix: assigned ruleset was lost when using disk queues
+ This looked quite hard to diagnose for disk-assisted queues, as the
+ pure memory part worked well, but ruleset info was lost for messages
+ stored inside the disk queue.
+- bugfix/imuxsock: solving abort if hostname was not set; configured
+ hostname was not used (both merge regressions)
+ -bugfix/omfile: template action parameter was not accepted
+ (and template name set to "??" if the parameter was used)
+ Thanks to Brian Knox for alerting us on this bug.
+- bugfix: ommysql did not properly init/exit the mysql runtime library
+ this could lead to segfaults. Triggering condition: multiple action
+ instances using ommysql. Thanks to Tomas Heinrich for reporting this
+ problem and providing an initial patch (which my solution is based on,
+ I need to add more code to clean the mess up).
+- bugfix: rsyslog did not terminate when delayable inputs were blocked
+ due to unvailable sources. Fixes:
+ http://bugzilla.adiscon.com/show_bug.cgi?id=299
+ Thanks to Marcin M for bringing up this problem and Andre Lorbach
+ for helping to reproduce and fix it.
+- added capability to specify substrings for field extraction mode
+- bugfix: disk queue was not persisted on shutdown, regression of fix to
+ http://bugzilla.adiscon.com/show_bug.cgi?id=299
+ The new code also handles the case of shutdown of blocking light and
+ full delayable sources somewhat smarter and permits, assuming sufficient
+ timouts, to persist message up to the max queue capacity. Also some nits
+ in debug instrumentation have been fixed.
+---------------------------------------------------------------------------
+Version 6.3.8 [DEVEL] 2012-04-16
+- added $PStatJSON directive to permit stats records in JSON format
+- added "date-unixtimestamp" property replacer option to format as a
+ unix timestamp (seconds since epoch)
+- added "json" property replacer option to support JSON encoding on a
+ per-property basis
+- added omhiredis (contributed module)
+- added mmjsonparse to support recognizing and parsing JSON enhanced syslog
+ messages
+- upgraded more plugins to support the new v6 config format:
+ - ommysql
+ - omlibdbi
+ - omsnmp
+- added configuration directives to customize queue light delay marks
+ $MainMsgQueueLightDelayMark, $ActionQueueLightDelayMark; both
+ specify number of messages starting at which a delay happens.
+- added message property parsesuccess to indicate if the last run
+ higher-level parser could successfully parse the message or not
+ (see property replacer html doc for details)
+- bugfix: abort during startup when rsyslog.conf v6+ format was used in
+ a certain way
+- bugfix: property $!all-json made rsyslog abort if no normalized data
+ was available
+- bugfix: memory leak in array passing output module mode
+- added configuration directives to customize queue light delay marks
+- permit size modifiers (k,m,g,...) in integer config parameters
+ Thanks to Jo Rhett for the suggestion.
+- bugfix: hostname was not requeried on HUP
+ Thanks to Per Jessen for reporting this bug and Marius Tomaschewski for
+ his help in testing the fix.
+- bugfix: imklog invalidly computed facility and severity
+ closes: http://bugzilla.adiscon.com/show_bug.cgi?id=313
+- added configuration directive to disable octet-counted framing
+ for imtcp, directive is $InputTCPServerSupportOctetCountedFraming
+ for imptcp, directive is $InputPTCPServerSupportOctetCountedFraming
+- added capability to use a local interface IP address as fromhost-ip for
+ locally originating messages. New directive $LocalHostIPIF
+---------------------------------------------------------------------------
+Version 6.3.7 [DEVEL] 2012-02-02
+- imported refactored v5.9.6 imklog linux driver, now combined with BSD
+ driver
+- removed imtemplate/omtemplate template modules, as this was waste of time
+ The actual input/output modules are better copy templates. Instead, the
+ now-removed modules cost time for maintenance AND often caused confusion
+ on what their role was.
+- added a couple of new stats objects
+- improved support for new v6 config system. The build-in output modules
+ now all support the new config language
+- bugfix: facility local<x> was not correctly interpreted in legacy filters
+ Was only accepted if it was the first PRI in a multi-filter PRI.
+ Thanks to forum user Mark for bringing this to our attention.
+- bugfix: potential abort after reading invalid X.509 certificate
+ closes: http://bugzilla.adiscon.com/show_bug.cgi?id=290
+ Thanks to Tomas Heinrich for the patch
+- bufgix: legacy parsing of some filters did not work correctly
+- bugfix: rsyslog aborted during startup if there is an error in loading
+ an action and legacy configuration mode is used
+- bugfix: bsd klog driver did no longer compile
+- relicensed larger parts of the code under Apache (ASL) 2.0
+---------------------------------------------------------------------------
+Version 6.3.6 [DEVEL] 2011-09-19
+- added $InputRELPServerBindRuleset directive to specify rulesets for RELP
+- bugfix: config parser did not support properties with dashes in them
+ inside property-based filters. Thanks to Gerrit Seré for reporting this.
+---------------------------------------------------------------------------
+Version 6.3.5 [DEVEL] (rgerhards/al), 2011-09-01
+- bugfix/security: off-by-two bug in legacy syslog parser, CVE-2011-3200
+- bugfix: mark message processing did not work correctly
+- imudp&imtcp now report error if no listener at all was defined
+ Thanks to Marcin for suggesting this error message.
+- bugfix: potential misadressing in property replacer
+---------------------------------------------------------------------------
+Version 6.3.4 [DEVEL] (rgerhards), 2011-08-02
+- added support for action() config object
+ * in rsyslog core engine
+ * in omfile
+ * in omusrmsg
+- bugfix: omusrmsg format usr1,usr2 was no longer supported
+- bugfix: misaddressing in config handler
+ In theory, can cause segfault, in practice this is extremely unlikely
+ Thanks to Marcin for alertig me.
+---------------------------------------------------------------------------
+Version 6.3.3 [DEVEL] (rgerhards), 2011-07-13
+- rsyslog.conf format: now parsed by RainerScript parser
+ this provides the necessary base for future enhancements as well as some
+ minor immediate ones. For details see:
+ http://blog.gerhards.net/2011/07/rsyslog-633-config-format-improvements.html
+- performance of script-based filters notably increased
+- removed compatibility mode as we expect people have adjusted their
+ confs by now
+- added support for the ":omfile:" syntax for actions
+---------------------------------------------------------------------------
+Version 6.3.2 [DEVEL] (rgerhards), 2011-07-06
+- added support for the ":omusrmsg:" syntax in configuring user messages
+- systemd support: set stdout/stderr to null - thx to Lennart for the patch
+- added support for obtaining timestamp for kernel message from message
+ If the kernel time-stamps messages, time is now take from that
+ timestamp instead of the system time when the message was read. This
+ provides much better accuracy. Thanks to Lennart Poettering for
+ suggesting this feature and his help during implementation.
+- added support for obtaining timestamp from system for imuxsock
+ This permits to read the time a message was submitted to the system
+ log socket. Most importantly, this is provided in microsecond resolution.
+ So we are able to obtain high precision timestampis even for messages
+ that were - as is usual - not formatted with them. This also simplifies
+ things in regard to local time calculation in chroot environments.
+ Many thanks to Lennart Poettering for suggesting this feature,
+ providing some guidance on implementing it and coordinating getting the
+ necessary support into the Linux kernel.
+- bugfix: timestamp was incorrectly calculated for timezones with minute
+ offset
+ closes: http://bugzilla.adiscon.com/show_bug.cgi?id=271
+- bugfix: memory leak in imtcp & subsystems under some circumstances
+ This leak is tied to error conditions which lead to incorrect cleanup
+ of some data structures.
+---------------------------------------------------------------------------
+Version 6.3.1 [DEVEL] (rgerhards), 2011-06-07
+- added a first implementation of a DNS name cache
+ this still has a couple of weaknesses, like no expiration of entries,
+ suboptimal algorithms -- but it should perform much better than
+ what we had previously. Implementation will be improved based on
+ feedback during the next couple of releases
+---------------------------------------------------------------------------
+Version 6.3.0 [DEVEL] (rgerhards), 2011-06-01
+- introduced new config system
+ http://blog.gerhards.net/2011/06/new-rsyslog-config-system-materializes.html
+---------------------------------------------------------------------------
Version 6.2.2 [v6-stable], 2012-06-13
- build system improvements and spec file templates
Thanks to Abby Edwards for providing these enhancements
@@ -77,9 +297,11 @@ Version 6.2.0 [v6-stable], 2012-01-09
- bugfix: potential abort after reading invalid X.509 certificate
closes: http://bugzilla.adiscon.com/show_bug.cgi?id=290
Thanks to Tomas Heinrich for the patch
-- $Begin, $End, $StrictScoping directives have been removed as v6.4 will
- provide the same functionality in a far better way. So we do not want
- to clutter the code.
+- enhanced module loader to not rely on PATH_MAX
+- imuxsock: added capability to "annotate" messages with "trusted
+ information", which contains some properties obtained from the system
+ and as such sure to not be faked. This is inspired by the similiar idea
+ introduced in systemd.
---------------------------------------------------------------------------
Version 6.1.12 [BETA], 2011-09-01
- bugfix/security: off-by-two bug in legacy syslog parser, CVE-2011-3200
@@ -122,8 +344,6 @@ Version 6.1.9 [BETA] (rgerhards), 2011-06-14
this via a testbench test, not a user report. But I assume that some
users may have had unreproducable aborts that were cause by this bug.
- bugfix/improvement:$WorkDirectory now gracefully handles trailing slashes
----------------------------------------------------------------------------
-Version 6.1.9 [BETA] (rgerhards), 2011-06-14
- bugfix: memory leak in imtcp & subsystems under some circumstances
This leak is tied to error conditions which lead to incorrect cleanup
of some data structures. [backport from v6.3]
@@ -149,6 +369,13 @@ Version 6.1.7 [DEVEL] (rgerhards), 2011-04-15
- bugfix: IPv6-address could not be specified in omrelp
this was due to improper parsing of ":"
closes: http://bugzilla.adiscon.com/show_bug.cgi?id=250
+- bugfix: do not open files with full privileges, if privs will be dropped
+ This make the privilege drop code more bulletproof, but breaks Ubuntu's
+ work-around for log files created by external programs with the wrong
+ user and/or group. Note that it was long said that this "functionality"
+ would break once we go for serious privilege drop code, so hopefully
+ nobody still depends on it (and, if so, they lost...).
+- bugfix: pipes not opened in full priv mode when privs are to be dropped
---------------------------------------------------------------------------
Version 6.1.6 [DEVEL] (rgerhards), 2011-03-14
- enhanced omhdfs to support batching mode. This permits to increase
@@ -294,8 +521,176 @@ expected that interfaces, even new ones, break during the initial
syslog plain tcp input plugin (NOT supporting TLS!)
[ported from v4]
---------------------------------------------------------------------------
-Version 5.9.0 [V5-DEVEL] (rgerhards), 2011-03-??
+Version 5.9.8 [V5-BETA], 2012-05-??
+- bugfix: delayble source could block action queue, even if there was
+ a disk queue associated with it. The root cause of this problem was
+ that it makes no sense to delay messages once they arrive in the
+ action queue - the "input" that is being held in that case is the main
+ queue worker, what makes no sense.
+ Thanks to Marcin for alerting us on this problem and providing
+ instructions to reproduce it.
+- bugfix: disk queue was not persisted on shutdown, regression of fix to
+ http://bugzilla.adiscon.com/show_bug.cgi?id=299
+ The new code also handles the case of shutdown of blocking light and
+ full delayable sources somewhat smarter and permits, assuming sufficient
+ timouts, to persist message up to the max queue capacity. Also some nits
+ in debug instrumentation have been fixed.
+- add small delay (50ms) after sending shutdown message
+ There seem to be cases where the shutdown message is otherwise not
+ processed, not even on an idle system. Thanks to Marcin for
+ bringing this problem up.
+- support for resolving huge groups
+ closes: http://bugzilla.adiscon.com/show_bug.cgi?id=310
+ Thanks to Alec Warner for the patch
+- bugfix: potential hang due to mutex deadlock
+ closes: http://bugzilla.adiscon.com/show_bug.cgi?id=316
+ Thanks to Andreas Piesk for reporting&analyzing this bug as well as
+ providing patches and other help in resolving it.
+- bugfix: property PROCID empty instead of proper nilvalue if not present
+ If it is not present, it must have the nilvalue "-" as of RFC5424
+ closes: http://bugzilla.adiscon.com/show_bug.cgi?id=332
+ Thanks to John N for reporting this issue.
+---------------------------------------------------------------------------
+Version 5.9.7 [V5-BETA], 2012-05-10
+- added capability to specify substrings for field extraction mode
+- bugfix: ommysql did not properly init/exit the mysql runtime library
+ this could lead to segfaults. Triggering condition: multiple action
+ instances using ommysql. Thanks to Tomas Heinrich for reporting this
+ problem and providing an initial patch (which my solution is based on,
+ I need to add more code to clean the mess up).
+- bugfix: rsyslog did not terminate when delayable inputs were blocked
+ due to unvailable sources. Fixes:
+ http://bugzilla.adiscon.com/show_bug.cgi?id=299
+ Thanks to Marcin M for bringing up this problem and Andre Lorbach
+ for helping to reproduce and fix it.
+- bugfix/tcpflood: sending small test files did not work correctly
+---------------------------------------------------------------------------
+Version 5.9.6 [V5-BETA], 2012-04-12
+- added configuration directives to customize queue light delay marks
+- permit size modifiers (k,m,g,...) in integer config parameters
+ Thanks to Jo Rhett for the suggestion.
+- bugfix: hostname was not requeried on HUP
+ Thanks to Per Jessen for reporting this bug and Marius Tomaschewski for
+ his help in testing the fix.
+- bugfix: imklog invalidly computed facility and severity
+ closes: http://bugzilla.adiscon.com/show_bug.cgi?id=313
+- bugfix: imptcp input name could not be set
+ config directive was accepted, but had no effect
+- added configuration directive to disable octet-counted framing
+ for imtcp, directive is $InputTCPServerSupportOctetCountedFraming
+ for imptcp, directive is $InputPTCPServerSupportOctetCountedFraming
+- added capability to use a local interface IP address as fromhost-ip for
+ locally originating messages. New directive $LocalHostIPIF
+- added configuration directives to customize queue light delay marks
+ $MainMsgQueueLightDelayMark, $ActionQueueLightDelayMark; both
+ specify number of messages starting at which a delay happens.
+---------------------------------------------------------------------------
+Version 5.9.5 [V5-DEVEL], 2012-01-27
+- improved impstats subsystem, added many new counters
+- enhanced module loader to not rely on PATH_MAX
+- refactored imklog linux driver, now combined with BSD driver
+ The Linux driver no longer supports outdated kernel symbol resolution,
+ which was disabled by default for very long. Also overall cleanup,
+ resulting in much smaller code. Linux and BSD are now covered by a
+ single small driver.
+- $IMUXSockRateLimitInterval DEFAULT CHANGED, was 5, now 0
+ The new default turns off rate limiting. This was chosen as people
+ experienced problems with rate-limiting activated by default. Now it
+ needs an explicit opt-in by setting this parameter.
+ Thanks to Chris Gaffney for suggesting to make it opt-in; thanks to
+ many unnamed others who already had complained at the time Chris made
+ the suggestion ;-)
+---------------------------------------------------------------------------
+Version 5.9.4 [V5-DEVEL], 2011-11-29
+- imuxsock: added capability to "annotate" messages with "trusted
+ information", which contains some properties obtained from the system
+ and as such sure to not be faked. This is inspired by the similiar idea
+ introduced in systemd.
+- removed dependency on gcrypt for recently-enough GnuTLS
+ see: http://bugzilla.adiscon.com/show_bug.cgi?id=289
+- bugfix: imuxsock did no longer ignore message-provided timestamp, if
+ so configured (the *default*). Lead to no longer sub-second timestamps.
+ closes: http://bugzilla.adiscon.com/show_bug.cgi?id=281
+- bugfix: omfile returns fatal error code for things that go really wrong
+ previously, RS_RET_RESUME was returned, which lead to a loop inside the
+ rule engine as omfile could not really recover.
+- bugfix: rsyslogd -v always said 64 atomics were not present
+ thanks to mono_matsuko for the patch
+---------------------------------------------------------------------------
+Version 5.9.3 [V5-DEVEL], 2011-09-01
+- bugfix/security: off-by-two bug in legacy syslog parser, CVE-2011-3200
+- bugfix: mark message processing did not work correctly
+- added capability to emit config error location info for warnings
+ otherwise, omusrmsg's warning about new config format was not
+ accompanied by problem location.
+- bugfix: potential misadressing in property replacer
+- bugfix: MSGID corruption in RFC5424 parser under some circumstances
+ closes: http://bugzilla.adiscon.com/show_bug.cgi?id=275
+- bugfix: The NUL-Byte for the syslogtag was not copied in MsgDup (msg.c)
+---------------------------------------------------------------------------
+Version 5.9.2 [V5-DEVEL] (rgerhards), 2011-07-11
+- systemd support: set stdout/stderr to null - thx to Lennart for the patch
+- added support for the ":omusrmsg:" syntax in configuring user messages
+- added support for the ":omfile:" syntax for actions
+---------------------------------------------------------------------------
+Version 5.9.1 [V5-DEVEL] (rgerhards), 2011-06-30
+- added support for obtaining timestamp for kernel message from message
+ If the kernel time-stamps messages, time is now take from that
+ timestamp instead of the system time when the message was read. This
+ provides much better accuracy. Thanks to Lennart Poettering for
+ suggesting this feature and his help during implementation.
+- added support for obtaining timestamp from system for imuxsock
+ This permits to read the time a message was submitted to the system
+ log socket. Most importantly, this is provided in microsecond resolution.
+ So we are able to obtain high precision timestampis even for messages
+ that were - as is usual - not formatted with them. This also simplifies
+ things in regard to local time calculation in chroot environments.
+ Many thanks to Lennart Poettering for suggesting this feature,
+ providing some guidance on implementing it and coordinating getting the
+ necessary support into the Linux kernel.
+- bugfix: timestamp was incorrectly calculated for timezones with minute
+ offset
+ closes: http://bugzilla.adiscon.com/show_bug.cgi?id=271
+- bugfix: problems in failover action handling
+ closes: http://bugzilla.adiscon.com/show_bug.cgi?id=270
+ closes: http://bugzilla.adiscon.com/show_bug.cgi?id=254
+- bugfix: mutex was invalidly left unlocked during action processing
+ At least one case where this can occur is during thread shutdown, which
+ may be initiated by lower activity. In most cases, this is quite
+ unlikely to happen. However, if it does, data structures may be
+ corrupted which could lead to fatal failure and segfault. I detected
+ this via a testbench test, not a user report. But I assume that some
+ users may have had unreproducable aborts that were cause by this bug.
+- bugfix: memory leak in imtcp & subsystems under some circumstances
+ This leak is tied to error conditions which lead to incorrect cleanup
+ of some data structures. [backport from v6]
+- bugfix/improvement:$WorkDirectory now gracefully handles trailing slashes
+---------------------------------------------------------------------------
+Version 5.9.0 [V5-DEVEL] (rgerhards), 2011-06-08
+- imfile: added $InputFileMaxLinesAtOnce directive
+- enhanced imfile to support input batching
+- added capability for imtcp and imptcp to activate keep-alive packets
+ at the socket layer. This has not been added to imttcp, as the latter is
+ only an experimental module, and one which did not prove to be useful.
+ reference: http://kb.monitorware.com/post20791.html
+- added support to control KEEPALIVE settings in imptcp
+ this has not yet been added to imtcp, but could be done on request.
+- $ActionName is now also used for naming of queues in impstats
+ as well as in the debug output
+- bugfix: do not open files with full privileges, if privs will be dropped
+ This make the privilege drop code more bulletproof, but breaks Ubuntu's
+ work-around for log files created by external programs with the wrong
+ user and/or group. Note that it was long said that this "functionality"
+ would break once we go for serious privilege drop code, so hopefully
+ nobody still depends on it (and, if so, they lost...).
+- bugfix: pipes not opened in full priv mode when privs are to be dropped
- this begins a new devel branch for v5
+- better handling of queue i/o errors in disk queues. This is kind of a
+ bugfix, but a very intrusive one, this it goes into the devel version
+ first. Right now, "file not found" is handled and leads to the new
+ emergency mode, in which disk action is stopped and the queue run
+ in direct mode. An error message is emited if this happens.
+- added support for user-level PRI provided via systemd
- added new config directive $InputTCPFlowControl to select if tcp
received messages shall be flagged as light delayable or not.
- enhanced omhdfs to support batching mode. This permits to increase
@@ -314,6 +709,11 @@ Version 5.8.13 [V5-stable] 2012-06-??
- bugfix: randomized IP option header in omudpspoof caused problems
closes: http://bugzilla.adiscon.com/show_bug.cgi?id=327
Thanks to Rick Brown for helping to test out the patch.
+- bugfix: potential abort if output plugin logged message during shutdown
+ note that none of the rsyslog-provided plugins does this
+ Thanks to bodik and Rohit Prasad for alerting us on this bug and
+ analyzing it.
+ fixes: http://bugzilla.adiscon.com/show_bug.cgi?id=347
---------------------------------------------------------------------------
Version 5.8.12 [V5-stable] 2012-06-06
- add small delay (50ms) after sending shutdown message
@@ -402,6 +802,12 @@ Version 5.8.9 [V5-stable] 2012-03-15
stats subsystem.
---------------------------------------------------------------------------
Version 5.8.8 [V5-stable] 2012-03-05
+- added capability to use a local interface IP address as fromhost-ip for
+ imuxsock imklog
+ new config directives: $IMUXSockLocalIPIF, $klogLocalIPIF
+- added configuration directives to customize queue light delay marks
+ $MainMsgQueueLightDelayMark, $ActionQueueLightDelayMark; both
+ specify number of messages starting at which a delay happens.
- bugfix: omprog made rsyslog abort on startup if not binary to
execute was configured
- bugfix: imklog invalidly computed facility and severity
@@ -468,7 +874,7 @@ Version 5.8.4 [V5-stable] (al), 2011-08-10
Version 5.8.3 [V5-stable] (rgerhards), 2011-07-11
- systemd support: set stdout/stderr to null - thx to Lennart for the patch
- added support for the ":omusrmsg:" syntax in configuring user messages
-- added support for the ":omfile:" syntax in configuring user messages
+- added support for the ":omfile:" syntax for actions
Note: previous outchannel syntax will generate a warning message. This
may be surprising to some users, but it is quite urgent to alert them
of the new syntax as v6 can no longer support the previous one.
@@ -1373,7 +1779,7 @@ Version 4.6.8 [v4-stable] (rgerhards), 2011-09-01
---------------------------------------------------------------------------
Version 4.6.7 [v4-stable] (rgerhards), 2011-07-11
- added support for the ":omusrmsg:" syntax in configuring user messages
-- added support for the ":omfile:" syntax in configuring user messages
+- added support for the ":omfile:" syntax for actions
---------------------------------------------------------------------------
Version 4.6.6 [v4-stable] (rgerhards), 2011-06-24
- bugfix: memory leak in imtcp & subsystems under some circumstances
diff --git a/Makefile.am b/Makefile.am
index 2f2f546..befc1d4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -70,7 +70,7 @@ EXTRA_DIST = \
contrib/gnutls/key.pem \
rsyslog.service.in
-SUBDIRS = doc runtime . plugins/immark plugins/imuxsock plugins/imtcp plugins/imudp plugins/omtesting
+SUBDIRS = doc runtime grammar . plugins/immark plugins/imuxsock plugins/imtcp plugins/imudp plugins/omtesting
if ENABLE_RSYSLOGD
SUBDIRS += tools
@@ -120,10 +120,6 @@ if ENABLE_SMCUSTBINDCDR
SUBDIRS += plugins/sm_cust_bindcdr
endif
-if ENABLE_IMTEMPLATE
-SUBDIRS += plugins/imtemplate
-endif
-
if ENABLE_OMSTDOUT
SUBDIRS += plugins/omstdout
endif
@@ -152,14 +148,18 @@ if ENABLE_OMRULESET
SUBDIRS += plugins/omruleset
endif
-if ENABLE_OMDBALERTING
-SUBDIRS += plugins/omdbalerting
-endif
-
if ENABLE_OMUDPSPOOF
SUBDIRS += plugins/omudpspoof
endif
+if ENABLE_OMMONGODB
+SUBDIRS += plugins/ommongodb
+endif
+
+if ENABLE_OMHIREDIS
+SUBDIRS += plugins/omhiredis
+endif
+
if ENABLE_OMUXSOCK
SUBDIRS += plugins/omuxsock
endif
@@ -168,8 +168,8 @@ if ENABLE_OMHDFS
SUBDIRS += plugins/omhdfs
endif
-if ENABLE_OMTEMPLATE
-SUBDIRS += plugins/omtemplate
+if ENABLE_ELASTICSEARCH
+SUBDIRS += plugins/omelasticsearch
endif
if ENABLE_MMSNMPTRAPD
@@ -208,6 +208,14 @@ if ENABLE_MMNORMALIZE
SUBDIRS += plugins/mmnormalize
endif
+if ENABLE_MMJSONPARSE
+SUBDIRS += plugins/mmjsonparse
+endif
+
+if ENABLE_MMAUDIT
+SUBDIRS += plugins/mmaudit
+endif
+
if ENABLE_ORACLE
SUBDIRS += plugins/omoracle
endif
@@ -254,8 +262,7 @@ DISTCHECK_CONFIGURE_FLAGS= --enable-gssapi_krb5 \
--enable-pmaixforwardedfrom \
--enable-pmcisconames \
--enable-pmsnare \
- --enable-imtemplate \
- --enable-omtemplate \
--enable-mmsnmptrapd \
+ --enable-elasticsearch \
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
ACLOCAL_AMFLAGS = -I m4
diff --git a/action.c b/action.c
index e3a58ba..5db6d73 100644
--- a/action.c
+++ b/action.c
@@ -8,7 +8,7 @@
* the right code in question): For performance reasons, this module
* uses different methods of message submission based on the user-selected
* configuration. This code is similar, but can not be abstracted because
- * of the performanse-affecting differences in it. As such, it is often
+ * of the performance-affecting differences in it. As such, it is often
* necessary to triple-check that everything works well in *all* modes.
* The different modes (and calling sequence) are:
*
@@ -103,15 +103,17 @@
#include "template.h"
#include "action.h"
#include "modules.h"
-#include "sync.h"
#include "cfsysline.h"
#include "srUtils.h"
#include "errmsg.h"
#include "batch.h"
#include "wti.h"
+#include "rsconf.h"
#include "datetime.h"
#include "unicode-helper.h"
#include "atomic.h"
+#include "ruleset.h"
+#include "statsobj.h"
#define NO_TIME_PROVIDED 0 /* indicate we do not provide any cached time */
@@ -127,6 +129,8 @@ DEFobjCurrIf(obj)
DEFobjCurrIf(datetime)
DEFobjCurrIf(module)
DEFobjCurrIf(errmsg)
+DEFobjCurrIf(statsobj)
+DEFobjCurrIf(ruleset)
typedef struct configSettings_s {
@@ -175,6 +179,25 @@ configSettings_t cs_save; /* our saved (scope!) config settings */
*/
static int iActionNbr = 0;
+/* tables for interfacing with the v6 config system */
+static struct cnfparamdescr cnfparamdescr[] = {
+ { "name", eCmdHdlrGetWord, 0 }, /* legacy: actionname */
+ { "type", eCmdHdlrString, CNFPARAM_REQUIRED }, /* legacy: actionname */
+ { "action.writeallmarkmessages", eCmdHdlrBinary, 0 }, /* legacy: actionwriteallmarkmessages */
+ { "action.execonlyeverynthtime", eCmdHdlrInt, 0 }, /* legacy: actionexeconlyeverynthtime */
+ { "action.execonlyeverynthtimetimeout", eCmdHdlrInt, 0 }, /* legacy: actionexeconlyeverynthtimetimeout */
+ { "action.execonlyonceeveryinterval", eCmdHdlrInt, 0 }, /* legacy: actionexeconlyonceeveryinterval */
+ { "action.execonlywhenpreviousissuspended", eCmdHdlrInt, 0 }, /* legacy: actionexeconlywhenpreviousissuspended */
+ { "action.repeatedmsgcontainsoriginalmsg", eCmdHdlrBinary, 0 }, /* legacy: repeatedmsgcontainsoriginalmsg */
+ { "action.resumeretrycount", eCmdHdlrInt, 0 }, /* legacy: actionresumeretrycount */
+ { "action.resumeinterval", eCmdHdlrInt, 0 }
+};
+static struct cnfparamblk pblk =
+ { CNFPARAMBLK_VERSION,
+ sizeof(cnfparamdescr)/sizeof(struct cnfparamdescr),
+ cnfparamdescr
+ };
+
/* ------------------------------ methods ------------------------------ */
/* This function returns the "current" time for this action. Current time
@@ -269,13 +292,19 @@ rsRetVal actionDestruct(action_t *pThis)
qqueueDestruct(&pThis->pQueue);
}
+ /* destroy stats object, if we have one (may not always be
+ * be the case, e.g. if turned off)
+ */
+ if(pThis->statsobj != NULL)
+ statsobj.Destruct(&pThis->statsobj);
+
if(pThis->pMod != NULL)
pThis->pMod->freeInstance(pThis->pModData);
if(pThis->f_pMsg != NULL)
msgDestruct(&pThis->f_pMsg);
- SYNC_OBJ_TOOL_EXIT(pThis);
+ pthread_mutex_destroy(&pThis->mutAction);
pthread_mutex_destroy(&pThis->mutActExec);
d_free(pThis->pszName);
d_free(pThis->ppTpl);
@@ -288,6 +317,8 @@ rsRetVal actionDestruct(action_t *pThis)
/* create a new action descriptor object
* rgerhards, 2007-08-01
+ * Note that it is vital to set proper initial values as the v6 config
+ * system depends on these!
*/
rsRetVal actionConstruct(action_t **ppThis)
{
@@ -297,12 +328,19 @@ rsRetVal actionConstruct(action_t **ppThis)
ASSERT(ppThis != NULL);
CHKmalloc(pThis = (action_t*) calloc(1, sizeof(action_t)));
- pThis->iResumeInterval = cs.glbliActionResumeInterval;
- pThis->iResumeRetryCount = cs.glbliActionResumeRetryCount;
+ pThis->iResumeInterval = 30;
+ pThis->iResumeRetryCount = 0;
+ pThis->pszName = NULL;
+ pThis->bWriteAllMarkMsgs = FALSE;
+ pThis->iExecEveryNthOccur = 0;
+ pThis->iExecEveryNthOccurTO = 0;
+ pThis->iSecsExecOnceInterval = 0;
+ pThis->bExecWhenPrevSusp = 0;
+ pThis->bRepMsgHasMsg = 0;
pThis->tLastOccur = datetime.GetTime(NULL); /* done once per action on startup only */
pthread_mutex_init(&pThis->mutActExec, NULL);
+ pthread_mutex_init(&pThis->mutAction, NULL);
INIT_ATOMIC_HELPER_MUT(pThis->mutCAS);
- SYNC_OBJ_TOOL_INIT(pThis);
/* indicate we have a new action */
++iActionNbr;
@@ -316,16 +354,45 @@ finalize_it:
/* action construction finalizer
*/
rsRetVal
-actionConstructFinalize(action_t *pThis)
+actionConstructFinalize(action_t *pThis, struct cnfparamvals *queueParams)
{
DEFiRet;
- uchar pszQName[64]; /* friendly name of our queue */
+ uchar pszAName[64]; /* friendly name of our action */
ASSERT(pThis != NULL);
- /* find a name for our queue */
- snprintf((char*) pszQName, sizeof(pszQName)/sizeof(uchar), "action %d queue", iActionNbr);
+ /* generate a friendly name for us action stats */
+ if(pThis->pszName == NULL) {
+ snprintf((char*) pszAName, sizeof(pszAName)/sizeof(uchar), "action %d", iActionNbr);
+ } else {
+ ustrncpy(pszAName, pThis->pszName, sizeof(pszAName));
+ pszAName[sizeof(pszAName)-1] = '\0'; /* to be on the save side */
+ }
+
+ /* support statistics gathering */
+ CHKiRet(statsobj.Construct(&pThis->statsobj));
+ CHKiRet(statsobj.SetName(pThis->statsobj, pszAName));
+
+ STATSCOUNTER_INIT(pThis->ctrProcessed, pThis->mutCtrProcessed);
+ CHKiRet(statsobj.AddCounter(pThis->statsobj, UCHAR_CONSTANT("processed"),
+ ctrType_IntCtr, &pThis->ctrProcessed));
+
+ STATSCOUNTER_INIT(pThis->ctrFail, pThis->mutCtrFail);
+ CHKiRet(statsobj.AddCounter(pThis->statsobj, UCHAR_CONSTANT("failed"),
+ ctrType_IntCtr, &pThis->ctrFail));
+
+ CHKiRet(statsobj.ConstructFinalize(pThis->statsobj));
+
+ /* create our queue */
+ /* generate a friendly name for the queue */
+ if(pThis->pszName == NULL) {
+ snprintf((char*) pszAName, sizeof(pszAName)/sizeof(uchar), "action %d queue",
+ iActionNbr);
+ } else {
+ ustrncpy(pszAName, pThis->pszName, sizeof(pszAName));
+ pszAName[63] = '\0'; /* to be on the save side */
+ }
/* now check if we can run the action in "firehose mode" during stage one of
* its processing (that is before messages are enqueued into the action q).
* This is only possible if some features, which require strict sequence, are
@@ -368,47 +435,51 @@ actionConstructFinalize(action_t *pThis)
*/
CHKiRet(qqueueConstruct(&pThis->pQueue, cs.ActionQueType, 1, cs.iActionQueueSize,
(rsRetVal (*)(void*, batch_t*, int*))processBatchMain));
- obj.SetName((obj_t*) pThis->pQueue, pszQName);
+ obj.SetName((obj_t*) pThis->pQueue, pszAName);
+ qqueueSetpUsr(pThis->pQueue, pThis);
- /* ... set some properties ... */
-# define setQPROP(func, directive, data) \
- CHKiRet_Hdlr(func(pThis->pQueue, data)) { \
- errmsg.LogError(0, NO_ERRCODE, "Invalid " #directive ", error %d. Ignored, running with default setting", iRet); \
- }
-# define setQPROPstr(func, directive, data) \
- CHKiRet_Hdlr(func(pThis->pQueue, data, (data == NULL)? 0 : strlen((char*) data))) { \
- errmsg.LogError(0, NO_ERRCODE, "Invalid " #directive ", error %d. Ignored, running with default setting", iRet); \
+ if(queueParams == NULL) { /* use legacy params? */
+ /* ... set some properties ... */
+# define setQPROP(func, directive, data) \
+ CHKiRet_Hdlr(func(pThis->pQueue, data)) { \
+ errmsg.LogError(0, NO_ERRCODE, "Invalid " #directive ", \
+ error %d. Ignored, running with default setting", iRet); \
+ }
+# define setQPROPstr(func, directive, data) \
+ CHKiRet_Hdlr(func(pThis->pQueue, data, (data == NULL)? 0 : strlen((char*) data))) { \
+ errmsg.LogError(0, NO_ERRCODE, "Invalid " #directive ", \
+ error %d. Ignored, running with default setting", iRet); \
+ }
+ setQPROP(qqueueSetsizeOnDiskMax, "$ActionQueueMaxDiskSpace", cs.iActionQueMaxDiskSpace);
+ setQPROP(qqueueSetiDeqBatchSize, "$ActionQueueDequeueBatchSize", cs.iActionQueueDeqBatchSize);
+ setQPROP(qqueueSetMaxFileSize, "$ActionQueueFileSize", cs.iActionQueMaxFileSize);
+ setQPROPstr(qqueueSetFilePrefix, "$ActionQueueFileName", cs.pszActionQFName);
+ setQPROP(qqueueSetiPersistUpdCnt, "$ActionQueueCheckpointInterval", cs.iActionQPersistUpdCnt);
+ setQPROP(qqueueSetbSyncQueueFiles, "$ActionQueueSyncQueueFiles", cs.bActionQSyncQeueFiles);
+ setQPROP(qqueueSettoQShutdown, "$ActionQueueTimeoutShutdown", cs.iActionQtoQShutdown );
+ setQPROP(qqueueSettoActShutdown, "$ActionQueueTimeoutActionCompletion", cs.iActionQtoActShutdown);
+ setQPROP(qqueueSettoWrkShutdown, "$ActionQueueWorkerTimeoutThreadShutdown", cs.iActionQtoWrkShutdown);
+ setQPROP(qqueueSettoEnq, "$ActionQueueTimeoutEnqueue", cs.iActionQtoEnq);
+ setQPROP(qqueueSetiHighWtrMrk, "$ActionQueueHighWaterMark", cs.iActionQHighWtrMark);
+ setQPROP(qqueueSetiLowWtrMrk, "$ActionQueueLowWaterMark", cs.iActionQLowWtrMark);
+ setQPROP(qqueueSetiDiscardMrk, "$ActionQueueDiscardMark", cs.iActionQDiscardMark);
+ setQPROP(qqueueSetiDiscardSeverity, "$ActionQueueDiscardSeverity", cs.iActionQDiscardSeverity);
+ setQPROP(qqueueSetiMinMsgsPerWrkr, "$ActionQueueWorkerThreadMinimumMessages", cs.iActionQWrkMinMsgs);
+ setQPROP(qqueueSetbSaveOnShutdown, "$ActionQueueSaveOnShutdown", cs.bActionQSaveOnShutdown);
+ setQPROP(qqueueSetiDeqSlowdown, "$ActionQueueDequeueSlowdown", cs.iActionQueueDeqSlowdown);
+ setQPROP(qqueueSetiDeqtWinFromHr, "$ActionQueueDequeueTimeBegin", cs.iActionQueueDeqtWinFromHr);
+ setQPROP(qqueueSetiDeqtWinToHr, "$ActionQueueDequeueTimeEnd", cs.iActionQueueDeqtWinToHr);
+ } else {
+ /* we have v6-style config params */
+ qqueueSetDefaultsActionQueue(pThis->pQueue);
+ qqueueApplyCnfParam(pThis->pQueue, queueParams);
}
- qqueueSetpUsr(pThis->pQueue, pThis);
- setQPROP(qqueueSetsizeOnDiskMax, "$ActionQueueMaxDiskSpace", cs.iActionQueMaxDiskSpace);
- setQPROP(qqueueSetiDeqBatchSize, "$ActionQueueDequeueBatchSize", cs.iActionQueueDeqBatchSize);
- setQPROP(qqueueSetMaxFileSize, "$ActionQueueFileSize", cs.iActionQueMaxFileSize);
- setQPROPstr(qqueueSetFilePrefix, "$ActionQueueFileName", cs.pszActionQFName);
- setQPROP(qqueueSetiPersistUpdCnt, "$ActionQueueCheckpointInterval", cs.iActionQPersistUpdCnt);
- setQPROP(qqueueSetbSyncQueueFiles, "$ActionQueueSyncQueueFiles", cs.bActionQSyncQeueFiles);
- setQPROP(qqueueSettoQShutdown, "$ActionQueueTimeoutShutdown", cs.iActionQtoQShutdown );
- setQPROP(qqueueSettoActShutdown, "$ActionQueueTimeoutActionCompletion", cs.iActionQtoActShutdown);
- setQPROP(qqueueSettoWrkShutdown, "$ActionQueueWorkerTimeoutThreadShutdown", cs.iActionQtoWrkShutdown);
- setQPROP(qqueueSettoEnq, "$ActionQueueTimeoutEnqueue", cs.iActionQtoEnq);
- setQPROP(qqueueSetiHighWtrMrk, "$ActionQueueHighWaterMark", cs.iActionQHighWtrMark);
- setQPROP(qqueueSetiLowWtrMrk, "$ActionQueueLowWaterMark", cs.iActionQLowWtrMark);
- setQPROP(qqueueSetiDiscardMrk, "$ActionQueueDiscardMark", cs.iActionQDiscardMark);
- setQPROP(qqueueSetiDiscardSeverity, "$ActionQueueDiscardSeverity", cs.iActionQDiscardSeverity);
- setQPROP(qqueueSetiMinMsgsPerWrkr, "$ActionQueueWorkerThreadMinimumMessages", cs.iActionQWrkMinMsgs);
- setQPROP(qqueueSetbSaveOnShutdown, "$ActionQueueSaveOnShutdown", cs.bActionQSaveOnShutdown);
- setQPROP(qqueueSetiDeqSlowdown, "$ActionQueueDequeueSlowdown", cs.iActionQueueDeqSlowdown);
- setQPROP(qqueueSetiDeqtWinFromHr, "$ActionQueueDequeueTimeBegin", cs.iActionQueueDeqtWinFromHr);
- setQPROP(qqueueSetiDeqtWinToHr, "$ActionQueueDequeueTimeEnd", cs.iActionQueueDeqtWinToHr);
-
# undef setQPROP
# undef setQPROPstr
- dbgoprint((obj_t*) pThis->pQueue, "save on shutdown %d, max disk space allowed %lld\n",
- cs.bActionQSaveOnShutdown, cs.iActionQueMaxDiskSpace);
-
+ qqueueDbgPrint(pThis->pQueue);
- CHKiRet(qqueueStart(pThis->pQueue));
DBGPRINTF("Action %p: queue %p created\n", pThis, pThis->pQueue);
/* and now reset the queue params (see comment in its function header!) */
@@ -699,7 +770,8 @@ rsRetVal actionDbgPrint(action_t *pThis)
dbgprintf("%s: ", module.GetStateName(pThis->pMod));
pThis->pMod->dbgPrintInstInfo(pThis->pModData);
- dbgprintf("\n\tInstance data: 0x%lx\n", (unsigned long) pThis->pModData);
+ dbgprintf("\n");
+ dbgprintf("\tInstance data: 0x%lx\n", (unsigned long) pThis->pModData);
dbgprintf("\tRepeatedMsgReduction: %d\n", pThis->f_ReduceRepeated);
dbgprintf("\tResume Interval: %d\n", pThis->iResumeInterval);
if(pThis->eState == ACT_STATE_SUSP) {
@@ -1079,6 +1151,7 @@ submitBatch(action_t *pAction, batch_t *pBatch, int nElem)
&& pBatch->pElem[i].state != BATCH_STATE_COMM ) {
pBatch->pElem[i].state = BATCH_STATE_BAD;
pBatch->pElem[i].bPrevWasSuspended = 1;
+ STATSCOUNTER_INC(pAction->ctrFail, pAction->mutCtrFail);
}
}
bDone = 1;
@@ -1268,6 +1341,7 @@ doSubmitToActionQ(action_t *pAction, msg_t *pMsg)
{
DEFiRet;
+ STATSCOUNTER_INC(pAction->ctrProcessed, pAction->mutCtrProcessed);
if(pAction->pQueue->qType == QUEUETYPE_DIRECT)
iRet = qqueueEnqObjDirect(pAction->pQueue, (void*) MsgAddRef(pMsg));
else
@@ -1473,6 +1547,33 @@ finalize_it:
}
+/* helper to activateActions, it activates a specific action.
+ */
+DEFFUNC_llExecFunc(doActivateActions)
+{
+ action_t *pThis = (action_t*) pData;
+ BEGINfunc
+ qqueueStart(pThis->pQueue);
+ DBGPRINTF("Action %p: queue %p started\n", pThis, pThis->pQueue);
+ ENDfunc
+ return RS_RET_OK; /* we ignore errors, we can not do anything either way */
+}
+
+
+/* This function "activates" the action after privileges have been dropped. Currently,
+ * this means that the queues are started.
+ * rgerhards, 2011-05-02
+ */
+rsRetVal
+activateActions(void)
+{
+ DEFiRet;
+ iRet = ruleset.IterateAllActions(ourConf, doActivateActions, NULL);
+ RETiRet;
+}
+
+
+
/* This submits the message to the action queue in case where we need to handle
* bWriteAllMarkMessage == FALSE only. Note that we use a non-blocking CAS loop
* for the synchronization. Here, we just modify the filter condition to be false when
@@ -1544,6 +1645,18 @@ finalize_it:
RETiRet;
}
+static inline void
+countStatsBatchEnq(action_t *pAction, batch_t *pBatch)
+{
+ int i;
+ for(i = 0 ; i < batchNumMsgs(pBatch) && !*(pBatch->pbShutdownImmediate) ; ++i) {
+ if( pBatch->pElem[i].bFilterOK
+ && pBatch->pElem[i].state != BATCH_STATE_DISC) {
+ STATSCOUNTER_INC(pAction->ctrProcessed, pAction->mutCtrProcessed);
+ }
+ }
+}
+
/* enqueue a batch in direct mode. We have put this into its own function just to avoid
* cluttering the actual submit function.
@@ -1580,16 +1693,19 @@ doQueueEnqObjDirectBatch(action_t *pAction, batch_t *pBatch)
pBatch->pElem[i].bFilterOK = 0;
bModifiedFilter = 1;
}
- if(pBatch->pElem[i].bFilterOK)
+ if(pBatch->pElem[i].bFilterOK && pBatch->pElem[i].state != BATCH_STATE_DISC) {
+ STATSCOUNTER_INC(pAction->ctrProcessed, pAction->mutCtrProcessed);
bNeedSubmit = 1;
+ }
DBGPRINTF("action %p[%d]: filterOK:%d state:%d execWhenPrev:%d prevWasSusp:%d\n",
pAction, i, pBatch->pElem[i].bFilterOK, pBatch->pElem[i].state,
pAction->bExecWhenPrevSusp, pBatch->pElem[i].bPrevWasSuspended);
}
if(bNeedSubmit) {
+ /* note: stats were already computed above */
iRet = qqueueEnqObjDirectBatch(pAction->pQueue, pBatch);
} else {
- DBGPRINTF("no need to submit batch, all bFilterOK==0\n");
+ DBGPRINTF("no need to submit batch, all bFilterOK==0 or discarded\n");
}
if(bModifiedFilter) {
for(i = 0 ; i < batchNumMsgs(pBatch) ; ++i) {
@@ -1601,6 +1717,8 @@ doQueueEnqObjDirectBatch(action_t *pAction, batch_t *pBatch)
}
}
} else {
+ if(GatherStats)
+ countStatsBatchEnq(pAction, pBatch);
iRet = qqueueEnqObjDirectBatch(pAction->pQueue, pBatch);
}
@@ -1678,23 +1796,68 @@ doSubmitToActionQComplexBatch(action_t *pAction, batch_t *pBatch)
{
DEFiRet;
- LockObj(pAction);
- pthread_cleanup_push(mutexCancelCleanup, pAction->Sync_mut);
+ d_pthread_mutex_lock(&pAction->mutAction);
+ pthread_cleanup_push(mutexCancelCleanup, &pAction->mutAction);
iRet = helperSubmitToActionQComplexBatch(pAction, pBatch);
- UnlockObj(pAction);
+ d_pthread_mutex_unlock(&pAction->mutAction);
pthread_cleanup_pop(0); /* remove mutex cleanup handler */
RETiRet;
}
#pragma GCC diagnostic warning "-Wempty-body"
+
+/* apply all params from param block to action. This supports the v6 config system.
+ * Defaults must have been set appropriately during action construct!
+ * rgerhards, 2011-08-01
+ */
+static rsRetVal
+actionApplyCnfParam(action_t *pAction, struct cnfparamvals *pvals)
+{
+ int i;
+
+ for(i = 0 ; i < pblk.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(pblk.descr[i].name, "name")) {
+ pAction->pszName = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(pblk.descr[i].name, "type")) {
+ continue; /* this is handled seperately during module select! */
+ } else if(!strcmp(pblk.descr[i].name, "action.writeallmarkmessages")) {
+ pAction->bWriteAllMarkMsgs = pvals[i].val.d.n;
+ } else if(!strcmp(pblk.descr[i].name, "action.execonlyeverynthtime")) {
+ pAction->iExecEveryNthOccur = pvals[i].val.d.n;
+ } else if(!strcmp(pblk.descr[i].name, "action.execonlyeverynthtimetimeout")) {
+ pAction->iExecEveryNthOccurTO = pvals[i].val.d.n;
+ } else if(!strcmp(pblk.descr[i].name, "action.execonlyonceeveryinterval")) {
+ pAction->iSecsExecOnceInterval = pvals[i].val.d.n;
+ } else if(!strcmp(pblk.descr[i].name, "action.execonlywhenpreviousissuspended")) {
+ pAction->bExecWhenPrevSusp = pvals[i].val.d.n;
+ } else if(!strcmp(pblk.descr[i].name, "action.repeatedmsgcontainsoriginalmsg")) {
+ pAction->bRepMsgHasMsg = pvals[i].val.d.n;
+ } else if(!strcmp(pblk.descr[i].name, "action.resumeretrycount")) {
+ pAction->iResumeRetryCount = pvals[i].val.d.n;
+ } else if(!strcmp(pblk.descr[i].name, "action.resumeinterval")) {
+ pAction->iResumeInterval = pvals[i].val.d.n;
+ } else {
+ dbgprintf("action: program error, non-handled "
+ "param '%s'\n", pblk.descr[i].name);
+ }
+ }
+ return RS_RET_OK;
+}
+
+
+
/* add an Action to the current selector
* The pOMSR is freed, as it is not needed after this function.
* Note: this function pulls global data that specifies action config state.
* rgerhards, 2007-07-27
*/
rsRetVal
-addAction(action_t **ppAction, modInfo_t *pMod, void *pModData, omodStringRequest_t *pOMSR, int bSuspended)
+addAction(action_t **ppAction, modInfo_t *pMod, void *pModData,
+ omodStringRequest_t *pOMSR, struct cnfparamvals *actParams,
+ struct cnfparamvals *queueParams, int bSuspended)
{
DEFiRet;
int i;
@@ -1706,22 +1869,28 @@ addAction(action_t **ppAction, modInfo_t *pMod, void *pModData, omodStringReques
assert(ppAction != NULL);
assert(pMod != NULL);
assert(pOMSR != NULL);
- DBGPRINTF("Module %s processed this config line.\n", module.GetName(pMod));
+ DBGPRINTF("Module %s processes this action.\n", module.GetName(pMod));
CHKiRet(actionConstruct(&pAction)); /* create action object first */
pAction->pMod = pMod;
pAction->pModData = pModData;
- pAction->pszName = cs.pszActionName;
- cs.pszActionName = NULL; /* free again! */
- pAction->bWriteAllMarkMsgs = cs.bActionWriteAllMarkMsgs;
- cs.bActionWriteAllMarkMsgs = FALSE; /* reset */
- pAction->bExecWhenPrevSusp = cs.bActExecWhenPrevSusp;
- pAction->iSecsExecOnceInterval = cs.iActExecOnceInterval;
- pAction->iExecEveryNthOccur = cs.iActExecEveryNthOccur;
- pAction->iExecEveryNthOccurTO = cs.iActExecEveryNthOccurTO;
- pAction->bRepMsgHasMsg = cs.bActionRepMsgHasMsg;
- cs.iActExecEveryNthOccur = 0; /* auto-reset */
- cs.iActExecEveryNthOccurTO = 0; /* auto-reset */
+ if(actParams == NULL) { /* use legacy systemn */
+ pAction->pszName = cs.pszActionName;
+ pAction->iResumeInterval = cs.glbliActionResumeInterval;
+ pAction->iResumeRetryCount = cs.glbliActionResumeRetryCount;
+ pAction->bWriteAllMarkMsgs = cs.bActionWriteAllMarkMsgs;
+ pAction->bExecWhenPrevSusp = cs.bActExecWhenPrevSusp;
+ pAction->iSecsExecOnceInterval = cs.iActExecOnceInterval;
+ pAction->iExecEveryNthOccur = cs.iActExecEveryNthOccur;
+ pAction->iExecEveryNthOccurTO = cs.iActExecEveryNthOccurTO;
+ pAction->bRepMsgHasMsg = cs.bActionRepMsgHasMsg;
+ cs.iActExecEveryNthOccur = 0; /* auto-reset */
+ cs.iActExecEveryNthOccurTO = 0; /* auto-reset */
+ cs.bActionWriteAllMarkMsgs = FALSE; /* auto-reset */
+ cs.pszActionName = NULL; /* free again! */
+ } else {
+ actionApplyCnfParam(pAction, actParams);
+ }
/* check if we can obtain the template pointers - TODO: move to separate function? */
pAction->iNumTpls = OMSRgetEntryCount(pOMSR);
@@ -1740,7 +1909,9 @@ addAction(action_t **ppAction, modInfo_t *pMod, void *pModData, omodStringReques
/* Ok, we got everything, so it now is time to look up the template
* (Hint: templates MUST be defined before they are used!)
*/
- if((pAction->ppTpl[i] = tplFind((char*)pTplName, strlen((char*)pTplName))) == NULL) {
+ if( !(iTplOpts & OMSR_TPL_AS_MSG)
+ && (pAction->ppTpl[i] =
+ tplFind(ourConf, (char*)pTplName, strlen((char*)pTplName))) == NULL) {
snprintf(errMsg, sizeof(errMsg) / sizeof(char),
" Could not find template '%s' - action disabled\n",
pTplName);
@@ -1750,7 +1921,7 @@ addAction(action_t **ppAction, modInfo_t *pMod, void *pModData, omodStringReques
}
/* check required template options */
if( (iTplOpts & OMSR_RQD_TPL_OPT_SQL)
- && (pAction->ppTpl[i]->optFormatForSQL == 0)) {
+ && (pAction->ppTpl[i]->optFormatEscape == 0)) {
errno = 0;
errmsg.LogError(0, RS_RET_RQD_TPLOPT_MISSING, "Action disabled. To use this action, you have to specify "
"the SQL or stdSQL option in your template!\n");
@@ -1772,9 +1943,9 @@ addAction(action_t **ppAction, modInfo_t *pMod, void *pModData, omodStringReques
pAction->pMod = pMod;
pAction->pModData = pModData;
/* now check if the module is compatible with select features */
- if(pMod->isCompatibleWithFeature(sFEATURERepeatedMsgReduction) == RS_RET_OK)
- pAction->f_ReduceRepeated = bReduceRepeatMsgs;
- else {
+ if(pMod->isCompatibleWithFeature(sFEATURERepeatedMsgReduction) == RS_RET_OK) {
+ pAction->f_ReduceRepeated = loadConf->globals.bReduceRepeatMsgs;
+ } else {
DBGPRINTF("module is incompatible with RepeatedMsgReduction - turned off\n");
pAction->f_ReduceRepeated = 0;
}
@@ -1783,7 +1954,7 @@ addAction(action_t **ppAction, modInfo_t *pMod, void *pModData, omodStringReques
if(bSuspended)
actionSuspend(pAction, datetime.GetTime(NULL)); /* "good" time call, only during init and unavoidable */
- CHKiRet(actionConstructFinalize(pAction));
+ CHKiRet(actionConstructFinalize(pAction, queueParams));
/* TODO: if we exit here, we have a memory leak... */
@@ -1838,34 +2009,90 @@ initConfigVariables(void)
}
-/* save our config and create a new scope. Note that things are messed up if
- * this is called while the config is already saved (we currently do not
- * have a stack as the design is we need none!
- * rgerhards, 2010-07-23
- */
rsRetVal
-actionNewScope(void)
+actionNewInst(struct nvlst *lst, action_t **ppAction)
{
+ struct cnfparamvals *paramvals;
+ struct cnfparamvals *queueParams;
+ modInfo_t *pMod;
+ uchar *cnfModName = NULL;
+ omodStringRequest_t *pOMSR;
+ void *pModData;
+ action_t *pAction;
+ int typeIdx;
DEFiRet;
- memcpy(&cs_save, &cs, sizeof(cs));
- initConfigVariables();
+
+ paramvals = nvlstGetParams(lst, &pblk, NULL);
+ if(paramvals == NULL) {
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ dbgprintf("action param blk after actionNewInst:\n");
+ cnfparamsPrint(&pblk, paramvals);
+ typeIdx = cnfparamGetIdx(&pblk, "type");
+ if(paramvals[typeIdx].bUsed == 0) {
+ errmsg.LogError(0, RS_RET_CONF_RQRD_PARAM_MISSING, "action type missing");
+ ABORT_FINALIZE(RS_RET_CONF_RQRD_PARAM_MISSING); // TODO: move this into rainerscript handlers
+ }
+ cnfModName = (uchar*)es_str2cstr(paramvals[cnfparamGetIdx(&pblk, ("type"))].val.d.estr, NULL);
+ if((pMod = module.FindWithCnfName(loadConf, cnfModName, eMOD_OUT)) == NULL) {
+ errmsg.LogError(0, RS_RET_MOD_UNKNOWN, "module name '%s' is unknown", cnfModName);
+ ABORT_FINALIZE(RS_RET_MOD_UNKNOWN);
+ }
+ iRet = pMod->mod.om.newActInst(cnfModName, lst, &pModData, &pOMSR);
+ // TODO: check if RS_RET_SUSPENDED is still valid in v6!
+ if(iRet != RS_RET_OK && iRet != RS_RET_SUSPENDED) {
+ FINALIZE; /* iRet is already set to error state */
+ }
+
+ qqueueDoCnfParams(lst, &queueParams);
+
+ if((iRet = addAction(&pAction, pMod, pModData, pOMSR, paramvals, queueParams,
+ (iRet == RS_RET_SUSPENDED)? 1 : 0)) == RS_RET_OK) {
+ /* now check if the module is compatible with select features */
+ if(pMod->isCompatibleWithFeature(sFEATURERepeatedMsgReduction) == RS_RET_OK)
+ pAction->f_ReduceRepeated = loadConf->globals.bReduceRepeatMsgs;
+ else {
+ DBGPRINTF("module is incompatible with RepeatedMsgReduction - turned off\n");
+ pAction->f_ReduceRepeated = 0;
+ }
+ pAction->eState = ACT_STATE_RDY; /* action is enabled */
+ loadConf->actions.nbrActions++; /* one more active action! */
+ }
+ *ppAction = pAction;
+
+finalize_it:
+ free(cnfModName);
+ cnfparamvalsDestruct(paramvals, &pblk);
RETiRet;
}
-/* restore previously saved scope.
- * rgerhards, 2010-07-23
+/* Process a rsyslog v6 action config object (the now-primary config method).
+ * rgerhards, 2011-07-19
*/
rsRetVal
-actionRestoreScope(void)
+actionProcessCnf(struct cnfobj *o)
{
DEFiRet;
- memcpy(&cs, &cs_save, sizeof(cs));
+#if 0 /* we need to check if we actually need this functionality -- later! */
+// This is for STAND-ALONE actions at the conf file TOP level
+ struct cnfparamvals *paramvals;
+
+ paramvals = nvlstGetParams(o->nvlst, &pblk, NULL);
+ if(paramvals == NULL) {
+ iRet = RS_RET_ERR;
+ goto finalize_it;
+ }
+ DBGPRINTF("action param blk after actionProcessCnf:\n");
+ cnfparamsPrint(&pblk, paramvals);
+
+ /* now find module to activate */
+finalize_it:
+#endif
RETiRet;
}
-
/* TODO: we are not yet a real object, the ClassInit here just looks like it is..
*/
rsRetVal actionClassInit(void)
@@ -1876,6 +2103,8 @@ rsRetVal actionClassInit(void)
CHKiRet(objUse(datetime, CORE_COMPONENT));
CHKiRet(objUse(module, CORE_COMPONENT));
CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(statsobj, CORE_COMPONENT));
+ CHKiRet(objUse(ruleset, CORE_COMPONENT));
CHKiRet(regCfSysLineHdlr((uchar *)"actionname", 0, eCmdHdlrGetWord, NULL, &cs.pszActionName, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuefilename", 0, eCmdHdlrGetWord, NULL, &cs.pszActionQFName, NULL));
diff --git a/action.h b/action.h
index a06b7c4..5f142b0 100644
--- a/action.h
+++ b/action.h
@@ -26,7 +26,6 @@
#define ACTION_H_INCLUDED 1
#include "syslogd-types.h"
-#include "sync.h"
#include "queue.h"
/* external data - this is to be removed when we change the action
@@ -85,17 +84,21 @@ struct action_s {
* processed - it is also used to detect duplicates.
*/
qqueue_t *pQueue; /* action queue */
- SYNC_OBJ_TOOL; /* required for mutex support */
+ pthread_mutex_t mutAction; /* primary action mutex */
pthread_mutex_t mutActExec; /* mutex to guard actual execution of doAction for single-threaded modules */
uchar *pszName; /* action name (for documentation) */
DEF_ATOMIC_HELPER_MUT(mutCAS);
+ /* for statistics subsystem */
+ statsobj_t *statsobj;
+ STATSCOUNTER_DEF(ctrProcessed, mutCtrProcessed);
+ STATSCOUNTER_DEF(ctrFail, mutCtrFail);
};
/* function prototypes
*/
rsRetVal actionConstruct(action_t **ppThis);
-rsRetVal actionConstructFinalize(action_t *pThis);
+rsRetVal actionConstructFinalize(action_t *pThis, struct cnfparamvals *queueParams);
rsRetVal actionDestruct(action_t *pThis);
rsRetVal actionDbgPrint(action_t *pThis);
rsRetVal actionSetGlobalResumeInterval(int iNewVal);
@@ -103,8 +106,8 @@ rsRetVal actionDoAction(action_t *pAction);
rsRetVal actionWriteToAction(action_t *pAction);
rsRetVal actionCallHUPHdlr(action_t *pAction);
rsRetVal actionClassInit(void);
-rsRetVal addAction(action_t **ppAction, modInfo_t *pMod, void *pModData, omodStringRequest_t *pOMSR, int bSuspended);
-rsRetVal actionNewScope(void);
-rsRetVal actionRestoreScope(void);
+rsRetVal addAction(action_t **ppAction, modInfo_t *pMod, void *pModData, omodStringRequest_t *pOMSR, struct cnfparamvals *actParams, struct cnfparamvals *queueParams, int bSuspended);
+rsRetVal activateActions(void);
+rsRetVal actionNewInst(struct nvlst *lst, action_t **ppAction);
#endif /* #ifndef ACTION_H_INCLUDED */
diff --git a/configure.ac b/configure.ac
index 6be773a..92aeebc 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],[6.2.2],[rsyslog@lists.adiscon.com])
+AC_INIT([rsyslog],[6.4.0],[rsyslog@lists.adiscon.com])
AM_INIT_AUTOMAKE
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -17,13 +17,9 @@ AC_GNU_SOURCE
AC_CHECK_PROG(have_valgrind, [valgrind], [yes])
AM_CONDITIONAL(HAVE_VALGRIND, test x$have_valgrind = xyes)
-# check for Java compiler
-AC_CHECK_PROG(HAVE_JAVAC, [javac], [yes])
-if test x"$HAVE_JAVAC" = x""; then
- AC_MSG_WARN([no javac found, disabling features depending on it])
-fi
-
# Checks for programs.
+AC_PROG_LEX
+AC_PROG_YACC
AC_PROG_CC
AM_PROG_CC_C_O
if test "$GCC" = "yes"
@@ -36,11 +32,12 @@ AC_CANONICAL_HOST
PKG_PROG_PKG_CONFIG
# modules we require
-PKG_CHECK_MODULES(LIBESTR, libestr >= 0.1.0)
-PKG_CHECK_MODULES(LIBEE, libee >= 0.3.1)
+PKG_CHECK_MODULES(LIBESTR, libestr >= 0.1.2)
+PKG_CHECK_MODULES(LIBEE, libee >= 0.4.0)
case "${host}" in
*-*-linux*)
+ AC_DEFINE([OS_LINUX], [1], [Indicator for a Linux OS])
os_type="linux"
;;
*-*-*darwin*|*-*-dragonfly*|*-*-freebsd*|*-*-netbsd*|*-*-openbsd*)
@@ -122,6 +119,8 @@ AC_CHECK_FUNCS([flock basename alarm clock_gettime gethostbyname gethostname get
# let me know! -- rgerhards, 2010-10-06
AC_CHECK_DECL([SCM_CREDENTIALS], [AC_DEFINE(HAVE_SCM_CREDENTIALS, [1], [set define])], [], [#include <sys/types.h>
#include <sys/socket.h>])
+AC_CHECK_DECL([SO_TIMESTAMP], [AC_DEFINE(HAVE_SO_TIMESTAMP, [1], [set define])], [], [#include <sys/types.h>
+#include <sys/socket.h>])
# Check for MAXHOSTNAMELEN
AC_MSG_CHECKING(for MAXHOSTNAMELEN)
@@ -689,6 +688,40 @@ AC_SUBST(SNMP_CFLAGS)
AC_SUBST(SNMP_LIBS)
+# elasticsearch support
+AC_ARG_ENABLE(elasticsearch,
+ [AS_HELP_STRING([--enable-elasticsearch],[Enable elasticsearch output module @<:@default=no@:>@])],
+ [case "${enableval}" in
+ yes) enable_elasticsearch="yes" ;;
+ no) enable_elasticsearch="no" ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-elasticsearch) ;;
+ esac],
+ [enable_elasticsearch=no]
+)
+if test "x$enable_elasticsearch" = "xyes"; then
+ AC_CHECK_PROG(
+ [HAVE_CURL_CONFIG],
+ [curl-config],
+ [yes],,,
+ )
+ if test "x${HAVE_CURL_CONFIG}" != "xyes"; then
+ AC_MSG_FAILURE([curl-config not found in PATH])
+ fi
+ AC_CHECK_LIB(
+ [curl],
+ [curl_global_init],
+ [CURL_CFLAGS="`curl-config --cflags`"
+ CURL_LIBS="`curl-config --libs`"
+ ],
+ [AC_MSG_FAILURE([curl library is missing])],
+ [`curl-config --libs --cflags`]
+ )
+fi
+AM_CONDITIONAL(ENABLE_ELASTICSEARCH, test x$enable_elasticsearch = xyes)
+AC_SUBST(CURL_CFLAGS)
+AC_SUBST(CURL_LIBS)
+
+
# GnuTLS support
AC_ARG_ENABLE(gnutls,
[AS_HELP_STRING([--enable-gnutls],[Enable GNU TLS support @<:@default=no@:>@])],
@@ -719,10 +752,15 @@ AC_ARG_ENABLE(rsyslogrt,
[enable_rsyslogrt=yes]
)
if test "x$enable_rsyslogrt" = "xyes"; then
- RSRT_CFLAGS="-I\$(top_srcdir)/runtime -I\$(top_srcdir)"
- RSRT_LIBS="\$(top_builddir)/runtime/librsyslog.la"
+ RSRT_CFLAGS1="-I\$(top_srcdir)/runtime -I\$(top_srcdir) -I\$(top_srcdir)/grammar"
+ RSRT_LIBS1="\$(top_builddir)/runtime/librsyslog.la"
+ #??CNF_LIBS="\$(top_builddir)/grammar/libgrammar.la"
fi
AM_CONDITIONAL(ENABLE_RSYSLOGRT, test x$enable_rsyslogrt = xyes)
+RSRT_CFLAGS="\$(RSRT_CFLAGS1) \$(LIBESTR_CFLAGS)"
+RSRT_LIBS="\$(RSRT_LIBS1) \$(LIBESTR_LIBS)"
+AC_SUBST(RSRT_CFLAGS1)
+AC_SUBST(RSRT_LIBS1)
AC_SUBST(RSRT_CFLAGS)
AC_SUBST(RSRT_LIBS)
@@ -822,6 +860,39 @@ AC_SUBST(LOGNORM_CFLAGS)
AC_SUBST(LOGNORM_LIBS)
+# mmnjsonparse
+AC_ARG_ENABLE(mmjsonparse,
+ [AS_HELP_STRING([--enable-mmjsonparse],[Enable building mmjsonparse support @<:@default=no@:>@])],
+ [case "${enableval}" in
+ yes) enable_mmjsonparse="yes" ;;
+ no) enable_mmjsonparse="no" ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-mmjsonparse) ;;
+ esac],
+ [enable_mmjsonparse=no]
+)
+if test "x$enable_mmjsonparse" = "xyes"; then
+ PKG_CHECK_MODULES(LIBLOGNORM, lognorm >= 0.3.1)
+fi
+AM_CONDITIONAL(ENABLE_MMJSONPARSE, test x$enable_mmjsonparse = xyes)
+
+
+
+# mmaudit
+AC_ARG_ENABLE(mmaudit,
+ [AS_HELP_STRING([--enable-mmaudit],[Enable building mmaudit support @<:@default=no@:>@])],
+ [case "${enableval}" in
+ yes) enable_mmaudit="yes" ;;
+ no) enable_mmaudit="no" ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-mmaudit) ;;
+ esac],
+ [enable_mmaudit=no]
+)
+if test "x$enable_mmaudit" = "xyes"; then
+ PKG_CHECK_MODULES(LIBLOGNORM, lognorm >= 0.3.1)
+fi
+AM_CONDITIONAL(ENABLE_MMAUDIT, test x$enable_mmaudit = xyes)
+
+
# RELP support
AC_ARG_ENABLE(relp,
[AS_HELP_STRING([--enable-relp],[Enable RELP support @<:@default=no@:>@])],
@@ -1070,20 +1141,6 @@ AC_ARG_ENABLE(omruleset,
AM_CONDITIONAL(ENABLE_OMRULESET, test x$enable_omruleset = xyes)
-# settings for omdbalerting
-AC_ARG_ENABLE(omdbalerting,
- [AS_HELP_STRING([--enable-omdbalerting],[Compiles omdbalerting module @<:@default=no@:>@])],
- [case "${enableval}" in
- yes) enable_omdbalerting="yes" ;;
- no) enable_omdbalerting="no" ;;
- *) AC_MSG_ERROR(bad value ${enableval} for --enable-omdbalerting) ;;
- esac],
- [enable_omdbalerting=no]
-)
-AM_CONDITIONAL(ENABLE_OMDBALERTING, test x$enable_omdbalerting = xyes)
-
-
-
# building the GUI (mostly for diagnostic reasons)
AC_ARG_ENABLE(gui,
[AS_HELP_STRING([--enable-gui],[Enable GUI programs @<:@default=no@:>@])],
@@ -1147,46 +1204,6 @@ AC_ARG_ENABLE(sm_cust_bindcdr,
AM_CONDITIONAL(ENABLE_SMCUSTBINDCDR, test x$enable_sm_cust_bindcdr = xyes)
-# settings for the template input module; copy and modify this code
-# if you intend to add your own module. Be sure to replace imtemplate
-# by the actual name of your module.
-AC_ARG_ENABLE(imtemplate,
- [AS_HELP_STRING([--enable-imtemplate],[Compiles imtemplate template module @<:@default=no@:>@])],
- [case "${enableval}" in
- yes) enable_imtemplate="yes" ;;
- no) enable_imtemplate="no" ;;
- *) AC_MSG_ERROR(bad value ${enableval} for --enable-imtemplate) ;;
- esac],
- [enable_imtemplate=no]
-)
-#
-# you may want to do some library checks here - see snmp, mysql, pgsql modules
-# for samples
-#
-AM_CONDITIONAL(ENABLE_IMTEMPLATE, test x$enable_imtemplate = xyes)
-# end of copy template - be sure to search for imtemplate to find everything!
-
-
-# settings for the template output module; copy and modify this code
-# if you intend to add your own module. Be sure to replace omtemplate
-# by the actual name of your module.
-AC_ARG_ENABLE(omtemplate,
- [AS_HELP_STRING([--enable-omtemplate],[Compiles omtemplate template module @<:@default=no@:>@])],
- [case "${enableval}" in
- yes) enable_omtemplate="yes" ;;
- no) enable_omtemplate="no" ;;
- *) AC_MSG_ERROR(bad value ${enableval} for --enable-omtemplate) ;;
- esac],
- [enable_omtemplate=no]
-)
-#
-# you may want to do some library checks here - see snmp, mysql, pgsql modules
-# for samples
-#
-AM_CONDITIONAL(ENABLE_OMTEMPLATE, test x$enable_omtemplate = xyes)
-# end of copy template - be sure to search for omtemplate to find everything!
-
-
# settings for mmsnmptrapd message modification module
AC_ARG_ENABLE(mmsnmptrapd,
[AS_HELP_STRING([--enable-mmsnmptrapd],[Compiles mmsnmptrapd module @<:@default=no@:>@])],
@@ -1232,12 +1249,38 @@ AC_ARG_ENABLE(ommongodb,
# you may want to do some library checks here - see snmp, mysql, pgsql modules
# for samples
#
+if test "x$enable_ommongodb" = "xyes"; then
+ PKG_CHECK_MODULES(LIBMONGO_CLIENT, libmongo-client >= 0.1.4)
+ AC_SUBST(LIBMONGO_CLIENT_CFLAGS)
+ AC_SUBST(LIBMONGO_CLIENT_LIBS)
+fi
AM_CONDITIONAL(ENABLE_OMMONGODB, test x$enable_ommongodb = xyes)
-# end of copy template - be sure to search for omtemplate to find everything!
+# end of mongodb code
+
+# HIREDIS SUPPORT
+
+AC_ARG_ENABLE(omhiredis,
+ [AS_HELP_STRING([--enable-omhiredis],[Compiles omhiredis template module @<:@default=no@:>@])],
+ [case "${enableval}" in
+ yes) enable_omhiredis="yes" ;;
+ no) enable_omhiredis="no" ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-omhiredis) ;;
+ esac],
+ [enable_omhiredis=no]
+)
+#
+if test "x$enable_omhiredis" = "xyes"; then
+ PKG_CHECK_MODULES(HIREDIS, hiredis >= 0.10.1)
+ AC_SUBST(HIREDIS_CFLAGS)
+ AC_SUBST(HIREDIS_LIBS)
+fi
+AM_CONDITIONAL(ENABLE_OMHIREDIS, test x$enable_omhiredis = xyes)
+# END HIREDIS SUPPORT
AC_CONFIG_FILES([Makefile \
runtime/Makefile \
+ grammar/Makefile \
tools/Makefile \
doc/Makefile \
plugins/imudp/Makefile \
@@ -1247,8 +1290,6 @@ AC_CONFIG_FILES([Makefile \
plugins/imuxsock/Makefile \
plugins/immark/Makefile \
plugins/imklog/Makefile \
- plugins/imtemplate/Makefile \
- plugins/omtemplate/Makefile \
plugins/omhdfs/Makefile \
plugins/omprog/Makefile \
plugins/omstdout/Makefile \
@@ -1258,7 +1299,6 @@ AC_CONFIG_FILES([Makefile \
plugins/pmsnare/Makefile \
plugins/pmaixforwardedfrom/Makefile \
plugins/omruleset/Makefile \
- plugins/omdbalerting/Makefile \
plugins/omuxsock/Makefile \
plugins/imfile/Makefile \
plugins/imsolaris/Makefile \
@@ -1277,7 +1317,12 @@ AC_CONFIG_FILES([Makefile \
plugins/omsnmp/Makefile \
plugins/omoracle/Makefile \
plugins/omudpspoof/Makefile \
+ plugins/ommongodb/Makefile \
+ plugins/omhiredis/Makefile \
plugins/mmnormalize/Makefile \
+ plugins/mmjsonparse/Makefile \
+ plugins/mmaudit/Makefile \
+ plugins/omelasticsearch/Makefile \
plugins/sm_cust_bindcdr/Makefile \
plugins/mmsnmptrapd/Makefile \
plugins/cust1/Makefile \
@@ -1306,19 +1351,16 @@ echo " imdiag enabled: $enable_imdiag"
echo " file input module enabled: $enable_imfile"
echo " Solaris input module enabled: $enable_imsolaris"
echo " periodic statistics module enabled: $enable_impstats"
-echo " input template module will be compiled: $enable_imtemplate"
echo
echo "---{ output plugins }---"
echo " Mail support enabled: $enable_mail"
echo " omprog module will be compiled: $enable_omprog"
-echo " output mongodb module will be compiled: $enable_ommongodb"
echo " omstdout module will be compiled: $enable_omstdout"
echo " omhdfs module will be compiled: $enable_omhdfs"
+echo " omelasticsearch module will be compiled: $enable_elasticsearch"
echo " omruleset module will be compiled: $enable_omruleset"
-echo " omdbalerting module will be compiled: $enable_omdbalerting"
echo " omudpspoof module will be compiled: $enable_omudpspoof"
echo " omuxsock module will be compiled: $enable_omuxsock"
-echo " output template module will be compiled: $enable_omtemplate"
echo
echo "---{ parser modules }---"
echo " pmrfc3164sd module will be compiled: $enable_pmrfc3164sd"
@@ -1329,6 +1371,8 @@ echo " pmsnare module will be compiled: $enable_pmsnare"
echo
echo "---{ message modification modules }---"
echo " mmnormalize module will be compiled: $enable_mmnormalize"
+echo " mmjsonparse module will be compiled: $enable_mmjsonparse"
+echo " mmjaduit module will be compiled: $enable_mmaudit"
echo " mmsnmptrapd module will be compiled: $enable_mmsnmptrapd"
echo
echo "---{ strgen modules }---"
@@ -1338,6 +1382,8 @@ echo "---{ database support }---"
echo " MySql support enabled: $enable_mysql"
echo " libdbi support enabled: $enable_libdbi"
echo " PostgreSQL support enabled: $enable_pgsql"
+echo " mongodb support enabled: $enable_ommongodb"
+echo " hiredis support enabled: $enable_omhiredis"
echo " Oracle (OCI) support enabled: $enable_oracle"
echo
echo "---{ protocol support }---"
diff --git a/dirty.h b/dirty.h
index 0428a00..a3940cb 100644
--- a/dirty.h
+++ b/dirty.h
@@ -30,7 +30,7 @@
rsRetVal multiSubmitMsg(multi_submit_t *pMultiSub);
rsRetVal submitMsg(msg_t *pMsg);
rsRetVal logmsgInternal(int iErr, int pri, uchar *msg, int flags);
-rsRetVal parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int flags, flowControl_t flowCtlTypeu, prop_t *pInputName, struct syslogTime *stTime, time_t ttGenTime);
+rsRetVal parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int flags, flowControl_t flowCtlTypeu, prop_t *pInputName, struct syslogTime *stTime, time_t ttGenTime, ruleset_t *pRuleset);
rsRetVal diagGetMainMsgQSize(int *piSize); /* for imdiag */
rsRetVal createMainQueue(qqueue_t **ppQueue, uchar *pszQueueName);
@@ -41,8 +41,9 @@ rsRetVal createMainQueue(qqueue_t **ppQueue, uchar *pszQueueName);
*/
extern int MarkInterval;
extern int repeatinterval[2];
-extern int bReduceRepeatMsgs;
extern qqueue_t *pMsgQueue; /* the main message queue */
+extern int iConfigVerify; /* is this just a config verify run? */
+extern int bHaveMainQueue;
#define MAXREPEAT ((int)((sizeof(repeatinterval) / sizeof(repeatinterval[0])) - 1))
#define REPEATTIME(f) ((f)->f_time + repeatinterval[(f)->f_repeatcount])
#define BACKOFF(f) { if (++(f)->f_repeatcount > MAXREPEAT) \
diff --git a/doc/design.tex b/doc/design.tex
index a3ec8f4..1def3fb 100644
--- a/doc/design.tex
+++ b/doc/design.tex
@@ -811,10 +811,80 @@ b) we push the failed message back to the main queue, but with an indication
that it failed in an action. This is harder to implement and most importantly
harder to understand/configure, but more flexible
+\section{Configuration System}
+The configration system found in all versions up to v5 is based on sysklogd's
+legacy. It does not have any clear distinction between config load and
+activation. Starting with v6, a new config system is build. That new system
+offers the necessary distinction. In the long term, the configuration language
+will be enhanced towards the more flexible and easy to use RainerScript idea.
+
+\section{Plugin Interface}
+This section describes some aspects of the plugin interface.
+\subsection{Configuration Related}
+To support the new v2 config system, plugins need to publish a number of entry
+points that will be called by the rsyslog configuration section at various
+stages of the configration load, activation and deactivation process. This list
+may be extended as the configuration interface evolves.
+
+Plugins must not necessarily implement support for the v2 config system. If
+they do, the ``beginCnfLoad'' entry point serves as a flag telling that support
+is available. In that case, all other entry points need to be defined as well.
+If a module does not support the v2 config system, it can still be run, but be
+configured only via the legacy config system. Note that with the old system
+there are also problems with droping privileges. So a legacy module may not
+work correctly if privileges are dropped.
+
+The following entry points are available:
+\begin{enumerate}
+ \item \emph{beginCnfLoad} -- called when a new config load begins. Only one
+config load can be active at one time (no concurrent loads).
+ \item \emph{endCnfLoad} -- called when config load ends. This gives the module
+a chance to do final changes and some cleanup.
+ \item \emph{checkCnf} -- called by the framework to verify a configuration.
+ \item \emph{activateCnfPrePrivDrop} -- called by the framework to activate a
+configuration before privileges are dropped. This is an optional entry point
+that shall only be implemented by plugins that need the do some processing
+before rsyslog drops privileges. Processing inside this entry point should be
+limited to what is absolutely necessary. The main activation work should be
+done in activateCnf() as usual.
+ \item \emph{activateCnf} -- called by the framework to activate a
+configuration.
+\item \emph{freeCnf} -- called by the framework to free
+(deallocate) a configuration.
+\end{enumerate}
+
+In the current implementation, entry points are sequentially called as given
+above. However, this will change. It is guaranteed that
+\begin{itemize}
+ \item beginCnfLoad() will be followed by a matching endCnfLoad() and there
+will be no new call to beginCnfLoad() before endCnfLoad() has been called. This
+means no nested config load needs to be supported,
+ \item checkCnf() may be called at any time, even during a config load phase.
+However, the config to check is a fully loaded one.
+ \item activateCnfPrePrivDrop(), if provided, will always be called before
+activateCnf() is called. No other config-related calls will be made in between.
+\end{itemize}
+
+\subsubsection{Output Modules}
+The v1 config load system for output modules seems to provide all functionality
+necessary to support the v2 system as well. As such, we currently do not
+require output modules to implement the new calls to be fully supported by the
+v2 system.
+
\section{Network Stream Subsystem}
-The idea of network streams was introduced when we implemented RFC5425 (syslog over TLS) in 2008. The core idea is to encapsulate all stream-oriented network data transfer into a single transport layer and make the upper layers independent of actual transport being used. This is in line with the traditional layer approaches in communication systems.
+The idea of network streams was introduced when we implemented RFC5425 (syslog
+over TLS) in 2008. The core idea is to encapsulate all stream-oriented network
+data transfer into a single transport layer and make the upper layers
+independent of actual transport being used. This is in line with the traditional
+layer approaches in communication systems.
+
+Under this system, the upper layer provides plugins to send and receive streams
+of syslog data. Framing is provided by the upper layer. The upper layer itself
+is integrated in input and output plugins, which then are used to provide
+application-level syslog message objects to and from the rsyslog core. To these
+upper layers, the netstream layer provides reliable and sequenced message
+delivery with much of the same semantics as a usual TCP stream.
-Under this system, the upper layer provides plugins to send and receive streams of syslog data. Framing is provided by the upper layer. The upper layer itself is integrated in input and output plugins, which then are used to provide application-level syslog message objects to and from the rsyslog core. To these upper layers, the netstream layer provides reliable and sequenced message delivery with much of the same semantics as a usual TCP stream.
\begin{figure}
\begin{center}
diff --git a/doc/imfile.html b/doc/imfile.html
index c44171d..7961729 100644
--- a/doc/imfile.html
+++ b/doc/imfile.html
@@ -100,9 +100,16 @@ performance, especially when set to a low value. Frequently writing the state
file is very time consuming.
<li><b>$InputFileReadMode</b> [mode]</b><br>
Available in 5.7.5+
+<li><b>$InputFileMaxLinesAtOnce</b> [number]</b><br>
+Available in 5.9.0+
<br>
-Mode to be used when reading lines. 0 (the default) means that each line is forwarded
-as its own log message.
+This is useful if multiple files need to be monitored. If set to 0, each file
+will be fully processed and then processing switches to the next file
+(this was the default in previous versions). If it is set, a maximum of
+[number] lines is processed in sequence for each file, and then the file is
+switched. This provides a kind of mutiplexing the load of multiple files and
+probably leads to a more natural distribution of events when multiple busy files
+are monitored. The default is 10240.
<li>$InputFileBindRuleset &lt;ruleset&gt;<br>
Available in 5.7.5+, 6.1.5+
Binds the listener to a specific <a href="multi_ruleset.html">ruleset</a>.</li>
diff --git a/doc/imklog.html b/doc/imklog.html
index f273753..2e3b3bc 100644
--- a/doc/imklog.html
+++ b/doc/imklog.html
@@ -36,6 +36,11 @@ processing.<span style="font-weight: bold;"></span></li>
<li><span style="font-weight: bold;"></span>$DebugPrintKernelSymbols
[on/<b>off</b>]<br>
Linux only, ignored on other platforms (but may be specified)</li>
+<li><b>$klogLocalIPIF</b> [interface name] - (available since 5.9.6) - if provided, the IP of the specified
+interface (e.g. "eth0") shall be used as fromhost-ip for imklog-originating messages.
+If this directive is not given OR the interface cannot be found (or has no IP address),
+the default of "127.0.0.1" is used.
+</li>
<li>$klogSymbolLookup [on/<b>off</b>] --
disables imklog kernel symbol translation (former klogd -x option). NOTE that
this option is counter-productive on recent kernels (>= 2.6) because the
diff --git a/doc/impstats.html b/doc/impstats.html
index cede487..64b04a3 100644
--- a/doc/impstats.html
+++ b/doc/impstats.html
@@ -18,7 +18,9 @@ prepared to change your trending scripts when you upgrade to a newer rsyslog ver
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).
<p>Note that loading this module has impact on rsyslog performance. Depending on
-settings, this impact may be severe (for high-load environments).
+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>
<ul>
@@ -34,6 +36,10 @@ is 5 (syslog).This is useful for filtering messages.</li>
<li>$PStatSeverity &lt;numerical severity&gt;<br>
The numerical syslog severity code to be used for generated messages. Default
is 6 (info).This is useful for filtering messages.</li>
+<li>$PStatJSON &lt;on/<b>off</b>&gt; (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).
+</li>
</ul>
<b>Caveats/Known Bugs:</b>
<ul>
diff --git a/doc/imptcp.html b/doc/imptcp.html
index c7a0e59..4307c60 100644
--- a/doc/imptcp.html
+++ b/doc/imptcp.html
@@ -43,9 +43,33 @@ very limited interest in fixing this issue. This directive <b>can not</b> fix th
That would require much more code changes, which I was unable to do so far. Full details
can be found at the <a href="http://www.rsyslog.com/Article321.phtml">Cisco tcp syslog anomaly</a>
page.
+<li><b>$InputPTCPSupportOctetCountedFraming</b> &lt;<b>on</b>|off&gt;<br>
+If set to "on", the legacy octed-counted framing (similar to RFC5425 framing) is
+activated. This is the default and should be left unchanged until you know
+very well what you do. It may be useful to turn it off, if you know this framing
+is not used and some senders emit multi-line messages into the message stream.
+</li>
<li>$InputPTCPServerNotifyOnConnectionClose [on/<b>off</b>]<br>
instructs imptcp to emit a message if the remote peer closes a connection.<br>
-<li>$InputPTCPServerRun &lt;port&gt;<br>
+<li><b>$InputPTCPServerKeepAlive</b> &lt;on/<b>off</b>&gt;<br>
+enable of disable keep-alive packets at the tcp socket layer. The default is
+to disable them.</li>
+<li><b>$InputPTCPServerKeepAlive_probes</b> &lt;number&gt;<br>
+The number of unacknowledged probes to send before considering the connection dead and notifying the application layer.
+The default, 0, means that the operating system defaults are used. This has only
+effect if keep-alive is enabled. The functionality may not be available on
+all platforms.
+<li><b>$InputPTCPServerKeepAlive_intvl</b> &lt;number&gt;<br>
+The interval between subsequential keepalive probes, regardless of what the connection has exchanged in the meantime.
+The default, 0, means that the operating system defaults are used. This has only
+effect if keep-alive is enabled. The functionality may not be available on
+all platforms.
+<li><b>$InputPTCPServerKeepAlive_time</b> &lt;number&gt;<br>
+The interval between the last data packet sent (simple ACKs are not considered data) and the first keepalive probe; after the connection is marked to need keepalive, this counter is not used any further.
+The default, 0, means that the operating system defaults are used. This has only
+effect if keep-alive is enabled. The functionality may not be available on
+all platforms.
+<li><b>$InputPTCPServerRun</b> &lt;port&gt;<br>
Starts a TCP server on selected port</li>
<li>$InputPTCPServerInputName &lt;name&gt;<br>
Sets a name for the inputname property. If no name is set "imptcp" is used by default. Setting a
diff --git a/doc/imrelp.html b/doc/imrelp.html
index 2cf9c1f..d83b2a1 100644
--- a/doc/imrelp.html
+++ b/doc/imrelp.html
@@ -29,6 +29,8 @@ syslog and so it is highly suggested to use RELP instead of plain tcp.
Clients send messages to the RELP server via omrelp.</p>
<p><b>Configuration Directives</b>:</p>
<ul>
+<li>InputRELPServerBindRuleset &lt;name&gt; (available in 6.3.6+)</br>
+Binds the specified ruleset to all RELP listeners.
<li>InputRELPServerRun &lt;port&gt;<br>
Starts a RELP server on selected port</li>
</ul>
@@ -38,6 +40,8 @@ Starts a RELP server on selected port</li>
<li>To obtain the remote system's IP address, you need to have at least
librelp 1.0.0 installed. Versions below it return the hostname instead
of the IP address.</li>
+<li>Contrary to other inputs, the ruleset can only be bound to all listeners,
+not specific ones. This is due to a currently existing limitation in librelp.
</ul>
<p><b>Sample:</b></p>
<p>This sets up a RELP server on port 20514.<br>
@@ -48,9 +52,8 @@ $InputRELPServerRun 20514
<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
+<a href="http://www.rsyslog.com/">rsyslog</a> project.<br>
+Copyright &copy; 2008-2011 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/imtcp.html b/doc/imtcp.html
index b0aaa3c..649b08f 100644
--- a/doc/imtcp.html
+++ b/doc/imtcp.html
@@ -55,6 +55,9 @@ so be prepared to wrangle with that!
instructs imtcp to emit a message if the remote peer closes a connection.<br>
<b>Important:</b> This directive is global to all listeners and must be given right
after loading imtcp, otherwise it may have no effect.</li>
+<li><b>$InputTCPServerKeepAlive</b> &lt;on/<b>off</b>&gt;<br>
+enable of disable keep-alive packets at the tcp socket layer. The default is
+to disable them.</li>
<li><b>$InputTCPServerRun</b> &lt;port&gt;<br>
Starts a TCP server on selected port</li>
<li><b>$InputTCPFlowControl</b> &lt;<b>on</b>/off&gt;<br>
@@ -83,6 +86,12 @@ listener. &lt;id-string&gt; semantics depend on the currently selected
AuthMode and&nbsp; <a href="netstream.html">network stream driver</a>. PermittedPeers may not be set in anonymous modes.</li>
<li><b>$InputTCPServerBindRuleset</b> &lt;ruleset&gt;<br>
Binds the listener to a specific <a href="multi_ruleset.html">ruleset</a>.</li>
+<li><b>$InputTCPSupportOctetCountedFraming</b> &lt;<b>on</b>|off&gt;<br>
+If set to "on", the legacy octed-counted framing (similar to RFC5425 framing) is
+activated. This is the default and should be left unchanged until you know
+very well what you do. It may be useful to turn it off, if you know this framing
+is not used and some senders emit multi-line messages into the message stream.
+</li>
</ul>
<b>Caveats/Known Bugs:</b>
<ul>
diff --git a/doc/imuxsock.html b/doc/imuxsock.html
index ee5db22..34a696d 100644
--- a/doc/imuxsock.html
+++ b/doc/imuxsock.html
@@ -49,6 +49,15 @@ are places as quickly as possible into the processing queues. If you would like
flow control, you need to enable it via the $SystemLogSocketFlowControl and
$InputUnixListenSocketFlowControl config directives. Just make sure you thought about
the implications. Note that for many systems, turning on flow control does not hurt.
+<p>Starting with rsyslog 5.9.4,
+<b><a href="http://www.rsyslog.com/what-are-trusted-properties/">trusted syslog properties</a>
+are available</b>. These require a recent enough Linux Kernel and access to the /proc file
+system. In other words, this may not work on all platforms and may not work fully when
+privileges are dropped (depending on how they are dropped). Note that trusted properties
+can be very useful, but also typically cause the message to grow rather large. Also, the
+format of log messages is obviously changed by adding the trusted properties at the end.
+For these reasons, the feature is <b>not enabled by default</b>. If you want to use it,
+you must turn it on (via $SystemLogSocketAnnotate and $InputUnixListenSocketAnnotate).
<p><b>Configuration Directives</b>:</p>
<ul>
<li><b>$InputUnixListenSocketIgnoreMsgTimestamp</b> [<b>on</b>/off]
@@ -56,7 +65,10 @@ the implications. Note that for many systems, turning on flow control does not h
<li><b>$InputUnixListenSocketFlowControl</b> [on/<b>off</b>] - specifies if flow control should be applied
to the next socket.</li>
<li><b>$IMUXSockRateLimitInterval</b> [number] - specifies the rate-limiting
-interval in seconds. Default value is 5 seconds. Set it to 0 to turn rate limiting off.
+interval in seconds. Default value is 0, which turns off rate limiting. Set it to a number
+of seconds (5 recommended) to activate rate-limiting. The default of 0 has been choosen in 5.9.6+,
+as people experienced problems with this feature activated by default. Now it needs an
+explicit opt-in by setting this parameter.
</li>
<li><b>$IMUXSockRateLimitBurst</b> [number] - specifies the rate-limiting
burst in number of messages. Default is 200.
@@ -64,13 +76,27 @@ burst in number of messages. Default is 200.
<li><b>$IMUXSockRateLimitSeverity</b> [numerical severity] - specifies the severity of
messages that shall be rate-limited.
</li>
+<li><b>$IMUXSockLocalIPIF</b> [interface name] - (available since 5.9.6) - if provided, the IP of the specified
+interface (e.g. "eth0") shall be used as fromhost-ip for imuxsock-originating messages.
+If this directive is not given OR the interface cannot be found (or has no IP address),
+the default of "127.0.0.1" is used.
+</li>
<li><b>$InputUnixListenSocketUsePIDFromSystem</b> [on/<b>off</b>] - specifies if the pid being logged shall
be obtained from the log socket itself. If so, the TAG part of the message is rewritten.
It is recommended to turn this option on, but the default is "off" to keep compatible
with earlier versions of rsyslog. This option was introduced in 5.7.0.</li>
+<li><b>$InputUnixListenSocketUseSysTimeStamp</b> [<b>on</b>/off] instructs imuxsock
+to obtain message time from the system (via control messages) insted of using time
+recorded inside the message. This may be most useful in combination with systemd. Note:
+this option was introduced with version 5.9.1. Due to the usefulness of it, we
+decided to enable it by default. As such, 5.9.1 and above behave slightly different
+than previous versions. However, we do not see how this could negatively affect
+existing environments.<br>
<li><b>$SystemLogSocketIgnoreMsgTimestamp</b> [<b>on</b>/off]<br>
Ignore timestamps included in the messages, applies to messages received via the system log socket.</li>
-<li><b>$OmitLocalLogging</b> (imuxsock) [on/<b>off</b>] -- former -o option</li>
+<li><b>$OmitLocalLogging</b> (imuxsock) [on/<b>off</b>] -- former -o option;
+do NOT listen for the local log socket. This is most useful if you run multiple
+instances of rsyslogd where only one shall handle the system log socket.</li>
<li><b>$SystemLogSocketName</b> &lt;name-of-socket&gt; -- former -p option</li>
<li><b>$SystemLogFlowControl</b> [on/<b>off</b>] - specifies if flow control should be applied
to the system log socket.</li>
@@ -87,6 +113,7 @@ burst in number of messages. Default is 200.
<li><b>$SystemLogRateLimitSeverity</b> [numerical severity] - specifies the severity of
messages that shall be rate-limited.
</li>
+<li><b>$SystemLogUseSysTimeStamp</b> [<b>on</b>/off] the same as $InputUnixListenSocketUseSysTimeStamp, but for the system log socket.
<li><b>$InputUnixListenSocketCreatePath</b> [on/<b>off</b>] - create directories in the socket path
if they do not already exist. They are created with 0755 permissions with the owner being the process under
which rsyslogd runs. The default is not to create directories. Keep in mind, though, that rsyslogd always
@@ -105,7 +132,12 @@ shall be used inside messages taken from the <b>next</b> $AddUnixListenSocket so
the hostname must be specified before the $AddUnixListenSocket configuration directive, and it
will only affect the next one and then automatically be reset. This functionality is provided so
that the local hostname can be overridden in cases where that is desired.</li>
+<li><b>$InputUnixListenSocketAnnotate</b> &lt;on/<b>off</b>&gt; turn on annotation/trusted
+properties for the non-system log socket in question.</li>
+<li><b>$SystemLogSocketAnnotate</b> &lt;on/<b>off</b>&gt; turn on annotation/trusted
+properties for the system log socket.</li>
</ul>
+
<b>Caveats/Known Bugs:</b><br>
<ul>
<li>There is a compile-time limit of 50 concurrent sockets. If you need more, you need to
@@ -141,16 +173,22 @@ $InputUnixListenSocketHostName /var/run/sshd/dev/log
</textarea>
<p>The following sample is used to turn off input rate limiting on the system log
socket.
-<textarea rows="6" cols="70">$ModLoad imuxsock # needs to be done just once
+<textarea rows="4" cols="70">$ModLoad imuxsock # needs to be done just once
$SystemLogRateLimitInterval 0 # turn off rate limiting
</textarea>
+<p>The following sample is used activate message annotation and thus trusted properties
+on the system log socket.
+<textarea rows="4" cols="70">$ModLoad imuxsock # needs to be done just once
+
+$SystemLogSocketAnnotate on
+</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 &copy; 2008-2010 by <a href="http://www.gerhards.net/rainer">Rainer
+Copyright &copy; 2008-2012 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/manual.html b/doc/manual.html
index f238631..de05d7a 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -19,7 +19,7 @@ rsyslog support</a> available directly from the source!</p>
<p><b>Please visit the <a href="http://www.rsyslog.com/sponsors">rsyslog sponsor's page</a>
to honor the project sponsors or become one yourself!</b> We are very grateful for any help towards the
project goals.</p>
-<p><b>This documentation is for version 6.2.2 (v6-stable branch) of rsyslog.</b>
+<p><b>This documentation is for version 6.4.0 (stable branch) of rsyslog.</b>
Visit the <i><a href="http://www.rsyslog.com/status">rsyslog status page</a></i></b>
to obtain current version information and project status.
</p><p><b>If you like rsyslog, you might
@@ -30,15 +30,17 @@ between rsyslog and syslog-ng</a>.</p>
<p>If you are upgrading from rsyslog v2 or stock sysklogd,
<a href="v3compatibility.html">be sure to read the rsyslog v3 compatibility notes</a>,
and if you are upgrading from v3, read the
-<a href="v4compatibility.html">rsyslog v4 compatibility notes</a> and
+<a href="v4compatibility.html">rsyslog v4 compatibility notes</a>,
if you upgrade from v4, read the
-<a href="v5compatibility.html">rsyslog v5 compatibility notes</a>. Ther currently is
-no compatibility mode document for v6, as none is required right now.
+<a href="v5compatibility.html">rsyslog v5 compatibility notes</a>, and
+if you upgrade from v5, read the
+<a href="v6compatibility.html">rsyslog v6 compatibility notes</a>.
<p>Rsyslog will work even
if you do not read the doc, but doing so will definitely improve your experience.</p>
<p><b>Follow the links below for the</b></p>
<ul>
<li><a href="troubleshoot.html">troubleshooting rsyslog problems</a></li>
+<li><a href="http://www.rsyslog.com/doc/node1.html">rsyslog.conf, new RainerScript-based format (v6+)</a></li>
<li><a href="rsyslog_conf.html">configuration file format (rsyslog.conf)</a></li>
<li><a href="http://www.rsyslog.com/tool-regex">a regular expression checker/generator tool for rsyslog</a></li>
<li> <a href="property_replacer.html">property replacer, an important core component</a></li>
@@ -47,8 +49,9 @@ if you do not read the doc, but doing so will definitely improve your experience
<li><a href="generic_design.html">backgrounder on generic syslog application design</a></li>
<li><a href="modules.html">description of rsyslog modules</a></li>
<li><a href="rsyslog_packages.html">rsyslog packages</a></li>
-<li><a href="http://cookbook.rsyslog.com">the rsyslog "cookbook"</a> - a set of configurations ready to use</li>
</ul>
+<p><b>To keep current on rsyslog development, follow
+<a href="http://twitter.com/rgerhards">Rainer's twitter feed</a>.</b></p>
<p><b>We have some in-depth papers on</b></p>
<ul>
<li><a href="install.html">installing rsyslog</a></li>
@@ -104,14 +107,16 @@ online documentation (most current version only)</a></li>
mailing list</a>. If you are interested in the "backstage", you
may find
<a href="http://www.gerhards.net/rainer">Rainer</a>'s
-<a href="http://rgerhards.blogspot.com/">blog</a> an
+<a href="http://blog.gerhards.net/">blog</a> an
interesting read (filter on syslog and rsyslog tags).
+Or meet <a href="http://www.facebook.com/people/Rainer-Gerhards/1349393098">Rainer Gerhards at Facebook</a>
+or <a href="https://plus.google.com/112402185904751517878/posts">Google+</a>.
If you would like to use rsyslog source code inside your open source project, you can do that without
any restriction as long as your license is GPLv3 compatible. If your license is incompatible to GPLv3,
you may even be still permitted to use rsyslog source code. However, then you need to look at the way
<a href="licensing.html">rsyslog is licensed</a>.</p>
<p>Feedback is always welcome, but if you have a support question, please do not
mail Rainer directly (<a href="free_support.html">why not?</a>) - use the
-<a href="http://lists.adiscon.net/mailman/listinfo/rsyslog">rsyslogmailing list</a>
+<a href="http://lists.adiscon.net/mailman/listinfo/rsyslog">rsyslog mailing list</a>
or <a href="http://kb.monitorware.com/rsyslog-f40.html">rsyslog formum</a> instead.
</body></html>
diff --git a/doc/mmsnmptrapd.html b/doc/mmsnmptrapd.html
index e69bc24..699049d 100644
--- a/doc/mmsnmptrapd.html
+++ b/doc/mmsnmptrapd.html
@@ -51,8 +51,11 @@ to control output modules are also available to mmsnmptrapd.
<ul>
<li><b>$mmsnmptrapdTag</b> [tagname]<br>
tells the module which start string inside the tag to look for. The default is
-"snmptrap/"
-<li><b>$mmsnmptrapdSevertiyMapping</b> [severtiymap]<br>
+"snmptrapd". Note that a slash is automatically added to this tag when it comes to
+matching incoming messages. It MUST not be given, except if two slashes are required
+for whatever reasons (so "tag/" results in a check for "tag//" at the start of
+the tag field).
+<li><b>$mmsnmptrapdSeverityMapping</b> [severtiymap]<br>
This specifies the severity mapping table. It needs to be specified as a list. Note that
due to the current config system <b>no whitespace</b> is supported inside the list, so be
sure not to use any whitespace inside it.<br>
@@ -76,7 +79,7 @@ severities. The default tag is used.<br>
# ... other module loads and listener setup ...
*.* /path/to/file/with/orignalMessage # this file receives *un*modified messages
$mmsnmptrapdSeverityMapping warning/4,error/3
-*.* ::mmsnmptrapd: # *now* message is modified
+*.* :mmsnmptrapd: # *now* message is modified
*.* /path/to/file/with/modifiedMessage # this file receives modified messages
# ... rest of config ...
</textarea>
diff --git a/doc/omlibdbi.html b/doc/omlibdbi.html
index ec1d01b..008dcb8 100644
--- a/doc/omlibdbi.html
+++ b/doc/omlibdbi.html
@@ -54,32 +54,38 @@ dlopen()ed plugin (as omlibdbi is). So in short, you probably save you
a lot of headache if you make sure you have at least libdbi version
0.8.3 on your system.
</p>
-<p><b>Configuration Directives</b>:</p>
+<p><b>Action Parameters</b>:</p>
<ul>
-<li><span style="font-weight: bold;">$ActionLibdbiDriverDirectory /path/to/dbd/drivers</span><br>This
-is a global setting. It points libdbi to its driver directory. Usually,
-you do not need to set it. If you installed libdbi-driver's at a
-non-standard location, you may need to specify the directory here. If
-you are unsure, do <span style="font-weight: bold;">not</span> use this configuration directive. Usually, everything works just fine.<strong></strong></li><li><strong>$ActionLibdbiDriver drivername</strong><br>
+<li><b>server</b><br>Name or address of the MySQL server
+<li><b>db</b><br>Database to use
+<li><b>uid</b><br>logon userid used to connect to server. Must have proper permissions.
+<li><b>pwd</b><br>the user's password
+<li><b>template</b><br>Template to use when submitting messages.
+<li><b>driver</b><br>
Name of the dbidriver to use, see libdbi-drivers documentation. As a
quick excerpt, at least those were available at the time of this
writiting "mysql" (suggest to use ommysql instead), "firebird" (Firbird
and InterBase), "ingres", "msql", "Oracle", "sqlite", "sqlite3",
"freetds" (for Microsoft SQL and Sybase) and "pgsql" (suggest to use
ompgsql instead).</li>
-<li><span style="font-weight: bold;">$ActionLibdbiHost
-hostname</span><br>
+<li><b>driverdirectory</b><br>
+Path to the libdbi drivers. Usually,
+you do not need to set it. If you installed libdbi-drivers at a
+non-standard location, you may need to specify the directory here. If
+you are unsure, do <b>not</b> use this configuration directive.
+Usually, everything works just fine.</li>
+</ul>
+<p><b>Legacy (pre-v6) Configuration Directives</b>:</p>
+<ul>
+<li><b>$ActionLibdbiDriverDirectory /path/to/dbd/drivers</b>
+- like the driverdirectory action parameter.
+<li><strong>$ActionLibdbiDriver drivername</strong><br> - like the drivername action parameter.
+<li><span style="font-weight: bold;">$ActionLibdbiHost hostname</span> - like the server action parameter
The host to connect to.</li>
-<li><span style="font-weight: bold;">$ActionLibdbiUserName
-user</span><br>
-The user used to connect to the database.</li>
-<li><span style="font-weight: bold;">$ActionlibdbiPassword</span><br>
-That user's password.</li>
-<li><span style="font-weight: bold;">$ActionlibdbiDBName
-db</span><br>
-The database that shall be written to.</li>
-<li><span style="font-weight: bold;">selector
-line: :omlibdbi:<span style="font-style: italic;">;template</span></span><br>
+<li><b>$ActionLibdbiUserName user</b> - like the uid action parameter
+<li><b>$ActionlibdbiPassword</b> - like the pwd action parameter
+<li><b>$ActionlibdbiDBName db</b> - like the db action parameter
+<li><b>selector line: :omlibdbi:<i>;template</i></b><br>
executes the recently configured omlibdbi action. The ;template part is
optional. If no template is provided, a default template is used (which
is currently optimized for MySQL - sorry, folks...)</li>
@@ -108,7 +114,14 @@ database "syslog_db" on mysqlsever.example.com. The server is MySQL and
being accessed under the account of "user" with password "pwd" (if you
have empty passwords, just remove the $ActionLibdbiPassword line).<br>
</p>
-<textarea rows="15" cols="60">$ModLoad omlibdbi
+<textarea rows="5" cols="60">$ModLoad omlibdbi
+*.* action(type="omlibdbi" driver="mysql"
+ server="mysqlserver.example.com" db="syslog_db"
+ uid="user" pwd="pwd"
+</textarea>
+<p><b>Sample:</b></p>
+<p>The same as above, but in legacy config format (pre rsyslog-v6):
+<textarea rows="10" cols="60">$ModLoad omlibdbi
$ActionLibdbiDriver mysql
$ActionLibdbiHost mysqlserver.example.com
$ActionLibdbiUserName user
@@ -121,8 +134,7 @@ $ActionLibdbiDBName syslog_db
<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
+Copyright &copy; 2008-2012 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>
+Released under the ASL 2.0.</font></p>
</body></html>
diff --git a/doc/ommysql.html b/doc/ommysql.html
index daef9ca..7769fb8 100644
--- a/doc/ommysql.html
+++ b/doc/ommysql.html
@@ -15,28 +15,37 @@
<p>This module provides native support for logging to MySQL databases. It offers
superior performance over the more generic <a href="omlibdbi.html">omlibdbi</a> module.
</p>
-<p><b>Configuration Directives</b>:</p>
-<p>ommysql mostly uses the "old style" configuration, with almost everything on the
-action line itself. A few newer features are being migrated to the new style-config
-directive configuration system.
+<p><b>Action Parameters</b>:</p>
<ul>
-<li><b>$ActionOmmysqlServerPort &lt;port&gt;</b><br>Permits to select
+<li><b>server</b><br>Name or address of the MySQL server
+<li><b>serverport</b><br>Permits to select
a non-standard port for the MySQL server. The default is 0, which means the
-system default port is used. There is no need to specify this directive unless
+system default port is used. There is no need to specify this parameter unless
you know the server is running on a non-standard listen port.
-<li><b>$OmMySQLConfigFile &lt;file name&gt;</b><br>Permits the selection
+<li><b>db</b><br>Database to use
+<li><b>uid</b><br>logon userid used to connect to server. Must have proper permissions.
+<li><b>pwd</b><br>the user's password
+<li><b>template</b><br>Template to use when submitting messages.
+<li><b>mysqlconfig.file</b><br>Permits the selection
of an optional MySQL Client Library configuration file (my.cnf) for extended
configuration functionality. The use of this configuration directive is necessary
only if you have a non-standard environment or if fine-grained control over the
database connection is desired.</li>
-<li><b>$OmMySQLConfigSection &lt;string&gt;</b><br>Permits the selection of the
-section within the configuration file specified by the <b>$OmMySQLConfigFile</b> directive.
+<li><b>mysqlconfig.section</b><br>Permits the selection of the
+section within the configuration file specified by the <b>myselconfig.file</b> parameter.
<br>This will likely only be used where the database administrator provides a single
configuration file with multiple profiles.
-<br>This configuration directive is ignored unless <b>$OmMySQLConfigFile</b> is also used
-in the rsyslog configration file.
+<br>This configuration parameter is ignored unless <b>mysqlconfig.file</b> is also used.
<br>If omitted, the MySQL Client Library default of &quot;client&quot; will be used.</li>
-<li>Action parameters:
+</ul>
+<p><b>Legacy (pre-v6) Configuration Directives</b>:</p>
+<p>ommysql mostly uses the "very old style" (v0) configuration, with almost everything on the
+action line itself.
+<ul>
+<li><b>$ActionOmmysqlServerPort &lt;port&gt;</b> - like the "serverport" action parameter.
+<li><b>$OmMySQLConfigFile &lt;file name&gt;</b> - like the "mysqlconfig.file" action parameter.
+<li><b>$OmMySQLConfigSection &lt;string&gt;</b> - like the "mysqlconfig.file" action parameter.
+<li>Action line:
<br><b>:ommysql:database-server,database-name,database-userid,database-password</b>
<br>All parameters should be filled in for a successful connect.
</ul>
@@ -57,15 +66,20 @@ database "syslog_db" on mysqlsever.example.com. The server is
being accessed under the account of "user" with password "pwd".
</p>
<textarea rows="5" cols="80">$ModLoad ommysql
+*.* action(type="ommysql" server="mysqlserver.example.com" serverport="1234"
+ db="syslog_db" uid="user" pwd="pwd")
+</textarea>
+<p><b>Legacy Sample:</b></p>
+<p>The same as above, but in legacy config format (pre rsyslog-v6):
+<textarea rows="5" cols="80">$ModLoad ommysql
$ActionOmmysqlServerPort 1234 # use non-standard port
*.*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :ommysql:mysqlserver.example.com,syslog_db,user,pwd
</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 &copy; 2008, 2009 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.rsyslog.com/">rsyslog</a> project.<br>
+Copyright &copy; 2008-2012 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>
+Released under the ASL 2.0.</font></p>
</body></html>
diff --git a/doc/pmlastmsg.html b/doc/pmlastmsg.html
index 2abeac6..fd26dbd 100644
--- a/doc/pmlastmsg.html
+++ b/doc/pmlastmsg.html
@@ -27,6 +27,13 @@ parser chain</a>. It processes all those messages that contain a PRI, then none
some spaces and then the exact text (case-insensitive) "last message repeated n times"
where n must be an integer. All other messages are left untouched.
+<p><b>Please note:</b> this parser module makes it possible that these messages
+are properly detected. It does <b>not</b> drop them. If you intend to drop those
+messages, you need to use the usual filter logic in combination with the discard
+action. As a side-note, please keep on your mind that the sender discarded messages
+when the "last message repeated n times" message is emited. You want to consider if
+that really is what you intend to happen. If not, go change the sender.
+
<p><b>Configuration Directives</b>:</p>
<p>There do not currently exist any configuration directives for this module.
<p><b>Examples:</b></p>
diff --git a/doc/property_replacer.html b/doc/property_replacer.html
index f4c4238..4c92bf4 100644
--- a/doc/property_replacer.html
+++ b/doc/property_replacer.html
@@ -13,7 +13,7 @@ the value, e.g. by converting all characters to lower case.</p>
<p>Syslog message properties are used inside templates. They are
accessed by putting them between percent signs. Properties can be
modified by the property replacer. The full syntax is as follows:</p>
-<blockquote><b><code>%propname:fromChar:toChar:options%</code></b></blockquote>
+<blockquote><b><code>%propname:fromChar:toChar:options:fieldname%</code></b></blockquote>
<h2>Available Properties</h2>
<p><b><code>propname</code></b> is the
name of the property to access. It is case-insensitive (prior to 3.17.0, they were case-senstive).
@@ -138,6 +138,25 @@ draft-ietf-syslog-protocol</td>
<td>The contents of the MSGID field from
IETF draft draft-ietf-syslog-protocol</td>
</tr>
+<tr>
+<td><b>parsesuccess</b></td>
+<td>This returns the status of the <b>last</b> called higher level parser,
+like mmjsonparse. A higher level parser parses the actual message for additional
+structured data and maintains an extra property table while doing so (this is
+often referred to as "cee data" because the idea was originally rooted in the
+cee effort, only (but has been extended since then). Note that higher level
+parsers must explicitely support (and set) this property. So, depending on the
+parser, it may not be set correctly.
+<br>If the parser properly supports it, the value "OK" means that parsing was
+successfull, while "FAIL" means the parser could not successfully obtain any data.
+Failure state is not necessarily an error. For example, it may simple indicate
+that the cee-enhanced syslog parser (mmjsonparse) did not detect cee-enhanced format,
+what can be totally valid. Using this property, further processing of the message
+can be directed based on this parsing outcome. If no parser has been called at the
+time this property is accessed, it will contain "FAIL".
+<br><b>This property is available since version 6.3.8.</b>
+</td>
+</tr>
<td><b>inputname</b></td>
<td>The name of the input module that generated the
message (e.g. "imuxsock", "imudp"). Note that not all modules
@@ -285,6 +304,15 @@ fields in the property is requested. The field number must be placed in
the "ToChar" parameter. An example where the 3rd field (delimited by
TAB) from the msg property is extracted is as follows: "%msg:F:3%". The
same example with semicolon as delimiter is "%msg:F,59:3%".</p>
+<p>The use of fields does not permit to select substrings, what is rather
+unfortunate. To solve this issue, starting with 6.3.9, fromPos and toPos
+can be specified for strings as well. However, the syntax is quite ugly, but
+it was the only way to integrate this functonality into the already-existing
+system. To do so, use ",fromPos" and ",toPos" during field extraction.
+Let's assume you want to extract the substring from position 5 to 9 in the previous
+example. Then, the syntax is as follows: "%msg:F,59,5:3,9%". As you can see,
+"F,59" means field-mode, with semicolon delimiter and ",5" means starting
+at position 5. Then "3,9" means field 3 and string extraction to position 9.
<p>Please note that the special characters "F" and "R" are
case-sensitive. Only upper case works, lower case will return an error.
There are no white spaces permitted inside the sequence (that will lead
@@ -327,6 +355,29 @@ case-insensitive. Currently, the following options are defined:
<td>convert property text to uppercase only</td>
</tr>
<tr>
+<td><b>json</b></td>
+<td>encode the value so that it can be used inside a JSON field. This means
+that several characters (according to the JSON spec) are being escaped, for
+example US-ASCII LF is replaced by "\n".
+The json option cannot be used together with either jsonf or csv options.
+</td>
+</tr>
+<tr>
+<td><b>jsonf</b></td>
+<td><i>(available in 6.3.9+)</i>
+This signifies that the property should be expressed as a json <b>f</b>ield.
+That means not only the property is written, but rather a complete json field in
+the format<br>
+"fieldname"="value"</b>
+where "filedname" is the assigend field name (or the property name if none was assigned)
+and value is the end result of property replacer operation. Note that value supports
+all property replacer options, like substrings, case converson and the like.
+Values are properly json-escaped. However, field names are (currently) not. It is
+expected that proper field names are configured.
+The jsonf option cannot be used together with either json or csv options.
+</td>
+</tr>
+<tr>
<td valign="top"><b>csv</b></td>
<td>formats the resulting field (after all modifications) in CSV format
as specified in <a href="http://www.ietf.org/rfc/rfc4180.txt">RFC 4180</a>.
@@ -335,6 +386,7 @@ text, you need to define a proper template. An example is this one:
<br>$template csvline,"%syslogtag:::csv%,%msg:::csv%"
<br>Most importantly, you need to provide the commas between the fields
inside the template.
+The csv option cannot be used together with either json or jsonf options.
<br><i>This feature was introduced in rsyslog 4.1.6.</i>
</td>
</tr>
@@ -369,6 +421,10 @@ option when forwarding to remote hosts - they may treat the date as invalid
<td>format as RFC 3339 date</td>
</tr>
<tr>
+<td><b>date-unixtimestamp</b></td>
+<td>format as unix timestamp (seconds since epoch)</td>
+</tr>
+<tr>
<td><b>date-subseconds</b></td>
<td>just the subseconds of a timestamp (always 0 for a low precision timestamp)</td>
</tr>
@@ -436,13 +492,19 @@ Useful for secure pathname generation (with dynafiles).
them. For example "escape-cc,sp-if-no-1st-sp". If you use conflicting options together,
the last one will override the previous one. For example, using "escape-cc,drop-cc" will
use drop-cc and "drop-cc,escape-cc" will use escape-cc mode.
+<h2>Fieldname</h2>
+<p><i>(available in 6.3.9+)</i>
+<p>This field permits to specify a field name for structured-data emitting property replacer
+options. It was initially introduced to support the "jsonf" option, for which it provides
+the capability to set an alternative field name. If it is not specified, it defaults to
+the property name.
<h2>Further Links</h2>
<ul>
<li>Article on "<a href="rsyslog_recording_pri.html">Recording
the Priority of Syslog Messages</a>" (describes use of templates
to record severity and facility of a message)</li>
<li><a href="rsyslog_conf.html">Configuration file
-syntax</a>, this is where you actually use the property replacer.</li>
+format</a>, this is where you actually use the property replacer.</li>
</ul>
<p>[<a href="manual.html">manual index</a>]
[<a href="rsyslog_conf.html">rsyslog.conf</a>]
diff --git a/doc/rsconf1_omfileforcechown.html b/doc/rsconf1_omfileforcechown.html
index 7415a6f..a680810 100644
--- a/doc/rsconf1_omfileforcechown.html
+++ b/doc/rsconf1_omfileforcechown.html
@@ -8,7 +8,10 @@
<h2>$omfileForceChown</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Parameter Values:</b> boolean (on/off, yes/no)</p>
-<p><b>Available since:</b> 4.7.0+, 5.3.0+</p>
+<p><b>Available:</b> 4.7.0+, 5.3.0-5.8.x, <b>NOT</b> available in 5.9.x or higher</p>
+<p><b>Note: this directive has been removed and is no longer available. The
+documentation is currently being retained for historical reaons.</b> Expect
+it to go away at some later stage as well.
<p><b>Default:</b> off</p>
<p><b>Description:</b></p>
<p>Forces rsyslogd to change the ownership for output files that already exist. Please note
diff --git a/doc/rsyslog_conf_global.html b/doc/rsyslog_conf_global.html
index 83eb876..6c20f4c 100644
--- a/doc/rsyslog_conf_global.html
+++ b/doc/rsyslog_conf_global.html
@@ -143,6 +143,7 @@ our paper on <a href="multi_ruleset.html">using multiple rule sets in rsyslog</a
<li><a href="rsconf1_escape8bitcharsonreceive.html">$Escape8BitCharactersOnReceive</a></li>
<li><a href="rsconf1_escapecontrolcharactersonreceive.html">$EscapeControlCharactersOnReceive</a></li>
<li><b>$EscapeControlCharactersOnReceive</b> [<b>on</b>|off] - escape USASCII HT character</li>
+<li>$SpaceLFOnReceive [on/<b>off</b>] - instructs rsyslogd to replace LF with spaces during message reception (sysklogd compatibility aid)</li>
<li>$ErrorMessagesToStderr [<b>on</b>|off] - direct rsyslogd error message to stderr (in addition to other targets)</li>
<li><a href="rsconf1_failonchownfailure.html">$FailOnChownFailure</a></li>
<li><a href="rsconf1_filecreatemode.html">$FileCreateMode</a></li>
@@ -292,9 +293,22 @@ the value, the less precise the timestamp.
<li><b>$Sleep</b> &lt;seconds&gt; - puts the rsyslog main thread to sleep for the specified
number of seconds immediately when the directive is encountered. You should have a
good reason for using this directive!</li>
+<li><b>$LocalHostIPIF</b> &lt;interface name&gt; - (available since 5.9.6) - if provided, the IP of the specified
+interface (e.g. "eth0") shall be used as fromhost-ip for locall-originating messages.
+If this directive is not given OR the interface cannot be found (or has no IP address),
+the default of "127.0.0.1" is used. Note that this directive can be given only
+once. Trying to reset will result in an error message and the new value will
+be ignored. Please note that modules must have support for obtaining the local
+IP address set via this directive. While this is the case for rsyslog-provided
+modules, it may not always be the case for contributed plugins.
+<br><b>Important:</b> This directive shall be placed <b>right at the top of
+rsyslog.conf</b>. Otherwise, if error messages are triggered before this directive
+is processed, rsyslog will fix the local host IP to "127.0.0.1", what than can
+not be reset.
+</li>
<li><a href="rsconf1_umask.html">$UMASK</a></li>
</ul>
-<p><b>Where &lt;size_nbr&gt; is specified above,</b>
+<p><b>Where &lt;size_nbr&gt; or integers are specified above,</b>
modifiers can be used after the number part. For example, 1k means
1024. Supported are k(ilo), m(ega), g(iga), t(era), p(eta) and e(xa).
Lower case letters refer to the traditional binary defintion (e.g. 1m
@@ -302,7 +316,7 @@ equals 1,048,576) whereas upper case letters refer to their new
1000-based definition (e.g 1M equals 1,000,000).</p>
<p>Numbers may include '.' and ',' for readability. So you can
for example specify either "1000" or "1,000" with the same result.
-Please note that rsyslogd simply ignores the punctuation. Form it's
+Please note that rsyslogd simply ignores the punctuation. From it's
point of view, "1,,0.0.,.,0" also has the value 1000. </p>
<p>[<a href="manual.html">manual index</a>]
diff --git a/doc/rsyslog_conf_templates.html b/doc/rsyslog_conf_templates.html
index 23a0204..bd0b325 100644
--- a/doc/rsyslog_conf_templates.html
+++ b/doc/rsyslog_conf_templates.html
@@ -146,6 +146,10 @@ with high-precision timestamps and timezone information</li>
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_SysklogdFileFormat</span>
+- sysklogd compatible log file format. If used with options: $SpaceLFOnReceive on;
+$EscapeControlCharactersOnReceive off; $DropTrailingLFOnReception off,
+the log format will conform to sysklogd log format.</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
diff --git a/doc/rsyslog_ng_comparison.html b/doc/rsyslog_ng_comparison.html
index 7d12a4a..44c895f 100644
--- a/doc/rsyslog_ng_comparison.html
+++ b/doc/rsyslog_ng_comparison.html
@@ -4,24 +4,45 @@
<a href="features.html">back</a>
<h1>rsyslog vs. syslog-ng</h1>
<p><small><i>Written by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a>
-(2008-05-06)</i></small></p>
-<p><i>Warning</i>: this comparison is a little outdated, take it with a grain
-of salt and be sure to check the links at the bottom (both syslog-ng as well as
-rsyslog features are missing, but our priority is on creating great software not
-continously updating this comparison ;)).
-<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
-comprehensive feature sheet available for syslog-ng (that recently
-changed, see below). So I started this
-comparison, but it probably is not complete. For sure, I miss some
-syslog-ng features. This is not an attempt to let rsyslog shine more
-than it should. I just used the <a href="features.html">rsyslog
-feature sheet</a> as a starting point, simply because it was
-available. If you would like to add anything to the chart, or correct
-it, please simply <a href="mailto:rgerhards@adiscon.com">drop
-me a line</a>. I would love to see a real honest and up-to-date
-comparison sheet, so please don't be shy ;)</p>
+(2008-05-06), slightly updated 2012-01-09</i></small></p>
+<p><b>This comparison page is rooted nearly 5 years in the past and has become severely
+outdated since then.</b> It was unmaintained for several years and contained false
+information on both syslog-ng and rsyslog as technology had advanced so much.
+<p>This page was initially written because so many people asked about a comparison when
+rsyslog was in its infancy. So I tried to create one, but it was hard to maintain as both
+projects grew and added feature after feature. I have to admit we did not try hard to keep
+it current -- there were many other priorities. I even had forgetten about this page, when I
+saw that Peter Czanik blogged about its
+<a href="http://blogs.balabit.com/2012/01/05/rsyslog-vs-syslog-ng/">incorrectness</a> (it must be noted
+that Peter is wrong on RELP -- it is well alive). I now remember
+that he asked me some time ago about this page, what I somehow lost... I guess he must have been
+rather grumpy about that :-(
+<p>Visiting this page after so many years is interesting, because it shows how much has changed since then.
+Obviously, one of my main goals in regard to syslog-ng is reached: in 2007, I blogged that
+<a href="http://blog.gerhards.net/2007/08/why-does-world-need-another-syslogd.html">the
+world needs another syslogd</a> in order to have healthy competition and a greate feature
+set in the free editions. In my opinion, the timeline clearly tells that rsyslog's competition
+has driven more syslog-ng features from the commercial to the free edition. Also, I found
+it interesting to see that syslog-ng has adapted rsyslog's licensing scheme, modular design and
+multi-threadedness. On the other hand, the Balabit folks have obviously done a quicker and
+better move on log normalization with what they call patterndb (it is very roughly equivalent
+to what rsyslog has just recently introduced with the help of liblognorm).
+
+<p>To that account, I think the projects are closer together than 5 years ago. I should now
+go ahead and create a new feature comparison. Given previous experience, I think this does not
+work out. In the future, we will probably focus on some top features, as Balabit does. However,
+that requires some time and I have to admit I do not like to drop this page that has a lot of
+inbound links. So I think I do the useful thing by providing these notes and removing the
+syslog-ng information. So it can't be wrong on syslog-ng any more. Note that it still contains
+some incorrect information about rsyslog (it's the state it had 5 years ago!). The core idea is
+to start with updating the <a href="features.html">rsyslog feature sheet</a> and from there
+on work to a complete comparision. Of course, feel free to read on if you like to get some sense
+of history (and inspiration on what you can still do -- but more ;)).
+<br><br>
+Thanks,<br>
+Rainer Gerhards
+<p>
+
<table border="1">
<tbody>
<tr>
@@ -37,50 +58,50 @@ comparison sheet, so please don't be shy ;)</p>
<tr>
<td valign="top">UNIX domain socket</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
<td></td>
</tr>
<tr>
<td valign="top">UDP</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
<td></td>
</tr>
<tr>
<td valign="top">TCP</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
<td></td>
</tr>
<tr>
<td valign="top"><a href="http://www.librelp.com">RELP</a></td>
<td valign="top">yes</td>
-<td valign="top">no</td>
+<td valign="top"></td>
<td></td>
</tr>
<tr>
<td valign="top">RFC 3195/BEEP</td>
<td valign="top">yes (via <a href="im3195.html">im3195</a>)</td>
-<td valign="top">no</td>
+<td valign="top"></td>
<td></td>
</tr>
<tr>
<td valign="top">kernel log</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
<td></td>
</tr>
<tr>
<td valign="top">file</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
<td></td>
</tr>
<tr>
<td valign="top">mark message generator as an
optional input</td>
<td valign="top">yes</td>
-<td valign="top">no (?)</td>
+<td valign="top"></td>
<td></td>
</tr>
<tr>
@@ -89,8 +110,7 @@ optional input</td>
<a href="http://www.eventreporter.com">EventReporter</a>
or <a href="http://www.mwagent.com">MonitorWare Agent</a>
(both commercial software, both fund rsyslog development)</td>
-<td valign="top">via separate Windows agent, paid
-edition only</td>
+<td valign="top"></td>
</tr>
<tr>
<td colspan="3" valign="top"><b><br>
@@ -100,83 +120,82 @@ Network (Protocol) Support</b><br>
<tr>
<td valign="top">support for (plain) tcp based syslog</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">support for GSS-API</td>
<td valign="top">yes</td>
-<td valign="top">no</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">ability to limit the allowed
network senders (syslog ACLs)</td>
<td valign="top">yes</td>
-<td valign="top">yes (?)</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">support for syslog-transport-tls
based framing on syslog/tcp connections</td>
<td valign="top">yes</td>
-<td valign="top">no (?)</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">udp syslog</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">syslog over RELP<br>
truly reliable message delivery (<a href="http://blog.gerhards.net/2008/05/why-you-cant-build-reliable-tcp.html">Why
is plain tcp syslog not reliable?</a>)</td>
<td valign="top">yes</td>
-<td valign="top">no</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">on the wire (zlib) message
compression</td>
<td valign="top">yes</td>
-<td valign="top">no (?)</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">support for receiving messages via
reliable <a href="http://www.monitorware.com/Common/en/glossary/rfc3195.php">RFC
3195</a> delivery</td>
<td valign="top">yes</td>
-<td valign="top">no</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">support for <a href="rsyslog_tls.html">TLS/SSL-protected
syslog</a> </td>
<td valign="top"><a href="rsyslog_tls.html">natively</a> (since 3.19.0)<br><a href="rsyslog_stunnel.html">via
stunnel</a></td>
-<td valign="top">via stunnel<br>
-paid edition natively</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">support for IETF's new syslog-protocol draft</td>
<td valign="top">yes</td>
-<td valign="top">no</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">support for IETF's new syslog-transport-tls draft</td>
<td valign="top">yes<br>(since 3.19.0 - world's first implementation)</td>
-<td valign="top">no</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">support for IPv6</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">native ability to send SNMP traps</td>
<td valign="top">yes</td>
-<td valign="top">no</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">ability to preserve the original
hostname in NAT environments and relay chains</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
</tr>
<tr>
<td colspan="3" valign="top"><br>
@@ -187,81 +206,81 @@ hostname in NAT environments and relay chains</td>
<td valign="top">Filtering for syslog facility and
priority</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
<td></td>
</tr>
<tr>
<td valign="top">Filtering for hostname</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
<td></td>
</tr>
<tr>
<td valign="top">Filtering for application</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
<td></td>
</tr>
<tr>
<td valign="top">Filtering for message contents</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
<td></td>
</tr>
<tr>
<td valign="top">Filtering for sending IP address</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
<td></td>
</tr>
<tr>
<td valign="top">ability to filter on any other message
field not mentioned above (including substrings and the like)</td>
<td valign="top">yes</td>
-<td valign="top">no</td>
+<td valign="top"></td>
</tr>
<tr>
<td>support for complex filters, using full boolean algebra
with and/or/not operators and parenthesis</td>
<td>yes</td>
-<td>yes</td>
+<td></td>
</tr>
<tr>
<td>Support for reusable filters: specify a filter once and
use it in multiple selector lines</td>
<td>no</td>
-<td>yes</td>
+<td></td>
</tr>
<tr>
<td>support for arbritrary complex arithmetic and string
expressions inside filters</td>
<td>yes</td>
-<td>no</td>
+<td></td>
</tr>
<tr>
<td valign="top">ability to use regular expressions
in filters</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">support for discarding messages
based on filters</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
<td></td>
</tr>
<tr>
<td valign="top">ability to filter out messages based on sequence of appearing</td>
<td valign="top">yes (starting with 3.21.3)</td>
-<td valign="top">no</td>
+<td valign="top"></td>
<td></td>
</tr>
<tr>
<td valign="top">powerful BSD-style hostname and
program name blocks for easy multi-host support</td>
<td valign="top">yes</td>
-<td valign="top">no</td>
+<td valign="top"></td>
</tr>
<tr>
<td></td>
@@ -277,47 +296,47 @@ program name blocks for easy multi-host support</td>
<td valign="top">MySQL</td>
<td valign="top"><a href="rsyslog_mysql.html">yes</a>
(native ommysql,&nbsp;<a href="omlibdbi.html">omlibdbi</a>)</td>
-<td valign="top">yes (via libdibi)</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">PostgreSQL</td>
<td valign="top">yes (native ompgsql,&nbsp;<a href="omlibdbi.html">omlibdbi</a>)</td>
-<td valign="top">yes (via libdibi)</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">Oracle</td>
<td valign="top">yes (<a href="omlibdbi.html">omlibdbi</a>)</td>
-<td valign="top">yes (via libdibi)</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">SQLite</td>
<td valign="top">yes (<a href="omlibdbi.html">omlibdbi</a>)</td>
-<td valign="top">yes (via libdibi)</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">Microsoft SQL (Open TDS)</td>
<td valign="top">yes (<a href="omlibdbi.html">omlibdbi</a>)</td>
-<td valign="top">no (?)</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">Sybase (Open TDS)</td>
<td valign="top">yes (<a href="omlibdbi.html">omlibdbi</a>)</td>
-<td valign="top">no (?)</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">Firebird/Interbase</td>
<td valign="top">yes (<a href="omlibdbi.html">omlibdbi</a>)</td>
-<td valign="top">no (?)</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">Ingres</td>
<td valign="top">yes (<a href="omlibdbi.html">omlibdbi</a>)</td>
-<td valign="top">no (?)</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">mSQL</td>
<td valign="top">yes (<a href="omlibdbi.html">omlibdbi</a>)</td>
-<td valign="top">no (?)</td>
+<td valign="top"></td>
</tr>
<tr>
<td colspan="3" valign="top"><br>
@@ -328,26 +347,26 @@ program name blocks for easy multi-host support</td>
<td valign="top">support for on-demand on-disk
spooling of messages</td>
<td valign="top">yes</td>
-<td valign="top">paid edition only</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">ability to limit disk space used
by spool files</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">each action can use its own,
independant
set of spool files</td>
<td valign="top">yes</td>
-<td valign="top">no</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">different sets of spool files can
be placed on different disk</td>
<td valign="top">yes</td>
-<td valign="top">no</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">ability to process spooled
@@ -356,18 +375,18 @@ 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>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">ability to configure backup
syslog/database servers </td>
<td valign="top">yes</td>
-<td valign="top">no</td>
+<td valign="top"></td>
</tr>
<tr>
<td>Professional Support</td>
<td><a href="professional_support.html">yes</a></td>
-<td>yes</td>
+<td></td>
</tr>
<tr>
<td colspan="3" valign="top"><br>
@@ -378,20 +397,20 @@ syslog/database servers </td>
<td valign="top">config file format</td>
<td valign="top">compatible to legacy syslogd but
ugly</td>
-<td valign="top">clean but not backwards compatible</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">ability to include config file from
within other config files</td>
<td valign="top">yes</td>
-<td valign="top">no</td>
+<td valign="top"></td>
</tr>
<tr>
<td height="25" valign="top">ability to
include all config files
existing in a specific directory</td>
<td height="25" valign="top">yes</td>
-<td height="25" valign="top">no</td>
+<td height="25" valign="top"></td>
</tr>
<tr>
<td colspan="3" valign="top"><br>
@@ -403,13 +422,13 @@ existing in a specific directory</td>
loadable
modules</td>
<td valign="top">yes</td>
-<td valign="top">no</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">Support for third-party input
plugins</td>
<td valign="top">yes</td>
-<td valign="top">no</td>
+<td valign="top"></td>
</tr>
<tr>
</tr>
@@ -417,7 +436,7 @@ plugins</td>
<td valign="top">Support for third-party output
plugins</td>
<td valign="top">yes</td>
-<td valign="top">no</td>
+<td valign="top"></td>
</tr>
<tr>
<td colspan="3" valign="top"><br>
@@ -430,79 +449,78 @@ plugins</td>
<td valign="top">ability to generate file names and
directories (log targets) dynamically</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">control of log output format,
including ability to present channel and priority as visible log data</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></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">no (only via piped external process)</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">good timestamp format control; at a
minimum, ISO 8601/RFC 3339 second-resolution UTC zone</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">ability to reformat message
contents and work with substrings</td>
<td valign="top">yes</td>
-<td valign="top">I think yes</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">support for log files larger than
2gb</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">support for log file size
limitation
and automatic rollover command execution</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">support for running multiple
syslogd instances on a single machine</td>
<td valign="top">yes</td>
-<td valign="top">? (but I think yes)</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">ability to execute shell scripts on
received messages</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
<td valign="top">yes</td>
</tr>
<tr>
<td valign="top">ability to pipe messages to a
continously running program</td>
-<td valign="top">no</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">massively multi-threaded for
tomorrow's multi-core machines</td>
<td valign="top">yes</td>
-<td valign="top">no (only multithreaded with
-database destinations)</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">ability to control repeated line
reduction ("last message repeated n times") on a per selector-line basis</td>
<td valign="top">yes</td>
-<td valign="top">yes (?)</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">supports multiple actions per
selector/filter condition</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
<td></td>
</tr>
<tr>
@@ -510,24 +528,23 @@ selector/filter condition</td>
<td valign="top"><a href="http://www.phplogcon.org">phpLogCon</a><br>
[also works with <a href="http://freshmeat.net/projects/php-syslog-ng/">
php-syslog-ng</a>]</td>
-<td valign="top"><a href="http://freshmeat.net/projects/php-syslog-ng/">
-php-syslog-ng</a></td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">using text files as input source</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">rate-limiting output actions</td>
<td valign="top">yes</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">discard low-priority messages under
system stress</td>
<td valign="top">yes</td>
-<td valign="top">no (?)</td>
+<td valign="top"></td>
</tr>
<tr>
<td height="43" valign="top">flow control
@@ -535,40 +552,39 @@ system stress</td>
<td height="43" valign="top">yes (advanced,
with multiple ways to slow down inputs depending on individual input
capabilities, based on watermarks)</td>
-<td height="43" valign="top">yes (limited?
-"stops accepting messages")</td>
+<td height="43" valign="top"></td>
</tr>
<tr>
<td valign="top">rewriting messages</td>
<td valign="top">yes</td>
-<td valign="top">yes (at least I think so...)</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">output data into various formats</td>
<td valign="top">yes</td>
-<td valign="top">yes (looks somewhat limited to me)</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">ability to control "message
repeated n times" generation</td>
<td valign="top">yes</td>
-<td valign="top">no (?)</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">license</td>
<td valign="top">GPLv3 (GPLv2 for v2 branch)</td>
-<td valign="top">GPL (paid edition is closed source)</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">supported platforms</td>
<td valign="top">Linux, BSD, anecdotical seen on
Solaris; compilation and basic testing done on HP UX</td>
-<td valign="top">many popular *nixes</td>
+<td valign="top"></td>
</tr>
<tr>
<td valign="top">DNS cache</td>
-<td valign="top">no</td>
-<td valign="top">yes</td>
+<td valign="top"></td>
+<td valign="top"></td>
</tr>
</tbody>
</table>
@@ -585,11 +601,6 @@ that vast experience and sometimes even on the code.</p>
argument why it is good to have another strong syslogd besides syslog-ng</b>.
You may want to read it at my blog at "<a href="http://rgerhards.blogspot.com/2007/08/why-does-world-need-another-syslogd.html">Why
does the world need another syslogd?</a>".</p>
-<p>Balabit, the vendor of syslog-ng, has just recently done a
-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>[<a href="manual.html">manual index</a>]
[<a href="rsyslog_conf.html">rsyslog.conf</a>]
[<a href="http://www.rsyslog.com/">rsyslog site</a>]</p>
diff --git a/doc/v6compatibility.html b/doc/v6compatibility.html
index bc803d2..1f83085 100644
--- a/doc/v6compatibility.html
+++ b/doc/v6compatibility.html
@@ -3,11 +3,14 @@
</head>
<body>
<h1>Compatibility Notes for rsyslog v6</h1>
+<p><small><i>Written by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a>
+(2011-10-27)</i></small></p>
<p>
This document describes things to keep in mind when moving from v5 to v6. It
does not list enhancements nor does it talk about compatibility concerns introduced
-by earlier versions (for this, see their respective compatibility documents).
-</p>
+by earlier versions (for this, see their respective compatibility documents). Its focus
+is primarily on what you need to know if you used a previous version and want to use the
+current one without hassle.
<p>Version 6 offers a better config language and some other improvements.
As the config system has many ties into the rsyslog engine AND all plugins,
the changes are somewhat intrusive. Note, however, that core processing has
@@ -41,4 +44,128 @@ upgraded, so this compatibility issue is only of interest for you if you have
custom plugins or use some user-contributed plugins from the rsyslog project
that are not maintained by the project itself (omoracle is an example). Please
expect some further plugin instablity during the initial v6 releases.
+<h2>RainerScript based rsyslog.conf</h2>
+<p>A better config format was the main release target for rsyslog v6. It comes in the
+flavor of so-called RainerScript
+(<a href="http://blog.gerhards.net/2008/02/introducing-rainerscript-and-some.html">why the
+name RainerScript?</a>). RainerScript supports legacy syslog.conf format, much as you know it
+from other syslogd's (like sysklogd or the BSD syslogd's) as well as previous versions
+of rsyslog. Initial work on RainerScript began in v4, and the if-construct was already
+supported in v4 and v5. Version 6 has now taken this further. After long discussions we
+decided to use the legacy format as a basis, and lightly extend it by native RainerScript
+constructs. The main goal was to make sure that previous knowledge and config systems
+could still be used while offering a much more intuitive and powerful way of configuring
+rsyslog.
+<p>RainerScript has been implemented from scratch and with new tools (flex/bison, for those in the
+know). Starting with 6.3.3, this new config file processor replaces the legacy one. Note that
+the new processor handles all formats, extended RainerScript as well as legacy syslog.conf format.
+There are some legacy construct that were especially hard to translate. You'll read about them in
+other parts of this document (especially outchannels, which require a format change).
+
+<p>In v6, all legacy formats are supported. In the long term, we may remove some of the ugly
+rsyslog-specific constructs. Good candidates are all configuration commands starting with
+a dollar sign, like "$ActionFileDefaultTemplate"). However, this will not be the case before
+rsyslog v7 or (much more likely) v8/9. Right now, you also need to use these commands, because
+not all have already been converted to the new RainerScript format.
+
+<p>In 6.3.3, the new parser is used, but almost none of the extended RainerScript capabilities
+are available. They will incrementally be introduced with the following releases. Note that for
+some features (most importantly if-then-else nested blocks), the v6 core engine is not
+capable enough. It is our aim to provide a much better config language to as many rsyslog
+users as quickly as possible. As such, we refrain from doing big engine changes in v6. This
+in turn means we cannot introduce some features into RainerScript that we really want to see.
+These features will come up with rsyslog v7, which will have even better flow control
+capabilities inside the core engine. Note that v7 will fully support v6 RainerScript.
+Let us also say that the v6 version is not a low-end quick hack: it offers full-fledged
+syslog message processing control, capable of doing the best you can find inside the
+industry. We just say that v7 will come up with even more advanced capabilites.
+<p>Please note that we tried hard to make the RainerScript parser compatible with
+all legacy config files. However, we may have failed in one case or another. So if you
+experience problems during config processing, chances are there may be a problem
+on the rsyslog side. In that case, please let us know.
+
+<p>Please see the
+<a href="http://blog.gerhards.net/2011/07/rsyslog-633-config-format-improvements.html">blog
+post about rsyslog 6.3.3 config format</a> for details of what is currently supported.
+
+<h2>compatibility mode</h2>
+<p>Compatibility mode (specified via -c option) has been removed. This was a migration aid from
+sysklogd and very early versions of rsyslog. As all major distros now have rsyslog as their
+default, and thus ship rsyslog-compliant config files, there is no longer a need for
+compatibility mode. Removing it provides easier to maintain code. Also, practice has shown
+that many users were confused by compatibility mode (and even some package maintainers got
+it wrong). So this not only cleans up the code but rather removes a frequent source of
+error.
+<p>It must be noted, though, that this means rsyslog is no longer a 100% drop-in replacement
+for sysklogd. If you convert an extremely old system, you need to checks its config and
+probably need to apply some very mild changes to the config file.
+<h2>abort on config errors</h2>
+<p>Previous versions accepted some malformedness inside the config file without aborting. This
+could lead to some uncertainty about which configuration was actually running. In v6 there
+are some situations where config file errors can not be ignored. In these cases rsyslog
+emits error messages to stderr, and then exists with a non-zero exit code. It is important
+to check for those cases as this means log data is potentially lost.
+Please note that
+the root problem is the same for earlier versions as well. With them, it was just harder
+to spot why things went wrong (and if at all).
+<h2>Default Batch Sizes</h2>
+<p>Due to their positive effect on performance and comparatively low overhead,
+default batch sizes have been increased. Starting with 6.3.4, the action queues
+have a default batch size of 128 messages.
+<h2>outchannels</h2>
+<p>Outchannels are a to-be-removed feature of rsyslog, at least as far as the config
+syntax is concerned. Nevertheless, v6 still supports it, but a new syntax is required
+for the action. Let's assume your outchannel is named "channel". The previous syntax was
+<blockquote><code>
+*.* $channel
+</code> </blockquote>
+This was deprecated in v5 and no longer works in v6. Instead, you need to specify
+<blockquote><code>
+*.* :omfile:$channel
+</code></blockquote>
+Note that this syntax is available starting with rsyslog v4. It is important to keep on your
+mind that future versions of rsyslog will require different syntax and/or drop outchannel support
+completely. So if at all possible, avoid using this feature. If you must use it, be prepared for
+future changes and watch announcements very carefully.
+<h2>omusrmsg</h2>
+<p>The omusrmsg module is used to send messages to users. In legacy-legacy
+config format (that is the very old sysklogd style), it was suffucient to use
+just the user name to call this action, like in this example:
+<blockquote><code>
+*.* rgerhards
+</code> </blockquote>
+This format is very ambigious and causes headache (see
+<a href="http://blog.gerhards.net/2011/07/why-omusrmsg-is-evil-and-how-it-is.html">blog post
+on omusrmsg</a> for details). Thus the format has been superseded by this syntax
+(which is legacy format ;-)):
+<blockquote><code>
+*.* :omusrmsg:rgerhards
+</code> </blockquote>
+That syntax is supported since later subversions of version 4.
+<p>Rsyslog v6 still supports the legacy-legacy format, but in a very strict
+sense. For example, if multiple users or templates are given, no spaces
+must be included in the action line. For example, this works up to v5, but no
+longer in v6:
+<blockquote><code>
+*.* rgerhards, bgerhards
+</code> </blockquote>
+To fix it in a way that is compatible with pre-v4, use (note the removed space!):
+<blockquote><code>
+*.* rgerhards,bgerhards
+</code> </blockquote>
+Of course, it probably is better to understand in native v6 format:
+<blockquote><code>
+*.* action(type="omusrmsg" users="rgerhards, bgerhards")
+</code> </blockquote>
+As you see, here you may include spaces between user names.
+<p>In the long term, legacy-legacy format will most probably totally disappear,
+so it is a wise decision to change config files at least to the legacy
+format (with ":omusrmsg:" in front of the name).
+
+<p>[<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 &copy; 2011 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 2 or higher.</font></p>
</body></html>
diff --git a/doc/version_naming.html b/doc/version_naming.html
index 8c1b918..3bfa19b 100644
--- a/doc/version_naming.html
+++ b/doc/version_naming.html
@@ -105,7 +105,7 @@ versions. It applies to versions 1.0.0 and above. Versions below that
are all unstable and have a different naming schema.</p>
<p><b>Please note that version naming is currently being
changed. There is a
-<a href="http://rgerhards.blogspot.com/2007/08/on-rsyslog-versions.html">blog
+<a href="http://blog.gerhards.net/2007/08/on-rsyslog-versions.html">blog
post about future rsyslog versions</a>.</b></p>
<p>The major version is incremented whenever a considerate, major
features have been added. This is expected to happen quite infrequently.</p>
@@ -127,4 +127,4 @@ expected to happen quite infrequently.</p>
<p>In general, the unstable branch carries all new development.
Once it concludes with a sufficiently-enhanced, quite stable version, a
new major stable version is assigned.</p>
-</body></html> \ No newline at end of file
+</body></html>
diff --git a/grammar/Makefile.am b/grammar/Makefile.am
new file mode 100644
index 0000000..d231bb4
--- /dev/null
+++ b/grammar/Makefile.am
@@ -0,0 +1,19 @@
+BUILT_SOURCES = grammar.h
+CLEANFILES = grammar.h grammar.c
+AM_YFLAGS = -d
+noinst_LTLIBRARIES = libgrammar.la
+#bin_PROGRAMS = testdriver # TODO: make this conditional
+
+libgrammar_la_SOURCES = \
+ grammar.y \
+ lexer.l \
+ rainerscript.c \
+ rainerscript.h \
+ parserif.h \
+ grammar.h
+libgrammar_la_CPPFLAGS = $(RSRT_CFLAGS)
+
+#testdriver_SOURCES = testdriver.c libgrammar.la
+#testdriver_CPPFLAGS = $(RSRT_CFLAGS)
+#testdriver_LDADD = libgrammar.la
+#testdriver_LDFLAGS = -lestr
diff --git a/grammar/conf-fmt b/grammar/conf-fmt
new file mode 100644
index 0000000..e34ab78
--- /dev/null
+++ b/grammar/conf-fmt
@@ -0,0 +1,145 @@
+PRI filter:
+
+- facility and severity may be numeric (but discouraged)
+- format: facility "." priority [";" next-selector] (no whitespace)
+- facility:
+ * auth, authpriv, cron, daemon, kern, lpr, mail, mark, news, security
+ (same as auth), syslog, user, uucp and local0 through local7
+ * multiple
+- "priority" (actually severity):
+ * debug, info, notice, warning, warn (same as warning),
+ err, error (same as err), crit, alert, emerg, panic (same as
+ emerg). The keywords error, warn and panic are deprecated and
+ should not be used anymore.
+ * "=" in front of sev --> exactly this
+ * "!" in front of sev --> ignore this priority
+ * "=" and "!" can be combined
+- * => all fac/severities
+- a '\' at end of line means that the following line f is a
+ continuation line. If so, leading whitespace is stripped from
+ f and then f as appended to the end of the current line, replacing
+ the backslash and all whitespace following it.
+ This makes it somewhat easier to grab selectors from an old-style
+ config stream.
+ '\' [WHITESPACE]* LF
+
+
+DEBIAN SAMPLE
+This probably includes everything that is problematic...
+
+# /etc/rsyslog.conf Configuration file for rsyslog.
+#
+# For more information see
+# /usr/share/doc/rsyslog-doc/html/rsyslog_conf.html
+
+
+#################
+#### MODULES ####
+#################
+
+$ModLoad imuxsock # provides support for local system logging
+$ModLoad imklog # provides kernel logging support (previously done by rklogd)
+#$ModLoad immark # provides --MARK-- message capability
+
+# provides UDP syslog reception
+#$ModLoad imudp
+#$UDPServerRun 514
+
+# provides TCP syslog reception
+#$ModLoad imtcp
+#$InputTCPServerRun 514
+
+
+###########################
+#### GLOBAL DIRECTIVES ####
+###########################
+
+#
+# Use traditional timestamp format.
+# To enable high precision timestamps, comment out the following line.
+#
+#$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
+
+#
+# Set the default permissions for all log files.
+#
+$FileOwner root
+$FileGroup adm
+$FileCreateMode 0640
+$DirCreateMode 0755
+$Umask 0022
+
+#
+# Include all config files in /etc/rsyslog.d/
+#
+$IncludeConfig /etc/rsyslog.d/*.conf
+
+
+###############
+#### RULES ####
+###############
+
+#
+# First some standard log files. Log by facility.
+#
+auth,authpriv.* /var/log/auth.log
+*.*;auth,authpriv.none -/var/log/syslog
+#cron.* /var/log/cron.log
+daemon.* -/var/log/daemon.log
+kern.* -/var/log/kern.log
+lpr.* -/var/log/lpr.log
+mail.* -/var/log/mail.log
+user.* -/var/log/user.log
+
+#
+# Logging for the mail system. Split it up so that
+# it is easy to write scripts to parse these files.
+#
+mail.info -/var/log/mail.info
+mail.warn -/var/log/mail.warn
+mail.err /var/log/mail.err
+
+#
+# Logging for INN news system.
+#
+news.crit /var/log/news/news.crit
+news.err /var/log/news/news.err
+news.notice -/var/log/news/news.notice
+
+#
+# Some "catch-all" log files.
+#
+*.=debug;\
+ auth,authpriv.none;\
+ news.none;mail.none -/var/log/debug
+*.=info;*.=notice;*.=warn;\
+ auth,authpriv.none;\
+ cron,daemon.none;\
+ mail,news.none -/var/log/messages
+
+#
+# Emergencies are sent to everybody logged in.
+#
+*.emerg *
+
+#
+# I like to have messages displayed on the console, but only on a virtual
+# console I usually leave idle.
+#
+#daemon,mail.*;\
+# news.=crit;news.=err;news.=notice;\
+# *.=debug;*.=info;\
+# *.=notice;*.=warn /dev/tty8
+
+# The named pipe /dev/xconsole is for the `xconsole' utility. To use it,
+# you must invoke `xconsole' with the `-file' option:
+#
+# $ xconsole -file /dev/xconsole [...]
+#
+# NOTE: adjust the list below, or you'll go crazy if you have a reasonably
+# busy site..
+#
+daemon.*;mail.*;\
+ news.err;\
+ *.=debug;*.=info;\
+ *.=notice;*.=warn |/dev/xconsole
diff --git a/grammar/debian.conf b/grammar/debian.conf
new file mode 100644
index 0000000..ff7708c
--- /dev/null
+++ b/grammar/debian.conf
@@ -0,0 +1,132 @@
+# /etc/rsyslog.conf Configuration file for rsyslog.
+#
+# For more information see
+# /usr/share/doc/rsyslog-doc/html/rsyslog_conf.html
+
+
+#################
+#### MODULES ####
+#################
+
+$ModLoad imuxsock # provides support for local system logging
+$ModLoad imklog # provides kernel logging support (previously done by rklogd)
+#$ModLoad immark # provides --MARK-- message capability
+
+# provides UDP syslog reception
+#$ModLoad imudp
+#$UDPServerRun 514
+
+# provides TCP syslog reception
+#$ModLoad imtcp
+#$InputTCPServerRun 514
+
+
+###########################
+#### GLOBAL DIRECTIVES ####
+###########################
+
+#
+# Use traditional timestamp format.
+# To enable high precision timestamps, comment out the following line.
+#
+#$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
+
+#
+# Set the default permissions for all log files.
+#
+$FileOwner root
+$FileGroup adm
+$FileCreateMode 0640
+$DirCreateMode 0755
+$Umask 0022
+
+#
+# Include all config files in /etc/rsyslog.d/
+#
+#$IncludeConfig /etc/rsyslog.d/*.conf
+
+
+###############
+#### RULES ####
+###############
+
+#
+# First some standard log files. Log by facility.
+#
+auth,authpriv.* /var/log/auth.log
+*.*;auth,authpriv.none -/var/log/syslog
+#cron.* /var/log/cron.log
+daemon.* -/var/log/daemon.log
+kern.* -/var/log/kern.log
+lpr.* -/var/log/lpr.log
+mail.* -/var/log/mail.log
+user.* -/var/log/user.log
+
+#
+# Logging for the mail system. Split it up so that
+# it is easy to write scripts to parse these files.
+#
+mail.info -/var/log/mail.info
+mail.warn -/var/log/mail.warn
+mail.err /var/log/mail.err
+
+#
+# Logging for INN news system.
+#
+news.crit /var/log/news/news.crit
+news.err /var/log/news/news.err
+news.notice -/var/log/news/news.notice
+
+#
+# Some "catch-all" log files.
+#
+*.=debug;\
+ auth,authpriv.none;\
+ news.none;mail.none -/var/log/debug
+*.=info;*.=notice;*.=warn;\
+ auth,authpriv.none;\
+ cron,daemon.none;\
+ mail,news.none -/var/log/messages
+
+#
+# Emergencies are sent to everybody logged in.
+#
+*.emerg *
+
+#
+# I like to have messages displayed on the console, but only on a virtual
+# console I usually leave idle.
+#
+#daemon,mail.*;\
+# news.=crit;news.=err;news.=notice;\
+# *.=debug;*.=info;\
+# *.=notice;*.=warn /dev/tty8
+
+# The named pipe /dev/xconsole is for the `xconsole' utility. To use it,
+# you must invoke `xconsole' with the `-file' option:
+#
+# $ xconsole -file /dev/xconsole [...]
+#
+# NOTE: adjust the list below, or you'll go crazy if you have a reasonably
+# busy site..
+#
+!ThisTag
++host1
+-host2
++*
+daemon.*;mail.*;\
+ news.err;\
+ *.=debug;*.=info;\
+ *.=notice;*.=warn |/dev/xconsole
+$cfs 21
+$cfs 22
+$cfs 23
+# samples added to get full "flavor" of what we need to support...
+:msg, contains, "error" /var/log/somelog
+$cfs 11
+$cfs 12
+$cfs 13
+module()
+$cfs 1
+$cfs 2
+$cfs 3
diff --git a/grammar/debian.new b/grammar/debian.new
new file mode 100644
index 0000000..4dbb590
--- /dev/null
+++ b/grammar/debian.new
@@ -0,0 +1,165 @@
+# /etc/rsyslog.conf Configuration file for rsyslog.
+#
+# For more information see
+# /usr/share/doc/rsyslog-doc/html/rsyslog_conf.html
+
+
+#################
+#### MODULES ####
+#################
+
+module(
+ name="imuxsock" # provides support for local system logging
+ )
+$ModLoad imklog # provides kernel logging support (previously done by rklogd)
+#$ModLoad immark # provides --MARK-- message capability
+
+# provides UDP syslog reception
+#$ModLoad imudp
+#$UDPServerRun 514
+module(name="imudp")
+input(type="imudp" port="514")
+
+# provides TCP syslog reception
+#$ModLoad imtcp
+#$InputTCPServerRun 514
+
+
+###########################
+#### GLOBAL DIRECTIVES ####
+###########################
+
+#
+# Use traditional timestamp format.
+# To enable high precision timestamps, comment out the following line.
+#
+#$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
+
+#
+# Set the default permissions for all log files.
+#
+$FileOwner root
+$FileGroup adm
+$FileCreateMode 0640
+$DirCreateMode 0755
+$Umask 0022
+
+#
+# Include all config files in /etc/rsyslog.d/
+#
+#$IncludeConfig /etc/rsyslog.d/*.conf
+
+
+###############
+#### RULES ####
+###############
+
+#
+# First some standard log files. Log by facility.
+#
+auth,authpriv.* /var/log/auth.log
+*.*;auth,authpriv.none -/var/log/syslog
+#cron.* /var/log/cron.log
+
+#
+# Some "catch-all" log files.
+#
+*.=debug;\
+ auth,authpriv.none;\
+ news.none;mail.none -/var/log/debug
+*.=info;*.=notice;*.=warn;\
+ auth,authpriv.none;\
+ cron,daemon.none;\
+ mail,news.none -/var/log/messages
+
+#
+# Emergencies are sent to everybody logged in.
+#
+*.emerg *
+
+#
+# I like to have messages displayed on the console, but only on a virtual
+# console I usually leave idle.
+#
+#daemon,mail.*;\
+# news.=crit;news.=err;news.=notice;\
+# *.=debug;*.=info;\
+# *.=notice;*.=warn /dev/tty8
+
+# The named pipe /dev/xconsole is for the `xconsole' utility. To use it,
+# you must invoke `xconsole' with the `-file' option:
+#
+# $ xconsole -file /dev/xconsole [...]
+#
+# NOTE: adjust the list below, or you'll go crazy if you have a reasonably
+# busy site..
+#
+daemon.*;mail.*;\
+ news.err;\
+ *.=debug;*.=info;\
+ *.=notice;*.=warn |/dev/xconsole
+
+global (dnscache="yes" arg1="1 2" arg2 = "1 2" arg3 ="1=2\"3")
+# samples added to get full "flavor" of what we need to support...
+:msg, contains, "error" /var/log/somelog
+action(type="omfile" target="/var/log/mail/log")
+*.* /* comment */ * # test
+*.info :ommysql:, tra, la , la # comment (comment to be part of old style line!)
+
+# from SUSE:
+if ( \
+ /* kernel up to warning except of firewall */ \
+ ($syslogfacility-text == 'kern') and \
+ ($syslogseverity <= 4 /* warning */ ) and not \
+ ($msg contains 'IN=' and $msg contains 'OUT=') \
+ ) or ( \
+ /* up to errors except of facility authpriv */ \
+ ($syslogseverity <= 3 /* errors */ ) and not \
+ ($syslogfacility-text == 'authpriv') \
+ ) \
+then /dev/tty10
+& |/dev/xconsole
+#
+# slightly modified to not use continuation lines
+if ( /* kernel up to warning except of firewall */
+ ($syslogfacility-text == 'kern') and
+ ($syslogseverity <= 4 /* warning */ ) and not
+ ($msg contains 'IN=' and $msg contains 'OUT=')
+ ) or (
+ /* up to errors except of facility authpriv */
+ ($syslogseverity <= 3 /* errors */ ) and not
+ ($syslogfacility-text == 'authpriv')
+ )
+then /dev/tty10
+& |/dev/xconsole
+
+*.* rger # write to user (ugly...)
+#ruleset name
+
+# FEDORA, a bit more complex config
+# ### begin forwarding rule ###
+# The statement between the begin ... end define a SINGLE forwarding
+# rule. They belong together, do NOT split them. If you create multiple
+# forwarding rules, duplicate the whole block!
+# Remote Logging (we use TCP for reliable delivery)
+#
+# An on-disk queue is created for this action. If the remote host is
+# down, messages are spooled to disk and sent when it is up again.
+#$WorkDirectory /var/spppl/rsyslog # where to place spool files
+#$ActionQueueFileName fwdRule1 # unique name prefix for spool files
+#$ActionQueueMaxDiskSpace 1g # 1gb space limit (use as much as possible)
+#$ActionQueueSaveOnShutdown on # save messages to disk on shutdown
+#$ActionQueueType LinkedList # run asynchronously
+#$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:514
+# ### end of the forwarding rule ###
+if $msg contains "error" then {
+ action(type="omfwd" protocol="tcp" target="10.0.0.1:514"
+ action.retryCount="-1"
+ queue.type="linkedList" queue.fileName="fwdRule" queue.maxDiskSpace="1g"
+ queue.saveOnShutdown="on"
+ )
+ action(type="omfile" target="/var/log/somelog.log")
+ action(type="omuser" target="all")
+}
diff --git a/grammar/grammar.y b/grammar/grammar.y
new file mode 100644
index 0000000..402b1a5
--- /dev/null
+++ b/grammar/grammar.y
@@ -0,0 +1,186 @@
+ /* Bison file for rsyslog config format v2 (RainerScript).
+ * Please note: this file introduces the new config format, but maintains
+ * backward compatibility. In order to do so, the grammar is not 100% clean,
+ * but IMHO still sufficiently easy both to understand for programmers
+ * maitaining the code as well as users writing the config file. Users are,
+ * of course, encouraged to use new constructs only. But it needs to be noted
+ * that some of the legacy constructs (specifically the in-front-of-action
+ * PRI filter) are very hard to beat in ease of use, at least for simpler
+ * cases. So while we hope that cfsysline support can be dropped some time in
+ * the future, we will probably keep these useful constructs.
+ *
+ * Copyright 2011 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of the rsyslog runtime library.
+ *
+ * The rsyslog runtime library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The rsyslog runtime library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
+ */
+%{
+#include <stdio.h>
+#include <libestr.h>
+#include "rainerscript.h"
+#include "parserif.h"
+#define YYDEBUG 1
+extern int yylineno;
+
+/* keep compile rule cleam of errors */
+extern int yylex(void);
+extern int yyerror(char*);
+%}
+
+%union {
+ char *s;
+ long long n;
+ es_str_t *estr;
+ enum cnfobjType objType;
+ struct cnfobj *obj;
+ struct nvlst *nvlst;
+ struct cnfactlst *actlst;
+ struct cnfexpr *expr;
+ struct cnfrule *rule;
+ struct cnffunc *func;
+ struct cnffparamlst *fparams;
+}
+
+%token <estr> NAME
+%token <estr> VALUE
+%token <estr> FUNC
+%token <objType> BEGINOBJ
+%token ENDOBJ
+%token <s> CFSYSLINE
+%token BEGIN_ACTION
+%token STOP
+%token <s> LEGACY_ACTION
+%token <s> PRIFILT
+%token <s> PROPFILT
+%token <s> BSD_TAG_SELECTOR
+%token <s> BSD_HOST_SELECTOR
+%token IF
+%token THEN
+%token OR
+%token AND
+%token NOT
+%token <s> VAR
+%token <estr> STRING
+%token <n> NUMBER
+%token CMP_EQ
+%token CMP_NE
+%token CMP_LE
+%token CMP_GE
+%token CMP_LT
+%token CMP_GT
+%token CMP_CONTAINS
+%token CMP_CONTAINSI
+%token CMP_STARTSWITH
+%token CMP_STARTSWITHI
+
+%type <nvlst> nv nvlst
+%type <obj> obj
+%type <actlst> actlst
+%type <actlst> act
+%type <s> cfsysline
+%type <actlst> block
+%type <expr> expr
+%type <rule> rule
+%type <rule> scriptfilt
+%type <fparams> fparams
+
+%left AND OR
+%left CMP_EQ CMP_NE CMP_LE CMP_GE CMP_LT CMP_GT CMP_CONTAINS CMP_CONTAINSI CMP_STARTSWITH CMP_STARTSWITHI
+%left '+' '-'
+%left '*' '/' '%'
+%nonassoc UMINUS NOT
+
+%expect 3
+/* these shift/reduce conflicts are created by the CFSYSLINE construct, which we
+ * unfortunately can not avoid. The problem is that CFSYSLINE can occur both in
+ * global context as well as within an action. It's not permitted somewhere else,
+ * but this is suficient for conflicts. The "dangling else" built-in resolution
+ * works well to solve this issue, so we accept it (it's a wonder that our
+ * old style grammar doesn't work at all, so we better do not complain...).
+ * Use "bison -v rscript.y" if more conflicts arise and check rscript.out for
+ * were exactly these conflicts exits.
+ */
+%%
+/* note: we use left recursion below, because that saves stack space AND
+ * offers the right sequence so that we can submit the top-layer objects
+ * one by one.
+ */
+conf: /* empty (to end recursion) */
+ | conf obj { cnfDoObj($2); }
+ | conf rule { cnfDoRule($2); }
+ | conf cfsysline { cnfDoCfsysline($2); }
+ | conf BSD_TAG_SELECTOR { cnfDoBSDTag($2); }
+ | conf BSD_HOST_SELECTOR { cnfDoBSDHost($2); }
+obj: BEGINOBJ nvlst ENDOBJ { $$ = cnfobjNew($1, $2); }
+ | BEGIN_ACTION nvlst ENDOBJ { $$ = cnfobjNew(CNFOBJ_ACTION, $2); }
+cfsysline: CFSYSLINE { $$ = $1; }
+nvlst: { $$ = NULL; }
+ | nvlst nv { $2->next = $1; $$ = $2; }
+nv: NAME '=' VALUE { $$ = nvlstNew($1, $3); }
+rule: PRIFILT actlst { $$ = cnfruleNew(CNFFILT_PRI, $2); $$->filt.s = $1; }
+ | PROPFILT actlst { $$ = cnfruleNew(CNFFILT_PROP, $2); $$->filt.s = $1; }
+ | scriptfilt { $$ = $1; }
+
+scriptfilt: IF expr THEN actlst { $$ = cnfruleNew(CNFFILT_SCRIPT, $4);
+ $$->filt.expr = $2; }
+block: actlst { $$ = $1; }
+ | block actlst { $2->next = $1; $$ = $2; }
+ /* v7: | actlst
+ v7: | block rule */ /* v7 extensions require new rule engine capabilities! */
+actlst: act { $$=$1; }
+ | actlst '&' act { $3->next = $1; $$ = $3; }
+ | actlst cfsysline { $$ = cnfactlstAddSysline($1, $2); }
+ | '{' block '}' { $$ = $2; }
+act: BEGIN_ACTION nvlst ENDOBJ { $$ = cnfactlstNew(CNFACT_V2, $2, NULL); }
+ | LEGACY_ACTION { $$ = cnfactlstNew(CNFACT_LEGACY, NULL, $1); }
+expr: expr AND expr { $$ = cnfexprNew(AND, $1, $3); }
+ | expr OR expr { $$ = cnfexprNew(OR, $1, $3); }
+ | NOT expr { $$ = cnfexprNew(NOT, NULL, $2); }
+ | expr CMP_EQ expr { $$ = cnfexprNew(CMP_EQ, $1, $3); }
+ | expr CMP_NE expr { $$ = cnfexprNew(CMP_NE, $1, $3); }
+ | expr CMP_LE expr { $$ = cnfexprNew(CMP_LE, $1, $3); }
+ | expr CMP_GE expr { $$ = cnfexprNew(CMP_GE, $1, $3); }
+ | expr CMP_LT expr { $$ = cnfexprNew(CMP_LT, $1, $3); }
+ | expr CMP_GT expr { $$ = cnfexprNew(CMP_GT, $1, $3); }
+ | expr CMP_CONTAINS expr { $$ = cnfexprNew(CMP_CONTAINS, $1, $3); }
+ | expr CMP_CONTAINSI expr { $$ = cnfexprNew(CMP_CONTAINSI, $1, $3); }
+ | expr CMP_STARTSWITH expr { $$ = cnfexprNew(CMP_STARTSWITH, $1, $3); }
+ | expr CMP_STARTSWITHI expr { $$ = cnfexprNew(CMP_STARTSWITHI, $1, $3); }
+ | expr '+' expr { $$ = cnfexprNew('+', $1, $3); }
+ | expr '-' expr { $$ = cnfexprNew('-', $1, $3); }
+ | expr '*' expr { $$ = cnfexprNew('*', $1, $3); }
+ | expr '/' expr { $$ = cnfexprNew('/', $1, $3); }
+ | expr '%' expr { $$ = cnfexprNew('%', $1, $3); }
+ | '(' expr ')' { $$ = $2; }
+ | '-' expr %prec UMINUS { $$ = cnfexprNew('M', NULL, $2); }
+ | FUNC '(' ')' { $$ = (struct cnfexpr*) cnffuncNew($1, NULL); }
+ | FUNC '(' fparams ')' { $$ = (struct cnfexpr*) cnffuncNew($1, $3); }
+ | NUMBER { $$ = (struct cnfexpr*) cnfnumvalNew($1); }
+ | STRING { $$ = (struct cnfexpr*) cnfstringvalNew($1); }
+ | VAR { $$ = (struct cnfexpr*) cnfvarNew($1); }
+fparams: expr { $$ = cnffparamlstNew($1, NULL); }
+ | expr ',' fparams { $$ = cnffparamlstNew($1, $3); }
+
+%%
+/*
+int yyerror(char *s)
+{
+ printf("parse failure on or before line %d: %s\n", yylineno, s);
+ return 0;
+}
+*/
diff --git a/grammar/lexer.l b/grammar/lexer.l
new file mode 100644
index 0000000..e688ffc
--- /dev/null
+++ b/grammar/lexer.l
@@ -0,0 +1,316 @@
+ /* Lex file for rsyslog config format v2 (RainerScript).
+ * Please note: this file introduces the new config format, but maintains
+ * backward compatibility. In order to do so, the grammar is not 100% clean,
+ * but IMHO still sufficiently easy both to understand for programmers
+ * maitaining the code as well as users writing the config file. Users are,
+ * of course, encouraged to use new constructs only. But it needs to be noted
+ * that some of the legacy constructs (specifically the in-front-of-action
+ * PRI filter) are very hard to beat in ease of use, at least for simpler
+ * cases. So while we hope that cfsysline support can be dropped some time in
+ * the future, we will probably keep these useful constructs.
+ *
+ * Copyright 2011 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of the rsyslog runtime library.
+ *
+ * The rsyslog runtime library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The rsyslog runtime library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
+ */
+
+%option noyywrap nodefault case-insensitive yylineno
+ /*%option noyywrap nodefault case-insensitive */
+
+/* avoid compiler warning: `yyunput' defined but not used */
+%option nounput noinput
+
+
+%x INOBJ
+ /* INOBJ is selected if we are inside an object (name/value pairs!) */
+%x COMMENT
+ /* COMMENT is "the usual trick" to handle C-style comments */
+%x INCL
+ /* INCL is in $IncludeConfig processing (skip to include file) */
+%x LINENO
+ /* LINENO: support for setting the linenumber */
+%x EXPR
+ /* EXPR is a bit ugly, but we need it to support pre v6-syntax. The problem
+ * is that cfsysline statement start with $..., the same like variables in
+ * an expression. However, cfsysline statements can never appear inside an
+ * expression. So we create a specific expr mode, which is turned on after
+ * we lexed a keyword that needs to be followed by an expression (using
+ * knowledge from the upper layer...). In expr mode, we strictly do
+ * expression-based parsing. Expr mode is stopped when we reach a token
+ * that can not be part of an expression (currently only "then"). As I
+ * wrote this ugly, but the price needed to pay in order to remain
+ * compatible to the previous format.
+ */
+%{
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <libestr.h>
+#include "rainerscript.h"
+#include "parserif.h"
+#include "grammar.h"
+static int preCommentState; /* save for lex state before a comment */
+
+struct bufstack {
+ struct bufstack *prev;
+ YY_BUFFER_STATE bs;
+ int lineno;
+ char *fn;
+ es_str_t *estr;
+} *currbs = NULL;
+
+char *cnfcurrfn; /* name of currently processed file */
+
+int popfile(void);
+int cnfSetLexFile(char *fname);
+
+extern int yydebug;
+
+/* somehow, I need these prototype even though the headers are
+ * included. I guess that's some autotools magic I don't understand...
+ */
+//char *strdup(char*);
+int fileno(FILE *stream);
+
+%}
+
+%%
+
+ /* keywords */
+"if" { BEGIN EXPR; return IF; }
+<EXPR>"then" { BEGIN INITIAL; return THEN; }
+<EXPR>"or" { return OR; }
+<EXPR>"and" { return AND; }
+<EXPR>"not" { return NOT; }
+<EXPR>"," |
+<EXPR>"*" |
+<EXPR>"/" |
+<EXPR>"%" |
+<EXPR>"+" |
+<EXPR>"-" |
+<EXPR>"(" |
+<EXPR>")" { return yytext[0]; }
+<EXPR>"==" { return CMP_EQ; }
+<EXPR>"<=" { return CMP_LE; }
+<EXPR>">=" { return CMP_GE; }
+<EXPR>"!=" |
+<EXPR>"<>" { return CMP_NE; }
+<EXPR>"<" { return CMP_LT; }
+<EXPR>">" { return CMP_GT; }
+<EXPR>"contains" { return CMP_CONTAINS; }
+<EXPR>"contains_i" { return CMP_CONTAINSI; }
+<EXPR>"startswith" { return CMP_STARTSWITH; }
+<EXPR>"startswith_i" { return CMP_STARTSWITHI; }
+<EXPR>0[0-7]+ | /* octal number */
+<EXPR>0x[0-7a-f] | /* hex number, following rule is dec; strtoll handles all! */
+<EXPR>([1-9][0-9]*|0) { yylval.n = strtoll(yytext, NULL, 0); return NUMBER; }
+<EXPR>\$[$!]{0,1}[a-z][a-z0-9\-_\.]* { yylval.s = strdup(yytext); return VAR; }
+<EXPR>\'([^'\\]|\\['])*\' { yylval.estr = es_newStrFromBuf(yytext+1, yyleng-2);
+ return STRING; }
+<EXPR>\"([^"\\]|\\["])*\" { yylval.estr = es_newStrFromBuf(yytext+1, yyleng-2);
+ return STRING; }
+<EXPR>[ \t\n]
+<EXPR>[a-z][a-z0-9_]* { yylval.estr = es_newStrFromCStr(yytext, yyleng);
+ return FUNC; }
+<EXPR>. { dbgprintf("invalid char in expr: %s\n", yytext); }
+"&" { return '&'; }
+"{" { return '{'; }
+"}" { return '}'; }
+"ruleset" { dbgprintf("RULESET\n"); }
+ /* line number support because the "preprocessor" combines lines and so needs
+ * to tell us the real source line.
+ */
+"stop" { dbgprintf("STOP\n"); return STOP; }
+"preprocfilelinenumber(" { BEGIN LINENO; }
+<LINENO>[0-9]+ { yylineno = atoi(yytext) - 1; }
+<LINENO>")" { BEGIN INITIAL; }
+<LINENO>.|\n
+ /* $IncludeConfig must be detected as part of CFSYSLINE, because this is
+ * always the longest match :-(
+ */
+<INCL>.|\n
+<INCL>[^ \t\n]+ { if(cnfDoInclude(yytext) != 0)
+ yyterminate();
+ BEGIN INITIAL; }
+"global"[ \n\t]*"(" { yylval.objType = CNFOBJ_GLOBAL;
+ BEGIN INOBJ; return BEGINOBJ; }
+"input"[ \n\t]*"(" { yylval.objType = CNFOBJ_INPUT;
+ BEGIN INOBJ; return BEGINOBJ; }
+"module"[ \n\t]*"(" { yylval.objType = CNFOBJ_MODULE;
+ BEGIN INOBJ; return BEGINOBJ; }
+"action"[ \n\t]*"(" { BEGIN INOBJ; return BEGIN_ACTION; }
+^[ \t]*:\$?[a-z\-]+[ ]*,[ ]*!?[a-z]+[ ]*,[ ]*\".*\" {
+ yylval.s = strdup(yytext); return PROPFILT; }
+^[ \t]*[\*a-z][,\*a-z]*[0-7]*\.[,!=;\.\*a-z0-7]+ { yylval.s = strdup(yytext); return PRIFILT; }
+"~" |
+"*" |
+\-\/[^*][^\n]* |
+\/[^*][^\n]* |
+:[a-z0-9]+:[^\n]* |
+[\|\.\-\@\^?~>][^\n]+ |
+[a-z0-9_][a-z0-9_\-\+,;]* { yylval.s = strdup(yytext);
+ dbgprintf("lex: LEGA ACT: '%s'\n", yytext);
+ return LEGACY_ACTION; }
+<INOBJ>")" { BEGIN INITIAL; return ENDOBJ; }
+<INOBJ>[a-z][a-z0-9_\.]* { yylval.estr = es_newStrFromCStr(yytext, yyleng);
+ return NAME; }
+<INOBJ>"=" { return(yytext[0]); }
+<INOBJ>\"([^"\\]|\\['"?\\abfnrtv]|\\[0-7]{1,3})*\" {
+ yylval.estr = es_newStrFromBuf(yytext+1, yyleng-2);
+ return VALUE; }
+"/*" { preCommentState = YY_START; BEGIN COMMENT; }
+<EXPR>"/*" { preCommentState = YY_START; BEGIN COMMENT; }
+<COMMENT>"*/" { BEGIN preCommentState; }
+<COMMENT>([^*]|\n)+|.
+<INOBJ>#.*$ /* skip comments in input */
+<INOBJ>[ \n\t]
+<INOBJ>. { dbgprintf("INOBJ: invalid char '%s'\n", yytext); }
+\$[a-z]+.*$ { /* see comment on $IncludeConfig above */
+ if(!strncasecmp(yytext, "$includeconfig ", 14)) {
+ yyless(14);
+ BEGIN INCL;
+ } else {
+ yylval.s = strdup(yytext);
+ return CFSYSLINE;
+ }
+ }
+![^ \t\n]+[ \t]*$ { yylval.s = strdup(yytext); return BSD_TAG_SELECTOR; }
+[+-]\*[ \t\n]*#.*$ { yylval.s = strdup(yytext); return BSD_HOST_SELECTOR; }
+[+-]\*[ \t\n]*$ { yylval.s = strdup(yytext); return BSD_HOST_SELECTOR; }
+^[ \t]*[+-][a-z0-9.:-]+[ \t]*$ { yylval.s = strdup(yytext); return BSD_HOST_SELECTOR; }
+\#.*\n /* skip comments in input */
+[\n\t ] /* drop whitespace */
+. { dbgprintf("invalid char: %s\n", yytext);
+ }
+<<EOF>> { if(popfile() != 0) yyterminate(); }
+
+%%
+int
+cnfParseBuffer(char *buf, unsigned lenBuf)
+{
+ struct bufstack *bs;
+ int r = 0;
+ yydebug = 1;
+ BEGIN INITIAL;
+ /* maintain stack */
+ if((bs = malloc(sizeof(struct bufstack))) == NULL) {
+ r = 1;
+ goto done;
+ }
+
+ if(currbs != NULL)
+ currbs->lineno = yylineno;
+ bs->prev = currbs;
+ bs->fn = strdup("*buffer*");
+ bs->bs = yy_scan_buffer(buf, lenBuf);
+ bs->estr = NULL;
+ currbs = bs;
+ cnfcurrfn = bs->fn;
+ yylineno = 1;
+done: return r;
+}
+
+/* set a new buffers. Returns 0 on success, something else otherwise. */
+int
+cnfSetLexFile(char *fname)
+{
+ es_str_t *str = NULL;
+ FILE *fp;
+ int r = 0;
+ struct bufstack *bs;
+
+ if(fname == NULL) {
+ fp = stdin;
+ } else {
+ if((fp = fopen(fname, "r")) == NULL) {
+ r = 1;
+ goto done;
+ }
+ }
+ readConfFile(fp, &str);
+ if(fp != stdin)
+ fclose(fp);
+
+ /* maintain stack */
+ if((bs = malloc(sizeof(struct bufstack))) == NULL) {
+ r = 1;
+ goto done;
+ }
+
+ if(currbs != NULL)
+ currbs->lineno = yylineno;
+ bs->prev = currbs;
+ bs->fn = strdup(fname == NULL ? "stdin" : fname);
+ bs->bs = yy_scan_buffer((char*)es_getBufAddr(str), es_strlen(str));
+ bs->estr = str; /* needed so we can free it later */
+ currbs = bs;
+ cnfcurrfn = bs->fn;
+ yylineno = 1;
+
+done:
+ if(r != 0) {
+ if(str != NULL)
+ es_deleteStr(str);
+ }
+ return r;
+}
+
+
+/* returns 0 on success, something else otherwise */
+int
+popfile(void)
+{
+ struct bufstack *bs = currbs;
+
+ if(bs == NULL)
+ return 1;
+
+ /* delete current entry. But we must not free the file name if
+ * this is the top-level file, because then it may still be used
+ * in error messages for other processing steps.
+ * TODO: change this to another method which stores the file
+ * name inside the config objects. In the longer term, this is
+ * necessary, as otherwise we may provide wrong file name information
+ * at the end of include files as well. -- rgerhards, 2011-07-22
+ */
+ yy_delete_buffer(bs->bs);
+ if(bs->prev != NULL)
+ free(bs->fn);
+ free(bs->estr);
+
+ /* switch back to previous */
+ currbs = bs->prev;
+ free(bs);
+
+ if(currbs == NULL)
+ return 1; /* all processed */
+
+ yy_switch_to_buffer(currbs->bs);
+ yylineno = currbs->lineno;
+ cnfcurrfn = currbs->fn;
+ return 0;
+}
+
+void
+tellLexEndParsing(void)
+{
+ free(cnfcurrfn);
+ cnfcurrfn= NULL;
+}
diff --git a/grammar/makefile.stand-alone b/grammar/makefile.stand-alone
new file mode 100644
index 0000000..b998a39
--- /dev/null
+++ b/grammar/makefile.stand-alone
@@ -0,0 +1,14 @@
+rscript: lex.yy.c utils.o rscript.tab.h utils.h
+ gcc -DSTAND_ALONE -g -o rscript lex.yy.c rscript.tab.c utils.o -lestr
+
+lex.yy.c: rscript.l rscript.tab.h
+ flex rscript.l
+
+rscript.tab.h: rscript.y
+ bison -d rscript.y
+
+utils.o: utils.c utils.h
+ gcc -g -DSTAND_ALONE -Wall -c utils.c
+
+clean:
+ rm *.o
diff --git a/grammar/mini.samp b/grammar/mini.samp
new file mode 100644
index 0000000..3bb0de4
--- /dev/null
+++ b/grammar/mini.samp
@@ -0,0 +1,33 @@
+#global (dnscache="yes" arg1="1 2" arg2 = "1 2" arg3 ="1=2\"3")
+action(type="omuser" target="all" target="all2")
+global (dnscache="no" b="2")
+$FileOwner root
+*.* *
+$action somelog 1
+& /var/log/somelog
+$action log2 1
+$action log2 2
+$action log2 3
+& action(type="fwd" target="10.1.1.2")
+& /var/log/log2
+
+if 1 then /var/log/log3
+/* sample bwlow is v7
+if 1 then { /var/log/log3
+ if 2 then /var/log/log4
+ *.* /var/log/log4b
+}
+*/
+*.* { /var/log/log5
+ /var/log/log6
+ $port 514
+ @@fwd
+ rger
+ }
+if not (1==0) and 2*4/-5--(10-3)>7/*pri("*.*")*/ then {
+ action(type="omfile" taget="/var/log/log5")
+ action(type="omfile" taget="/var/log/log6")
+ action(type="omfwd" taget="10.0.0.1" port="514")
+ action(type="omwusr" taget="rger" taget="rger2")
+}
+if getenv("user") == "test" then /var/log/testlog
diff --git a/grammar/parserif.h b/grammar/parserif.h
new file mode 100644
index 0000000..597cfe4
--- /dev/null
+++ b/grammar/parserif.h
@@ -0,0 +1,23 @@
+#ifndef PARSERIF_H_DEFINED
+#define PARSERIF_H_DEFINED
+#include "rainerscript.h"
+int cnfSetLexFile(char*);
+int yyparse();
+char *cnfcurrfn;
+void dbgprintf(char *fmt, ...) __attribute__((format(printf, 1, 2)));
+void parser_errmsg(char *fmt, ...) __attribute__((format(printf, 1, 2)));
+void tellLexEndParsing(void);
+extern int yydebug;
+extern int yylineno;
+
+/* entry points to be called after the parser has processed the
+ * element in question. Actual processing must than be done inside
+ * these functions.
+ */
+void cnfDoObj(struct cnfobj *o);
+void cnfDoRule(struct cnfrule *rule);
+void cnfDoCfsysline(char *ln);
+void cnfDoBSDTag(char *ln);
+void cnfDoBSDHost(char *ln);
+es_str_t *cnfGetVar(char *name, void *usrptr);
+#endif
diff --git a/grammar/rainerscript.c b/grammar/rainerscript.c
new file mode 100644
index 0000000..a5cc10c
--- /dev/null
+++ b/grammar/rainerscript.c
@@ -0,0 +1,1618 @@
+/* rainerscript.c - routines to support RainerScript config language
+ *
+ * Module begun 2011-07-01 by Rainer Gerhards
+ *
+ * Copyright 2011 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of the rsyslog runtime library.
+ *
+ * The rsyslog runtime library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The rsyslog runtime library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <glob.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <libestr.h>
+#include "rsyslog.h"
+#include "rainerscript.h"
+#include "parserif.h"
+#include "grammar.h"
+#include "queue.h"
+#include "srUtils.h"
+
+void
+readConfFile(FILE *fp, es_str_t **str)
+{
+ char ln[10240];
+ char buf[512];
+ int lenBuf;
+ int bWriteLineno = 0;
+ int len, i;
+ int start; /* start index of to be submitted text */
+ int bContLine = 0;
+ int lineno = 0;
+
+ *str = es_newStr(4096);
+
+ while(fgets(ln, sizeof(ln), fp) != NULL) {
+ ++lineno;
+ if(bWriteLineno) {
+ bWriteLineno = 0;
+ lenBuf = sprintf(buf, "PreprocFileLineNumber(%d)\n", lineno);
+ es_addBuf(str, buf, lenBuf);
+ }
+ len = strlen(ln);
+ /* if we are continuation line, we need to drop leading WS */
+ if(bContLine) {
+ for(start = 0 ; start < len && isspace(ln[start]) ; ++start)
+ /* JUST SCAN */;
+ } else {
+ start = 0;
+ }
+ for(i = len - 1 ; i >= start && isspace(ln[i]) ; --i)
+ /* JUST SCAN */;
+ if(i >= 0) {
+ if(ln[i] == '\\') {
+ --i;
+ bContLine = 1;
+ } else {
+ if(bContLine) /* write line number if we had cont line */
+ bWriteLineno = 1;
+ bContLine = 0;
+ }
+ /* add relevant data to buffer */
+ es_addBuf(str, ln+start, i+1 - start);
+ }
+ if(!bContLine)
+ es_addChar(str, '\n');
+ }
+ /* indicate end of buffer to flex */
+ es_addChar(str, '\0');
+ es_addChar(str, '\0');
+}
+
+struct nvlst*
+nvlstNew(es_str_t *name, es_str_t *value)
+{
+ struct nvlst *lst;
+
+ if((lst = malloc(sizeof(struct nvlst))) != NULL) {
+ lst->next = NULL;
+ lst->name = name;
+ lst->val.datatype = 'S';
+ lst->val.d.estr = value;
+ lst->bUsed = 0;
+ }
+
+ return lst;
+}
+
+void
+nvlstDestruct(struct nvlst *lst)
+{
+ struct nvlst *toDel;
+
+ while(lst != NULL) {
+ toDel = lst;
+ lst = lst->next;
+ es_deleteStr(toDel->name);
+ if(toDel->val.datatype == 'S')
+ es_deleteStr(toDel->val.d.estr);
+ free(toDel);
+ }
+}
+
+void
+nvlstPrint(struct nvlst *lst)
+{
+ char *name, *value;
+ dbgprintf("nvlst %p:\n", lst);
+ while(lst != NULL) {
+ name = es_str2cstr(lst->name, NULL);
+ // TODO: support for non-string types
+ value = es_str2cstr(lst->val.d.estr, NULL);
+ dbgprintf("\tname: '%s', value '%s'\n", name, value);
+ free(name);
+ free(value);
+ lst = lst->next;
+ }
+}
+
+/* find a name starting at node lst. Returns node with this
+ * name or NULL, if none found.
+ */
+struct nvlst*
+nvlstFindName(struct nvlst *lst, es_str_t *name)
+{
+ while(lst != NULL && es_strcmp(lst->name, name))
+ lst = lst->next;
+ return lst;
+}
+
+
+/* find a name starting at node lst. Same as nvlstFindName, but
+ * for classical C strings. This is useful because the config system
+ * uses C string constants.
+ */
+static inline struct nvlst*
+nvlstFindNameCStr(struct nvlst *lst, char *name)
+{
+ es_size_t lenName = strlen(name);
+ while(lst != NULL && es_strcasebufcmp(lst->name, (uchar*)name, lenName))
+ lst = lst->next;
+ return lst;
+}
+
+
+/* check if there are duplicate names inside a nvlst and emit
+ * an error message, if so.
+ */
+static inline void
+nvlstChkDupes(struct nvlst *lst)
+{
+ char *cstr;
+
+ while(lst != NULL) {
+ if(nvlstFindName(lst->next, lst->name) != NULL) {
+ cstr = es_str2cstr(lst->name, NULL);
+ parser_errmsg("duplicate parameter '%s' -- "
+ "interpretation is ambigious, one value "
+ "will be randomly selected. Fix this problem.",
+ cstr);
+ free(cstr);
+ }
+ lst = lst->next;
+ }
+}
+
+
+/* check for unused params and emit error message is found. This must
+ * be called after all config params have been pulled from the object
+ * (otherwise the flags are not correctly set).
+ */
+void
+nvlstChkUnused(struct nvlst *lst)
+{
+ char *cstr;
+
+ while(lst != NULL) {
+ if(!lst->bUsed) {
+ cstr = es_str2cstr(lst->name, NULL);
+ parser_errmsg("parameter '%s' not known -- "
+ "typo in config file?",
+ cstr);
+ free(cstr);
+ }
+ lst = lst->next;
+ }
+}
+
+
+static inline void
+doGetSize(struct nvlst *valnode, struct cnfparamdescr *param,
+ struct cnfparamvals *val)
+{
+ unsigned char *c;
+ es_size_t i;
+ long long n;
+ c = es_getBufAddr(valnode->val.d.estr);
+ n = 0;
+ i = 0;
+ while(i < es_strlen(valnode->val.d.estr) && isdigit(*c)) {
+ n = 10 * n + *c - '0';
+ ++i;
+ ++c;
+ }
+ if(i < es_strlen(valnode->val.d.estr)) {
+ ++i;
+ switch(*c) {
+ /* traditional binary-based definitions */
+ case 'k': n *= 1024; break;
+ case 'm': n *= 1024 * 1024; break;
+ case 'g': n *= 1024 * 1024 * 1024; break;
+ case 't': n *= (int64) 1024 * 1024 * 1024 * 1024; break; /* tera */
+ case 'p': n *= (int64) 1024 * 1024 * 1024 * 1024 * 1024; break; /* peta */
+ case 'e': n *= (int64) 1024 * 1024 * 1024 * 1024 * 1024 * 1024; break; /* exa */
+ /* and now the "new" 1000-based definitions */
+ case 'K': n *= 1000; break;
+ case 'M': n *= 1000000; break;
+ case 'G': n *= 1000000000; break;
+ /* we need to use the multiplication below because otherwise
+ * the compiler gets an error during constant parsing */
+ case 'T': n *= (int64) 1000 * 1000000000; break; /* tera */
+ case 'P': n *= (int64) 1000000 * 1000000000; break; /* peta */
+ case 'E': n *= (int64) 1000000000 * 1000000000; break; /* exa */
+ default: --i; break; /* indicates error */
+ }
+ }
+ if(i == es_strlen(valnode->val.d.estr)) {
+ val->val.datatype = 'N';
+ val->val.d.n = n;
+ } else {
+ parser_errmsg("parameter '%s' does not contain a valid size",
+ param->name);
+ }
+}
+
+
+static inline void
+doGetBinary(struct nvlst *valnode, struct cnfparamdescr *param,
+ struct cnfparamvals *val)
+{
+ val->val.datatype = 'N';
+ if(!es_strbufcmp(valnode->val.d.estr, (unsigned char*) "on", 2)) {
+ val->val.d.n = 1;
+ } else if(!es_strbufcmp(valnode->val.d.estr, (unsigned char*) "off", 3)) {
+ val->val.d.n = 0;
+ } else {
+ parser_errmsg("parameter '%s' must be \"on\" or \"off\" but "
+ "is neither. Results unpredictable.", param->name);
+ val->val.d.n = 0;
+ }
+}
+
+static inline void
+doGetQueueType(struct nvlst *valnode, struct cnfparamdescr *param,
+ struct cnfparamvals *val)
+{
+ char *cstr;
+ if(!es_strcasebufcmp(valnode->val.d.estr, (uchar*)"fixedarray", 10)) {
+ val->val.d.n = QUEUETYPE_FIXED_ARRAY;
+ } else if(!es_strcasebufcmp(valnode->val.d.estr, (uchar*)"linkedlist", 10)) {
+ val->val.d.n = QUEUETYPE_LINKEDLIST;
+ } else if(!es_strcasebufcmp(valnode->val.d.estr, (uchar*)"disk", 4)) {
+ val->val.d.n = QUEUETYPE_DISK;
+ } else if(!es_strcasebufcmp(valnode->val.d.estr, (uchar*)"direct", 6)) {
+ val->val.d.n = QUEUETYPE_DIRECT;
+ } else {
+ cstr = es_str2cstr(valnode->val.d.estr, NULL);
+ parser_errmsg("param '%s': unknown queue type: '%s'",
+ param->name, cstr);
+ free(cstr);
+ }
+ val->val.datatype = 'N';
+}
+
+
+/* A file create-mode must be a four-digit octal number
+ * starting with '0'.
+ */
+static inline void
+doGetFileCreateMode(struct nvlst *valnode, struct cnfparamdescr *param,
+ struct cnfparamvals *val)
+{
+ int fmtOK = 0;
+ char *cstr;
+ uchar *c;
+
+ if(es_strlen(valnode->val.d.estr) == 4) {
+ c = es_getBufAddr(valnode->val.d.estr);
+ if(!( (c[0] == '0')
+ && (c[1] >= '0' && c[1] <= '7')
+ && (c[2] >= '0' && c[2] <= '7')
+ && (c[3] >= '0' && c[3] <= '7') ) ) {
+ fmtOK = 1;
+ }
+ }
+
+ if(fmtOK) {
+ val->val.datatype = 'N';
+ val->val.d.n = (c[1]-'0') * 64 + (c[2]-'0') * 8 + (c[3]-'0');;
+ } else {
+ cstr = es_str2cstr(valnode->val.d.estr, NULL);
+ parser_errmsg("file modes need to be specified as "
+ "4-digit octal numbers starting with '0' -"
+ "parameter '%s=\"%s\"' is not a file mode",
+ param->name, cstr);
+ free(cstr);
+ }
+}
+
+static inline void
+doGetGID(struct nvlst *valnode, struct cnfparamdescr *param,
+ struct cnfparamvals *val)
+{
+ char *cstr;
+ struct group *resultBuf;
+ struct group wrkBuf;
+ char stringBuf[2048]; /* 2048 has been proven to be large enough */
+
+ cstr = es_str2cstr(valnode->val.d.estr, NULL);
+ getgrnam_r(cstr, &wrkBuf, stringBuf, sizeof(stringBuf), &resultBuf);
+ if(resultBuf == NULL) {
+ parser_errmsg("parameter '%s': ID for group %s could not "
+ "be found", param->name, cstr);
+ } else {
+ val->val.datatype = 'N';
+ val->val.d.n = resultBuf->gr_gid;
+ dbgprintf("param '%s': uid %d obtained for group '%s'\n",
+ param->name, (int) resultBuf->gr_gid, cstr);
+ }
+ free(cstr);
+}
+
+static inline void
+doGetUID(struct nvlst *valnode, struct cnfparamdescr *param,
+ struct cnfparamvals *val)
+{
+ char *cstr;
+ struct passwd *resultBuf;
+ struct passwd wrkBuf;
+ char stringBuf[2048]; /* 2048 has been proven to be large enough */
+
+ cstr = es_str2cstr(valnode->val.d.estr, NULL);
+ getpwnam_r(cstr, &wrkBuf, stringBuf, sizeof(stringBuf), &resultBuf);
+ if(resultBuf == NULL) {
+ parser_errmsg("parameter '%s': ID for user %s could not "
+ "be found", param->name, cstr);
+ } else {
+ val->val.datatype = 'N';
+ val->val.d.n = resultBuf->pw_uid;
+ dbgprintf("param '%s': uid %d obtained for user '%s'\n",
+ param->name, (int) resultBuf->pw_uid, cstr);
+ }
+ free(cstr);
+}
+
+/* note: we support all integer formats that es_str2num support,
+ * so hex and octal representations are also valid.
+ */
+static inline void
+doGetInt(struct nvlst *valnode, struct cnfparamdescr *param,
+ struct cnfparamvals *val)
+{
+ long long n;
+ int bSuccess;
+
+ n = es_str2num(valnode->val.d.estr, &bSuccess);
+ if(!bSuccess) {
+ parser_errmsg("parameter '%s' is not a proper number",
+ param->name);
+ }
+ val->val.datatype = 'N';
+ val->val.d.n = n;
+}
+
+static inline void
+doGetWord(struct nvlst *valnode, struct cnfparamdescr *param,
+ struct cnfparamvals *val)
+{
+ es_size_t i;
+ unsigned char *c;
+ val->val.datatype = 'S';
+ val->val.d.estr = es_newStr(32);
+ c = es_getBufAddr(valnode->val.d.estr);
+ for(i = 0 ; i < es_strlen(valnode->val.d.estr) && !isspace(c[i]) ; ++i) {
+ es_addChar(&val->val.d.estr, c[i]);
+ }
+ if(i != es_strlen(valnode->val.d.estr)) {
+ parser_errmsg("parameter '%s' contains whitespace, which is not "
+ "permitted - data after first whitespace ignored",
+ param->name);
+ }
+}
+
+static inline void
+doGetChar(struct nvlst *valnode, struct cnfparamdescr *param,
+ struct cnfparamvals *val)
+{
+ if(es_strlen(valnode->val.d.estr) != 1) {
+ parser_errmsg("parameter '%s' must contain exactly one character "
+ "but contains %d - cannot be processed",
+ param->name, es_strlen(valnode->val.d.estr));
+ }
+ val->val.datatype = 'S';
+ val->val.d.estr = es_strdup(valnode->val.d.estr);
+}
+
+/* get a single parameter according to its definition. Helper to
+ * nvlstGetParams.
+ */
+static inline void
+nvlstGetParam(struct nvlst *valnode, struct cnfparamdescr *param,
+ struct cnfparamvals *val)
+{
+ uchar *cstr;
+
+ dbgprintf("XXXX: in nvlstGetParam, name '%s', type %d, valnode->bUsed %d\n",
+ param->name, (int) param->type, valnode->bUsed);
+ valnode->bUsed = 1;
+ val->bUsed = 1;
+ switch(param->type) {
+ case eCmdHdlrQueueType:
+ doGetQueueType(valnode, param, val);
+ break;
+ case eCmdHdlrUID:
+ doGetUID(valnode, param, val);
+ break;
+ case eCmdHdlrGID:
+ doGetGID(valnode, param, val);
+ break;
+ case eCmdHdlrBinary:
+ doGetBinary(valnode, param, val);
+ break;
+ case eCmdHdlrFileCreateMode:
+ doGetFileCreateMode(valnode, param, val);
+ break;
+ case eCmdHdlrInt:
+ doGetInt(valnode, param, val);
+ break;
+ case eCmdHdlrSize:
+ doGetSize(valnode, param, val);
+ break;
+ case eCmdHdlrGetChar:
+ doGetChar(valnode, param, val);
+ break;
+ case eCmdHdlrFacility:
+ cstr = (uchar*) es_str2cstr(valnode->val.d.estr, NULL);
+ val->val.datatype = 'N';
+ val->val.d.n = decodeSyslogName(cstr, syslogFacNames);
+ free(cstr);
+ break;
+ case eCmdHdlrSeverity:
+ cstr = (uchar*) es_str2cstr(valnode->val.d.estr, NULL);
+ val->val.datatype = 'N';
+ val->val.d.n = decodeSyslogName(cstr, syslogPriNames);
+ free(cstr);
+ break;
+ case eCmdHdlrGetWord:
+ doGetWord(valnode, param, val);
+ break;
+ case eCmdHdlrString:
+ val->val.datatype = 'S';
+ val->val.d.estr = es_strdup(valnode->val.d.estr);
+ break;
+ case eCmdHdlrGoneAway:
+ parser_errmsg("parameter '%s' is no longer supported",
+ param->name);
+ break;
+ default:
+ dbgprintf("error: invalid param type\n");
+ break;
+ }
+}
+
+
+/* obtain conf params from an nvlst and emit error messages if
+ * necessary. If an already-existing param value is passed, that is
+ * used. If NULL is passed instead, a new one is allocated. In that case,
+ * it is the caller's duty to free it when no longer needed.
+ * NULL is returned on error, otherwise a pointer to the vals array.
+ */
+struct cnfparamvals*
+nvlstGetParams(struct nvlst *lst, struct cnfparamblk *params,
+ struct cnfparamvals *vals)
+{
+ int i;
+ struct nvlst *valnode;
+ struct cnfparamdescr *param;
+
+ if(params->version != CNFPARAMBLK_VERSION) {
+ dbgprintf("nvlstGetParams: invalid param block version "
+ "%d, expected %d\n",
+ params->version, CNFPARAMBLK_VERSION);
+ return NULL;
+ }
+
+ if(vals == NULL) {
+ if((vals = calloc(params->nParams,
+ sizeof(struct cnfparamvals))) == NULL)
+ return NULL;
+ }
+
+ for(i = 0 ; i < params->nParams ; ++i) {
+ param = params->descr + i;
+ if((valnode = nvlstFindNameCStr(lst, param->name)) == NULL)
+ continue;
+ if(vals[i].bUsed) {
+ parser_errmsg("parameter '%s' specified more than once - "
+ "one instance is ignored. Fix config", param->name);
+ continue;
+ }
+ nvlstGetParam(valnode, param, vals + i);
+ }
+ return vals;
+}
+
+
+void
+cnfparamsPrint(struct cnfparamblk *params, struct cnfparamvals *vals)
+{
+ int i;
+ char *cstr;
+
+ for(i = 0 ; i < params->nParams ; ++i) {
+ dbgprintf("%s: ", params->descr[i].name);
+ if(vals[i].bUsed) {
+ // TODO: other types!
+ switch(vals[i].val.datatype) {
+ case 'S':
+ cstr = es_str2cstr(vals[i].val.d.estr, NULL);
+ dbgprintf(" '%s'", cstr);
+ free(cstr);
+ break;
+ case 'N':
+ dbgprintf("%lld", vals[i].val.d.n);
+ break;
+ default:
+ dbgprintf("(unsupported datatype %c)",
+ vals[i].val.datatype);
+ }
+ } else {
+ dbgprintf("(unset)");
+ }
+ dbgprintf("\n");
+ }
+}
+
+struct cnfobj*
+cnfobjNew(enum cnfobjType objType, struct nvlst *lst)
+{
+ struct cnfobj *o;
+
+ if((o = malloc(sizeof(struct nvlst))) != NULL) {
+ nvlstChkDupes(lst);
+ o->objType = objType;
+ o->nvlst = lst;
+ }
+
+ return o;
+}
+
+void
+cnfobjDestruct(struct cnfobj *o)
+{
+ if(o != NULL) {
+ nvlstDestruct(o->nvlst);
+ free(o);
+ }
+}
+
+void
+cnfobjPrint(struct cnfobj *o)
+{
+ dbgprintf("obj: '%s'\n", cnfobjType2str(o->objType));
+ nvlstPrint(o->nvlst);
+}
+
+
+struct cnfactlst*
+cnfactlstNew(enum cnfactType actType, struct nvlst *lst, char *actLine)
+{
+ struct cnfactlst *actlst;
+
+ if((actlst = malloc(sizeof(struct cnfactlst))) != NULL) {
+ actlst->next = NULL;
+ actlst->syslines = NULL;
+ actlst->actType = actType;
+ actlst->lineno = yylineno;
+ actlst->cnfFile = strdup(cnfcurrfn);
+ if(actType == CNFACT_V2)
+ actlst->data.lst = lst;
+ else
+ actlst->data.legActLine = actLine;
+ }
+ return actlst;
+}
+
+struct cnfactlst*
+cnfactlstAddSysline(struct cnfactlst* actlst, char *line)
+{
+ struct cnfcfsyslinelst *cflst;
+
+ if((cflst = malloc(sizeof(struct cnfcfsyslinelst))) != NULL) {
+ cflst->line = line;
+ if(actlst->syslines == NULL) {
+ cflst->next = NULL;
+ } else {
+ cflst->next = actlst->syslines;
+ }
+ actlst->syslines = cflst;
+ }
+ return actlst;
+}
+
+
+void
+cnfactlstDestruct(struct cnfactlst *actlst)
+{
+ struct cnfactlst *toDel;
+
+ while(actlst != NULL) {
+ toDel = actlst;
+ actlst = actlst->next;
+ free(toDel->cnfFile);
+ cnfcfsyslinelstDestruct(toDel->syslines);
+ if(toDel->actType == CNFACT_V2)
+ nvlstDestruct(toDel->data.lst);
+ else
+ free(toDel->data.legActLine);
+ free(toDel);
+ }
+
+}
+
+static inline struct cnfcfsyslinelst*
+cnfcfsyslinelstReverse(struct cnfcfsyslinelst *lst)
+{
+ struct cnfcfsyslinelst *curr, *prev;
+ if(lst == NULL)
+ return NULL;
+ prev = NULL;
+ while(lst != NULL) {
+ curr = lst;
+ lst = lst->next;
+ curr->next = prev;
+ prev = curr;
+ }
+ return prev;
+}
+
+struct cnfactlst*
+cnfactlstReverse(struct cnfactlst *actlst)
+{
+ struct cnfactlst *curr, *prev;
+
+ prev = NULL;
+ while(actlst != NULL) {
+ //dbgprintf("reversing: %s\n", actlst->data.legActLine);
+ curr = actlst;
+ actlst = actlst->next;
+ curr->syslines = cnfcfsyslinelstReverse(curr->syslines);
+ curr->next = prev;
+ prev = curr;
+ }
+ return prev;
+}
+
+void
+cnfactlstPrint(struct cnfactlst *actlst)
+{
+ struct cnfcfsyslinelst *cflst;
+
+ while(actlst != NULL) {
+ dbgprintf("aclst %p: ", actlst);
+ if(actlst->actType == CNFACT_V2) {
+ dbgprintf("V2 action type: ");
+ nvlstPrint(actlst->data.lst);
+ } else {
+ dbgprintf("legacy action line: '%s'\n",
+ actlst->data.legActLine);
+ }
+ for( cflst = actlst->syslines
+ ; cflst != NULL ; cflst = cflst->next) {
+ dbgprintf("action:cfsysline: '%s'\n", cflst->line);
+ }
+ actlst = actlst->next;
+ }
+}
+
+struct cnfexpr*
+cnfexprNew(unsigned nodetype, struct cnfexpr *l, struct cnfexpr *r)
+{
+ struct cnfexpr *expr;
+
+ /* optimize some constructs during parsing */
+ if(nodetype == 'M' && r->nodetype == 'N') {
+ ((struct cnfnumval*)r)->val *= -1;
+ expr = r;
+ goto done;
+ }
+
+ if((expr = malloc(sizeof(struct cnfexpr))) != NULL) {
+ expr->nodetype = nodetype;
+ expr->l = l;
+ expr->r = r;
+ }
+done:
+ return expr;
+}
+
+
+/* ensure that retval is a number; if string is no number,
+ * try to convert it to one. The semantics from es_str2num()
+ * are used (bSuccess tells if the conversion went well or not).
+ */
+static inline long long
+var2Number(struct var *r, int *bSuccess)
+{
+ long long n;
+ if(r->datatype == 'S') {
+ n = es_str2num(r->d.estr, bSuccess);
+ } else {
+ n = r->d.n;
+ if(bSuccess)
+ *bSuccess = 1;
+ }
+ return n;
+}
+
+/* ensure that retval is a string; if string is no number,
+ * emit error message and set number to 0.
+ */
+static inline es_str_t *
+var2String(struct var *r, int *bMustFree)
+{
+ if(r->datatype == 'N') {
+ *bMustFree = 1;
+ return es_newStrFromNumber(r->d.n);
+ }
+ *bMustFree = 0;
+ return r->d.estr;
+}
+
+/* Perform a function call. This has been moved out of cnfExprEval in order
+ * to keep the code small and easier to maintain.
+ */
+static inline void
+doFuncCall(struct cnffunc *func, struct var *ret, void* usrptr)
+{
+ char *fname;
+ char *envvar;
+ int bMustFree;
+ es_str_t *estr;
+ char *str;
+ struct var r[CNFFUNC_MAX_ARGS];
+
+ dbgprintf("rainerscript: executing function id %d\n", func->fID);
+ switch(func->fID) {
+ case CNFFUNC_STRLEN:
+ if(func->expr[0]->nodetype == 'S') {
+ /* if we already have a string, we do not need to
+ * do one more recursive call.
+ */
+ ret->d.n = es_strlen(((struct cnfstringval*) func->expr[0])->estr);
+ } else {
+ cnfexprEval(func->expr[0], &r[0], usrptr);
+ estr = var2String(&r[0], &bMustFree);
+ ret->d.n = es_strlen(estr);
+ if(bMustFree) es_deleteStr(estr);
+ }
+ ret->datatype = 'N';
+ break;
+ case CNFFUNC_GETENV:
+ /* note: the optimizer shall have replaced calls to getenv()
+ * with a constant argument to a single string (once obtained via
+ * getenv()). So we do NOT need to check if there is just a
+ * string following.
+ */
+ cnfexprEval(func->expr[0], &r[0], usrptr);
+ estr = var2String(&r[0], &bMustFree);
+ str = (char*) es_str2cstr(estr, NULL);
+ envvar = getenv(str);
+ ret->datatype = 'S';
+ ret->d.estr = es_newStrFromCStr(envvar, strlen(envvar));
+ if(bMustFree) es_deleteStr(estr);
+ if(r[0].datatype == 'S') es_deleteStr(r[0].d.estr);
+ free(str);
+ break;
+ case CNFFUNC_TOLOWER:
+ cnfexprEval(func->expr[0], &r[0], usrptr);
+ estr = var2String(&r[0], &bMustFree);
+ if(!bMustFree) /* let caller handle that M) */
+ estr = es_strdup(estr);
+ es_tolower(estr);
+ ret->datatype = 'S';
+ ret->d.estr = estr;
+ break;
+ case CNFFUNC_CSTR:
+ cnfexprEval(func->expr[0], &r[0], usrptr);
+ estr = var2String(&r[0], &bMustFree);
+ if(!bMustFree) /* let caller handle that M) */
+ estr = es_strdup(estr);
+ ret->datatype = 'S';
+ ret->d.estr = estr;
+ break;
+ case CNFFUNC_CNUM:
+ if(func->expr[0]->nodetype == 'N') {
+ ret->d.n = ((struct cnfnumval*)func->expr[0])->val;
+ } else if(func->expr[0]->nodetype == 'S') {
+ ret->d.n = es_str2num(((struct cnfstringval*) func->expr[0])->estr,
+ NULL);
+ } else {
+ cnfexprEval(func->expr[0], &r[0], usrptr);
+ ret->d.n = var2Number(&r[0], NULL);
+ if(r[0].datatype == 'S') es_deleteStr(r[0].d.estr);
+ }
+ ret->datatype = 'N';
+ break;
+ default:
+ if(Debug) {
+ fname = es_str2cstr(func->fname, NULL);
+ dbgprintf("rainerscript: invalid function id %u (name '%s')\n",
+ (unsigned) func->fID, fname);
+ free(fname);
+ }
+ ret->datatype = 'N';
+ ret->d.n = 0;
+ }
+}
+
+#define FREE_BOTH_RET \
+ if(r.datatype == 'S') es_deleteStr(r.d.estr); \
+ if(l.datatype == 'S') es_deleteStr(l.d.estr)
+
+#define COMP_NUM_BINOP(x) \
+ cnfexprEval(expr->l, &l, usrptr); \
+ cnfexprEval(expr->r, &r, usrptr); \
+ ret->datatype = 'N'; \
+ ret->d.n = var2Number(&l, &convok_l) x var2Number(&r, &convok_r); \
+ FREE_BOTH_RET
+
+#define PREP_TWO_STRINGS \
+ cnfexprEval(expr->l, &l, usrptr); \
+ estr_l = var2String(&l, &bMustFree2); \
+ cnfexprEval(expr->r, &r, usrptr); \
+ estr_r = var2String(&r, &bMustFree)
+
+#define FREE_TWO_STRINGS \
+ if(bMustFree) es_deleteStr(estr_r); \
+ if(bMustFree2) es_deleteStr(estr_l); \
+ FREE_BOTH_RET
+
+/* evaluate an expression.
+ * Note that we try to avoid malloc whenever possible (because of
+ * the large overhead it has, especially on highly threaded programs).
+ * As such, the each caller level must provide buffer space for the
+ * result on its stack during recursion. This permits the callee to store
+ * the return value without malloc. As the value is a somewhat larger
+ * struct, we could otherwise not return it without malloc.
+ * Note that we implement boolean shortcut operations. For our needs, there
+ * simply is no case where full evaluation would make any sense at all.
+ */
+void
+cnfexprEval(struct cnfexpr *expr, struct var *ret, void* usrptr)
+{
+ struct var r, l; /* memory for subexpression results */
+ es_str_t *estr_r, *estr_l;
+ int convok_r, convok_l;
+ int bMustFree, bMustFree2;
+ long long n_r, n_l;
+
+ //dbgprintf("eval expr %p, type '%c'(%u)\n", expr, expr->nodetype, expr->nodetype);
+ switch(expr->nodetype) {
+ /* note: comparison operations are extremely similar. The code can be copyied, only
+ * places flagged with "CMP" need to be changed.
+ */
+ case CMP_EQ:
+ cnfexprEval(expr->l, &l, usrptr);
+ cnfexprEval(expr->r, &r, usrptr);
+ ret->datatype = 'N';
+ if(l.datatype == 'S') {
+ if(r.datatype == 'S') {
+ ret->d.n = !es_strcmp(l.d.estr, r.d.estr); /*CMP*/
+ } else {
+ n_l = var2Number(&l, &convok_l);
+ if(convok_l) {
+ ret->d.n = (n_l == r.d.n); /*CMP*/
+ } else {
+ estr_r = var2String(&r, &bMustFree);
+ ret->d.n = !es_strcmp(l.d.estr, estr_r); /*CMP*/
+ if(bMustFree) es_deleteStr(estr_r);
+ }
+ }
+ } else {
+ if(r.datatype == 'S') {
+ n_r = var2Number(&r, &convok_r);
+ if(convok_r) {
+ ret->d.n = (l.d.n == n_r); /*CMP*/
+ } else {
+ estr_l = var2String(&l, &bMustFree);
+ ret->d.n = !es_strcmp(r.d.estr, estr_l); /*CMP*/
+ if(bMustFree) es_deleteStr(estr_l);
+ }
+ } else {
+ ret->d.n = (l.d.n == r.d.n); /*CMP*/
+ }
+ }
+ FREE_BOTH_RET;
+ break;
+ case CMP_NE:
+ cnfexprEval(expr->l, &l, usrptr);
+ cnfexprEval(expr->r, &r, usrptr);
+ ret->datatype = 'N';
+ if(l.datatype == 'S') {
+ if(r.datatype == 'S') {
+ ret->d.n = es_strcmp(l.d.estr, r.d.estr); /*CMP*/
+ } else {
+ n_l = var2Number(&l, &convok_l);
+ if(convok_l) {
+ ret->d.n = (n_l != r.d.n); /*CMP*/
+ } else {
+ estr_r = var2String(&r, &bMustFree);
+ ret->d.n = es_strcmp(l.d.estr, estr_r); /*CMP*/
+ if(bMustFree) es_deleteStr(estr_r);
+ }
+ }
+ } else {
+ if(r.datatype == 'S') {
+ n_r = var2Number(&r, &convok_r);
+ if(convok_r) {
+ ret->d.n = (l.d.n != n_r); /*CMP*/
+ } else {
+ estr_l = var2String(&l, &bMustFree);
+ ret->d.n = es_strcmp(r.d.estr, estr_l); /*CMP*/
+ if(bMustFree) es_deleteStr(estr_l);
+ }
+ } else {
+ ret->d.n = (l.d.n != r.d.n); /*CMP*/
+ }
+ }
+ FREE_BOTH_RET;
+ break;
+ case CMP_LE:
+ cnfexprEval(expr->l, &l, usrptr);
+ cnfexprEval(expr->r, &r, usrptr);
+ ret->datatype = 'N';
+ if(l.datatype == 'S') {
+ if(r.datatype == 'S') {
+ ret->d.n = es_strcmp(l.d.estr, r.d.estr) <= 0; /*CMP*/
+ } else {
+ n_l = var2Number(&l, &convok_l);
+ if(convok_l) {
+ ret->d.n = (n_l <= r.d.n); /*CMP*/
+ } else {
+ estr_r = var2String(&r, &bMustFree);
+ ret->d.n = es_strcmp(l.d.estr, estr_r) <= 0; /*CMP*/
+ if(bMustFree) es_deleteStr(estr_r);
+ }
+ }
+ } else {
+ if(r.datatype == 'S') {
+ n_r = var2Number(&r, &convok_r);
+ if(convok_r) {
+ ret->d.n = (l.d.n <= n_r); /*CMP*/
+ } else {
+ estr_l = var2String(&l, &bMustFree);
+ ret->d.n = es_strcmp(r.d.estr, estr_l) <= 0; /*CMP*/
+ if(bMustFree) es_deleteStr(estr_l);
+ }
+ } else {
+ ret->d.n = (l.d.n <= r.d.n); /*CMP*/
+ }
+ }
+ FREE_BOTH_RET;
+ break;
+ case CMP_GE:
+ cnfexprEval(expr->l, &l, usrptr);
+ cnfexprEval(expr->r, &r, usrptr);
+ ret->datatype = 'N';
+ if(l.datatype == 'S') {
+ if(r.datatype == 'S') {
+ ret->d.n = es_strcmp(l.d.estr, r.d.estr) >= 0; /*CMP*/
+ } else {
+ n_l = var2Number(&l, &convok_l);
+ if(convok_l) {
+ ret->d.n = (n_l >= r.d.n); /*CMP*/
+ } else {
+ estr_r = var2String(&r, &bMustFree);
+ ret->d.n = es_strcmp(l.d.estr, estr_r) >= 0; /*CMP*/
+ if(bMustFree) es_deleteStr(estr_r);
+ }
+ }
+ } else {
+ if(r.datatype == 'S') {
+ n_r = var2Number(&r, &convok_r);
+ if(convok_r) {
+ ret->d.n = (l.d.n >= n_r); /*CMP*/
+ } else {
+ estr_l = var2String(&l, &bMustFree);
+ ret->d.n = es_strcmp(r.d.estr, estr_l) >= 0; /*CMP*/
+ if(bMustFree) es_deleteStr(estr_l);
+ }
+ } else {
+ ret->d.n = (l.d.n >= r.d.n); /*CMP*/
+ }
+ }
+ FREE_BOTH_RET;
+ break;
+ case CMP_LT:
+ cnfexprEval(expr->l, &l, usrptr);
+ cnfexprEval(expr->r, &r, usrptr);
+ ret->datatype = 'N';
+ if(l.datatype == 'S') {
+ if(r.datatype == 'S') {
+ ret->d.n = es_strcmp(l.d.estr, r.d.estr) < 0; /*CMP*/
+ } else {
+ n_l = var2Number(&l, &convok_l);
+ if(convok_l) {
+ ret->d.n = (n_l < r.d.n); /*CMP*/
+ } else {
+ estr_r = var2String(&r, &bMustFree);
+ ret->d.n = es_strcmp(l.d.estr, estr_r) < 0; /*CMP*/
+ if(bMustFree) es_deleteStr(estr_r);
+ }
+ }
+ } else {
+ if(r.datatype == 'S') {
+ n_r = var2Number(&r, &convok_r);
+ if(convok_r) {
+ ret->d.n = (l.d.n < n_r); /*CMP*/
+ } else {
+ estr_l = var2String(&l, &bMustFree);
+ ret->d.n = es_strcmp(r.d.estr, estr_l) < 0; /*CMP*/
+ if(bMustFree) es_deleteStr(estr_l);
+ }
+ } else {
+ ret->d.n = (l.d.n < r.d.n); /*CMP*/
+ }
+ }
+ FREE_BOTH_RET;
+ break;
+ case CMP_GT:
+ cnfexprEval(expr->l, &l, usrptr);
+ cnfexprEval(expr->r, &r, usrptr);
+ ret->datatype = 'N';
+ if(l.datatype == 'S') {
+ if(r.datatype == 'S') {
+ ret->d.n = es_strcmp(l.d.estr, r.d.estr) > 0; /*CMP*/
+ } else {
+ n_l = var2Number(&l, &convok_l);
+ if(convok_l) {
+ ret->d.n = (n_l > r.d.n); /*CMP*/
+ } else {
+ estr_r = var2String(&r, &bMustFree);
+ ret->d.n = es_strcmp(l.d.estr, estr_r) > 0; /*CMP*/
+ if(bMustFree) es_deleteStr(estr_r);
+ }
+ }
+ } else {
+ if(r.datatype == 'S') {
+ n_r = var2Number(&r, &convok_r);
+ if(convok_r) {
+ ret->d.n = (l.d.n > n_r); /*CMP*/
+ } else {
+ estr_l = var2String(&l, &bMustFree);
+ ret->d.n = es_strcmp(r.d.estr, estr_l) > 0; /*CMP*/
+ if(bMustFree) es_deleteStr(estr_l);
+ }
+ } else {
+ ret->d.n = (l.d.n > r.d.n); /*CMP*/
+ }
+ }
+ FREE_BOTH_RET;
+ break;
+ case CMP_STARTSWITH:
+ PREP_TWO_STRINGS;
+ ret->datatype = 'N';
+ ret->d.n = es_strncmp(estr_l, estr_r, estr_r->lenStr) == 0;
+ FREE_TWO_STRINGS;
+ break;
+ case CMP_STARTSWITHI:
+ PREP_TWO_STRINGS;
+ ret->datatype = 'N';
+ ret->d.n = es_strncasecmp(estr_l, estr_r, estr_r->lenStr) == 0;
+ FREE_TWO_STRINGS;
+ break;
+ case CMP_CONTAINS:
+ PREP_TWO_STRINGS;
+ ret->datatype = 'N';
+ ret->d.n = es_strContains(estr_l, estr_r) != -1;
+ FREE_TWO_STRINGS;
+ break;
+ case CMP_CONTAINSI:
+ PREP_TWO_STRINGS;
+ ret->datatype = 'N';
+ ret->d.n = es_strCaseContains(estr_l, estr_r) != -1;
+ FREE_TWO_STRINGS;
+ break;
+ case OR:
+ cnfexprEval(expr->l, &l, usrptr);
+ ret->datatype = 'N';
+ if(var2Number(&l, &convok_l)) {
+ ret->d.n = 1ll;
+ } else {
+ cnfexprEval(expr->r, &r, usrptr);
+ if(var2Number(&r, &convok_r))
+ ret->d.n = 1ll;
+ else
+ ret->d.n = 0ll;
+ if(r.datatype == 'S') es_deleteStr(r.d.estr);
+ }
+ if(l.datatype == 'S') es_deleteStr(l.d.estr);
+ break;
+ case AND:
+ cnfexprEval(expr->l, &l, usrptr);
+ ret->datatype = 'N';
+ if(var2Number(&l, &convok_l)) {
+ cnfexprEval(expr->r, &r, usrptr);
+ if(var2Number(&r, &convok_r))
+ ret->d.n = 1ll;
+ else
+ ret->d.n = 0ll;
+ if(r.datatype == 'S') es_deleteStr(r.d.estr);
+ } else {
+ ret->d.n = 0ll;
+ }
+ if(l.datatype == 'S') es_deleteStr(l.d.estr);
+ break;
+ case NOT:
+ cnfexprEval(expr->r, &r, usrptr);
+ ret->datatype = 'N';
+ ret->d.n = !var2Number(&r, &convok_r);
+ if(r.datatype == 'S') es_deleteStr(r.d.estr);
+ break;
+ case 'N':
+ ret->datatype = 'N';
+ ret->d.n = ((struct cnfnumval*)expr)->val;
+ break;
+ case 'S':
+ ret->datatype = 'S';
+ ret->d.estr = es_strdup(((struct cnfstringval*)expr)->estr);
+ break;
+ case 'V':
+ ret->datatype = 'S';
+ ret->d.estr = cnfGetVar(((struct cnfvar*)expr)->name, usrptr);
+ break;
+ case '+':
+ COMP_NUM_BINOP(+);
+ break;
+ case '-':
+ COMP_NUM_BINOP(-);
+ break;
+ case '*':
+ COMP_NUM_BINOP(*);
+ break;
+ case '/':
+ COMP_NUM_BINOP(/);
+ break;
+ case '%':
+ COMP_NUM_BINOP(%);
+ break;
+ case 'M':
+ cnfexprEval(expr->r, &r, usrptr);
+ ret->datatype = 'N';
+ ret->d.n = -var2Number(&r, &convok_r);
+ if(r.datatype == 'S') es_deleteStr(r.d.estr);
+ break;
+ case 'F':
+ doFuncCall((struct cnffunc*) expr, ret, usrptr);
+ break;
+ default:
+ ret->datatype = 'N';
+ ret->d.n = 0ll;
+ dbgprintf("eval error: unknown nodetype %u['%c']\n",
+ (unsigned) expr->nodetype, (char) expr->nodetype);
+ break;
+ }
+}
+
+/* Evaluate an expression as a bool. This is added because expressions are
+ * mostly used inside filters, and so this function is quite common and
+ * important.
+ */
+int
+cnfexprEvalBool(struct cnfexpr *expr, void *usrptr)
+{
+ int convok;
+ struct var ret;
+ cnfexprEval(expr, &ret, usrptr);
+ return var2Number(&ret, &convok);
+}
+
+inline static void
+doIndent(int indent)
+{
+ int i;
+ for(i = 0 ; i < indent ; ++i)
+ dbgprintf(" ");
+}
+void
+cnfexprPrint(struct cnfexpr *expr, int indent)
+{
+ struct cnffunc *func;
+ int i;
+
+ //dbgprintf("expr %p, indent %d, type '%c'\n", expr, indent, expr->nodetype);
+ switch(expr->nodetype) {
+ case CMP_EQ:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf("==\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case CMP_NE:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf("!=\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case CMP_LE:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf("<=\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case CMP_GE:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf(">=\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case CMP_LT:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf("<\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case CMP_GT:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf(">\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case CMP_CONTAINS:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf("CONTAINS\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case CMP_CONTAINSI:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf("CONTAINS_I\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case CMP_STARTSWITH:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf("STARTSWITH\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case CMP_STARTSWITHI:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf("STARTSWITH_I\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case OR:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf("OR\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case AND:
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf("AND\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case NOT:
+ doIndent(indent);
+ dbgprintf("NOT\n");
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ case 'S':
+ doIndent(indent);
+ cstrPrint("string '", ((struct cnfstringval*)expr)->estr);
+ dbgprintf("'\n");
+ break;
+ case 'N':
+ doIndent(indent);
+ dbgprintf("%lld\n", ((struct cnfnumval*)expr)->val);
+ break;
+ case 'V':
+ doIndent(indent);
+ dbgprintf("var '%s'\n", ((struct cnfvar*)expr)->name);
+ break;
+ case 'F':
+ doIndent(indent);
+ func = (struct cnffunc*) expr;
+ cstrPrint("function '", func->fname);
+ dbgprintf("' (id:%d, params:%hu)\n", func->fID, func->nParams);
+ for(i = 0 ; i < func->nParams ; ++i) {
+ cnfexprPrint(func->expr[i], indent+1);
+ }
+ break;
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '%':
+ case 'M':
+ if(expr->l != NULL)
+ cnfexprPrint(expr->l, indent+1);
+ doIndent(indent);
+ dbgprintf("%c\n", (char) expr->nodetype);
+ cnfexprPrint(expr->r, indent+1);
+ break;
+ default:
+ dbgprintf("error: unknown nodetype %u\n",
+ (unsigned) expr->nodetype);
+ break;
+ }
+}
+
+struct cnfnumval*
+cnfnumvalNew(long long val)
+{
+ struct cnfnumval *numval;
+ if((numval = malloc(sizeof(struct cnfnumval))) != NULL) {
+ numval->nodetype = 'N';
+ numval->val = val;
+ }
+ return numval;
+}
+
+struct cnfstringval*
+cnfstringvalNew(es_str_t *estr)
+{
+ struct cnfstringval *strval;
+ if((strval = malloc(sizeof(struct cnfstringval))) != NULL) {
+ strval->nodetype = 'S';
+ strval->estr = estr;
+ }
+ return strval;
+}
+
+struct cnfvar*
+cnfvarNew(char *name)
+{
+ struct cnfvar *var;
+ if((var = malloc(sizeof(struct cnfvar))) != NULL) {
+ var->nodetype = 'V';
+ var->name = name;
+ }
+ return var;
+}
+
+struct cnfrule *
+cnfruleNew(enum cnfFiltType filttype, struct cnfactlst *actlst)
+{
+ struct cnfrule* cnfrule;
+ if((cnfrule = malloc(sizeof(struct cnfrule))) != NULL) {
+ cnfrule->nodetype = 'R';
+ cnfrule->filttype = filttype;
+ cnfrule->actlst = cnfactlstReverse(actlst);
+ }
+ return cnfrule;
+}
+
+void
+cnfrulePrint(struct cnfrule *rule)
+{
+ dbgprintf("------ start rule %p:\n", rule);
+ dbgprintf("%s: ", cnfFiltType2str(rule->filttype));
+ switch(rule->filttype) {
+ case CNFFILT_NONE:
+ break;
+ case CNFFILT_PRI:
+ case CNFFILT_PROP:
+ dbgprintf("%s\n", rule->filt.s);
+ break;
+ case CNFFILT_SCRIPT:
+ dbgprintf("\n");
+ cnfexprPrint(rule->filt.expr, 0);
+ break;
+ }
+ cnfactlstPrint(rule->actlst);
+ dbgprintf("------ end rule %p\n", rule);
+}
+
+/* note: the sysline itself was already freed during processing
+ * and as such MUST NOT be freed again!
+ */
+void
+cnfcfsyslinelstDestruct(struct cnfcfsyslinelst *cfslst)
+{
+ struct cnfcfsyslinelst *toDel;
+ while(cfslst != NULL) {
+ toDel = cfslst;
+ cfslst = cfslst->next;
+ free(toDel);
+ }
+}
+
+void
+cnfruleDestruct(struct cnfrule *rule)
+{
+ if( rule->filttype == CNFFILT_PRI
+ || rule->filttype == CNFFILT_PROP)
+ free(rule->filt.s);
+ cnfactlstDestruct(rule->actlst);
+ free(rule);
+}
+
+struct cnffparamlst *
+cnffparamlstNew(struct cnfexpr *expr, struct cnffparamlst *next)
+{
+ struct cnffparamlst* lst;
+ if((lst = malloc(sizeof(struct cnffparamlst))) != NULL) {
+ lst->nodetype = 'P';
+ lst->expr = expr;
+ lst->next = next;
+ }
+ return lst;
+}
+
+/* Obtain function id from name AND number of params. Issues the
+ * relevant error messages if errors are detected.
+ */
+static inline enum cnffuncid
+funcName2ID(es_str_t *fname, unsigned short nParams)
+{
+ if(!es_strbufcmp(fname, (unsigned char*)"strlen", sizeof("strlen") - 1)) {
+ if(nParams != 1) {
+ parser_errmsg("number of parameters for strlen() must be one "
+ "but is %d.", nParams);
+ return CNFFUNC_INVALID;
+ }
+ return CNFFUNC_STRLEN;
+ } else if(!es_strbufcmp(fname, (unsigned char*)"getenv", sizeof("getenv") - 1)) {
+ if(nParams != 1) {
+ parser_errmsg("number of parameters for getenv() must be one "
+ "but is %d.", nParams);
+ return CNFFUNC_INVALID;
+ }
+ return CNFFUNC_GETENV;
+ } else if(!es_strbufcmp(fname, (unsigned char*)"tolower", sizeof("tolower") - 1)) {
+ if(nParams != 1) {
+ parser_errmsg("number of parameters for tolower() must be one "
+ "but is %d.", nParams);
+ return CNFFUNC_INVALID;
+ }
+ return CNFFUNC_TOLOWER;
+ } else if(!es_strbufcmp(fname, (unsigned char*)"cstr", sizeof("cstr") - 1)) {
+ if(nParams != 1) {
+ parser_errmsg("number of parameters for cstr() must be one "
+ "but is %d.", nParams);
+ return CNFFUNC_INVALID;
+ }
+ return CNFFUNC_CSTR;
+ } else if(!es_strbufcmp(fname, (unsigned char*)"cnum", sizeof("cnum") - 1)) {
+ if(nParams != 1) {
+ parser_errmsg("number of parameters for cnum() must be one "
+ "but is %d.", nParams);
+ return CNFFUNC_INVALID;
+ }
+ return CNFFUNC_CNUM;
+ } else {
+ return CNFFUNC_INVALID;
+ }
+}
+
+struct cnffunc *
+cnffuncNew(es_str_t *fname, struct cnffparamlst* paramlst)
+{
+ struct cnffunc* func;
+ struct cnffparamlst *param, *toDel;
+ unsigned short i;
+ unsigned short nParams;
+
+ /* we first need to find out how many params we have */
+ nParams = 0;
+ for(param = paramlst ; param != NULL ; param = param->next)
+ ++nParams;
+ if((func = malloc(sizeof(struct cnffunc) + (nParams * sizeof(struct cnfexp*))))
+ != NULL) {
+ func->nodetype = 'F';
+ func->fname = fname;
+ func->nParams = nParams;
+ func->fID = funcName2ID(fname, nParams);
+ /* shuffle params over to array (access speed!) */
+ param = paramlst;
+ for(i = 0 ; i < nParams ; ++i) {
+ func->expr[i] = param->expr;
+ toDel = param;
+ param = param->next;
+ free(toDel);
+ }
+ }
+ return func;
+}
+
+int
+cnfDoInclude(char *name)
+{
+ char *cfgFile;
+ unsigned i;
+ int result;
+ glob_t cfgFiles;
+ struct stat fileInfo;
+
+ /* Use GLOB_MARK to append a trailing slash for directories.
+ * Required by doIncludeDirectory().
+ */
+ result = glob(name, GLOB_MARK, NULL, &cfgFiles);
+ if(result == GLOB_NOSPACE || result == GLOB_ABORTED) {
+#if 0
+ char errStr[1024];
+ rs_strerror_r(errno, errStr, sizeof(errStr));
+ errmsg.LogError(0, RS_RET_FILE_NOT_FOUND, "error accessing config file or directory '%s': %s",
+ pattern, errStr);
+ ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND);
+#endif
+ dbgprintf("includeconfig glob error %d\n", errno);
+ return 1;
+ }
+
+ for(i = 0; i < cfgFiles.gl_pathc; i++) {
+ cfgFile = cfgFiles.gl_pathv[i];
+
+ if(stat(cfgFile, &fileInfo) != 0)
+ continue; /* continue with the next file if we can't stat() the file */
+
+ if(S_ISREG(fileInfo.st_mode)) { /* config file */
+ dbgprintf("requested to include config file '%s'\n", cfgFile);
+ cnfSetLexFile(cfgFile);
+ } else if(S_ISDIR(fileInfo.st_mode)) { /* config directory */
+ if(strcmp(name, cfgFile)) {
+ /* do not include ourselves! */
+ dbgprintf("requested to include directory '%s'\n", cfgFile);
+ cnfDoInclude(cfgFile);
+ }
+ } else {
+ dbgprintf("warning: unable to process IncludeConfig directive '%s'\n", cfgFile);
+ }
+ }
+
+ globfree(&cfgFiles);
+ return 0;
+}
+
+void
+varDelete(struct var *v)
+{
+ if(v->datatype == 'S')
+ es_deleteStr(v->d.estr);
+}
+
+void
+cnfparamvalsDestruct(struct cnfparamvals *paramvals, struct cnfparamblk *blk)
+{
+ int i;
+ for(i = 0 ; i < blk->nParams ; ++i) {
+ varDelete(&paramvals[i].val);
+ }
+ free(paramvals);
+}
+
+/* find the index (or -1!) for a config param by name. This is used to
+ * address the parameter array. Of course, we could use with static
+ * indices, but that would create some extra bug potential. So we
+ * resort to names. As we do this only during the initial config parsing
+ * stage the (considerable!) extra overhead is OK. -- rgerhards, 2011-07-19
+ */
+int
+cnfparamGetIdx(struct cnfparamblk *params, char *name)
+{
+ int i;
+ for(i = 0 ; i < params->nParams ; ++i)
+ if(!strcmp(params->descr[i].name, name))
+ break;
+ if(i == params->nParams)
+ i = -1; /* not found */
+ return i;
+}
+
+
+void
+cstrPrint(char *text, es_str_t *estr)
+{
+ char *str;
+ str = es_str2cstr(estr, NULL);
+ dbgprintf("%s%s", text, str);
+ free(str);
+}
diff --git a/grammar/rainerscript.h b/grammar/rainerscript.h
new file mode 100644
index 0000000..e11ae62
--- /dev/null
+++ b/grammar/rainerscript.h
@@ -0,0 +1,255 @@
+#ifndef INC_UTILS_H
+#define INC_UTILS_H
+#include <stdio.h>
+#include <libestr.h>
+#include <typedefs.h>
+
+#define CNFFUNC_MAX_ARGS 32
+ /**< maximum number of arguments that any function can have (among
+ * others, this is used to size data structures).
+ */
+
+extern int Debug; /* 1 if in debug mode, 0 otherwise -- to be enhanced */
+
+enum cnfobjType {
+ CNFOBJ_ACTION,
+ CNFOBJ_GLOBAL,
+ CNFOBJ_INPUT,
+ CNFOBJ_MODULE,
+ CNFOBJ_INVALID = 0
+};
+
+static inline char*
+cnfobjType2str(enum cnfobjType ot)
+{
+ switch(ot) {
+ case CNFOBJ_ACTION:
+ return "action";
+ break;
+ case CNFOBJ_GLOBAL:
+ return "global";
+ break;
+ case CNFOBJ_INPUT:
+ return "input";
+ break;
+ case CNFOBJ_MODULE:
+ return "module";
+ break;
+ default:return "error: invalid cnfobjType";
+ }
+}
+
+enum cnfactType { CNFACT_V2, CNFACT_LEGACY };
+
+/* a variant type, for example used for expression evaluation
+ * 2011-07-15/rger: note that there exists a "legacy" object var_t,
+ * which implements the same idea, but in a suboptimal manner. I have
+ * stipped this down as much as possible, but will keep it for a while
+ * to avoid unnecessary complexity during development. TODO: in the long
+ * term, var_t shall be replaced by struct var.
+ */
+struct var {
+ union {
+ es_str_t *estr;
+ struct cnfexpr *expr;
+ long long n;
+ } d;
+ char datatype; /* 'N' number, 'S' string, 'E' expression */
+};
+
+struct cnfobj {
+ enum cnfobjType objType;
+ struct nvlst *nvlst;
+};
+
+struct nvlst {
+ struct nvlst *next;
+ es_str_t *name;
+ struct var val;
+ unsigned char bUsed;
+ /**< was this node used during config processing? If not, this
+ * indicates an error. After all, the user specified a setting
+ * that the software does not know.
+ */
+};
+
+struct cnfcfsyslinelst {
+ struct cnfcfsyslinelst *next;
+ char *line;
+};
+
+struct cnfactlst {
+ struct cnfactlst *next;
+ struct cnfcfsyslinelst *syslines;
+ enum cnfactType actType;
+ union {
+ struct nvlst *lst;
+ char *legActLine;
+ } data;
+ char *cnfFile;
+ int lineno;
+};
+
+/* the following structures support expressions, and may (very much later
+ * be the sole foundation for the AST.
+ *
+ * nodetypes (list not yet complete)
+ * F - function
+ * N - number
+ * P - fparamlst
+ * R - rule
+ * S - string
+ * V - var
+ */
+enum cnfFiltType { CNFFILT_NONE, CNFFILT_PRI, CNFFILT_PROP, CNFFILT_SCRIPT };
+static inline char*
+cnfFiltType2str(enum cnfFiltType filttype)
+{
+ switch(filttype) {
+ case CNFFILT_NONE:
+ return("filter:none");
+ case CNFFILT_PRI:
+ return("filter:pri");
+ case CNFFILT_PROP:
+ return("filter:prop");
+ case CNFFILT_SCRIPT:
+ return("filter:script");
+ }
+ return("error:invalid_filter_type"); /* should never be reached */
+}
+
+
+struct cnfrule {
+ unsigned nodetype;
+ enum cnfFiltType filttype;
+ union {
+ char *s;
+ struct cnfexpr *expr;
+ } filt;
+ struct cnfactlst *actlst;
+};
+
+struct cnfexpr {
+ unsigned nodetype;
+ struct cnfexpr *l;
+ struct cnfexpr *r;
+};
+
+struct cnfnumval {
+ unsigned nodetype;
+ long long val;
+};
+
+struct cnfstringval {
+ unsigned nodetype;
+ es_str_t *estr;
+};
+
+struct cnfvar {
+ unsigned nodetype;
+ char *name;
+};
+
+struct cnffparamlst {
+ unsigned nodetype; /* P */
+ struct cnffparamlst *next;
+ struct cnfexpr *expr;
+};
+
+enum cnffuncid {
+ CNFFUNC_INVALID = 0, /**< defunct entry, do not use (should normally not be present) */
+ CNFFUNC_NAME = 1, /**< use name to call function (for future use) */
+ CNFFUNC_STRLEN,
+ CNFFUNC_GETENV,
+ CNFFUNC_TOLOWER,
+ CNFFUNC_CSTR,
+ CNFFUNC_CNUM
+};
+
+struct cnffunc {
+ unsigned nodetype;
+ es_str_t *fname;
+ unsigned short nParams;
+ enum cnffuncid fID; /* function ID for built-ins, 0 means use name */
+ struct cnfexpr *expr[];
+};
+
+/* future extensions
+struct x {
+ int nodetype;
+};
+*/
+
+
+/* the following defines describe the parameter block for puling
+ * config parameters. Note that the focus is on ease and saveness of
+ * use, not performance. For example, we address parameters by name
+ * instead of index, because the former is less error-prone. The (severe)
+ * performance hit does not matter, as it is a one-time hit during config
+ * load but never during actual processing. So there is really no reason
+ * to care.
+ */
+struct cnfparamdescr { /* first the param description */
+ char *name; /**< not a es_str_t to ease definition in code */
+ ecslCmdHdrlType type;
+ unsigned flags;
+};
+/* flags for cnfparamdescr: */
+#define CNFPARAM_REQUIRED 0x0001
+
+struct cnfparamblk { /* now the actual param block use in API calls */
+ unsigned short version;
+ unsigned short nParams;
+ struct cnfparamdescr *descr;
+};
+#define CNFPARAMBLK_VERSION 1
+ /**< caller must have same version as engine -- else things may
+ * be messed up. But note that we may support multiple versions
+ * inside the engine, if at some later stage we want to do
+ * that. -- rgerhards, 2011-07-15
+ */
+struct cnfparamvals { /* the values we obtained for param descr. */
+ struct var val;
+ unsigned char bUsed;
+};
+
+
+int cnfParseBuffer(char *buf, unsigned lenBuf);
+void readConfFile(FILE *fp, es_str_t **str);
+struct nvlst* nvlstNew(es_str_t *name, es_str_t *value);
+void nvlstDestruct(struct nvlst *lst);
+void nvlstPrint(struct nvlst *lst);
+void nvlstChkUnused(struct nvlst *lst);
+struct nvlst* nvlstFindName(struct nvlst *lst, es_str_t *name);
+struct cnfobj* cnfobjNew(enum cnfobjType objType, struct nvlst *lst);
+void cnfobjDestruct(struct cnfobj *o);
+void cnfobjPrint(struct cnfobj *o);
+struct cnfactlst* cnfactlstNew(enum cnfactType actType, struct nvlst *lst, char *actLine);
+void cnfactlstDestruct(struct cnfactlst *actlst);
+void cnfactlstPrint(struct cnfactlst *actlst);
+struct cnfactlst* cnfactlstAddSysline(struct cnfactlst* actlst, char *line);
+struct cnfactlst* cnfactlstReverse(struct cnfactlst *actlst);
+struct cnfexpr* cnfexprNew(unsigned nodetype, struct cnfexpr *l, struct cnfexpr *r);
+void cnfexprPrint(struct cnfexpr *expr, int indent);
+void cnfexprEval(struct cnfexpr *expr, struct var *ret, void *pusr);
+int cnfexprEvalBool(struct cnfexpr *expr, void *usrptr);
+struct cnfnumval* cnfnumvalNew(long long val);
+struct cnfstringval* cnfstringvalNew(es_str_t *estr);
+struct cnfrule * cnfruleNew(enum cnfFiltType filttype, struct cnfactlst *actlst);
+void cnfruleDestruct(struct cnfrule *rule);
+void cnfrulePrint(struct cnfrule *rule);
+struct cnfvar* cnfvarNew(char *name);
+struct cnffunc * cnffuncNew(es_str_t *fname, struct cnffparamlst* paramlst);
+struct cnffparamlst * cnffparamlstNew(struct cnfexpr *expr, struct cnffparamlst *next);
+int cnfDoInclude(char *name);
+int cnfparamGetIdx(struct cnfparamblk *params, char *name);
+struct cnfparamvals* nvlstGetParams(struct nvlst *lst, struct cnfparamblk *params,
+ struct cnfparamvals *vals);
+void cnfparamsPrint(struct cnfparamblk *params, struct cnfparamvals *vals);
+void varDelete(struct var *v);
+void cnfparamvalsDestruct(struct cnfparamvals *paramvals, struct cnfparamblk *blk);
+void cnfcfsyslinelstDestruct(struct cnfcfsyslinelst *cfslst);
+
+/* debug helper */
+void cstrPrint(char *text, es_str_t *estr);
+#endif
diff --git a/grammar/samp b/grammar/samp
new file mode 100644
index 0000000..91d475b
--- /dev/null
+++ b/grammar/samp
@@ -0,0 +1,11 @@
+daemon.*;mail.*;\
+ news.err;\
+ *.=debug;*.=info;\
+ *.=notice;*.=warn |/dev/xconsole
+*.=info;*.=notice;*.=warn;\
+ auth,authpriv.none;\
+ cron,daemon.none;\
+ mail,news.none -/var/log/messages
+
+mail.info -/var/log/mail.info
+
diff --git a/grammar/testdriver.c b/grammar/testdriver.c
new file mode 100644
index 0000000..b29626d
--- /dev/null
+++ b/grammar/testdriver.c
@@ -0,0 +1,109 @@
+/* This is a stand-alone test driver for grammar processing. We try to
+ * keep this separate as it simplyfies grammer development.
+ *
+ * Copyright 2011 by Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of the rsyslog runtime library.
+ *
+ * The rsyslog runtime library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The rsyslog runtime library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
+ */
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <libestr.h>
+#include "rainerscript.h"
+#include "parserif.h"
+
+extern int yylineno;
+int Debug = 1;
+
+void
+parser_errmsg(char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ printf("error on or before line %d: ", yylineno);
+ vprintf(fmt, ap);
+ printf("\n");
+ va_end(ap);
+}
+
+int
+yyerror(char *s)
+{
+ parser_errmsg("%s", s);
+ return 0;
+}
+
+void
+dbgprintf(char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stdout, fmt, ap);
+ va_end(ap);
+}
+
+void cnfDoObj(struct cnfobj *o)
+{
+ dbgprintf("global:obj: ");
+ cnfobjPrint(o);
+ cnfobjDestruct(o);
+}
+
+void cnfDoRule(struct cnfrule *rule)
+{
+ dbgprintf("global:rule processed\n");
+ cnfrulePrint(rule);
+}
+
+void cnfDoCfsysline(char *ln)
+{
+ dbgprintf("global:cfsysline: %s\n", ln);
+}
+
+void cnfDoBSDTag(char *ln)
+{
+ dbgprintf("global:BSD tag: %s\n", ln);
+}
+
+void cnfDoBSDHost(char *ln)
+{
+ dbgprintf("global:BSD host: %s\n", ln);
+}
+
+es_str_t*
+cnfGetVar(char __attribute__((unused)) *name,
+ void __attribute__((unused)) *usrptr)
+{
+ es_str_t *estr;
+ estr = es_newStrFromCStr("", 1);
+ return estr;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int r;
+
+ cnfSetLexFile(argc == 1 ? NULL : argv[1]);
+ yydebug = 0;
+ r = yyparse();
+ printf("yyparse() returned %d\n", r);
+ return r;
+}
diff --git a/outchannel.c b/outchannel.c
index 84a9c08..c97d220 100644
--- a/outchannel.c
+++ b/outchannel.c
@@ -35,12 +35,9 @@
#include <assert.h>
#include "stringbuf.h"
#include "outchannel.h"
-#include "dirty.h"
+#include "rsconf.h"
#include "debug.h"
-static struct outchannel *ochRoot = NULL; /* the root of the outchannel list */
-static struct outchannel *ochLast = NULL; /* points to the last element of the outchannel list */
-
/* Constructs a outchannel list object. Returns pointer to it
* or NULL (if it fails).
*/
@@ -53,14 +50,14 @@ struct outchannel* ochConstruct(void)
/* basic initialisaion is done via calloc() - need to
* initialize only values != 0. */
- if(ochLast == NULL)
+ if(loadConf->och.ochLast == NULL)
{ /* we are the first element! */
- ochRoot = ochLast = pOch;
+ loadConf->och.ochRoot = loadConf->och.ochLast = pOch;
}
else
{
- ochLast->pNext = pOch;
- ochLast = pOch;
+ loadConf->och.ochLast->pNext = pOch;
+ loadConf->och.ochLast = pOch;
}
return(pOch);
@@ -249,7 +246,7 @@ struct outchannel *ochFind(char *pName, int iLenName)
assert(pName != NULL);
- pOch = ochRoot;
+ pOch = loadConf->och.ochRoot;
while(pOch != NULL &&
!(pOch->iLenName == iLenName &&
!strcmp(pOch->pszName, pName)
@@ -268,7 +265,7 @@ void ochDeleteAll(void)
{
struct outchannel *pOch, *pOchDel;
- pOch = ochRoot;
+ pOch = loadConf->och.ochRoot;
while(pOch != NULL) {
dbgprintf("Delete Outchannel: Name='%s'\n ", pOch->pszName == NULL? "NULL" : pOch->pszName);
pOchDel = pOch;
@@ -287,7 +284,7 @@ void ochPrintList(void)
{
struct outchannel *pOch;
- pOch = ochRoot;
+ pOch = loadConf->och.ochRoot;
while(pOch != NULL) {
dbgprintf("Outchannel: Name='%s'\n", pOch->pszName == NULL? "NULL" : pOch->pszName);
dbgprintf("\tFile Template: '%s'\n", pOch->pszFileTemplate == NULL ? "NULL" : (char*) pOch->pszFileTemplate);
diff --git a/parse.c b/parse.c
index 659d49c..2d89030 100644
--- a/parse.c
+++ b/parse.c
@@ -211,7 +211,7 @@ rsRetVal parsSkipAfterChar(rsParsObj *pThis, char c)
* If bRequireOne is set to true, at least one whitespace
* must exist, else an error is returned.
*/
-rsRetVal parsSkipWhitespace(rsParsObj *pThis, sbool bRequireOne)
+rsRetVal parsSkipWhitespace(rsParsObj *pThis)
{
register unsigned char *pC;
int numSkipped;
@@ -230,9 +230,6 @@ rsRetVal parsSkipWhitespace(rsParsObj *pThis, sbool bRequireOne)
++numSkipped;
}
- if(bRequireOne && numSkipped == 0)
- iRet = RS_RET_MISSING_WHITESPACE;
-
RETiRet;
}
@@ -261,7 +258,7 @@ rsRetVal parsDelimCStr(rsParsObj *pThis, cstr_t **ppCStr, char cDelim, int bTrim
CHKiRet(rsCStrConstruct(&pCStr));
if(bTrimLeading)
- parsSkipWhitespace(pThis, 0);
+ parsSkipWhitespace(pThis);
pC = rsCStrGetBufBeg(pThis->pCStr) + pThis->iCurrPos;
@@ -392,7 +389,7 @@ rsRetVal parsAddrWithBits(rsParsObj *pThis, struct NetAddr **pIP, int *pBits)
CHKiRet(cstrConstruct(&pCStr));
- parsSkipWhitespace(pThis, 0);
+ parsSkipWhitespace(pThis);
pC = rsCStrGetBufBeg(pThis->pCStr) + pThis->iCurrPos;
/* we parse everything until either '/', ',' or
diff --git a/parse.h b/parse.h
index 69ae4b8..5121a84 100644
--- a/parse.h
+++ b/parse.h
@@ -76,7 +76,7 @@ rsRetVal rsParsAssignString(rsParsObj *pThis, cstr_t *pCStr);
rsRetVal parsInt(rsParsObj *pThis, int* pInt);
/* Skip whitespace. Often used to trim parsable entries. */
-rsRetVal parsSkipWhitespace(rsParsObj *pThis, sbool bRequireOne);
+rsRetVal parsSkipWhitespace(rsParsObj *pThis);
/* Parse string up to a delimiter.
*
diff --git a/plugins/im3195/im3195.c b/plugins/im3195/im3195.c
index 28ca885..c75e0e3 100644
--- a/plugins/im3195/im3195.c
+++ b/plugins/im3195/im3195.c
@@ -51,12 +51,18 @@
MODULE_TYPE_INPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("im3195")
/* Module static data */
DEF_IMOD_STATIC_DATA
DEFobjCurrIf(errmsg)
/* configuration settings */
+
+struct modConfData_s {
+ EMPTY_STRUCT;
+};
+
static int listenPort = 601;
/* we use a global API object below, because this listener is
@@ -84,10 +90,37 @@ void OnReceive(srAPIObj __attribute__((unused)) *pMyAPI, srSLMGObj* pSLMG)
srSLMGGetRawMSG(pSLMG, &pszRawMsg);
parseAndSubmitMessage(fromHost, fromHostIP, pszRawMsg, strlen((char*)pszRawMsg),
- PARSE_HOSTNAME, eFLOWCTL_FULL_DELAY, (uchar*)"im3195", NULL, 0);
+ PARSE_HOSTNAME, eFLOWCTL_FULL_DELAY, (uchar*)"im3195", NULL, 0, NULL);
}
+#if 0
+BEGINbeginCnfLoad
+CODESTARTbeginCnfLoad
+ENDbeginCnfLoad
+
+
+BEGINendCnfLoad
+CODESTARTendCnfLoad
+ENDendCnfLoad
+
+
+BEGINcheckCnf
+CODESTARTcheckCnf
+ENDcheckCnf
+
+
+BEGINactivateCnf
+CODESTARTactivateCnf
+ENDactivateCnf
+
+
+BEGINfreeCnf
+CODESTARTfreeCnf
+ENDfreeCnf
+#endif
+
+
BEGINrunInput
CODESTARTrunInput
/* this is an endless loop - it is terminated when the thread is
diff --git a/plugins/imdiag/imdiag.c b/plugins/imdiag/imdiag.c
index b346892..6ea615a 100644
--- a/plugins/imdiag/imdiag.c
+++ b/plugins/imdiag/imdiag.c
@@ -57,6 +57,7 @@
MODULE_TYPE_INPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("imdiag")
/* static data */
DEF_IMOD_STATIC_DATA
@@ -77,6 +78,10 @@ static prop_t *pRcvIPDummy = NULL;
/* config settings */
+struct modConfData_s {
+ EMPTY_STRUCT;
+};
+
static int iTCPSessMax = 20; /* max number of sessions */
static int iStrmDrvrMode = 0; /* mode for stream driver, driver-dependent (0 mostly means plain tcp) */
static uchar *pszStrmDrvrAuthMode = NULL; /* authentication mode to use */
@@ -376,7 +381,8 @@ static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVa
/* initialized, now add socket */
CHKiRet(tcpsrv.SetInputName(pOurTcpsrv, pszInputName == NULL ?
UCHAR_CONSTANT("imdiag") : pszInputName));
- tcpsrv.configureTCPListen(pOurTcpsrv, pNewVal);
+ /* we support octect-cuunted frame (constant 1 below) */
+ tcpsrv.configureTCPListen(pOurTcpsrv, pNewVal, 1);
finalize_it:
if(iRet != RS_RET_OK) {
@@ -387,6 +393,33 @@ finalize_it:
RETiRet;
}
+
+#if 0 /* can be used to integrate into new config system */
+BEGINbeginCnfLoad
+CODESTARTbeginCnfLoad
+ENDbeginCnfLoad
+
+
+BEGINendCnfLoad
+CODESTARTendCnfLoad
+ENDendCnfLoad
+
+
+BEGINcheckCnf
+CODESTARTcheckCnf
+ENDcheckCnf
+
+
+BEGINactivateCnf
+CODESTARTactivateCnf
+ENDactivateCnf
+
+
+BEGINfreeCnf
+CODESTARTfreeCnf
+ENDfreeCnf
+#endif
+
/* This function is called to gather input.
*/
BEGINrunInput
diff --git a/plugins/imfile/imfile.c b/plugins/imfile/imfile.c
index ba8318d..440f599 100644
--- a/plugins/imfile/imfile.c
+++ b/plugins/imfile/imfile.c
@@ -51,6 +51,7 @@
MODULE_TYPE_INPUT /* must be present for input modules, do not remove */
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("imfile")
/* defines */
@@ -63,6 +64,7 @@ DEFobjCurrIf(strm)
DEFobjCurrIf(prop)
DEFobjCurrIf(ruleset)
+#define NUM_MULTISUB 1024 /* max number of submits -- TODO: make configurable */
typedef struct fileInfo_s {
uchar *pszFileName;
uchar *pszTag;
@@ -70,11 +72,13 @@ typedef struct fileInfo_s {
uchar *pszStateFile; /* file in which state between runs is to be stored */
int iFacility;
int iSeverity;
+ int maxLinesAtOnce;
int nRecords; /**< How many records did we process before persisting the stream? */
int iPersistStateInterval; /**< how often should state be persisted? (0=on close only) */
strm_t *pStrm; /* its stream (NULL if not assigned) */
int readMode; /* which mode to use in ReadMulteLine call? */
ruleset_t *pRuleset; /* ruleset to bind listener to (use system default if unspecified) */
+ multi_submit_t multiSub;
} fileInfo_t;
@@ -82,6 +86,10 @@ typedef struct fileInfo_s {
static rsRetVal persistStrmState(fileInfo_t *pInfo);
/* config variables */
+struct modConfData_s {
+ EMPTY_STRUCT;
+};
+
static uchar *pszFileName = NULL;
static uchar *pszFileTag = NULL;
static uchar *pszStateFile = NULL;
@@ -90,6 +98,7 @@ static int iPersistStateInterval = 0; /* how often if state file to be persisted
static int iFacility = 128; /* local0 */
static int iSeverity = 5; /* notice, as of rfc 3164 */
static int readMode = 0; /* mode to use for ReadMultiLine call */
+static int maxLinesAtOnce = 10240; /* how many lines to process in a row? */
static ruleset_t *pBindRuleset = NULL; /* ruleset to bind listener to (use system default if unspecified) */
static int iFilPtr = 0; /* number of files to be monitored; pointer to next free spot during config */
@@ -121,7 +130,9 @@ static rsRetVal enqLine(fileInfo_t *pInfo, cstr_t *cstrLine)
pMsg->iFacility = LOG_FAC(pInfo->iFacility);
pMsg->iSeverity = LOG_PRI(pInfo->iSeverity);
MsgSetRuleset(pMsg, pInfo->pRuleset);
- CHKiRet(submitMsg(pMsg));
+ pInfo->multiSub.ppMsgs[pInfo->multiSub.nElem++] = pMsg;
+ if(pInfo->multiSub.nElem == pInfo->multiSub.maxElem)
+ CHKiRet(multiSubmitMsg(&pInfo->multiSub));
finalize_it:
RETiRet;
}
@@ -205,6 +216,7 @@ static void pollFileCancelCleanup(void *pArg)
static rsRetVal pollFile(fileInfo_t *pThis, int *pbHadFileData)
{
cstr_t *pCStr = NULL;
+ int nProcessed = 0;
DEFiRet;
ASSERT(pbHadFileData != NULL);
@@ -219,7 +231,10 @@ static rsRetVal pollFile(fileInfo_t *pThis, int *pbHadFileData)
/* loop below will be exited when strmReadLine() returns EOF */
while(glbl.GetGlobalInputTermState() == 0) {
+ if(pThis->maxLinesAtOnce != 0 && nProcessed >= pThis->maxLinesAtOnce)
+ break;
CHKiRet(strm.ReadLine(pThis->pStrm, &pCStr, pThis->readMode));
+ ++nProcessed;
*pbHadFileData = 1; /* this is just a flag, so set it and forget it */
CHKiRet(enqLine(pThis, pCStr)); /* process line */
rsCStrDestruct(&pCStr); /* discard string (must be done by us!) */
@@ -230,6 +245,10 @@ static rsRetVal pollFile(fileInfo_t *pThis, int *pbHadFileData)
}
finalize_it:
+ if(pThis->multiSub.nElem > 0) {
+ /* submit everything that was not yet submitted */
+ CHKiRet(multiSubmitMsg(&pThis->multiSub));
+ }
; /*EMPTY STATEMENT - needed to keep compiler happy - see below! */
/* Note: the problem above is that pthread:cleanup_pop() is a macro which
* evaluates to something like "} while(0);". So the code would become
@@ -321,6 +340,7 @@ ENDrunInput
* ------------------------------------------------------------------------------------------ */
+
/* The function is called by rsyslog before runInput() is called. It is a last chance
* to set up anything specific. Most importantly, it can be used to tell rsyslog if the
* input shall run or not. The idea is that if some config settings (or similiar things)
@@ -474,6 +494,7 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
iSeverity = 5; /* notice, as of rfc 3164 */
readMode = 0;
pBindRuleset = NULL;
+ maxLinesAtOnce = 10240;
RETiRet;
}
@@ -512,8 +533,12 @@ static rsRetVal addMonitor(void __attribute__((unused)) *pVal, uchar *pNewVal)
pThis->pszStateFile = (uchar*) strdup((char*) pszStateFile);
}
+ CHKmalloc(pThis->multiSub.ppMsgs = MALLOC(NUM_MULTISUB * sizeof(msg_t*)));
+ pThis->multiSub.maxElem = NUM_MULTISUB;
+ pThis->multiSub.nElem = 0;
pThis->iSeverity = iSeverity;
pThis->iFacility = iFacility;
+ pThis->maxLinesAtOnce = maxLinesAtOnce;
pThis->iPersistStateInterval = iPersistStateInterval;
pThis->nRecords = 0;
pThis->readMode = readMode;
@@ -542,7 +567,7 @@ setRuleset(void __attribute__((unused)) *pVal, uchar *pszName)
rsRetVal localRet;
DEFiRet;
- localRet = ruleset.GetRuleset(&pRuleset, pszName);
+ localRet = ruleset.GetRuleset(ourConf, &pRuleset, pszName);
if(localRet == RS_RET_NOT_FOUND) {
errmsg.LogError(0, NO_ERRCODE, "error: ruleset '%s' not found - ignored", pszName);
}
@@ -591,6 +616,8 @@ CODEmodInit_QueryRegCFSLineHdlr
NULL, &iPollInterval, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfilereadmode", 0, eCmdHdlrInt,
NULL, &readMode, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfilemaxlinesatonce", 0, eCmdHdlrSize,
+ NULL, &maxLinesAtOnce, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfilepersiststateinterval", 0, eCmdHdlrInt,
NULL, &iPersistStateInterval, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfilebindruleset", 0, eCmdHdlrGetWord,
diff --git a/plugins/imgssapi/imgssapi.c b/plugins/imgssapi/imgssapi.c
index 446795d..4e3a70a 100644
--- a/plugins/imgssapi/imgssapi.c
+++ b/plugins/imgssapi/imgssapi.c
@@ -63,6 +63,7 @@
MODULE_TYPE_INPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("imgssapi")
/* defines */
#define ALLOWEDMETHOD_GSS 2
@@ -104,6 +105,10 @@ typedef struct gss_sess_s {
/* config variables */
+struct modConfData_s {
+ EMPTY_STRUCT;
+};
+
static int iTCPSessMax = 200; /* max number of sessions */
static char *gss_listen_service_name = NULL;
static int bPermitPlainTcp = 0; /* plain tcp syslog allowed on GSSAPI port? */
@@ -335,7 +340,7 @@ addGSSListener(void __attribute__((unused)) *pVal, uchar *pNewVal)
CHKiRet(tcpsrv.SetCBOnRegularClose(pOurTcpsrv, onRegularClose));
CHKiRet(tcpsrv.SetCBOnErrClose(pOurTcpsrv, onErrClose));
CHKiRet(tcpsrv.SetInputName(pOurTcpsrv, UCHAR_CONSTANT("imgssapi")));
- tcpsrv.configureTCPListen(pOurTcpsrv, pNewVal);
+ tcpsrv.configureTCPListen(pOurTcpsrv, pNewVal, 1);
CHKiRet(tcpsrv.ConstructFinalize(pOurTcpsrv));
}
@@ -640,6 +645,33 @@ TCPSessGSSDeinit(void)
RETiRet;
}
+
+#if 0 /* can be used to integrate into new config system */
+BEGINbeginCnfLoad
+CODESTARTbeginCnfLoad
+ENDbeginCnfLoad
+
+
+BEGINendCnfLoad
+CODESTARTendCnfLoad
+ENDendCnfLoad
+
+
+BEGINcheckCnf
+CODESTARTcheckCnf
+ENDcheckCnf
+
+
+BEGINactivateCnf
+CODESTARTactivateCnf
+ENDactivateCnf
+
+
+BEGINfreeCnf
+CODESTARTfreeCnf
+ENDfreeCnf
+#endif
+
/* This function is called to gather input.
*/
BEGINrunInput
diff --git a/plugins/imklog/Makefile.am b/plugins/imklog/Makefile.am
index 5d4d046..7d0d37c 100644
--- a/plugins/imklog/Makefile.am
+++ b/plugins/imklog/Makefile.am
@@ -1,5 +1,4 @@
pkglib_LTLIBRARIES = imklog.la
-
imklog_la_SOURCES = imklog.c imklog.h
# select klog "driver"
@@ -8,7 +7,7 @@ imklog_la_SOURCES += bsd.c
endif
if ENABLE_IMKLOG_LINUX
-imklog_la_SOURCES += linux.c module.h ksym.c ksyms.h ksym_mod.c
+imklog_la_SOURCES += bsd.c
endif
imklog_la_CPPFLAGS = -I$(top_srcdir) $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
diff --git a/plugins/imklog/bsd.c b/plugins/imklog/bsd.c
index 0a4c7cd..80ff949 100644
--- a/plugins/imklog/bsd.c
+++ b/plugins/imklog/bsd.c
@@ -1,69 +1,30 @@
-/* klog for BSD, based on the FreeBSD syslogd implementation.
+/* combined imklog driver for BSD and Linux
*
* This contains OS-specific functionality to read the BSD
- * kernel log. For a general overview, see head comment in
- * imklog.c.
+ * or Linux kernel log. For a general overview, see head comment in
+ * imklog.c. This started out as the BSD-specific drivers, but it
+ * turned out that on modern Linux the implementation details
+ * are very small, and so we use a single driver for both OS's with
+ * a little help of conditional compilation.
*
- * Copyright (C) 2008 by Rainer Gerhards for the modifications of
- * the original FreeBSD sources.
- *
- * I would like to express my gratitude to those folks which
- * layed an important foundation for rsyslog to build on.
+ * Copyright 2008-2012 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.
- *
- * This file is based on earlier work included in the FreeBSD sources. We
- * integrated it into the rsyslog project. The copyright below applies, and
- * I also reproduce the original license under which we aquired the code:
- *
- * Copyright (c) 1983, 1988, 1993, 1994
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * If you would like to use the code under the BSD license, you should
- * aquire your own copy of BSD's syslogd, from which we have taken it. The
- * code in this file is modified and may only be used under the terms of
- * the GPLv3+ as specified above.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
-
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
@@ -72,46 +33,165 @@
#include <fcntl.h>
#include <errno.h>
#include <string.h>
+#include <ctype.h>
+#ifdef OS_LINUX
+# include <sys/klog.h>
+#endif
#include "rsyslog.h"
-#include "imklog.h"
+#include "srUtils.h"
#include "debug.h"
+#include "imklog.h"
/* globals */
-static int fklog = -1; /* /dev/klog */
+static int fklog = -1; /* kernel log fd */
#ifndef _PATH_KLOG
-# define _PATH_KLOG "/dev/klog"
+# ifdef OS_LINUX
+# define _PATH_KLOG "/proc/kmsg"
+# else
+# define _PATH_KLOG "/dev/klog"
+# endif
#endif
-static uchar *GetPath(void)
+
+#ifdef OS_LINUX
+/* submit a message to imklog Syslog() API. In this function, we check if
+ * a kernel timestamp is present and, if so, extract and strip it.
+ * Note: this is an extra processing step. We should revisit the whole
+ * idea in v6 and remove all that old stuff that we do not longer need
+ * (like symbol resolution). <-- TODO
+ * Note that this is heavily Linux specific and thus is not compiled or
+ * used for BSD.
+ * Special thanks to Lennart Poettering for suggesting on how to convert
+ * the kernel timestamp to a realtime timestamp. This method depends on
+ * the fact the the kernel timestamp is written using the monotonic clock.
+ * Shall that change (very unlikely), this code must be changed as well. Note
+ * that due to the way we generate the delta, we are unable to write the
+ * absolutely correct timestamp (system call overhead of the clock calls
+ * prevents us from doing so). However, the difference is very minor.
+ * rgerhards, 2011-06-24
+ */
+static void
+submitSyslog(int pri, uchar *buf)
+{
+ long secs;
+ long nsecs;
+ long secOffs;
+ long nsecOffs;
+ unsigned i;
+ unsigned bufsize;
+ struct timespec monotonic, realtime;
+ struct timeval tv;
+ struct timeval *tp = NULL;
+
+ if(buf[3] != '[')
+ goto done;
+ DBGPRINTF("imklog: kernel timestamp detected, extracting it\n");
+
+ /* we now try to parse the timestamp. iff it parses, we assume
+ * it is a timestamp. Otherwise we know for sure it is no ts ;)
+ */
+ i = 4; /* first digit after '[' */
+ secs = 0;
+ while(buf[i] && isdigit(buf[i])) {
+ secs = secs * 10 + buf[i] - '0';
+ ++i;
+ }
+ if(buf[i] != '.') {
+ DBGPRINTF("no dot --> no kernel timestamp\n");
+ goto done; /* no TS! */
+ }
+
+ ++i; /* skip dot */
+ nsecs = 0;
+ while(buf[i] && isdigit(buf[i])) {
+ nsecs = nsecs * 10 + buf[i] - '0';
+ ++i;
+ }
+ if(buf[i] != ']') {
+ DBGPRINTF("no trailing ']' --> no kernel timestamp\n");
+ goto done; /* no TS! */
+ }
+ ++i; /* skip ']' */
+
+ /* we have a timestamp */
+ DBGPRINTF("kernel timestamp is %ld %ld\n", secs, nsecs);
+ bufsize= strlen((char*)buf);
+ memcpy(buf+3, buf+i, bufsize - i + 1);
+
+ clock_gettime(CLOCK_MONOTONIC, &monotonic);
+ clock_gettime(CLOCK_REALTIME, &realtime);
+ secOffs = realtime.tv_sec - monotonic.tv_sec;
+ nsecOffs = realtime.tv_nsec - monotonic.tv_nsec;
+ if(nsecOffs < 0) {
+ secOffs--;
+ nsecOffs += 1000000000l;
+ }
+
+ nsecs +=nsecOffs;
+ if(nsecs > 999999999l) {
+ secs++;
+ nsecs -= 1000000000l;
+ }
+ secs += secOffs;
+ tv.tv_sec = secs;
+ tv.tv_usec = nsecs / 1000;
+ tp = &tv;
+
+done:
+ Syslog(pri, buf, tp);
+}
+#else /* now comes the BSD "code" (just a shim) */
+static void
+submitSyslog(int pri, uchar *buf)
{
- return pszPath ? pszPath : (uchar*) _PATH_KLOG;
+ Syslog(pri, buf, NULL);
+}
+#endif /* #ifdef LINUX */
+
+
+static uchar *GetPath(modConfData_t *pModConf)
+{
+ return pModConf->pszPath ? pModConf->pszPath : (uchar*) _PATH_KLOG;
}
/* open the kernel log - will be called inside the willRun() imklog
* entry point. -- rgerhards, 2008-04-09
*/
rsRetVal
-klogWillRun(void)
+klogWillRun(modConfData_t *pModConf)
{
+ char errmsg[2048];
+ int r;
DEFiRet;
- fklog = open((char*)GetPath(), O_RDONLY, 0);
+ fklog = open((char*)GetPath(pModConf), O_RDONLY, 0);
if (fklog < 0) {
- dbgprintf("can't open %s (%d)\n", GetPath(), errno);
- iRet = RS_RET_ERR; // TODO: better error code
+ imklogLogIntMsg(RS_RET_ERR_OPEN_KLOG, "imklog: cannot open kernel log(%s): %s.",
+ GetPath(pModConf), rs_strerror_r(errno, errmsg, sizeof(errmsg)));
+ ABORT_FINALIZE(RS_RET_ERR_OPEN_KLOG);
+ }
+
+# ifdef OS_LINUX
+ /* Set level of kernel console messaging.. */
+ if(pModConf->console_log_level != -1) {
+ r = klogctl(8, NULL, pModConf->console_log_level);
+ if(r != 0) {
+ imklogLogIntMsg(LOG_WARNING, "imklog: cannot set console log level: %s",
+ rs_strerror_r(errno, errmsg, sizeof(errmsg)));
+ /* make sure we do not try to re-set! */
+ pModConf->console_log_level = -1;
+ }
}
+# endif /* #ifdef OS_LINUX */
+finalize_it:
RETiRet;
}
-/* Read /dev/klog while data are available, split into lines.
- * Contrary to standard BSD syslogd, we do a blocking read. We can
- * afford this as imklog is running on its own threads. So if we have
- * a single file, it really doesn't matter if we wait inside a 1-file
- * select or the read() directly.
+/* Read kernel log while data are available, split into lines.
*/
static void
readklog(void)
@@ -119,13 +199,14 @@ readklog(void)
char *p, *q;
int len, i;
int iMaxLine;
- uchar bufRcv[4096+1];
+ uchar bufRcv[128*1024+1];
+ char errmsg[2048];
uchar *pRcv = NULL; /* receive buffer */
iMaxLine = klog_getMaxLine();
- /* we optimize performance: if iMaxLine is below 4K (which it is in almost all
- * cases, we use a fixed buffer on the stack. Only if it is higher, heap memory
+ /* we optimize performance: if iMaxLine is below our fixed size buffer (which
+ * usually is sufficiently large), we use this buffer. if it is higher, heap memory
* is used. We could use alloca() to achive a similar aspect, but there are so
* many issues with alloca() that I do not want to take that route.
* rgerhards, 2008-09-02
@@ -139,15 +220,15 @@ readklog(void)
len = 0;
for (;;) {
- dbgprintf("----------imklog(BSD) waiting for kernel log line\n");
+ dbgprintf("imklog(BSD/Linux) waiting for kernel log line\n");
i = read(fklog, pRcv + len, iMaxLine - len);
if (i > 0) {
pRcv[i + len] = '\0';
} else {
if (i < 0 && errno != EINTR && errno != EAGAIN) {
imklogLogIntMsg(LOG_ERR,
- "imklog error %d reading kernel log - shutting down imklog",
- errno);
+ "imklog: error reading kernel log - shutting down: %s",
+ rs_strerror_r(errno, errmsg, sizeof(errmsg)));
fklog = -1;
}
break;
@@ -155,18 +236,18 @@ readklog(void)
for (p = (char*)pRcv; (q = strchr(p, '\n')) != NULL; p = q + 1) {
*q = '\0';
- Syslog(LOG_INFO, (uchar*) p);
+ submitSyslog(LOG_INFO, (uchar*) p);
}
len = strlen(p);
if (len >= iMaxLine - 1) {
- Syslog(LOG_INFO, (uchar*)p);
+ submitSyslog(LOG_INFO, (uchar*)p);
len = 0;
}
- if (len > 0)
+ if(len > 0)
memmove(pRcv, p, len + 1);
}
if (len > 0)
- Syslog(LOG_INFO, pRcv);
+ submitSyslog(LOG_INFO, pRcv);
if(pRcv != NULL && (size_t) iMaxLine >= sizeof(bufRcv) - 1)
free(pRcv);
@@ -176,11 +257,16 @@ readklog(void)
/* to be called in the module's AfterRun entry point
* rgerhards, 2008-04-09
*/
-rsRetVal klogAfterRun(void)
+rsRetVal klogAfterRun(modConfData_t *pModConf)
{
DEFiRet;
if(fklog != -1)
close(fklog);
+# ifdef OS_LINUX
+ /* Turn on logging of messages to console, but only if a log level was speficied */
+ if(pModConf->console_log_level != -1)
+ klogctl(7, NULL, 0);
+# endif
RETiRet;
}
@@ -190,7 +276,7 @@ rsRetVal klogAfterRun(void)
* "message pull" mechanism.
* rgerhards, 2008-04-09
*/
-rsRetVal klogLogKMsg(void)
+rsRetVal klogLogKMsg(modConfData_t __attribute__((unused)) *pModConf)
{
DEFiRet;
readklog();
diff --git a/plugins/imklog/imklog.c b/plugins/imklog/imklog.c
index 16adbc2..f476c5f 100644
--- a/plugins/imklog/imklog.c
+++ b/plugins/imklog/imklog.c
@@ -18,6 +18,9 @@
* Please note that this file replaces the klogd daemon that was
* also present in pre-v3 versions of rsyslog.
*
+ * To test under Linux:
+ * echo test1 > /dev/kmsg
+ *
* Copyright (C) 2008-2012 Adiscon GmbH
*
* This file is part of rsyslog.
@@ -44,6 +47,7 @@
#include <stdarg.h>
#include <ctype.h>
#include <stdlib.h>
+#include <sys/socket.h>
#include "dirty.h"
#include "cfsysline.h"
@@ -52,55 +56,78 @@
#include "module-template.h"
#include "datetime.h"
#include "imklog.h"
+#include "net.h"
#include "glbl.h"
#include "prop.h"
#include "unicode-helper.h"
MODULE_TYPE_INPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("imklog")
/* Module static data */
DEF_IMOD_STATIC_DATA
DEFobjCurrIf(datetime)
DEFobjCurrIf(glbl)
DEFobjCurrIf(prop)
+DEFobjCurrIf(net)
-/* configuration settings */
-int dbgPrintSymbols = 0; /* this one is extern so the helpers can access it! */
-int symbols_twice = 0;
-int use_syscall = 0;
-int symbol_lookup = 0; /* on recent kernels > 2.6, the kernel does this */
-int bPermitNonKernel = 0; /* permit logging of messages not having LOG_KERN facility */
-int iFacilIntMsg; /* the facility to use for internal messages (set by driver) */
-uchar *pszPath = NULL;
-int console_log_level = -1;
-/* TODO: configuration for the following directives must be implemented. It
- * was not done yet because we either do not yet have a config handler for
- * that type or I thought it was acceptable to push it to a later stage when
- * I gained more handson experience with the input module interface (and the
- * changes resulting from that). -- rgerhards, 2007-12-20
- */
-char *symfile = NULL;
+/* config settings */
+typedef struct configSettings_s {
+ int dbgPrintSymbols; /* this one is extern so the helpers can access it! */
+ int symbols_twice;
+ int use_syscall;
+ int symbol_lookup; /* on recent kernels > 2.6, the kernel does this */
+ int bPermitNonKernel; /* permit logging of messages not having LOG_KERN facility */
+ int iFacilIntMsg; /* the facility to use for internal messages (set by driver) */
+ uchar *pszPath;
+ int console_log_level;
+} configSettings_t;
+static configSettings_t cs;
+
+static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
+static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current load process */
static prop_t *pInputName = NULL; /* there is only one global inputName for all messages generated by this module */
static prop_t *pLocalHostIP = NULL; /* a pseudo-constant propterty for 127.0.0.1 */
+
+static inline void
+initConfigSettings(void)
+{
+ cs.dbgPrintSymbols = 0;
+ cs.symbols_twice = 0;
+ cs.use_syscall = 0;
+ cs.symbol_lookup = 0;
+ cs.bPermitNonKernel = 0;
+ cs.console_log_level = -1;
+ cs.pszPath = NULL;
+ cs.iFacilIntMsg = klogFacilIntMsg();
+}
+
+
/* enqueue the the kernel message into the message queue.
* The provided msg string is not freed - thus must be done
* by the caller.
* rgerhards, 2008-04-12
*/
static rsRetVal
-enqMsg(uchar *msg, uchar* pszTag, int iFacility, int iSeverity)
+enqMsg(uchar *msg, uchar* pszTag, int iFacility, int iSeverity, struct timeval *tp)
{
- DEFiRet;
+ struct syslogTime st;
msg_t *pMsg;
+ DEFiRet;
assert(msg != NULL);
assert(pszTag != NULL);
- CHKiRet(msgConstruct(&pMsg));
+ if(tp == NULL) {
+ CHKiRet(msgConstruct(&pMsg));
+ } else {
+ datetime.timeval2syslogTime(tp, &st);
+ CHKiRet(msgConstructWithTime(&pMsg, &st, tp->tv_sec));
+ }
MsgSetFlowControlType(pMsg, eFLOWCTL_LIGHT_DELAY);
MsgSetInputName(pMsg, pInputName);
MsgSetRawMsgWOSize(pMsg, (char*)msg);
@@ -165,39 +192,52 @@ rsRetVal imklogLogIntMsg(int priority, char *fmt, ...)
DEFiRet;
va_list ap;
uchar msgBuf[2048]; /* we use the same size as sysklogd to remain compatible */
- uchar *pLogMsg;
va_start(ap, fmt);
vsnprintf((char*)msgBuf, sizeof(msgBuf) / sizeof(char), fmt, ap);
- pLogMsg = msgBuf;
va_end(ap);
- iRet = enqMsg((uchar*)pLogMsg, (uchar*) ((iFacilIntMsg == LOG_KERN) ? "kernel:" : "imklog:"),
- iFacilIntMsg, LOG_PRI(priority));
+ logmsgInternal(NO_ERRCODE ,priority, msgBuf, 0);
RETiRet;
}
-/* log a kernel message
+/* log a kernel message. If tp is non-NULL, it contains the message creation
+ * time to use.
* rgerhards, 2008-04-14
*/
-rsRetVal Syslog(int priority, uchar *pMsg)
+rsRetVal Syslog(int priority, uchar *pMsg, struct timeval *tp)
{
- DEFiRet;
+ int pri = -1;
rsRetVal localRet;
+ DEFiRet;
- /* Output using syslog */
- localRet = parsePRI(&pMsg, &priority);
- if(localRet != RS_RET_INVALID_PRI && localRet != RS_RET_OK)
- FINALIZE;
+ /* then check if we have two PRIs. This can happen in case of systemd,
+ * in which case the second PRI is the right one.
+ */
+ if(pMsg[3] == '<' || (pMsg[3] == ' ' && pMsg[4] == '<')) { /* could be a pri... */
+ uchar *pMsgTmp = pMsg + ((pMsg[3] == '<') ? 3 : 4);
+ localRet = parsePRI(&pMsgTmp, &pri);
+ if(localRet == RS_RET_OK && pri >= 8 && pri <= 192) {
+ /* *this* is our PRI */
+ DBGPRINTF("imklog detected secondary PRI(%d) in klog msg\n", pri);
+ pMsg = pMsgTmp;
+ priority = pri;
+ }
+ }
+ if(pri == -1) {
+ localRet = parsePRI(&pMsg, &priority);
+ if(localRet != RS_RET_INVALID_PRI && localRet != RS_RET_OK)
+ FINALIZE;
+ }
/* if we don't get the pri, we use whatever we were supplied */
/* ignore non-kernel messages if not permitted */
- if(bPermitNonKernel == 0 && LOG_FAC(priority) != LOG_KERN)
+ if(cs.bPermitNonKernel == 0 && LOG_FAC(priority) != LOG_KERN)
FINALIZE; /* silently ignore */
- iRet = enqMsg((uchar*)pMsg, (uchar*) "kernel:", LOG_FAC(priority), LOG_PRI(priority));
+ iRet = enqMsg((uchar*)pMsg, (uchar*) "kernel:", LOG_FAC(priority), LOG_PRI(priority), tp);
finalize_it:
RETiRet;
@@ -226,63 +266,110 @@ CODESTARTrunInput
* and then submits it to the rsyslog main queue.
* rgerhards, 2008-04-09
*/
- CHKiRet(klogLogKMsg());
+ CHKiRet(klogLogKMsg(runModConf));
}
finalize_it:
ENDrunInput
+BEGINbeginCnfLoad
+CODESTARTbeginCnfLoad
+ loadModConf = pModConf;
+ pModConf->pConf = pConf;
+ /* init legacy config vars */
+ initConfigSettings();
+ENDbeginCnfLoad
+
+
+BEGINendCnfLoad
+CODESTARTendCnfLoad
+ /* persist module-specific settings from legacy config system */
+ loadModConf->dbgPrintSymbols = cs.dbgPrintSymbols;
+ loadModConf->symbols_twice = cs.symbols_twice;
+ loadModConf->use_syscall = cs.use_syscall;
+ loadModConf->bPermitNonKernel = cs.bPermitNonKernel;
+ loadModConf->iFacilIntMsg = cs.iFacilIntMsg;
+ loadModConf->console_log_level = cs.console_log_level;
+ if((cs.pszPath == NULL) || (cs.pszPath[0] == '\0')) {
+ loadModConf->pszPath = NULL;
+ if(cs.pszPath != NULL)
+ free(cs.pszPath);
+ } else {
+ loadModConf->pszPath = cs.pszPath;
+ }
+ cs.pszPath = NULL;
+
+ loadModConf = NULL; /* done loading */
+ENDendCnfLoad
+
+
+BEGINcheckCnf
+CODESTARTcheckCnf
+ENDcheckCnf
+
+
+BEGINactivateCnfPrePrivDrop
+CODESTARTactivateCnfPrePrivDrop
+ runModConf = pModConf;
+ iRet = klogWillRun(runModConf);
+ENDactivateCnfPrePrivDrop
+
+
+BEGINactivateCnf
+CODESTARTactivateCnf
+ENDactivateCnf
+
+
+BEGINfreeCnf
+CODESTARTfreeCnf
+ENDfreeCnf
+
+
BEGINwillRun
CODESTARTwillRun
- /* we need to create the inputName property (only once during our lifetime) */
- CHKiRet(prop.CreateStringProp(&pInputName, UCHAR_CONSTANT("imklog"), sizeof("imklog") - 1));
- CHKiRet(prop.CreateStringProp(&pLocalHostIP, UCHAR_CONSTANT("127.0.0.1"), sizeof("127.0.0.1") - 1));
-
- iRet = klogWillRun();
-finalize_it:
ENDwillRun
BEGINafterRun
CODESTARTafterRun
- iRet = klogAfterRun();
+ iRet = klogAfterRun(runModConf);
+ENDafterRun
+
+BEGINmodExit
+CODESTARTmodExit
if(pInputName != NULL)
prop.Destruct(&pInputName);
if(pLocalHostIP != NULL)
prop.Destruct(&pLocalHostIP);
-ENDafterRun
-
-BEGINmodExit
-CODESTARTmodExit
/* release objects we used */
objRelease(glbl, CORE_COMPONENT);
+ objRelease(net, CORE_COMPONENT);
objRelease(datetime, CORE_COMPONENT);
objRelease(prop, CORE_COMPONENT);
- if(pszPath != NULL)
- free(pszPath);
ENDmodExit
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
+CODEqueryEtryPt_STD_CONF2_QUERIES
+CODEqueryEtryPt_STD_CONF2_PREPRIVDROP_QUERIES
ENDqueryEtryPt
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
- dbgPrintSymbols = 0;
- symbols_twice = 0;
- use_syscall = 0;
- symfile = NULL;
- symbol_lookup = 0;
- bPermitNonKernel = 0;
- if(pszPath != NULL) {
- free(pszPath);
- pszPath = NULL;
+ cs.dbgPrintSymbols = 0;
+ cs.symbols_twice = 0;
+ cs.use_syscall = 0;
+ cs.symbol_lookup = 0;
+ cs.bPermitNonKernel = 0;
+ if(cs.pszPath != NULL) {
+ free(cs.pszPath);
+ cs.pszPath = NULL;
}
- iFacilIntMsg = klogFacilIntMsg();
+ cs.iFacilIntMsg = klogFacilIntMsg();
return RS_RET_OK;
}
@@ -293,18 +380,33 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(datetime, CORE_COMPONENT));
CHKiRet(objUse(glbl, CORE_COMPONENT));
CHKiRet(objUse(prop, CORE_COMPONENT));
+ CHKiRet(objUse(net, CORE_COMPONENT));
+
+ /* we need to create the inputName property (only once during our lifetime) */
+ CHKiRet(prop.CreateStringProp(&pInputName, UCHAR_CONSTANT("imklog"), sizeof("imklog") - 1));
+ CHKiRet(prop.CreateStringProp(&pLocalHostIP, UCHAR_CONSTANT("127.0.0.1"), sizeof("127.0.0.1") - 1));
- iFacilIntMsg = klogFacilIntMsg();
-
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"debugprintkernelsymbols", 0, eCmdHdlrBinary, NULL, &dbgPrintSymbols, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogpath", 0, eCmdHdlrGetWord, NULL, &pszPath, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogsymbollookup", 0, eCmdHdlrBinary, NULL, &symbol_lookup, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogsymbolstwice", 0, eCmdHdlrBinary, NULL, &symbols_twice, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogusesyscallinterface", 0, eCmdHdlrBinary, NULL, &use_syscall, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogpermitnonkernelfacility", 0, eCmdHdlrBinary, NULL, &bPermitNonKernel, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogconsoleloglevel", 0, eCmdHdlrInt, NULL, &console_log_level, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"kloginternalmsgfacility", 0, eCmdHdlrFacility, NULL, &iFacilIntMsg, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ /* init legacy config settings */
+ initConfigSettings();
+
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"debugprintkernelsymbols", 0, eCmdHdlrBinary,
+ NULL, &cs.dbgPrintSymbols, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogpath", 0, eCmdHdlrGetWord,
+ NULL, &cs.pszPath, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogsymbollookup", 0, eCmdHdlrBinary,
+ NULL, &cs.symbol_lookup, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogsymbolstwice", 0, eCmdHdlrBinary,
+ NULL, &cs.symbols_twice, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogusesyscallinterface", 0, eCmdHdlrBinary,
+ NULL, &cs.use_syscall, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogpermitnonkernelfacility", 0, eCmdHdlrBinary,
+ NULL, &cs.bPermitNonKernel, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogconsoleloglevel", 0, eCmdHdlrInt,
+ NULL, &cs.console_log_level, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"kloginternalmsgfacility", 0, eCmdHdlrFacility,
+ NULL, &cs.iFacilIntMsg, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
+ resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
/* vim:set ai:
*/
diff --git a/plugins/imklog/imklog.h b/plugins/imklog/imklog.h
index 22f1005..795dd68 100644
--- a/plugins/imklog/imklog.h
+++ b/plugins/imklog/imklog.h
@@ -29,37 +29,37 @@
#include "rsyslog.h"
#include "dirty.h"
+/* we need to have the modConf type present in all submodules */
+struct modConfData_s {
+ int dbgPrintSymbols;
+ int symbols_twice;
+ int use_syscall;
+ int symbol_lookup;
+ int bPermitNonKernel;
+ int iFacilIntMsg;
+ uchar *pszPath;
+ int console_log_level;
+ rsconf_t *pConf;
+};
+
/* interface to "drivers"
* the platform specific drivers must implement these entry points. Only one
* driver may be active at any given time, thus we simply rely on the linker
* to resolve the addresses.
* rgerhards, 2008-04-09
*/
-rsRetVal klogLogKMsg(void);
-rsRetVal klogWillRun(void);
-rsRetVal klogAfterRun(void);
-int klogFacilIntMsg(void);
-
-/* the following data members may be accessed by the "drivers"
- * I admit this is not the cleanest way to doing things, but I honestly
- * believe it is appropriate for the job that needs to be done.
- * rgerhards, 2008-04-09
- */
-extern int symbols_twice;
-extern int use_syscall;
-extern int symbol_lookup;
-extern char *symfile;
-extern int console_log_level;
-extern int dbgPrintSymbols;
-extern uchar *pszPath;
+rsRetVal klogLogKMsg(modConfData_t *pModConf);
+rsRetVal klogWillRun(modConfData_t *pModConf);
+rsRetVal klogAfterRun(modConfData_t *pModConf);
+int klogFacilIntMsg();
/* the functions below may be called by the drivers */
rsRetVal imklogLogIntMsg(int priority, char *fmt, ...) __attribute__((format(printf,2, 3)));
-rsRetVal Syslog(int priority, uchar *msg);
+rsRetVal Syslog(int priority, uchar *msg, struct timeval *tp);
/* prototypes */
extern int klog_getMaxLine(void); /* work-around for klog drivers to get configured max line size */
-extern int InitKsyms(char *);
+extern int InitKsyms(modConfData_t*);
extern void DeinitKsyms(void);
extern int InitMsyms(void);
extern void DeinitMsyms(void);
diff --git a/plugins/imklog/ksym.c b/plugins/imklog/ksym.c
deleted file mode 100644
index ebaec01..0000000
--- a/plugins/imklog/ksym.c
+++ /dev/null
@@ -1,832 +0,0 @@
-/* ksym.c - functions for kernel address->symbol translation
- * Copyright (c) 1995, 1996 Dr. G.W. Wettstein <greg@wind.rmcc.com>
- * Copyright (c) 1996 Enjellic Systems Development
- * Copyright (c) 1998-2007 Martin Schulze <joey@infodrom.org>
- * Copyright (C) 2007-2008 Rainer Gerhards <rgerhards@adiscon.com>
- *
- * 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.
-*/
-
-/*
- * This file contains functions which handle the translation of kernel
- * numeric addresses into symbols for the klogd utility.
- *
- * Sat Oct 28 09:00:14 CDT 1995: Dr. Wettstein
- * Initial Version.
- *
- * Fri Nov 24 12:50:52 CST 1995: Dr. Wettstein
- * Added VERBOSE_DEBUGGING define to make debugging output more
- * manageable.
- *
- * Added support for verification of the loaded kernel symbols. If
- * no version information can be be found in the mapfile a warning
- * message is issued but translation will still take place. This
- * will be the default case if kernel versions < 1.3.43 are used.
- *
- * If the symbols in the mapfile are of the same version as the kernel
- * that is running an informative message is issued. If the symbols
- * in the mapfile do not match the current kernel version a warning
- * message is issued and translation is disabled.
- *
- * Wed Dec 6 16:14:11 CST 1995: Dr. Wettstein
- * Added /boot/System.map to the list of symbol maps to search for.
- * Also made this map the first item in the search list. I am open
- * to CONSTRUCTIVE suggestions for any additions or corrections to
- * the list of symbol maps to search for. Be forewarned that the
- * list in use is the consensus agreement between myself, Linus and
- * some package distributers. It is a given that no list will suit
- * everyone's taste. If you have rabid concerns about the list
- * please feel free to edit the system_maps array and compile your
- * own binaries.
- *
- * Added support for searching of the list of symbol maps. This
- * allows support for access to multiple symbol maps. The theory
- * behind this is that a production kernel may have a system map in
- * /boot/System.map. If a test kernel is booted this system map
- * would be skipped in favor of one found in /usr/src/linux.
- *
- * Thu Jan 18 11:18:31 CST 1996: Dr. Wettstein
- * Added patch from beta-testers to allow for reading of both
- * ELF and a.out map files.
- *
- * Wed Aug 21 09:15:49 CDT 1996: Dr. Wettstein
- * Reloading of kernel module symbols is now turned on by the
- * SetParanoiaLevel function. The default behavior is to NOT reload
- * the kernel module symbols when a protection fault is detected.
- *
- * Added support for freeing of the current kernel module symbols.
- * This was necessary to support reloading of the kernel module symbols.
- *
- * When a matching static symbol table is loaded the kernel version
- * number is printed.
- *
- * Mon Jun 9 17:12:42 CST 1997: Martin Schulze
- * Added #1 and #2 to some error messages in order to being able
- * to divide them (ulmo@Q.Net)
- *
- * Fri Jun 13 10:50:23 CST 1997: Martin Schulze
- * Changed definition of LookupSymbol to non-static because it is
- * used in klogd.c, too.
- *
- * Fri Jan 9 23:00:08 CET 1998: Martin Schulze <joey@infodrom.north.de>
- * Fixed bug that caused klogd to die if there is no System.map available.
- *
- * Sun 29 Mar 18:14:07 BST 1998: Mark Simon Phillips <M.S.Phillips@nortel.co.uk>
- * Switched to fgets() as gets() is not buffer overrun secure.
- *
- * Mon Apr 13 18:18:45 CEST 1998: Martin Schulze <joey@infodrom.north.de>
- * Modified loop for detecting the correct system map. Now it won't
- * stop if a file has been found but doesn't contain the correct map.
- * Special thanks go go Mark Simon Phillips for the hint.
- *
- * Mon Oct 12 00:42:30 CEST 1998: Martin Schulze <joey@infodrom.north.de>
- * Modified CheckVersion()
- * . Use shift to decode the kernel version
- * . Compare integers of kernel version
- * . extract major.minor.patch from utsname.release via sscanf()
- * The reason lays in possible use of kernel flavours which
- * modify utsname.release but no the Version_ symbol.
- *
- * Sun Feb 21 22:27:49 EST 1999: Keith Owens <kaos@ocs.com.au>
- * Fixed bug that caused klogd to die if there is no sym_array available.
- *
- * Tue Sep 12 23:48:12 CEST 2000: Martin Schulze <joey@infodrom.ffis.de>
- * Close symbol file in InitKsyms() when an error occurred.
- */
-
-
-/* Includes. */
-#include "config.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/utsname.h>
-#include <ctype.h>
-#include <stdarg.h>
-#include <string.h>
-#include <syslog.h>
-#include "imklog.h"
-#include "ksyms.h"
-#include "module.h"
-#include "debug.h"
-
-
-int num_syms = 0;
-static int i_am_paranoid = 0;
-static char vstring[12];
-static struct sym_table *sym_array = (struct sym_table *) 0;
-
-static char *system_maps[] =
-{
- "/boot/System.map",
- "/System.map",
- NULL
-};
-
-
-/* Function prototypes. */
-static char *FindSymbolFile(void);
-static int AddSymbol(unsigned long, char*);
-static void FreeSymbols(void);
-static int CheckVersion(char *);
-static int CheckMapVersion(char *);
-
-
-/*************************************************************************
- * Function: InitKsyms
- *
- * Purpose: This function is responsible for initializing and loading
- * the data tables used by the kernel address translations.
- *
- * Arguements: (char *) mapfile
- *
- * mapfile:-> A pointer to a complete path
- * specification of the file containing
- * the kernel map to use.
- *
- * Return: int
- *
- * A boolean style context is returned. The return value will
- * be true if initialization was successful. False if not.
- **************************************************************************/
-extern int InitKsyms(char *mapfile)
-{
- auto char type,
- sym[512];
-
- auto int version = 0;
-
- auto unsigned long int address;
-
- auto FILE *sym_file;
-
- BEGINfunc
-
- /* Check and make sure that we are starting with a clean slate. */
- if ( num_syms > 0 )
- FreeSymbols();
-
-
- /* Search for and open the file containing the kernel symbols. */
- if ( mapfile != NULL ) {
- if ( (sym_file = fopen(mapfile, "r")) == NULL )
- {
- imklogLogIntMsg(LOG_WARNING, "Cannot open map file: %s.", mapfile);
- return(0);
- }
- } else {
- if ( (mapfile = FindSymbolFile()) == NULL ) {
- imklogLogIntMsg(LOG_WARNING, "Cannot find map file.");
- dbgprintf("Cannot find map file.\n");
- return(0);
- }
-
- if ( (sym_file = fopen(mapfile, "r")) == NULL ) {
- imklogLogIntMsg(LOG_WARNING, "Cannot open map file.");
- dbgprintf("Cannot open map file.\n");
- return(0);
- }
- }
-
-
- /* Read the kernel symbol table file and add entries for each
- * line. I suspect that the use of fscanf is not really in vogue
- * but it was quick and dirty and IMHO suitable for fixed format
- * data such as this. If anybody doesn't agree with this please
- * e-mail me a diff containing a parser with suitable political
- * correctness -- GW.
- */
- while ( !feof(sym_file) ) {
- if ( fscanf(sym_file, "%lx %c %s\n", &address, &type, sym) != 3 ) {
- imklogLogIntMsg(LOG_ERR, "Error in symbol table input (#1).");
- fclose(sym_file);
- return(0);
- }
- if(dbgPrintSymbols)
- dbgprintf("Address: %lx, Type: %c, Symbol: %s\n", address, type, sym);
-
- if ( AddSymbol(address, sym) == 0 ) {
- imklogLogIntMsg(LOG_ERR, "Error adding symbol - %s.", sym);
- fclose(sym_file);
- return(0);
- }
-
- if ( version == 0 )
- version = CheckVersion(sym);
- }
-
-
- imklogLogIntMsg(LOG_INFO, "Loaded %d symbols from %s.", num_syms, mapfile);
- switch(version) {
- case -1:
- imklogLogIntMsg(LOG_WARNING, "Symbols do not match kernel version.");
- num_syms = 0;
- break;
-
- case 0:
- imklogLogIntMsg(LOG_WARNING, "Cannot verify that symbols match kernel version.");
- break;
-
- case 1:
- imklogLogIntMsg(LOG_INFO, "Symbols match kernel version %s.", vstring);
- break;
- }
-
- fclose(sym_file);
- ENDfunc
- return(1);
-}
-
-
-extern void DeinitKsyms(void)
-{
- FreeSymbols();
-}
-
-
-/**************************************************************************
- * Function: FindSymbolFile
- *
- * Purpose: This function is responsible for encapsulating the search
- * for a valid symbol file. Encapsulating the search for
- * the map file in this function allows an intelligent search
- * process to be implemented.
- *
- * The list of symbol files will be searched until either a
- * symbol file is found whose version matches the currently
- * executing kernel or the end of the list is encountered. If
- * the end of the list is encountered the first available
- * symbol file is returned to the caller.
- *
- * This strategy allows klogd to locate valid symbol files
- * for both a production and an experimental kernel. For
- * example a map for a production kernel could be installed
- * in /boot. If an experimental kernel is loaded the map
- * in /boot will be skipped and the map in /usr/src/linux would
- * be used if its version number matches the executing kernel.
- *
- * Arguements: None specified.
- *
- * Return: char *
- *
- * If a valid system map cannot be located a null pointer
- * is returned to the caller.
- *
- * If the search is succesful a pointer is returned to the
- * caller which points to the name of the file containing
- * the symbol table to be used.
- **************************************************************************/
-static char *FindSymbolFile(void)
-{
- auto char *file = NULL,
- **mf = system_maps;
- auto struct utsname utsname;
- static char mysymfile[100];
- auto FILE *sym_file = NULL;
- BEGINfunc
-
- if(uname(&utsname) < 0) {
- imklogLogIntMsg(LOG_ERR, "Cannot get kernel version information.");
- return(0);
- }
-
- dbgprintf("Searching for symbol map.\n");
-
- for(mf = system_maps; *mf != NULL && file == NULL; ++mf) {
- snprintf(mysymfile, sizeof(mysymfile), "%s-%s", *mf, utsname.release);
- dbgprintf("Trying %s.\n", mysymfile);
- if((sym_file = fopen(mysymfile, "r")) != NULL) {
- if(CheckMapVersion(mysymfile) == 1)
- file = mysymfile;
- fclose(sym_file);
- }
- if(sym_file == NULL || file == NULL) {
- sprintf (mysymfile, "%s", *mf);
- dbgprintf("Trying %s.\n", mysymfile);
- if((sym_file = fopen(mysymfile, "r")) != NULL ) {
- if (CheckMapVersion(mysymfile) == 1)
- file = mysymfile;
- fclose(sym_file);
- }
- }
- }
-
- /* At this stage of the game we are at the end of the symbol tables. */
- dbgprintf("End of search list encountered.\n");
- ENDfunc
- return(file);
-}
-
-
-/**************************************************************************
- * Function: CheckVersion
- *
- * Purpose: This function is responsible for determining whether or
- * the system map being loaded matches the version of the
- * currently running kernel.
- *
- * The kernel version is checked by examing a variable which
- * is of the form: _Version_66347 (a.out) or Version_66437 (ELF).
- *
- * The suffix of this variable is the current kernel version
- * of the kernel encoded in base 256. For example the
- * above variable would be decoded as:
- *
- * (66347 = 1*65536 + 3*256 + 43 = 1.3.43)
- *
- * (Insert appropriate deities here) help us if Linus ever
- * needs more than 255 patch levels to get a kernel out the
- * door... :-)
- *
- * Arguements: (char *) version
- *
- * version:-> A pointer to the string which
- * is to be decoded as a kernel
- * version variable.
- *
- * Return: int
- *
- * -1:-> The currently running kernel version does
- * not match this version string.
- *
- * 0:-> The string is not a kernel version variable.
- *
- * 1:-> The executing kernel is of the same version
- * as the version string.
- **************************************************************************/
-static int CheckVersion(char *version)
-{
- auto int vnum,
- major,
- minor,
- patch;
- int kvnum;
- auto struct utsname utsname;
-
- static char *prefix = { "Version_" };
-
-
- /* Early return if there is no hope. */
- if ( strncmp(version, prefix, strlen(prefix)) == 0 /* ELF */ ||
- (*version == '_' &&
- strncmp(++version, prefix, strlen(prefix)) == 0 ) /* a.out */ )
- ;
- else
- return(0);
-
-
- /* Since the symbol looks like a kernel version we can start
- * things out by decoding the version string into its component
- * parts.
- */
- vnum = atoi(version + strlen(prefix));
- patch = vnum & 0x000000FF;
- minor = (vnum >> 8) & 0x000000FF;
- major = (vnum >> 16) & 0x000000FF;
- dbgprintf("Version string = %s, Major = %d, Minor = %d, Patch = %d.\n", version +
- strlen(prefix), major, minor, patch);
- sprintf(vstring, "%d.%d.%d", major, minor, patch);
-
- /* We should now have the version string in the vstring variable in
- * the same format that it is stored in by the kernel. We now
- * ask the kernel for its version information and compare the two
- * values to determine if our system map matches the kernel
- * version level.
- */
- if ( uname(&utsname) < 0 ) {
- imklogLogIntMsg(LOG_ERR, "Cannot get kernel version information.");
- return(0);
- }
- dbgprintf("Comparing kernel %s with symbol table %s.\n", utsname.release, vstring);
-
- if ( sscanf (utsname.release, "%d.%d.%d", &major, &minor, &patch) < 3 ) {
- imklogLogIntMsg(LOG_ERR, "Kernel send bogus release string `%s'.", utsname.release);
- return(0);
- }
-
- /* Compute the version code from data sent by the kernel */
- kvnum = (major << 16) | (minor << 8) | patch;
-
- /* Failure. */
- if ( vnum != kvnum )
- return(-1);
-
- /* Success. */
- return(1);
-}
-
-
-/**************************************************************************
- * Function: CheckMapVersion
- *
- * Purpose: This function is responsible for determining whether or
- * the system map being loaded matches the version of the
- * currently running kernel. It uses CheckVersion as
- * backend.
- *
- * Arguements: (char *) fname
- *
- * fname:-> A pointer to the string which
- * references the system map file to
- * be used.
- *
- * Return: int
- *
- * -1:-> The currently running kernel version does
- * not match the version in the given file.
- *
- * 0:-> No system map file or no version information.
- *
- * 1:-> The executing kernel is of the same version
- * as the version of the map file.
- **************************************************************************/
-static int CheckMapVersion(char *fname)
-{
- int version;
- FILE *sym_file;
- auto unsigned long int address;
- auto char type,
- sym[512];
-
- if ( (sym_file = fopen(fname, "r")) != NULL ) {
- /*
- * At this point a map file was successfully opened. We
- * now need to search this file and look for version
- * information.
- */
- imklogLogIntMsg(LOG_INFO, "Inspecting %s", fname);
-
- version = 0;
- while ( !feof(sym_file) && (version == 0) ) {
- if ( fscanf(sym_file, "%lx %c %s\n", &address, &type, sym) != 3 ) {
- imklogLogIntMsg(LOG_ERR, "Error in symbol table input (#2).");
- fclose(sym_file);
- return(0);
- }
- if(dbgPrintSymbols)
- dbgprintf("Address: %lx, Type: %c, Symbol: %s\n", address, type, sym);
- version = CheckVersion(sym);
- }
- fclose(sym_file);
-
- switch ( version ) {
- case -1:
- imklogLogIntMsg(LOG_ERR, "Symbol table has incorrect version number.\n");
- break;
- case 0:
- dbgprintf("No version information found.\n");
- break;
- case 1:
- dbgprintf("Found table with matching version number.\n");
- break;
- }
-
- return(version);
- }
-
- return(0);
-}
-
-
-/**************************************************************************
- * Function: AddSymbol
- *
- * Purpose: This function is responsible for adding a symbol name
- * and its address to the symbol table.
- *
- * Arguements: (unsigned long) address, (char *) symbol
- *
- * Return: int
- *
- * A boolean value is assumed. True if the addition is
- * successful. False if not.
- **************************************************************************/
-static int AddSymbol(unsigned long address, char *symbol)
-{
- /* Allocate the the symbol table entry. */
- sym_array = (struct sym_table *) realloc(sym_array, (num_syms+1) *
- sizeof(struct sym_table));
- if ( sym_array == (struct sym_table *) 0 )
- return(0);
-
- /* Then the space for the symbol. */
- sym_array[num_syms].name = (char *) MALLOC(strlen(symbol)*sizeof(char) + 1);
- if ( sym_array[num_syms].name == NULL )
- return(0);
-
- sym_array[num_syms].value = address;
- strcpy(sym_array[num_syms].name, symbol);
- ++num_syms;
- return(1);
-}
-
-
-/**************************************************************************
- * Function: LookupSymbol
- *
- * Purpose: Find the symbol which is related to the given kernel
- * address.
- *
- * Arguements: (long int) value, (struct symbol *) sym
- *
- * value:-> The address to be located.
- *
- * sym:-> A pointer to a structure which will be
- * loaded with the symbol's parameters.
- *
- * Return: (char *)
- *
- * If a match cannot be found a diagnostic string is printed.
- * If a match is found the pointer to the symbolic name most
- * closely matching the address is returned.
- **************************************************************************/
-char * LookupSymbol(unsigned long value, struct symbol *sym)
-{
- auto int lp;
-
- auto char *last;
- auto char *name;
-
- struct symbol ksym, msym;
-
- if (!sym_array)
- return(NULL);
-
- last = sym_array[0].name;
- ksym.offset = 0;
- ksym.size = 0;
- if ( value < sym_array[0].value )
- return(NULL);
-
- for(lp = 0; lp <= num_syms; ++lp) {
- if ( sym_array[lp].value > value ) {
- ksym.offset = value - sym_array[lp-1].value;
- ksym.size = sym_array[lp].value - \
- sym_array[lp-1].value;
- break;
- }
- last = sym_array[lp].name;
- }
-
- name = LookupModuleSymbol(value, &msym);
-
- if ( ksym.offset == 0 && msym.offset == 0 ) {
- return(NULL);
- }
-
- if ( ksym.offset == 0 || msym.offset < 0 ||
- (ksym.offset > 0 && ksym.offset < msym.offset) ) {
- sym->offset = ksym.offset;
- sym->size = ksym.size;
- return(last);
- } else {
- sym->offset = msym.offset;
- sym->size = msym.size;
- return(name);
- }
-
-
- return(NULL);
-}
-
-/**************************************************************************
- * Function: FreeSymbols
- *
- * Purpose: This function is responsible for freeing all memory which
- * has been allocated to hold the static symbol table. It
- * also initializes the symbol count and in general prepares
- * for a re-read of a static symbol table.
- *
- * Arguements: void
- *
- * Return: void
- **************************************************************************/
-static void FreeSymbols(void)
-{
- auto int lp;
-
- /* Free each piece of memory allocated for symbol names. */
- for(lp= 0; lp < num_syms; ++lp)
- free(sym_array[lp].name);
-
- /* Whack the entire array and initialize everything. */
- free(sym_array);
- sym_array = (struct sym_table *) 0;
- num_syms = 0;
-
- return;
-}
-
-
-/**************************************************************************
- * Function: LogExpanded
- *
- * Purpose: This function is responsible for logging a kernel message
- * line after all potential numeric kernel addresses have
- * been resolved symolically.
- *
- * Arguements: (char *) line, (char *) el
- *
- * line:-> A pointer to the buffer containing the kernel
- * message to be expanded and logged.
- *
- * el:-> A pointer to the buffer into which the expanded
- * kernel line will be written.
- *
- * Return: void
- **************************************************************************/
-extern char *ExpandKadds(char *line, char *el)
-{
- auto char *kp,
- *sl = line,
- *elp = el,
- *symbol;
- char num[15];
- auto unsigned long int value;
- auto struct symbol sym;
-
- sym.offset = 0;
- sym.size = 0;
-
- /*
- * This is as handy a place to put this as anyplace.
- *
- * Since the insertion of kernel modules can occur in a somewhat
- * dynamic fashion we need some mechanism to insure that the
- * kernel symbol tables get read just prior to when they are
- * needed.
- *
- * To accomplish this we look for the Oops string and use its
- * presence as a signal to load the module symbols.
- *
- * This is not the best solution of course, especially if the
- * kernel is rapidly going out to lunch. What really needs to
- * be done is to somehow generate a callback from the
- * kernel whenever a module is loaded or unloaded. I am
- * open for patches.
- */
- if ( i_am_paranoid &&
- (strstr(line, "Oops:") != NULL) && !InitMsyms() )
- imklogLogIntMsg(LOG_WARNING, "Cannot load kernel module symbols.\n");
-
-
- /*
- * Early return if there do not appear to be any kernel
- * messages in this line.
- */
- if ( (num_syms == 0) ||
- (kp = strstr(line, "[<")) == NULL ) {
-#ifdef __sparc__
- if (num_syms) {
- /* On SPARC, register dumps do not have the [< >] characters in it.
- */
- static struct sparc_tests {
- char *str;
- int len;
- } tests[] = { { "PC: ", 4 },
- { " o7: ", 5 },
- { " ret_pc: ", 9 },
- { " i7: ", 5 },
- { "Caller[", 7 }
- };
- int i, j, ndigits;
- char *kp2;
- for (i = 0; i < 5; i++) {
- kp = strstr(line, tests[i].str);
- if (!kp) continue;
- kp2 = kp + tests[i].len;
- if (!isxdigit(*kp2)) continue;
- for (ndigits = 1; isxdigit(kp2[ndigits]); ndigits++);
- if (ndigits != 8 && ndigits != 16) continue;
- /* On sparc64, all kernel addresses are in first 4GB */
- if (ndigits == 16) {
- if (strncmp (kp2, "00000000", 8)) continue;
- kp2 += 8;
- }
- if (!i) {
- char *kp3;
- if (ndigits == 16 && kp > line && kp[-1L] != 'T') continue;
- kp3 = kp2 + 8;
- if (ndigits == 16) {
- if (strncmp (kp3, " TNPC: 00000000", 15) || !isxdigit(kp3[15]))
- continue;
- kp3 += 15;
- } else {
- if (strncmp (kp3, " NPC: ", 6) || !isxdigit(kp3[6]))
- continue;
- kp3 += 6;
- }
- for (j = 0; isxdigit(kp3[j]); j++);
- if (j != 8) continue;
- strncpy(elp, line, kp2 + 8 - line);
- elp += kp2 + 8 - line;
- value = strtol(kp2, (char **) 0, 16);
- if ( (symbol = LookupSymbol(value, &sym)) ) {
- if (sym.size)
- elp += sprintf(elp, " (%s+%d/%d)", symbol, sym.offset, sym.size);
- else
- elp += sprintf(elp, " (%s)", symbol);
- }
- strncpy(elp, kp2 + 8, kp3 - kp2);
- elp += kp3 - kp2;
- value = strtol(kp3, (char **) 0, 16);
- if ( (symbol = LookupSymbol(value, &sym)) ) {
- if (sym.size)
- elp += sprintf(elp, " (%s+%d/%d)", symbol, sym.offset, sym.size);
- else
- elp += sprintf(elp, " (%s)", symbol);
- }
- strcpy(elp, kp3 + 8);
- } else {
- strncpy(elp, line, kp2 + 8 - line);
- elp += kp2 + 8 - line;
- value = strtol(kp2, (char **) 0, 16);
- if ( (symbol = LookupSymbol(value, &sym)) ) {
- if (sym.size)
- elp += sprintf(elp, " (%s+%d/%d)", symbol, sym.offset, sym.size);
- else
- elp += sprintf(elp, " (%s)", symbol);
- }
- strcpy(elp, kp2 + 8);
- }
- return el;
- }
- }
-#endif
- strcpy(el, line);
- return(el);
- }
-
- /* Loop through and expand all kernel messages. */
- do {
- while ( sl < kp+1 )
- *elp++ = *sl++;
-
- /* Now poised at a kernel delimiter. */
- if ( (kp = strstr(sl, ">]")) == NULL ) {
- strcpy(el, sl);
- return(el);
- }
- strncpy(num,sl+1,kp-sl-1);
- num[kp-sl-1] = '\0';
- value = strtoul(num, (char **) 0, 16);
- if ( (symbol = LookupSymbol(value, &sym)) == NULL )
- symbol = sl;
-
- strcat(elp, symbol);
- elp += strlen(symbol);
- dbgprintf("Symbol: %s = %lx = %s, %x/%d\n", sl+1, value,
- (sym.size==0) ? symbol+1 : symbol, sym.offset, sym.size);
-
- value = 2;
- if ( sym.size != 0 ) {
- --value;
- ++kp;
- elp += sprintf(elp, "+0x%x/0x%02x", sym.offset, sym.size);
- }
- strncat(elp, kp, value);
- elp += value;
- sl = kp + value;
- if ( (kp = strstr(sl, "[<")) == NULL )
- strcat(elp, sl);
- }
- while ( kp != NULL);
-
- dbgprintf("Expanded line: %s\n", el);
- return(el);
-}
-
-
-/**************************************************************************
- * Function: SetParanoiaLevel
- *
- * Purpose: This function is an interface function for setting the
- * mode of loadable module symbol lookups. Probably overkill
- * but it does slay another global variable.
- *
- * Arguements: (int) level
- *
- * level:-> The amount of paranoia which is to be
- * present when resolving kernel exceptions.
- * Return: void
- **************************************************************************/
-extern void SetParanoiaLevel(int level)
-{
- i_am_paranoid = level;
- return;
-}
-
diff --git a/plugins/imklog/ksym_mod.c b/plugins/imklog/ksym_mod.c
deleted file mode 100644
index 8297889..0000000
--- a/plugins/imklog/ksym_mod.c
+++ /dev/null
@@ -1,485 +0,0 @@
-/* ksym_mod.c - functions for building symbol lookup tables for klogd
- * Copyright (c) 1995, 1996 Dr. G.W. Wettstein <greg@wind.rmcc.com>
- * Copyright (c) 1996 Enjellic Systems Development
- * Copyright (c) 1998-2007 Martin Schulze <joey@infodrom.org>
- * Copyright (C) 2007-2009 Rainer Gerhards <rgerhards@adiscon.com>
- *
- * 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.
-*/
-
-/*
- * This file implements functions which are useful for building
- * a symbol lookup table based on the in kernel symbol table
- * maintained by the Linux kernel.
- *
- * Proper logging of kernel panics generated by loadable modules
- * tends to be difficult. Since the modules are loaded dynamically
- * their addresses are not known at kernel load time. A general
- * protection fault (Oops) cannot be properly deciphered with
- * classic methods using the static symbol map produced at link time.
- *
- * One solution to this problem is to have klogd attempt to translate
- * addresses from module when the fault occurs. By referencing the
- * the kernel symbol table proper resolution of these symbols is made
- * possible.
- *
- * At least that is the plan.
- *
- * Wed Aug 21 09:20:09 CDT 1996: Dr. Wettstein
- * The situation where no module support has been compiled into a
- * kernel is now detected. An informative message is output indicating
- * that the kernel has no loadable module support whenever kernel
- * module symbols are loaded.
- *
- * An informative message is printed indicating the number of kernel
- * modules and the number of symbols loaded from these modules.
- *
- * Sun Jun 15 16:23:29 MET DST 1997: Michael Alan Dorman
- * Some more glibc patches made by <mdorman@debian.org>.
- *
- * Sat Jan 10 15:00:18 CET 1998: Martin Schulze <joey@infodrom.north.de>
- * Fixed problem with klogd not being able to be built on a kernel
- * newer than 2.1.18. It was caused by modified structures
- * inside the kernel that were included. I have worked in a
- * patch from Alessandro Suardi <asuardi@uninetcom.it>.
- *
- * Sun Jan 25 20:57:34 CET 1998: Martin Schulze <joey@infodrom.north.de>
- * Another patch for Linux/alpha by Christopher C Chimelis
- * <chris@classnet.med.miami.edu>.
- *
- * Thu Mar 19 23:39:29 CET 1998: Manuel Rodrigues <pmanuel@cindy.fe.up.pt>
- * Changed lseek() to llseek() in order to support > 2GB address
- * space which provided by kernels > 2.1.70.
- *
- * Mon Apr 13 18:18:45 CEST 1998: Martin Schulze <joey@infodrom.north.de>
- * Removed <sys/module.h> as it's no longer part of recent glibc
- * versions. Added prototyp for llseek() which has been
- * forgotton in <unistd.h> from glibc. Added more log
- * information if problems occurred while reading a system map
- * file, by submission from Mark Simon Phillips <M.S.Phillips@nortel.co.uk>.
- *
- * Sun Jan 3 18:38:03 CET 1999: Martin Schulze <joey@infodrom.north.de>
- * Corrected return value of AddModule if /dev/kmem can't be
- * loaded. This will prevent klogd from segfaulting if /dev/kmem
- * is not available. Patch from Topi Miettinen <tom@medialab.sonera.net>.
- *
- * Tue Sep 12 23:11:13 CEST 2000: Martin Schulze <joey@infodrom.ffis.de>
- * Changed llseek() to lseek64() in order to skip a libc warning.
- */
-
-/* Includes. */
-#include "config.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <unistd.h>
-#include <signal.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/fcntl.h>
-#include <sys/stat.h>
-#if !defined(__GLIBC__)
-#include <linux/time.h>
-#include <linux/module.h>
-#else /* __GLIBC__ */
-#include "module.h"
-#endif /* __GLIBC__ */
-#include <stdarg.h>
-#include <paths.h>
-#include <linux/version.h>
-
-#include "rsyslog.h"
-#include "imklog.h"
-#include "ksyms.h"
-#include "debug.h"
-
-#define KSYMS "/proc/kallsyms"
-
-static int num_modules = 0;
-struct Module *sym_array_modules = (struct Module *) NULL;
-
-static int have_modules = 0;
-
-
-/* Function prototypes. */
-static void FreeModules(void);
-static int AddSymbol(const char *);
-struct Module *AddModule(const char *);
-static int symsort(const void *, const void *);
-
-/* Imported from ksym.c */
-extern int num_syms;
-
-
-/**************************************************************************
- * Function: InitMsyms
- *
- * Purpose: This function is responsible for building a symbol
- * table which can be used to resolve addresses for
- * loadable modules.
- *
- * Arguements: Void
- *
- * Return: A boolean return value is assumed.
- *
- * A false value indicates that something went wrong.
- *
- * True if loading is successful.
- **************************************************************************/
-extern int InitMsyms(void)
-{
-
- auto int rtn,
- tmp;
- FILE *ksyms;
- char buf[128];
- char *p;
-
- /* Initialize the kernel module symbol table. */
- FreeModules();
-
- ksyms = fopen(KSYMS, "r");
-
- if ( ksyms == NULL ) {
- if ( errno == ENOENT )
- imklogLogIntMsg(LOG_INFO, "No module symbols loaded - "
- "kernel modules not enabled.\n");
- else
- imklogLogIntMsg(LOG_ERR, "Error loading kernel symbols " \
- "- %s\n", strerror(errno));
- return(0);
- }
-
- dbgprintf("Loading kernel module symbols - Source: %s\n", KSYMS);
-
- while ( fgets(buf, sizeof(buf), ksyms) != NULL ) {
- if (num_syms > 0 && index(buf, '[') == NULL)
- continue;
-
- p = index(buf, ' ');
-
- if ( p == NULL )
- continue;
-
- if ( buf[strlen(buf)-1] == '\n' )
- buf[strlen(buf)-1] = '\0';
- /* overlong lines will be ignored above */
-
- AddSymbol(buf);
- }
-
- if(ksyms != NULL)
- fclose(ksyms);
-
- have_modules = 1;
-
- /* Sort the symbol tables in each module. */
- for (rtn = tmp = 0; tmp < num_modules; ++tmp) {
- rtn += sym_array_modules[tmp].num_syms;
- if ( sym_array_modules[tmp].num_syms < 2 )
- continue;
- qsort(sym_array_modules[tmp].sym_array, \
- sym_array_modules[tmp].num_syms, \
- sizeof(struct sym_table), symsort);
- }
-
- if ( rtn == 0 )
- imklogLogIntMsg(LOG_INFO, "No module symbols loaded.");
- else
- imklogLogIntMsg(LOG_INFO, "Loaded %d %s from %d module%s", rtn, \
- (rtn == 1) ? "symbol" : "symbols", \
- num_modules, (num_modules == 1) ? "." : "s.");
-
- return(1);
-}
-
-
-static int symsort(const void *p1, const void *p2)
-{
- auto const struct sym_table *sym1 = p1,
- *sym2 = p2;
-
- if ( sym1->value < sym2->value )
- return(-1);
- if ( sym1->value == sym2->value )
- return(0);
- return(1);
-}
-
-
-extern void DeinitMsyms(void)
-{
- FreeModules();
-}
-
-
-/**************************************************************************
- * Function: FreeModules
- *
- * Purpose: This function is used to free all memory which has been
- * allocated for the modules and their symbols.
- *
- * Arguements: None specified.
- *
- * Return: void
- **************************************************************************/
-static void FreeModules()
-{
- auto int nmods,
- nsyms;
- auto struct Module *mp;
-
- /* Check to see if the module symbol tables need to be cleared. */
- have_modules = 0;
- if ( num_modules == 0 )
- return;
-
- if ( sym_array_modules == NULL )
- return;
-
- for (nmods = 0; nmods < num_modules; ++nmods) {
- mp = &sym_array_modules[nmods];
- if ( mp->num_syms == 0 )
- continue;
-
- for (nsyms= 0; nsyms < mp->num_syms; ++nsyms)
- free(mp->sym_array[nsyms].name);
- free(mp->sym_array);
- if ( mp->name != NULL )
- free(mp->name);
- }
-
- free(sym_array_modules);
- sym_array_modules = (struct Module *) NULL;
- num_modules = 0;
- return;
-}
-
-
-/**************************************************************************
- * Function: AddModule
- *
- * Purpose: This function is responsible for adding a module to
- * the list of currently loaded modules.
- *
- * Arguments: (const char *) module
- *
- * module:-> The name of the module.
- *
- * Return: struct Module *
- **************************************************************************/
-
-struct Module *AddModule(module)
- const char *module;
-{
- struct Module *mp;
-
- if ( num_modules == 0 ) {
- sym_array_modules = (struct Module *)MALLOC(sizeof(struct Module));
-
- if ( sym_array_modules == NULL )
- {
- imklogLogIntMsg(LOG_WARNING, "Cannot allocate Module array.\n");
- return NULL;
- }
- mp = sym_array_modules;
- } else {
- /* Allocate space for the module. */
- mp = (struct Module *) \
- realloc(sym_array_modules, \
- (num_modules+1) * sizeof(struct Module));
-
- if ( mp == NULL )
- {
- imklogLogIntMsg(LOG_WARNING, "Cannot allocate Module array.\n");
- return NULL;
- }
-
- sym_array_modules = mp;
- mp = &sym_array_modules[num_modules];
- }
-
- num_modules++;
- mp->sym_array = NULL;
- mp->num_syms = 0;
-
- if ( module != NULL )
- mp->name = strdup(module);
- else
- mp->name = NULL;
-
- return mp;
-}
-
-
-/**************************************************************************
- * Function: AddSymbol
- *
- * Purpose: This function is responsible for adding a symbol name
- * and its address to the symbol table.
- *
- * Arguements: const char *
- *
- * Return: int
- *
- * A boolean value is assumed. True if the addition is
- * successful. False if not.
- **************************************************************************/
-static int AddSymbol(line)
- const char *line;
-{
- char *module;
- unsigned long address;
- char *p;
- static char *lastmodule = NULL;
- struct Module *mp;
-
- module = index(line, '[');
-
- if ( module != NULL ) {
- p = index(module, ']');
- if ( p != NULL )
- *p = '\0';
- p = module++;
- while ( isspace(*(--p)) )
- /*SKIP*/;
- *(++p) = '\0';
- }
-
- p = index(line, ' ');
-
- if ( p == NULL )
- return(0);
-
- *p = '\0';
-
- address = strtoul(line, (char **) 0, 16);
-
- p += 3;
-
- if ( num_modules == 0 ||
- ( lastmodule == NULL && module != NULL ) ||
- ( module == NULL && lastmodule != NULL) ||
- ( module != NULL && strcmp(module, lastmodule))) {
- mp = AddModule(module);
-
- if ( mp == NULL )
- return(0);
- } else
- mp = &sym_array_modules[num_modules-1];
-
- lastmodule = mp->name;
-
- /* Allocate space for the symbol table entry. */
- mp->sym_array = (struct sym_table *) realloc(mp->sym_array, \
- (mp->num_syms+1) * sizeof(struct sym_table));
-
- if ( mp->sym_array == (struct sym_table *) NULL )
- return(0);
-
- mp->sym_array[mp->num_syms].name = strdup(p);
- if ( mp->sym_array[mp->num_syms].name == (char *) NULL )
- return(0);
-
- /* Stuff interesting information into the module. */
- mp->sym_array[mp->num_syms].value = address;
- ++mp->num_syms;
-
- return(1);
-}
-
-
-
-/**************************************************************************
- * Function: LookupModuleSymbol
- *
- * Purpose: Find the symbol which is related to the given address from
- * a kernel module.
- *
- * Arguements: (long int) value, (struct symbol *) sym
- *
- * value:-> The address to be located.
- *
- * sym:-> A pointer to a structure which will be
- * loaded with the symbol's parameters.
- *
- * Return: (char *)
- *
- * If a match cannot be found a diagnostic string is printed.
- * If a match is found the pointer to the symbolic name most
- * closely matching the address is returned.
- *
- * TODO: We are using int values for the offset, but longs for the value
- * values. This may create some trouble in the future (on 64 Bit OS?).
- * Anyhow, I have not changed this, because we do not seem to have any
- * issue and my understanding of this code is limited (and I don't see
- * need to invest more time to dig much deeper).
- * rgerhards, 2009-04-17
- **************************************************************************/
-extern char * LookupModuleSymbol(value, sym)
- unsigned long value;
- struct symbol *sym;
-{
- int nmod, nsym;
- struct sym_table *last;
- struct Module *mp;
- static char ret[100];
-
- sym->size = 0;
- sym->offset = 0;
- if ( num_modules == 0 )
- return((char *) 0);
-
- for (nmod = 0; nmod < num_modules; ++nmod) {
- mp = &sym_array_modules[nmod];
-
- /* Run through the list of symbols in this module and
- * see if the address can be resolved.
- */
- for(nsym = 1, last = &mp->sym_array[0];
- nsym < mp->num_syms;
- ++nsym) {
- if ( mp->sym_array[nsym].value > value )
- {
- if ( sym->size == 0 ||
- (int) (value - last->value) < sym->offset ||
- ( (sym->offset == (int) (value - last->value)) &&
- (int) (mp->sym_array[nsym].value-last->value) < sym->size ) )
- {
- sym->offset = value - last->value;
- sym->size = mp->sym_array[nsym].value - last->value;
- ret[sizeof(ret)-1] = '\0';
- if ( mp->name == NULL )
- snprintf(ret, sizeof(ret)-1,
- "%s", last->name);
- else
- snprintf(ret, sizeof(ret)-1,
- "%s:%s", mp->name, last->name);
- }
- break;
- }
- last = &mp->sym_array[nsym];
- }
- }
-
- if ( sym->size > 0 )
- return(ret);
-
- /* It has been a hopeless exercise. */
- return(NULL);
-}
diff --git a/plugins/imklog/ksyms.h b/plugins/imklog/ksyms.h
deleted file mode 100644
index a168947..0000000
--- a/plugins/imklog/ksyms.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* ksym.h - Definitions for symbol table utilities.
- * Copyright (c) 1995, 1996 Dr. G.W. Wettstein <greg@wind.rmcc.com>
- * Copyright (c) 1996 Enjellic Systems Development
- * Copyright (c) 2004-7 Martin Schulze <joey@infodrom.org>
- * Copyright (c) 2007-2009 Rainer Gerhards <rgerhards@adiscon.com>
- *
- * 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.
- */
-
-/* Variables, structures and type definitions static to this module. */
-
-struct symbol
-{
- uchar *name;
- int size;
- int offset;
-};
-
-
-/* Function prototypes. */
-extern char * LookupSymbol(unsigned long, struct symbol *);
-extern char * LookupModuleSymbol(unsigned long int, struct symbol *);
diff --git a/plugins/imklog/linux.c b/plugins/imklog/linux.c
deleted file mode 100644
index 727708a..0000000
--- a/plugins/imklog/linux.c
+++ /dev/null
@@ -1,542 +0,0 @@
-/* klog for linux, based on the FreeBSD syslogd implementation.
- *
- * This contains OS-specific functionality to read the BSD
- * kernel log. For a general overview, see head comment in
- * imklog.c.
- *
- * This file heavily borrows from the klogd daemon provided by
- * the sysklogd project. Many thanks for this piece of software.
- *
- * 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 <stdlib.h>
-#include <stdio.h>
-#include <assert.h>
-#include <signal.h>
-#include <string.h>
-#include <pthread.h>
-#include "cfsysline.h"
-#include "template.h"
-#include "msg.h"
-#include "module-template.h"
-#include "imklog.h"
-#include "unicode-helper.h"
-
-
-/* Includes. */
-#include <unistd.h>
-#include <errno.h>
-#include <sys/fcntl.h>
-#include <sys/stat.h>
-
-#if HAVE_TIME_H
-# include <time.h>
-#endif
-
-#include <stdarg.h>
-#include <paths.h>
-#include "ksyms.h"
-
-#define __LIBRARY__
-#include <unistd.h>
-
-
-#if !defined(__GLIBC__)
-# define __NR_ksyslog __NR_syslog
-_syscall3(int,ksyslog,int, type, char *, buf, int, len);
-#else
-#include <sys/klog.h>
-#define ksyslog klogctl
-#endif
-
-
-
-#ifndef _PATH_KLOG
-#define _PATH_KLOG "/proc/kmsg"
-#endif
-
-#define LOG_BUFFER_SIZE 4096
-#define LOG_LINE_LENGTH 1000
-
-static int kmsg;
-static char log_buffer[LOG_BUFFER_SIZE];
-
-static enum LOGSRC {none, proc, kernel} logsrc;
-
-
-/* Function prototypes. */
-extern int ksyslog(int type, char *buf, int len);
-
-
-static uchar *GetPath(void)
-{
- return pszPath ? pszPath : UCHAR_CONSTANT(_PATH_KLOG);
-}
-
-static void CloseLogSrc(void)
-{
- /* Turn on logging of messages to console, but only if a log level was speficied */
- if(console_log_level != -1)
- ksyslog(7, NULL, 0);
-
- /* Shutdown the log sources. */
- switch(logsrc) {
- case kernel:
- ksyslog(0, NULL, 0);
- imklogLogIntMsg(LOG_INFO, "Kernel logging (ksyslog) stopped.");
- break;
- case proc:
- close(kmsg);
- imklogLogIntMsg(LOG_INFO, "Kernel logging (proc) stopped.");
- break;
- case none:
- break;
- }
-
- return;
-}
-
-
-static enum LOGSRC GetKernelLogSrc(void)
-{
- auto struct stat sb;
-
- /* Set level of kernel console messaging.. */
- if ( (console_log_level != -1) &&
- (ksyslog(8, NULL, console_log_level) < 0) &&
- (errno == EINVAL) )
- {
- /*
- * An invalid arguement error probably indicates that
- * a pre-0.14 kernel is being run. At this point we
- * issue an error message and simply shut-off console
- * logging completely.
- */
- imklogLogIntMsg(LOG_WARNING, "Cannot set console log level - disabling "
- "console output.");
- }
-
- /*
- * First do a stat to determine whether or not the proc based
- * file system is available to get kernel messages from.
- */
- if ( use_syscall ||
- ((stat((char*)GetPath(), &sb) < 0) && (errno == ENOENT)) )
- {
- /* Initialize kernel logging. */
- ksyslog(1, NULL, 0);
- imklogLogIntMsg(LOG_INFO, "imklog %s, log source = ksyslog "
- "started.", VERSION);
- return(kernel);
- }
-
- if ( (kmsg = open((char*)GetPath(), O_RDONLY|O_CLOEXEC)) < 0 )
- {
- imklogLogIntMsg(LOG_ERR, "imklog: Cannot open proc file system, %d.\n", errno);
- ksyslog(7, NULL, 0);
- return(none);
- }
-
- imklogLogIntMsg(LOG_INFO, "imklog %s, log source = %s started.", VERSION, GetPath());
- return(proc);
-}
-
-
-/* Copy characters from ptr to line until a char in the delim
- * string is encountered or until min( space, len ) chars have
- * been copied.
- *
- * Returns the actual number of chars copied.
- */
-static int copyin( uchar *line, int space,
- const char *ptr, int len,
- const char *delim )
-{
- auto int i;
- auto int count;
-
- count = len < space ? len : space;
-
- for(i=0; i<count && !strchr(delim, *ptr); i++ ) {
- *line++ = *ptr++;
- }
-
- return(i);
-}
-
-/*
- * Messages are separated by "\n". Messages longer than
- * LOG_LINE_LENGTH are broken up.
- *
- * Kernel symbols show up in the input buffer as : "[<aaaaaa>]",
- * where "aaaaaa" is the address. These are replaced with
- * "[symbolname+offset/size]" in the output line - symbolname,
- * offset, and size come from the kernel symbol table.
- *
- * If a kernel symbol happens to fall at the end of a message close
- * in length to LOG_LINE_LENGTH, the symbol will not be expanded.
- * (This should never happen, since the kernel should never generate
- * messages that long.
- *
- * To preserve the original addresses, lines containing kernel symbols
- * are output twice. Once with the symbols converted and again with the
- * original text. Just in case somebody wants to run their own Oops
- * analysis on the syslog, e.g. ksymoops.
- */
-static void LogLine(char *ptr, int len)
-{
- enum parse_state_enum {
- PARSING_TEXT,
- PARSING_SYMSTART, /* at < */
- PARSING_SYMBOL,
- PARSING_SYMEND /* at ] */
- };
-
- static uchar line_buff[LOG_LINE_LENGTH];
-
- static uchar *line =line_buff;
- static enum parse_state_enum parse_state = PARSING_TEXT;
- static int space = sizeof(line_buff)-1;
-
- static uchar *sym_start; /* points at the '<' of a symbol */
-
- auto int delta = 0; /* number of chars copied */
- auto int symbols_expanded = 0; /* 1 if symbols were expanded */
- auto int skip_symbol_lookup = 0; /* skip symbol lookup on this pass */
- auto char *save_ptr = ptr; /* save start of input line */
- auto int save_len = len; /* save length at start of input line */
-
- while( len > 0 )
- {
- if( space == 0 ) /* line buffer is full */
- {
- /*
- ** Line too long. Start a new line.
- */
- *line = 0; /* force null terminator */
-
- //dbgprintf("Line buffer full:\n");
- //dbgprintf("\tLine: %s\n", line);
-
- Syslog(LOG_INFO, line_buff);
- line = line_buff;
- space = sizeof(line_buff)-1;
- parse_state = PARSING_TEXT;
- symbols_expanded = 0;
- skip_symbol_lookup = 0;
- save_ptr = ptr;
- save_len = len;
- }
-
- switch( parse_state )
- {
- case PARSING_TEXT:
- delta = copyin(line, space, ptr, len, "\n[" );
- line += delta;
- ptr += delta;
- space -= delta;
- len -= delta;
-
- if( space == 0 || len == 0 )
- {
- break; /* full line_buff or end of input buffer */
- }
-
- if( *ptr == '\0' ) /* zero byte */
- {
- ptr++; /* skip zero byte */
- space -= 1;
- len -= 1;
-
- break;
- }
-
- if( *ptr == '\n' ) /* newline */
- {
- ptr++; /* skip newline */
- space -= 1;
- len -= 1;
-
- *line = 0; /* force null terminator */
- Syslog(LOG_INFO, line_buff);
- line = line_buff;
- space = sizeof(line_buff)-1;
- if (symbols_twice) {
- if (symbols_expanded) {
- /* reprint this line without symbol lookup */
- symbols_expanded = 0;
- skip_symbol_lookup = 1;
- ptr = save_ptr;
- len = save_len;
- }
- else
- {
- skip_symbol_lookup = 0;
- save_ptr = ptr;
- save_len = len;
- }
- }
- break;
- }
- if( *ptr == '[' ) /* possible kernel symbol */
- {
- *line++ = *ptr++;
- space -= 1;
- len -= 1;
- if (!skip_symbol_lookup)
- parse_state = PARSING_SYMSTART; /* at < */
- break;
- }
- /* Now that line_buff is no longer fed to *printf as format
- * string, '%'s are no longer "dangerous".
- */
- break;
-
- case PARSING_SYMSTART:
- if( *ptr != '<' )
- {
- parse_state = PARSING_TEXT; /* not a symbol */
- break;
- }
-
- /*
- ** Save this character for now. If this turns out to
- ** be a valid symbol, this char will be replaced later.
- ** If not, we'll just leave it there.
- */
-
- sym_start = line; /* this will point at the '<' */
-
- *line++ = *ptr++;
- space -= 1;
- len -= 1;
- parse_state = PARSING_SYMBOL; /* symbol... */
- break;
-
- case PARSING_SYMBOL:
- delta = copyin( line, space, ptr, len, ">\n[" );
- line += delta;
- ptr += delta;
- space -= delta;
- len -= delta;
- if( space == 0 || len == 0 )
- {
- break; /* full line_buff or end of input buffer */
- }
- if( *ptr != '>' )
- {
- parse_state = PARSING_TEXT;
- break;
- }
-
- *line++ = *ptr++; /* copy the '>' */
- space -= 1;
- len -= 1;
-
- parse_state = PARSING_SYMEND;
-
- break;
-
- case PARSING_SYMEND:
- if( *ptr != ']' )
- {
- parse_state = PARSING_TEXT; /* not a symbol */
- break;
- }
-
- /*
- ** It's really a symbol! Replace address with the
- ** symbol text.
- */
- {
- auto int sym_space;
-
- unsigned long value;
- auto struct symbol sym;
- auto char *symbol;
-
- *(line-1) = 0; /* null terminate the address string */
- value = strtoul((char*)(sym_start+1), (char **) 0, 16);
- *(line-1) = '>'; /* put back delim */
-
- if ( !symbol_lookup || (symbol = LookupSymbol(value, &sym)) == (char *)0 )
- {
- parse_state = PARSING_TEXT;
- break;
- }
-
- /*
- ** verify there is room in the line buffer
- */
- sym_space = space + ( line - sym_start );
- if( (unsigned) sym_space < strlen(symbol) + 30 ) /*(30 should be overkill)*/
- {
- parse_state = PARSING_TEXT; /* not enough space */
- break;
- }
-
- // TODO: sprintf!!!!
- delta = sprintf( (char*) sym_start, "%s+%d/%d]",
- symbol, sym.offset, sym.size );
-
- space = sym_space + delta;
- line = sym_start + delta;
- symbols_expanded = 1;
- }
- ptr++;
- len--;
- parse_state = PARSING_TEXT;
- break;
-
- default: /* Can't get here! */
- parse_state = PARSING_TEXT;
-
- }
- }
-
- return;
-}
-
-
-static void LogKernelLine(void)
-{
- auto int rdcnt;
-
- /*
- * Zero-fill the log buffer. This should cure a multitude of
- * problems with klogd logging the tail end of the message buffer
- * which will contain old messages. Then read the kernel log
- * messages into this fresh buffer.
- */
- memset(log_buffer, '\0', sizeof(log_buffer));
- if ( (rdcnt = ksyslog(2, log_buffer, sizeof(log_buffer)-1)) < 0 )
- {
- if(errno == EINTR)
- return;
- imklogLogIntMsg(LOG_ERR, "imklog Error return from sys_sycall: %d\n", errno);
- }
- else
- LogLine(log_buffer, rdcnt);
- return;
-}
-
-
-static void LogProcLine(void)
-{
- auto int rdcnt;
-
- /*
- * Zero-fill the log buffer. This should cure a multitude of
- * problems with klogd logging the tail end of the message buffer
- * which will contain old messages. Then read the kernel messages
- * from the message pseudo-file into this fresh buffer.
- */
- memset(log_buffer, '\0', sizeof(log_buffer));
- if ( (rdcnt = read(kmsg, log_buffer, sizeof(log_buffer)-1)) < 0 ) {
- if ( errno == EINTR )
- return;
- imklogLogIntMsg(LOG_ERR, "Cannot read proc file system: %d - %s.", errno, strerror(errno));
- } else {
- LogLine(log_buffer, rdcnt);
- }
-
- return;
-}
-
-
-/* to be called in the module's WillRun entry point
- * rgerhards, 2008-04-09
- */
-rsRetVal klogLogKMsg(void)
-{
- DEFiRet;
- switch(logsrc) {
- case kernel:
- LogKernelLine();
- break;
- case proc:
- LogProcLine();
- break;
- case none:
- /* TODO: We need to handle this case here somewhat more intelligent
- * This is now at least partly done - code should never reach this point
- * as willRun() already checked for the "none" status -- rgerhards, 2007-12-17
- */
- pause();
- break;
- }
- RETiRet;
-}
-
-
-/* to be called in the module's WillRun entry point
- * rgerhards, 2008-04-09
- */
-rsRetVal klogWillRun(void)
-{
- DEFiRet;
- /* Initialize this module. If that fails, we tell the engine we don't like to run */
- /* Determine where kernel logging information is to come from. */
- logsrc = GetKernelLogSrc();
- if(logsrc == none) {
- iRet = RS_RET_NO_KERNEL_LOGSRC;
- } else {
- if (symbol_lookup) {
- symbol_lookup = (InitKsyms(symfile) == 1);
- symbol_lookup |= InitMsyms();
- if (symbol_lookup == 0) {
- imklogLogIntMsg(LOG_WARNING, "cannot find any symbols, turning off symbol lookups");
- }
- }
- }
-
- RETiRet;
-}
-
-
-/* to be called in the module's AfterRun entry point
- * rgerhards, 2008-04-09
- */
-rsRetVal klogAfterRun(void)
-{
- DEFiRet;
- /* cleanup here */
- if(logsrc != none)
- CloseLogSrc();
-
- DeinitKsyms();
- DeinitMsyms();
-
- RETiRet;
-}
-
-
-/* provide the (system-specific) default facility for internal messages
- * rgerhards, 2008-04-14
- */
-int
-klogFacilIntMsg(void)
-{
- return LOG_KERN;
-}
-
-
-/* vi:set ai:
- */
diff --git a/plugins/imklog/module.h b/plugins/imklog/module.h
deleted file mode 100644
index 38a26fe..0000000
--- a/plugins/imklog/module.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* module.h - Miscellaneous module definitions
- * Copyright (c) 1996 Richard Henderson <rth@tamu.edu>
- * Copyright (c) 2004-7 Martin Schulze <joey@infodrom.org>
- * Copyright (c) 2007-2008 Rainer Gerhards <rgerhards@adiscon.com>
- *
- * 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.
- */
-struct sym_table
-{
- unsigned long value;
- char *name;
-};
-
-struct Module
-{
- struct sym_table *sym_array;
- int num_syms;
-
- char *name;
-};
diff --git a/plugins/imklog/solaris.c b/plugins/imklog/solaris.c
index 8a6d5af..0a169cd 100644
--- a/plugins/imklog/solaris.c
+++ b/plugins/imklog/solaris.c
@@ -80,74 +80,6 @@ klogWillRun(void)
}
-#if 0
-/* Read /dev/klog while data are available, split into lines.
- * Contrary to standard BSD syslogd, we do a blocking read. We can
- * afford this as imklog is running on its own threads. So if we have
- * a single file, it really doesn't matter if we wait inside a 1-file
- * select or the read() directly.
- */
-static void
-readklog(void)
-{
- char *p, *q;
- int len, i;
- int iMaxLine;
- uchar bufRcv[4096+1];
- uchar *pRcv = NULL; /* receive buffer */
-
- iMaxLine = klog_getMaxLine();
-
- /* we optimize performance: if iMaxLine is below 4K (which it is in almost all
- * cases, we use a fixed buffer on the stack. Only if it is higher, heap memory
- * is used. We could use alloca() to achive a similar aspect, but there are so
- * many issues with alloca() that I do not want to take that route.
- * rgerhards, 2008-09-02
- */
- if((size_t) iMaxLine < sizeof(bufRcv) - 1) {
- pRcv = bufRcv;
- } else {
- if((pRcv = (uchar*) malloc(sizeof(uchar) * (iMaxLine + 1))) == NULL)
- iMaxLine = sizeof(bufRcv) - 1; /* better this than noting */
- }
-
- len = 0;
- for (;;) {
- dbgprintf("----------imklog(BSD) waiting for kernel log line\n");
- i = read(fklog, pRcv + len, iMaxLine - len);
- if (i > 0) {
- pRcv[i + len] = '\0';
- } else {
- if (i < 0 && errno != EINTR && errno != EAGAIN) {
- imklogLogIntMsg(LOG_ERR,
- "imklog error %d reading kernel log - shutting down imklog",
- errno);
- fklog = -1;
- }
- break;
- }
-
- for(p = pRcv; (q = strchr(p, '\n')) != NULL; p = q + 1) {
- *q = '\0';
- Syslog(LOG_INFO, (uchar*) p);
- }
- len = strlen(p);
- if (len >= iMaxLine - 1) {
- Syslog(LOG_INFO, (uchar*)p);
- len = 0;
- }
- if (len > 0)
- memmove(pRcv, p, len + 1);
- }
- if (len > 0)
- Syslog(LOG_INFO, pRcv);
-
- if(pRcv != NULL && (size_t) iMaxLine >= sizeof(bufRcv) - 1)
- free(pRcv);
-}
-#endif
-
-
/* to be called in the module's AfterRun entry point
* rgerhards, 2008-04-09
*/
diff --git a/plugins/immark/immark.c b/plugins/immark/immark.c
index 358b3b1..273af02 100644
--- a/plugins/immark/immark.c
+++ b/plugins/immark/immark.c
@@ -46,6 +46,7 @@
MODULE_TYPE_INPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("immark")
/* defines */
#define DEFAULT_MARK_PERIOD (20 * 60)
@@ -53,7 +54,12 @@ MODULE_TYPE_NOKEEP
/* Module static data */
DEF_IMOD_STATIC_DATA
DEFobjCurrIf(glbl)
+DEFobjCurrIf(errmsg)
+
static int iMarkMessagePeriod = DEFAULT_MARK_PERIOD;
+struct modConfData_s {
+ int iMarkMessagePeriod;
+};
BEGINisCompatibleWithFeature
CODESTARTisCompatibleWithFeature
@@ -62,6 +68,43 @@ CODESTARTisCompatibleWithFeature
ENDisCompatibleWithFeature
+BEGINafterRun
+CODESTARTafterRun
+ENDafterRun
+
+
+BEGINbeginCnfLoad
+CODESTARTbeginCnfLoad
+ENDbeginCnfLoad
+
+
+BEGINendCnfLoad
+CODESTARTendCnfLoad
+ pModConf->iMarkMessagePeriod = iMarkMessagePeriod;
+ENDendCnfLoad
+
+
+BEGINcheckCnf
+CODESTARTcheckCnf
+ if(pModConf->iMarkMessagePeriod == 0) {
+ errmsg.LogError(0, NO_ERRCODE, "immark: mark message period must not be 0, can not run");
+ ABORT_FINALIZE(RS_RET_NO_RUN); /* we can not run with this error */
+ }
+finalize_it:
+ENDcheckCnf
+
+
+BEGINactivateCnf
+CODESTARTactivateCnf
+ MarkInterval = pModConf->iMarkMessagePeriod;
+ENDactivateCnf
+
+
+BEGINfreeCnf
+CODESTARTfreeCnf
+ENDfreeCnf
+
+
/* This function is called to gather input. It must terminate only
* a) on failure (iRet set accordingly)
* b) on termination of the input module (as part of the unload process)
@@ -81,7 +124,7 @@ CODESTARTrunInput
* right into the sleep below.
*/
while(1) {
- srSleep(iMarkMessagePeriod, 0); /* seconds, micro seconds */
+ srSleep(MarkInterval, 0); /* seconds, micro seconds */
if(glbl.GetGlobalInputTermState() == 1)
break; /* terminate input! */
@@ -94,33 +137,25 @@ ENDrunInput
BEGINwillRun
CODESTARTwillRun
- /* We set the global MarkInterval to what is configured here -- rgerhards, 2008-07-15 */
- MarkInterval = iMarkMessagePeriod;
- if(iMarkMessagePeriod == 0)
- iRet = RS_RET_NO_RUN;
ENDwillRun
-BEGINafterRun
-CODESTARTafterRun
-ENDafterRun
-
-
BEGINmodExit
CODESTARTmodExit
+ objRelease(errmsg, CORE_COMPONENT);
ENDmodExit
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
+CODEqueryEtryPt_STD_CONF2_QUERIES
CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
ENDqueryEtryPt
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
iMarkMessagePeriod = DEFAULT_MARK_PERIOD;
-
return RS_RET_OK;
}
@@ -129,8 +164,13 @@ CODESTARTmodInit
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(glbl, CORE_COMPONENT));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"markmessageperiod", 0, eCmdHdlrInt, NULL, &iMarkMessagePeriod, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+
+ /* legacy config handlers */
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"markmessageperiod", 0, eCmdHdlrInt, NULL,
+ &iMarkMessagePeriod, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
+ resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
/* vi:set ai:
*/
diff --git a/plugins/impstats/impstats.c b/plugins/impstats/impstats.c
index 3012136..4fec8e7 100644
--- a/plugins/impstats/impstats.c
+++ b/plugins/impstats/impstats.c
@@ -40,6 +40,7 @@
MODULE_TYPE_INPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("impstats")
/* defines */
#define DEFAULT_STATS_PERIOD (5 * 60)
@@ -57,12 +58,23 @@ typedef struct configSettings_s {
int iStatsInterval;
int iFacility;
int iSeverity;
+ int bJSON;
} configSettings_t;
+struct modConfData_s {
+ rsconf_t *pConf; /* our overall config object */
+ int iStatsInterval;
+ int iFacility;
+ int iSeverity;
+ statsFmtType_t statsFmt;
+};
+static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
+static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current load process */
+
+
static configSettings_t cs;
static prop_t *pInputName = NULL;
-static prop_t *pLocalHostIP = NULL;
BEGINisCompatibleWithFeature
CODESTARTisCompatibleWithFeature
@@ -76,6 +88,7 @@ initConfigSettings(void)
cs.iStatsInterval = DEFAULT_STATS_PERIOD;
cs.iFacility = DEFAULT_FACILITY;
cs.iSeverity = DEFAULT_SEVERITY;
+ cs.bJSON = 0;
}
@@ -92,11 +105,11 @@ doSubmitMsg(uchar *line)
MsgSetRawMsgWOSize(pMsg, (char*)line);
MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName(), ustrlen(glbl.GetLocalHostName()));
MsgSetRcvFrom(pMsg, glbl.GetLocalHostNameProp());
- MsgSetRcvFromIP(pMsg, pLocalHostIP);
+ MsgSetRcvFromIP(pMsg, glbl.GetLocalHostIP());
MsgSetMSGoffs(pMsg, 0);
MsgSetTAG(pMsg, UCHAR_CONSTANT("rsyslogd-pstats:"), sizeof("rsyslogd-pstats:") - 1);
- pMsg->iFacility = cs.iFacility;
- pMsg->iSeverity = cs.iSeverity;
+ pMsg->iFacility = runModConf->iFacility;
+ pMsg->iSeverity = runModConf->iSeverity;
pMsg->msgFlags = 0;
submitMsg(pMsg);
@@ -125,10 +138,58 @@ doStatsLine(void __attribute__((unused)) *usrptr, cstr_t *cstr)
static inline void
generateStatsMsgs(void)
{
- statsobj.GetAllStatsLines(doStatsLine, NULL);
+ statsobj.GetAllStatsLines(doStatsLine, NULL, runModConf->statsFmt);
}
+BEGINbeginCnfLoad
+CODESTARTbeginCnfLoad
+ loadModConf = pModConf;
+ pModConf->pConf = pConf;
+ /* init legacy config vars */
+ initConfigSettings();
+ENDbeginCnfLoad
+
+
+BEGINendCnfLoad
+CODESTARTendCnfLoad
+ /* persist module-specific settings from legacy config system */
+ loadModConf->iStatsInterval = cs.iStatsInterval;
+ loadModConf->iFacility = cs.iFacility;
+ loadModConf->iSeverity = cs.iSeverity;
+ loadModConf->statsFmt = cs.bJSON ? statsFmt_JSON : statsFmt_Legacy;
+ENDendCnfLoad
+
+
+BEGINcheckCnf
+CODESTARTcheckCnf
+ if(pModConf->iStatsInterval == 0) {
+ errmsg.LogError(0, NO_ERRCODE, "impstats: stats interval zero not permitted, using "
+ "defaul of %d seconds", DEFAULT_STATS_PERIOD);
+ pModConf->iStatsInterval = DEFAULT_STATS_PERIOD;
+ }
+ENDcheckCnf
+
+
+BEGINactivateCnf
+ rsRetVal localRet;
+CODESTARTactivateCnf
+ runModConf = pModConf;
+ DBGPRINTF("impstats: stats interval %d seconds\n", runModConf->iStatsInterval);
+ localRet = statsobj.EnableStats();
+ if(localRet != RS_RET_OK) {
+ errmsg.LogError(0, localRet, "impstats: error enabling statistics gathering");
+ ABORT_FINALIZE(RS_RET_NO_RUN);
+ }
+finalize_it:
+ENDactivateCnf
+
+
+BEGINfreeCnf
+CODESTARTfreeCnf
+ENDfreeCnf
+
+
BEGINrunInput
CODESTARTrunInput
/* this is an endless loop - it is terminated when the thread is
@@ -136,7 +197,7 @@ CODESTARTrunInput
* right into the sleep below.
*/
while(1) {
- srSleep(cs.iStatsInterval, 0); /* seconds, micro seconds */
+ srSleep(runModConf->iStatsInterval, 0); /* seconds, micro seconds */
if(glbl.GetGlobalInputTermState() == 1)
break; /* terminate input! */
@@ -147,17 +208,7 @@ ENDrunInput
BEGINwillRun
- rsRetVal localRet;
CODESTARTwillRun
- DBGPRINTF("impstats: stats interval %d seconds\n", cs.iStatsInterval);
- if(cs.iStatsInterval == 0)
- ABORT_FINALIZE(RS_RET_NO_RUN);
- localRet = statsobj.EnableStats();
- if(localRet != RS_RET_OK) {
- errmsg.LogError(0, localRet, "impstat: error enabling statistics gathering");
- ABORT_FINALIZE(RS_RET_NO_RUN);
- }
-finalize_it:
ENDwillRun
@@ -169,7 +220,6 @@ ENDafterRun
BEGINmodExit
CODESTARTmodExit
prop.Destruct(&pInputName);
- prop.Destruct(&pLocalHostIP);
/* release objects we used */
objRelease(glbl, CORE_COMPONENT);
objRelease(prop, CORE_COMPONENT);
@@ -182,6 +232,7 @@ ENDmodExit
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
+CODEqueryEtryPt_STD_CONF2_QUERIES
CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
ENDqueryEtryPt
@@ -207,15 +258,12 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(omsdRegCFSLineHdlr((uchar *)"pstatinterval", 0, eCmdHdlrInt, NULL, &cs.iStatsInterval, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"pstatfacility", 0, eCmdHdlrInt, NULL, &cs.iFacility, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"pstatseverity", 0, eCmdHdlrInt, NULL, &cs.iSeverity, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"pstatjson", 0, eCmdHdlrBinary, NULL, &cs.bJSON, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
CHKiRet(prop.Construct(&pInputName));
CHKiRet(prop.SetString(pInputName, UCHAR_CONSTANT("impstats"), sizeof("impstats") - 1));
CHKiRet(prop.ConstructFinalize(pInputName));
-
- CHKiRet(prop.Construct(&pLocalHostIP));
- CHKiRet(prop.SetString(pLocalHostIP, UCHAR_CONSTANT("127.0.0.1"), sizeof("127.0.0.1") - 1));
- CHKiRet(prop.ConstructFinalize(pLocalHostIP));
ENDmodInit
/* vi:set ai:
*/
diff --git a/plugins/imptcp/imptcp.c b/plugins/imptcp/imptcp.c
index 92383f9..aa1ad81 100644
--- a/plugins/imptcp/imptcp.c
+++ b/plugins/imptcp/imptcp.c
@@ -49,6 +49,7 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
+#include <netinet/tcp.h>
#if HAVE_FCNTL_H
#include <fcntl.h>
#endif
@@ -65,6 +66,7 @@
#include "datetime.h"
#include "ruleset.h"
#include "msg.h"
+#include "statsobj.h"
#include "net.h" /* for permittedPeers, may be removed when this is removed */
/* the define is from tcpsrv.h, we need to find a new (but easier!!!) abstraction layer some time ... */
@@ -73,6 +75,7 @@
MODULE_TYPE_INPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("imptcp")
/* static data */
DEF_IMOD_STATIC_DATA
@@ -82,22 +85,54 @@ DEFobjCurrIf(prop)
DEFobjCurrIf(datetime)
DEFobjCurrIf(errmsg)
DEFobjCurrIf(ruleset)
+DEFobjCurrIf(statsobj)
/* forward references */
static void * wrkr(void *myself);
/* config settings */
typedef struct configSettings_s {
+ int bKeepAlive; /* support keep-alive packets */
+ int iKeepAliveIntvl;
+ int iKeepAliveProbes;
+ int iKeepAliveTime;
int bEmitMsgOnClose; /* emit an informational message on close by remote peer */
+ int bSuppOctetFram; /* support octet-counted framing? */
int iAddtlFrameDelim; /* addtl frame delimiter, e.g. for netscreen, default none */
uchar *pszInputName; /* value for inputname property, NULL is OK and handled by core engine */
uchar *lstnIP; /* which IP we should listen on? */
- ruleset_t *pRuleset; /* ruleset to bind listener to (use system default if unspecified) */
+ uchar *pszBindRuleset;
int wrkrMax; /* max number of workers (actually "helper workers") */
} configSettings_t;
-
static configSettings_t cs;
+struct instanceConf_s {
+ int bKeepAlive; /* support keep-alive packets */
+ int iKeepAliveIntvl;
+ int iKeepAliveProbes;
+ int iKeepAliveTime;
+ int bEmitMsgOnClose;
+ int bSuppOctetFram; /* support octet-counted framing? */
+ int iAddtlFrameDelim;
+ uchar *pszBindPort; /* port to bind to */
+ uchar *pszBindAddr; /* IP to bind socket to */
+ uchar *pszBindRuleset; /* name of ruleset to bind to */
+ uchar *pszInputName; /* value for inputname property, NULL is OK and handled by core engine */
+ ruleset_t *pBindRuleset; /* ruleset to bind listener to (use system default if unspecified) */
+ struct instanceConf_s *next;
+};
+
+
+struct modConfData_s {
+ rsconf_t *pConf; /* our overall config object */
+ instanceConf_t *root, *tail;
+ int wrkrMax;
+};
+
+static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
+static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current load process */
+
+#include "im-helper.h" /* must be included AFTER the type definitions! */
/* data elements describing our running config */
typedef struct ptcpsrv_s ptcpsrv_t;
typedef struct ptcplstn_s ptcplstn_t;
@@ -112,27 +147,34 @@ struct ptcpsrv_s {
ptcpsrv_t *pNext; /* linked list maintenance */
uchar *port; /* Port to listen to */
uchar *lstnIP; /* which IP we should listen on? */
- int bEmitMsgOnClose;
int iAddtlFrameDelim;
+ int iKeepAliveIntvl;
+ int iKeepAliveProbes;
+ int iKeepAliveTime;
uchar *pszInputName;
prop_t *pInputName; /* InputName in (fast to process) property format */
ruleset_t *pRuleset;
ptcplstn_t *pLstn; /* root of our listeners */
ptcpsess_t *pSess; /* root of our sessions */
pthread_mutex_t mutSessLst;
+ sbool bKeepAlive; /* support keep-alive packets */
+ sbool bEmitMsgOnClose;
+ sbool bSuppOctetFram;
};
/* the ptcp session object. Describes a single active session.
* includes support for doubly-linked list.
*/
struct ptcpsess_s {
- ptcpsrv_t *pSrv; /* our server */
+// ptcpsrv_t *pSrv; /* our server TODO: check remove! */
+ ptcplstn_t *pLstn; /* our listener */
ptcpsess_t *prev, *next;
int sock;
epolld_t *epd;
//--- from tcps_sess.h
int iMsg; /* index of next char to store in msg */
int bAtStrtOfFram; /* are we at the very beginning of a new frame? */
+ sbool bSuppOctetFram; /**< copy from listener, to speed up access */
enum {
eAtStrtFram,
eInOctetCnt,
@@ -153,7 +195,10 @@ struct ptcplstn_s {
ptcpsrv_t *pSrv; /* our server */
ptcplstn_t *prev, *next;
int sock;
+ sbool bSuppOctetFram;
epolld_t *epd;
+ statsobj_t *stats; /* listener stats */
+ STATSCOUNTER_DEF(ctrSubmit, mutCtrSubmit)
};
@@ -195,7 +240,7 @@ static int iMaxLine; /* maximum size of a single message */
/* forward definitions */
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal);
-static rsRetVal addLstn(ptcpsrv_t *pSrv, int sock);
+static rsRetVal addLstn(ptcpsrv_t *pSrv, int sock, int isIPv6);
/* some simple constructors/destructors */
@@ -217,6 +262,7 @@ destructSrv(ptcpsrv_t *pSrv)
{
prop.Destruct(&pSrv->pInputName);
pthread_mutex_destroy(&pSrv->mutSessLst);
+ free(pSrv->pszInputName);
free(pSrv->port);
free(pSrv);
}
@@ -241,10 +287,11 @@ startupSrv(ptcpsrv_t *pSrv)
int sockflags;
struct addrinfo hints, *res = NULL, *r;
uchar *lstnIP;
+ int isIPv6 = 0;
lstnIP = pSrv->lstnIP == NULL ? UCHAR_CONSTANT("") : pSrv->lstnIP;
- DBGPRINTF("imptcp creating listen socket on server '%s', port %s\n", lstnIP, pSrv->port);
+ DBGPRINTF("imptcp: creating listen socket on server '%s', port %s\n", lstnIP, pSrv->port);
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE;
@@ -273,8 +320,9 @@ startupSrv(ptcpsrv_t *pSrv)
continue;
}
-#ifdef IPV6_V6ONLY
if(r->ai_family == AF_INET6) {
+ isIPv6 = 1;
+#ifdef IPV6_V6ONLY
int iOn = 1;
if(setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
(char *)&iOn, sizeof (iOn)) < 0) {
@@ -282,8 +330,10 @@ startupSrv(ptcpsrv_t *pSrv)
sock = -1;
continue;
}
- }
#endif
+ } else {
+ isIPv6 = 0;
+ }
if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0 ) {
DBGPRINTF("error %d setting tcp socket option\n", errno);
close(sock);
@@ -345,7 +395,7 @@ startupSrv(ptcpsrv_t *pSrv)
/* if we reach this point, we were able to obtain a valid socket, so we can
* create our listener object. -- rgerhards, 2010-08-10
*/
- CHKiRet(addLstn(pSrv, sock));
+ CHKiRet(addLstn(pSrv, sock, isIPv6));
++numSocks;
}
@@ -436,12 +486,80 @@ finalize_it:
}
+/* Enable KEEPALIVE handling on the socket. */
+static inline rsRetVal
+EnableKeepAlive(ptcplstn_t *pLstn, int sock)
+{
+ int ret;
+ int optval;
+ socklen_t optlen;
+ DEFiRet;
+
+ optval = 1;
+ optlen = sizeof(optval);
+ ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen);
+ if(ret < 0) {
+ dbgprintf("EnableKeepAlive socket call returns error %d\n", ret);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+# if defined(TCP_KEEPCNT)
+ if(pLstn->pSrv->iKeepAliveProbes > 0) {
+ optval = pLstn->pSrv->iKeepAliveProbes;
+ optlen = sizeof(optval);
+ ret = setsockopt(sock, SOL_TCP, TCP_KEEPCNT, &optval, optlen);
+ } else {
+ ret = 0;
+ }
+# else
+ ret = -1;
+# endif
+ if(ret < 0) {
+ errmsg.LogError(ret, NO_ERRCODE, "imptcp cannot set keepalive probes - ignored");
+ }
+
+# if defined(TCP_KEEPCNT)
+ if(pLstn->pSrv->iKeepAliveTime > 0) {
+ optval = pLstn->pSrv->iKeepAliveTime;
+ optlen = sizeof(optval);
+ ret = setsockopt(sock, SOL_TCP, TCP_KEEPIDLE, &optval, optlen);
+ } else {
+ ret = 0;
+ }
+# else
+ ret = -1;
+# endif
+ if(ret < 0) {
+ errmsg.LogError(ret, NO_ERRCODE, "imptcp cannot set keepalive time - ignored");
+ }
+
+# if defined(TCP_KEEPCNT)
+ if(pLstn->pSrv->iKeepAliveIntvl > 0) {
+ optval = pLstn->pSrv->iKeepAliveIntvl;
+ optlen = sizeof(optval);
+ ret = setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, &optval, optlen);
+ } else {
+ ret = 0;
+ }
+# else
+ ret = -1;
+# endif
+ if(ret < 0) {
+ errmsg.LogError(errno, NO_ERRCODE, "imptcp cannot set keepalive intvl - ignored");
+ }
+
+ dbgprintf("KEEPALIVE enabled for socket %d\n", sock);
+
+finalize_it:
+ RETiRet;
+}
+
/* accept an incoming connection request
* rgerhards, 2008-04-22
*/
static rsRetVal
-AcceptConnReq(int sock, int *newSock, prop_t **peerName, prop_t **peerIP)
+AcceptConnReq(ptcplstn_t *pLstn, int *newSock, prop_t **peerName, prop_t **peerIP)
{
int sockflags;
struct sockaddr_storage addr;
@@ -450,13 +568,17 @@ AcceptConnReq(int sock, int *newSock, prop_t **peerName, prop_t **peerIP)
DEFiRet;
- iNewSock = accept(sock, (struct sockaddr*) &addr, &addrlen);
+ iNewSock = accept(pLstn->sock, (struct sockaddr*) &addr, &addrlen);
if(iNewSock < 0) {
if(errno == EAGAIN || errno == EWOULDBLOCK)
ABORT_FINALIZE(RS_RET_NO_MORE_DATA);
ABORT_FINALIZE(RS_RET_ACCEPT_ERR);
}
+ if(pLstn->pSrv->bKeepAlive)
+ EnableKeepAlive(pLstn, iNewSock);/* we ignore errors, best to do! */
+
+
CHKiRet(getPeerNames(peerName, peerIP, (struct sockaddr*) &addr));
/* set the new socket to non-blocking IO */
@@ -500,22 +622,25 @@ static rsRetVal
doSubmitMsg(ptcpsess_t *pThis, struct syslogTime *stTime, time_t ttGenTime, multi_submit_t *pMultiSub)
{
msg_t *pMsg;
+ ptcpsrv_t *pSrv;
DEFiRet;
if(pThis->iMsg == 0) {
DBGPRINTF("discarding zero-sized message\n");
FINALIZE;
}
+ pSrv = pThis->pLstn->pSrv;
/* we now create our own message object and submit it to the queue */
CHKiRet(msgConstructWithTime(&pMsg, stTime, ttGenTime));
MsgSetRawMsg(pMsg, (char*)pThis->pMsg, pThis->iMsg);
- MsgSetInputName(pMsg, pThis->pSrv->pInputName);
+ MsgSetInputName(pMsg, pSrv->pInputName);
MsgSetFlowControlType(pMsg, eFLOWCTL_LIGHT_DELAY);
pMsg->msgFlags = NEEDS_PARSING | PARSE_HOSTNAME;
MsgSetRcvFrom(pMsg, pThis->peerName);
CHKiRet(MsgSetRcvFromIP(pMsg, pThis->peerIP));
- MsgSetRuleset(pMsg, pThis->pSrv->pRuleset);
+ MsgSetRuleset(pMsg, pSrv->pRuleset);
+ STATSCOUNTER_INC(pThis->pLstn->ctrSubmit, pThis->pLstn->mutCtrSubmit);
if(pMultiSub == NULL) {
CHKiRet(submitMsg(pMsg));
@@ -548,7 +673,7 @@ processDataRcvd(ptcpsess_t *pThis, char c, struct syslogTime *stTime, time_t ttG
DEFiRet;
if(pThis->inputState == eAtStrtFram) {
- if(isdigit((int) c)) {
+ if(pThis->bSuppOctetFram && isdigit((int) c)) {
pThis->inputState = eInOctetCnt;
pThis->iOctetsRemain = 0;
pThis->eFraming = TCP_FRAMING_OCTET_COUNTING;
@@ -597,7 +722,8 @@ processDataRcvd(ptcpsess_t *pThis, char c, struct syslogTime *stTime, time_t ttG
}
if(( (c == '\n')
- || ((pThis->pSrv->iAddtlFrameDelim != TCPSRV_NO_ADDTL_DELIMITER) && (c == pThis->pSrv->iAddtlFrameDelim))
+ || ((pThis->pLstn->pSrv->iAddtlFrameDelim != TCPSRV_NO_ADDTL_DELIMITER)
+ && (c == pThis->pLstn->pSrv->iAddtlFrameDelim))
) && pThis->eFraming == TCP_FRAMING_OCTET_STUFFING) { /* record delimiter? */
doSubmitMsg(pThis, stTime, ttGenTime, pMultiSub);
pThis->inputState = eAtStrtFram;
@@ -687,9 +813,11 @@ initConfigSettings(void)
{
cs.bEmitMsgOnClose = 0;
cs.wrkrMax = 2;
+ cs.bSuppOctetFram = 1;
cs.iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER;
cs.pszInputName = NULL;
- cs.pRuleset = NULL;
+ cs.pszBindRuleset = NULL;
+ cs.pszInputName = NULL;
cs.lstnIP = NULL;
}
@@ -702,7 +830,7 @@ addEPollSock(epolld_type_t typ, void *ptr, int sock, epolld_t **pEpd)
DEFiRet;
epolld_t *epd = NULL;
- CHKmalloc(epd = malloc(sizeof(epolld_t)));
+ CHKmalloc(epd = calloc(sizeof(epolld_t), 1));
epd->typ = typ;
epd->ptr = ptr;
*pEpd = epd;
@@ -756,14 +884,27 @@ finalize_it:
/* add a listener to the server
*/
static rsRetVal
-addLstn(ptcpsrv_t *pSrv, int sock)
+addLstn(ptcpsrv_t *pSrv, int sock, int isIPv6)
{
DEFiRet;
ptcplstn_t *pLstn;
+ uchar statname[64];
CHKmalloc(pLstn = malloc(sizeof(ptcplstn_t)));
pLstn->pSrv = pSrv;
+ pLstn->bSuppOctetFram = pSrv->bSuppOctetFram;
pLstn->sock = sock;
+ /* support statistics gathering */
+ CHKiRet(statsobj.Construct(&(pLstn->stats)));
+ snprintf((char*)statname, sizeof(statname), "imptcp(%s/%s/%s)",
+ (pSrv->lstnIP == NULL) ? "*" : (char*)pSrv->lstnIP, pSrv->port,
+ isIPv6 ? "IPv6" : "IPv4");
+ statname[sizeof(statname)-1] = '\0'; /* just to be on the save side... */
+ CHKiRet(statsobj.SetName(pLstn->stats, statname));
+ STATSCOUNTER_INIT(pLstn->ctrSubmit, pLstn->mutCtrSubmit);
+ CHKiRet(statsobj.AddCounter(pLstn->stats, UCHAR_CONSTANT("submitted"),
+ ctrType_IntCtr, &(pLstn->ctrSubmit)));
+ CHKiRet(statsobj.ConstructFinalize(pLstn->stats));
/* add to start of server's listener list */
pLstn->prev = NULL;
@@ -782,15 +923,17 @@ finalize_it:
/* add a session to the server
*/
static rsRetVal
-addSess(ptcpsrv_t *pSrv, int sock, prop_t *peerName, prop_t *peerIP)
+addSess(ptcplstn_t *pLstn, int sock, prop_t *peerName, prop_t *peerIP)
{
DEFiRet;
ptcpsess_t *pSess = NULL;
+ ptcpsrv_t *pSrv = pLstn->pSrv;
CHKmalloc(pSess = malloc(sizeof(ptcpsess_t)));
CHKmalloc(pSess->pMsg = malloc(iMaxLine * sizeof(uchar)));
- pSess->pSrv = pSrv;
+ pSess->pLstn = pLstn;
pSess->sock = sock;
+ pSess->bSuppOctetFram = pLstn->bSuppOctetFram;
pSess->inputState = eAtStrtFram;
pSess->iMsg = 0;
pSess->bAtStrtOfFram = 1;
@@ -827,17 +970,17 @@ closeSess(ptcpsess_t *pSess)
CHKiRet(removeEPollSock(sock, pSess->epd));
close(sock);
- pthread_mutex_lock(&pSess->pSrv->mutSessLst);
+ pthread_mutex_lock(&pSess->pLstn->pSrv->mutSessLst);
/* finally unlink session from structures */
if(pSess->next != NULL)
pSess->next->prev = pSess->prev;
if(pSess->prev == NULL) {
/* need to update root! */
- pSess->pSrv->pSess = pSess->next;
+ pSess->pLstn->pSrv->pSess = pSess->next;
} else {
pSess->prev->next = pSess->next;
}
- pthread_mutex_unlock(&pSess->pSrv->mutSessLst);
+ pthread_mutex_unlock(&pSess->pLstn->pSrv->mutSessLst);
/* unlinked, now remove structure */
destructSess(pSess);
@@ -848,47 +991,91 @@ finalize_it:
}
-/* accept a new ruleset to bind. Checks if it exists and complains, if not */
-static rsRetVal setRuleset(void __attribute__((unused)) *pVal, uchar *pszName)
+/* This function is called when a new listener instace shall be added to
+ * the current config object via the legacy config system. It just shuffles
+ * all parameters to the listener in-memory instance.
+ */
+static rsRetVal addInstance(void __attribute__((unused)) *pVal, uchar *pNewVal)
{
- ruleset_t *pRuleset;
- rsRetVal localRet;
+ instanceConf_t *inst;
DEFiRet;
- localRet = ruleset.GetRuleset(&pRuleset, pszName);
- if(localRet == RS_RET_NOT_FOUND) {
- errmsg.LogError(0, NO_ERRCODE, "error: ruleset '%s' not found - ignored", pszName);
+ CHKmalloc(inst = MALLOC(sizeof(instanceConf_t)));
+ if(pNewVal == NULL || *pNewVal == '\0') {
+ errmsg.LogError(0, NO_ERRCODE, "imptcp: port number must be specified, listener ignored");
+ }
+ if((pNewVal == NULL) || (pNewVal == '\0')) {
+ inst->pszBindPort = NULL;
+ } else {
+ CHKmalloc(inst->pszBindPort = ustrdup(pNewVal));
+ }
+ if((cs.lstnIP == NULL) || (cs.lstnIP[0] == '\0')) {
+ inst->pszBindAddr = NULL;
+ } else {
+ CHKmalloc(inst->pszBindAddr = ustrdup(cs.lstnIP));
+ }
+ if((cs.pszBindRuleset == NULL) || (cs.pszBindRuleset[0] == '\0')) {
+ inst->pszBindRuleset = NULL;
+ } else {
+ CHKmalloc(inst->pszBindRuleset = ustrdup(cs.pszBindRuleset));
+ }
+ if((cs.pszInputName == NULL) || (cs.pszInputName[0] == '\0')) {
+ inst->pszInputName = NULL;
+ } else {
+ CHKmalloc(inst->pszInputName = ustrdup(cs.pszInputName));
+ }
+ inst->pBindRuleset = NULL;
+ inst->bSuppOctetFram = cs.bSuppOctetFram;
+ inst->bKeepAlive = cs.bKeepAlive;
+ inst->iKeepAliveIntvl = cs.iKeepAliveTime;
+ inst->iKeepAliveProbes = cs.iKeepAliveProbes;
+ inst->iKeepAliveTime = cs.iKeepAliveTime;
+ inst->bEmitMsgOnClose = cs.bEmitMsgOnClose;
+ inst->iAddtlFrameDelim = cs.iAddtlFrameDelim;
+ inst->next = NULL;
+
+ /* node created, let's add to config */
+ if(loadModConf->tail == NULL) {
+ loadModConf->tail = loadModConf->root = inst;
+ } else {
+ loadModConf->tail->next = inst;
+ loadModConf->tail = inst;
}
- CHKiRet(localRet);
- cs.pRuleset = pRuleset;
- DBGPRINTF("imptcp current bind ruleset %p: '%s'\n", pRuleset, pszName);
finalize_it:
- free(pszName); /* no longer needed */
+ free(pNewVal);
RETiRet;
}
-static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVal)
+static inline rsRetVal
+addListner(modConfData_t __attribute__((unused)) *modConf, instanceConf_t *inst)
{
DEFiRet;
ptcpsrv_t *pSrv;
- CHKmalloc(pSrv = malloc(sizeof(ptcpsrv_t)));
+ CHKmalloc(pSrv = MALLOC(sizeof(ptcpsrv_t)));
pthread_mutex_init(&pSrv->mutSessLst, NULL);
pSrv->pSess = NULL;
pSrv->pLstn = NULL;
- pSrv->bEmitMsgOnClose = cs.bEmitMsgOnClose;
- pSrv->port = pNewVal;
- pSrv->iAddtlFrameDelim = cs.iAddtlFrameDelim;
- pSrv->lstnIP = cs.lstnIP;
- pSrv->pRuleset = cs.pRuleset;
- pSrv->pszInputName = (cs.pszInputName == NULL) ? UCHAR_CONSTANT("imptcp") : cs.pszInputName;
+ pSrv->bSuppOctetFram = inst->bSuppOctetFram;
+ pSrv->bKeepAlive = inst->bKeepAlive;
+ pSrv->iKeepAliveIntvl = inst->iKeepAliveTime;
+ pSrv->iKeepAliveProbes = inst->iKeepAliveProbes;
+ pSrv->iKeepAliveTime = inst->iKeepAliveTime;
+ pSrv->bEmitMsgOnClose = inst->bEmitMsgOnClose;
+ CHKmalloc(pSrv->port = ustrdup(inst->pszBindPort));
+ pSrv->iAddtlFrameDelim = inst->iAddtlFrameDelim;
+ if(inst->pszBindAddr == NULL)
+ pSrv->lstnIP = NULL;
+ else {
+ CHKmalloc(pSrv->lstnIP = ustrdup(inst->pszBindAddr));
+ }
+ pSrv->pRuleset = inst->pBindRuleset;
+ pSrv->pszInputName = ustrdup((inst->pszInputName == NULL) ? UCHAR_CONSTANT("imptcp") : inst->pszInputName);
CHKiRet(prop.Construct(&pSrv->pInputName));
CHKiRet(prop.SetString(pSrv->pInputName, pSrv->pszInputName, ustrlen(pSrv->pszInputName)));
CHKiRet(prop.ConstructFinalize(pSrv->pInputName));
- cs.pszInputName = NULL; /* moved over to pSrv, we do not own */
- cs.lstnIP = NULL; /* moved over to pSrv, we do not own */
/* add to linked list */
pSrv->pNext = pSrvRoot;
@@ -914,11 +1101,11 @@ startWorkerPool(void)
{
int i;
wrkrRunning = 0;
- if(cs.wrkrMax > 16)
- cs.wrkrMax = 16; /* TODO: make dynamic? */
+ if(runModConf->wrkrMax > 16)
+ runModConf->wrkrMax = 16; /* TODO: make dynamic? */
pthread_mutex_init(&wrkrMut, NULL);
pthread_cond_init(&wrkrIdle, NULL);
- for(i = 0 ; i < cs.wrkrMax ; ++i) {
+ for(i = 0 ; i < runModConf->wrkrMax ; ++i) {
/* init worker info structure! */
pthread_cond_init(&wrkrInfo[i].run, NULL);
wrkrInfo[i].event = NULL;
@@ -934,7 +1121,7 @@ static inline void
stopWorkerPool(void)
{
int i;
- for(i = 0 ; i < cs.wrkrMax ; ++i) {
+ for(i = 0 ; i < runModConf->wrkrMax ; ++i) {
pthread_cond_signal(&wrkrInfo[i].run); /* awake wrkr if not running */
pthread_join(wrkrInfo[i].tid, NULL);
DBGPRINTF("imptcp: info: worker %d was called %llu times\n", i, wrkrInfo[i].numCalled);
@@ -954,15 +1141,29 @@ static inline rsRetVal
startupServers()
{
DEFiRet;
+ rsRetVal localRet, lastErr;
+ int iOK;
+ int iAll;
ptcpsrv_t *pSrv;
+ iAll = iOK = 0;
+ lastErr = RS_RET_ERR;
pSrv = pSrvRoot;
while(pSrv != NULL) {
DBGPRINTF("imptcp: starting up server for port %s, name '%s'\n", pSrv->port, pSrv->pszInputName);
- startupSrv(pSrv);
+ localRet = startupSrv(pSrv);
+ if(localRet == RS_RET_OK)
+ iOK++;
+ else
+ lastErr = localRet;
+ ++iAll;
pSrv = pSrv->pNext;
}
+ DBGPRINTF("imptcp: %d out of %d servers started successfully\n", iOK, iAll);
+ if(iOK == 0) /* iff all fails, we report an error */
+ iRet = lastErr;
+
RETiRet;
}
@@ -981,11 +1182,11 @@ lstnActivity(ptcplstn_t *pLstn)
DBGPRINTF("imptcp: new connection on listen socket %d\n", pLstn->sock);
while(glbl.GetGlobalInputTermState() == 0) {
- localRet = AcceptConnReq(pLstn->sock, &newSock, &peerName, &peerIP);
+ localRet = AcceptConnReq(pLstn, &newSock, &peerName, &peerIP);
if(localRet == RS_RET_NO_MORE_DATA || glbl.GetGlobalInputTermState() == 1)
break;
CHKiRet(localRet);
- CHKiRet(addSess(pLstn->pSrv, newSock, peerName, peerIP));
+ CHKiRet(addSess(pLstn, newSock, peerName, peerIP));
}
finalize_it:
@@ -1016,7 +1217,7 @@ sessActivity(ptcpsess_t *pSess)
CHKiRet(DataRcvd(pSess, rcvBuf, lenRcv));
} else if (lenRcv == 0) {
/* session was closed, do clean-up */
- if(pSess->pSrv->bEmitMsgOnClose) {
+ if(pSess->pLstn->pSrv->bEmitMsgOnClose) {
uchar *peerName;
int lenPeer;
prop.GetString(pSess->peerName, &peerName, &lenPeer);
@@ -1082,9 +1283,9 @@ processWorkSet(int nEvents, struct epoll_event events[])
} else {
pthread_mutex_lock(&wrkrMut);
/* check if there is a free worker */
- for(i = 0 ; (i < cs.wrkrMax) && (wrkrInfo[i].event != NULL) ; ++i)
+ for(i = 0 ; (i < runModConf->wrkrMax) && (wrkrInfo[i].event != NULL) ; ++i)
/*do search*/;
- if(i < cs.wrkrMax) {
+ if(i < runModConf->wrkrMax) {
/* worker free -> use it! */
wrkrInfo[i].event = events+iEvt;
++wrkrRunning;
@@ -1144,33 +1345,57 @@ wrkr(void *myself)
}
-/* This function is called to gather input.
- */
-BEGINrunInput
- int nEvents;
- struct epoll_event events[128];
-CODESTARTrunInput
- startWorkerPool();
- DBGPRINTF("imptcp: now beginning to process input data\n");
- while(glbl.GetGlobalInputTermState() == 0) {
- DBGPRINTF("imptcp going on epoll_wait\n");
- nEvents = epoll_wait(epollfd, events, sizeof(events)/sizeof(struct epoll_event), -1);
- DBGPRINTF("imptcp: epoll returned %d events\n", nEvents);
- processWorkSet(nEvents, events);
+BEGINbeginCnfLoad
+CODESTARTbeginCnfLoad
+ loadModConf = pModConf;
+ pModConf->pConf = pConf;
+ /* init legacy config vars */
+ initConfigSettings();
+ENDbeginCnfLoad
+
+
+BEGINendCnfLoad
+CODESTARTendCnfLoad
+ /* persist module-specific settings from legacy config system */
+ loadModConf->wrkrMax = cs.wrkrMax;
+
+ loadModConf = NULL; /* done loading */
+ /* free legacy config vars */
+ free(cs.pszInputName);
+ free(cs.lstnIP);
+ cs.pszInputName = NULL;
+ cs.lstnIP = NULL;
+ENDendCnfLoad
+
+
+/* function to generate error message if framework does not find requested ruleset */
+static inline void
+std_checkRuleset_genErrMsg(__attribute__((unused)) modConfData_t *modConf, instanceConf_t *inst)
+{
+ errmsg.LogError(0, NO_ERRCODE, "imptcp: ruleset '%s' for port %s not found - "
+ "using default ruleset instead", inst->pszBindRuleset,
+ inst->pszBindPort);
+}
+BEGINcheckCnf
+ instanceConf_t *inst;
+CODESTARTcheckCnf
+ for(inst = pModConf->root ; inst != NULL ; inst = inst->next) {
+ std_checkRuleset(pModConf, inst);
}
- DBGPRINTF("imptcp: successfully terminated\n");
- /* we stop the worker pool in AfterRun, in case we get cancelled for some reason (old Interface) */
-ENDrunInput
+ENDcheckCnf
-/* initialize and return if will run or not */
-BEGINwillRun
-CODESTARTwillRun
- /* first apply some config settings */
+BEGINactivateCnfPrePrivDrop
+ instanceConf_t *inst;
+CODESTARTactivateCnfPrePrivDrop
iMaxLine = glbl.GetMaxLine(); /* get maximum size we currently support */
+ runModConf = pModConf;
+ for(inst = runModConf->root ; inst != NULL ; inst = inst->next) {
+ addListner(pModConf, inst);
+ }
if(pSrvRoot == NULL) {
- errmsg.LogError(0, RS_RET_NO_LSTN_DEFINED, "error: no ptcp server defined, module can not run.");
+ errmsg.LogError(0, RS_RET_NO_LSTN_DEFINED, "imptcp: no ptcp server defined, module can not run.");
ABORT_FINALIZE(RS_RET_NO_RUN);
}
@@ -1197,6 +1422,52 @@ CODESTARTwillRun
CHKiRet(startupServers());
DBGPRINTF("imptcp started up, but not yet receiving data\n");
finalize_it:
+ENDactivateCnfPrePrivDrop
+
+
+BEGINactivateCnf
+CODESTARTactivateCnf
+ /* nothing to do, all done pre priv drop */
+ENDactivateCnf
+
+
+BEGINfreeCnf
+ instanceConf_t *inst, *del;
+CODESTARTfreeCnf
+ for(inst = pModConf->root ; inst != NULL ; ) {
+ free(inst->pszBindPort);
+ free(inst->pszBindAddr);
+ free(inst->pszBindRuleset);
+ free(inst->pszInputName);
+ del = inst;
+ inst = inst->next;
+ free(del);
+ }
+ENDfreeCnf
+
+
+/* This function is called to gather input.
+ */
+BEGINrunInput
+ int nEvents;
+ struct epoll_event events[128];
+CODESTARTrunInput
+ startWorkerPool();
+ DBGPRINTF("imptcp: now beginning to process input data\n");
+ while(glbl.GetGlobalInputTermState() == 0) {
+ DBGPRINTF("imptcp going on epoll_wait\n");
+ nEvents = epoll_wait(epollfd, events, sizeof(events)/sizeof(struct epoll_event), -1);
+ DBGPRINTF("imptcp: epoll returned %d events\n", nEvents);
+ processWorkSet(nEvents, events);
+ }
+ DBGPRINTF("imptcp: successfully terminated\n");
+ /* we stop the worker pool in AfterRun, in case we get cancelled for some reason (old Interface) */
+ENDrunInput
+
+
+/* initialize and return if will run or not */
+BEGINwillRun
+CODESTARTwillRun
ENDwillRun
@@ -1213,6 +1484,8 @@ shutdownSrv(ptcpsrv_t *pSrv)
pLstn = pSrv->pLstn;
while(pLstn != NULL) {
close(pLstn->sock);
+ statsobj.Destruct(&(pLstn->stats));
+ /* now unlink listner */
lstnDel = pLstn;
pLstn = pLstn->next;
DBGPRINTF("imptcp shutdown listen socket %d\n", lstnDel->sock);
@@ -1255,6 +1528,7 @@ CODESTARTmodExit
pthread_attr_destroy(&wrkrThrdAttr);
/* release objects we used */
objRelease(glbl, CORE_COMPONENT);
+ objRelease(statsobj, CORE_COMPONENT);
objRelease(prop, CORE_COMPONENT);
objRelease(net, LM_NET_FILENAME);
objRelease(datetime, CORE_COMPONENT);
@@ -1268,6 +1542,11 @@ resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unus
{
cs.bEmitMsgOnClose = 0;
cs.wrkrMax = 2;
+ cs.bKeepAlive = 0;
+ cs.iKeepAliveProbes = 0;
+ cs.iKeepAliveTime = 0;
+ cs.iKeepAliveIntvl = 0;
+ cs.bSuppOctetFram = 1;
cs.iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER;
free(cs.pszInputName);
cs.pszInputName = NULL;
@@ -1287,6 +1566,8 @@ ENDisCompatibleWithFeature
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
+CODEqueryEtryPt_STD_CONF2_QUERIES
+CODEqueryEtryPt_STD_CONF2_PREPRIVDROP_QUERIES
CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
ENDqueryEtryPt
@@ -1295,9 +1576,9 @@ BEGINmodInit()
CODESTARTmodInit
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
- initConfigSettings();
/* request objects we use */
CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(statsobj, CORE_COMPONENT));
CHKiRet(objUse(prop, CORE_COMPONENT));
CHKiRet(objUse(net, LM_NET_FILENAME));
CHKiRet(objUse(errmsg, CORE_COMPONENT));
@@ -1306,11 +1587,24 @@ CODEmodInit_QueryRegCFSLineHdlr
/* initialize "read-only" thread attributes */
pthread_attr_init(&wrkrThrdAttr);
- pthread_attr_setstacksize(&wrkrThrdAttr, 2048*1024);
+ pthread_attr_setstacksize(&wrkrThrdAttr, 4096*1024);
+
+ /* init legacy config settings */
+ initConfigSettings();
/* register config file handlers */
CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverrun"), 0, eCmdHdlrGetWord,
- addTCPListener, NULL, STD_LOADABLE_MODULE_ID));
+ addInstance, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverkeepalive"), 0, eCmdHdlrBinary,
+ NULL, &cs.bKeepAlive, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverkeepalive_probes"), 0, eCmdHdlrInt,
+ NULL, &cs.iKeepAliveProbes, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverkeepalive_time"), 0, eCmdHdlrInt,
+ NULL, &cs.iKeepAliveTime, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverkeepalive_intvl"), 0, eCmdHdlrInt,
+ NULL, &cs.iKeepAliveIntvl, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserversupportoctetcountedframing"), 0, eCmdHdlrBinary,
+ NULL, &cs.bSuppOctetFram, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpservernotifyonconnectionclose"), 0,
eCmdHdlrBinary, NULL, &cs.bEmitMsgOnClose, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserveraddtlframedelimiter"), 0, eCmdHdlrInt,
@@ -1322,7 +1616,7 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverlistenip"), 0,
eCmdHdlrGetWord, NULL, &cs.lstnIP, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverbindruleset"), 0,
- eCmdHdlrGetWord, setRuleset, NULL, STD_LOADABLE_MODULE_ID));
+ eCmdHdlrGetWord, NULL, &cs.pszBindRuleset, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("resetconfigvariables"), 1, eCmdHdlrCustomHandler,
resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
diff --git a/plugins/imrelp/imrelp.c b/plugins/imrelp/imrelp.c
index 602809f..99fabd1 100644
--- a/plugins/imrelp/imrelp.c
+++ b/plugins/imrelp/imrelp.c
@@ -38,39 +38,56 @@
#include <librelp.h>
#include "rsyslog.h"
#include "dirty.h"
+#include "errmsg.h"
#include "cfsysline.h"
#include "module-template.h"
#include "net.h"
#include "msg.h"
#include "unicode-helper.h"
#include "prop.h"
+#include "ruleset.h"
MODULE_TYPE_INPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("imrelp")
/* static data */
DEF_IMOD_STATIC_DATA
DEFobjCurrIf(net)
DEFobjCurrIf(prop)
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(ruleset)
+
+/* forward definitions */
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal);
+
/* Module static data */
+/* config vars for legacy config system */
static relpEngine_t *pRelpEngine; /* our relp engine */
static prop_t *pInputName = NULL; /* there is only one global inputName for all messages generated by this module */
+static struct configSettings_s {
+ uchar *pszBindRuleset; /* name of Ruleset to bind to */
+} cs;
+struct instanceConf_s {
+ uchar *pszBindPort; /* port to bind to */
+ struct instanceConf_s *next;
+};
-/* config settings */
-/* ------------------------------ callbacks ------------------------------ */
-#if 0
-/* this shall go into a specific ACL module! */
-static int
-isPermittedHost(struct sockaddr *addr, char *fromHostFQDN, void __attribute__((unused)) *pUsrSrv,
- void __attribute__((unused)) *pUsrSess)
-{
- return net.isAllowedSender(net.pAllowedSenders_TCP, addr, fromHostFQDN);
-}
+struct modConfData_s {
+ rsconf_t *pConf; /* our overall config object */
+ instanceConf_t *root, *tail;
+ uchar *pszBindRuleset; /* name of Ruleset to bind to */
+ ruleset_t *pBindRuleset; /* due to librelp limitation, we need to bind all listerns to the same set */
+};
-#endif // #if 0
+static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
+static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current load process */
+
+
+/* ------------------------------ callbacks ------------------------------ */
/* callback for receiving syslog messages. This function is invoked from the
* RELP engine when a syslog message arrived. It must return a relpRetVal,
@@ -87,7 +104,7 @@ onSyslogRcv(uchar *pHostname, uchar *pIP, uchar *pMsg, size_t lenMsg)
{
DEFiRet;
parseAndSubmitMessage(pHostname, pIP, pMsg, lenMsg, PARSE_HOSTNAME,
- eFLOWCTL_LIGHT_DELAY, pInputName, NULL, 0);
+ eFLOWCTL_LIGHT_DELAY, pInputName, NULL, 0, runModConf->pBindRuleset);
RETiRet;
}
@@ -96,7 +113,48 @@ onSyslogRcv(uchar *pHostname, uchar *pIP, uchar *pMsg, size_t lenMsg)
/* ------------------------------ end callbacks ------------------------------ */
-static rsRetVal addListener(void __attribute__((unused)) *pVal, uchar *pNewVal)
+/* modified to work for module, not instance (as usual) */
+static inline void
+std_checkRuleset_genErrMsg(modConfData_t *modConf, __attribute__((unused)) instanceConf_t *inst)
+{
+ errmsg.LogError(0, NO_ERRCODE, "imrelp: ruleset '%s' not found - "
+ "using default ruleset instead", modConf->pszBindRuleset);
+}
+
+
+/* This function is called when a new listener instace shall be added to
+ * the current config object via the legacy config system. It just shuffles
+ * all parameters to the listener in-memory instance.
+ * rgerhards, 2011-05-04
+ */
+static rsRetVal addInstance(void __attribute__((unused)) *pVal, uchar *pNewVal)
+{
+ instanceConf_t *inst;
+ DEFiRet;
+
+ CHKmalloc(inst = MALLOC(sizeof(instanceConf_t)));
+
+ if(pNewVal == NULL || *pNewVal == '\0') {
+ errmsg.LogError(0, NO_ERRCODE, "imrelp: port number must be specified, listener ignored");
+ }
+ inst->pszBindPort = pNewVal;
+ inst->next = NULL;
+
+ /* node created, let's add to config */
+ if(loadModConf->tail == NULL) {
+ loadModConf->tail = loadModConf->root = inst;
+ } else {
+ loadModConf->tail->next = inst;
+ loadModConf->tail = inst;
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+static rsRetVal
+addListner(modConfData_t __attribute__((unused)) *modConf, instanceConf_t *inst)
{
DEFiRet;
if(pRelpEngine == NULL) {
@@ -106,14 +164,78 @@ static rsRetVal addListener(void __attribute__((unused)) *pVal, uchar *pNewVal)
CHKiRet(relpEngineSetSyslogRcv(pRelpEngine, onSyslogRcv));
}
- CHKiRet(relpEngineAddListner(pRelpEngine, pNewVal));
-
- free(pNewVal); /* we do no longer need it */
+ CHKiRet(relpEngineAddListner(pRelpEngine, inst->pszBindPort));
finalize_it:
RETiRet;
}
+
+BEGINbeginCnfLoad
+CODESTARTbeginCnfLoad
+ loadModConf = pModConf;
+ pModConf->pConf = pConf;
+ /* init legacy config variables */
+ cs.pszBindRuleset = NULL;
+ENDbeginCnfLoad
+
+
+BEGINendCnfLoad
+CODESTARTendCnfLoad
+ if((cs.pszBindRuleset == NULL) || (cs.pszBindRuleset[0] == '\0')) {
+ loadModConf->pszBindRuleset = NULL;
+ } else {
+ CHKmalloc(loadModConf->pszBindRuleset = ustrdup(cs.pszBindRuleset));
+ }
+ loadModConf->pBindRuleset = NULL;
+finalize_it:
+ free(cs.pszBindRuleset);
+ loadModConf = NULL; /* done loading */
+ENDendCnfLoad
+
+
+BEGINcheckCnf
+ rsRetVal localRet;
+ ruleset_t *pRuleset;
+CODESTARTcheckCnf
+ /* we emulate the standard "ruleset query" code provided by the framework
+ * for *instances* (which we can currently not support due to librelp).
+ */
+ if(pModConf->pszBindRuleset == NULL) {
+ pModConf->pBindRuleset = NULL;
+ } else {
+ localRet = ruleset.GetRuleset(pModConf->pConf, &pRuleset, pModConf->pszBindRuleset);
+ if(localRet == RS_RET_NOT_FOUND) {
+ std_checkRuleset_genErrMsg(pModConf, NULL);
+ }
+ CHKiRet(localRet);
+ pModConf->pBindRuleset = pRuleset;
+ }
+finalize_it:
+ENDcheckCnf
+
+
+BEGINactivateCnfPrePrivDrop
+ instanceConf_t *inst;
+CODESTARTactivateCnfPrePrivDrop
+ runModConf = pModConf;
+ for(inst = runModConf->root ; inst != NULL ; inst = inst->next) {
+ addListner(pModConf, inst);
+ }
+ if(pRelpEngine == NULL)
+ ABORT_FINALIZE(RS_RET_NO_RUN);
+finalize_it:
+ENDactivateCnfPrePrivDrop
+
+BEGINactivateCnf
+CODESTARTactivateCnf
+ENDactivateCnf
+
+
+BEGINfreeCnf
+CODESTARTfreeCnf
+ENDfreeCnf
+
/* This function is called to gather input.
*/
BEGINrunInput
@@ -125,34 +247,14 @@ CODESTARTrunInput
ENDrunInput
-/* initialize and return if will run or not */
BEGINwillRun
CODESTARTwillRun
- /* first apply some config settings */
- //net.PrintAllowedSenders(2); /* TCP */
- if(pRelpEngine == NULL)
- ABORT_FINALIZE(RS_RET_NO_RUN);
-
- /* we need to create the inputName property (only once during our lifetime) */
- CHKiRet(prop.Construct(&pInputName));
- CHKiRet(prop.SetString(pInputName, UCHAR_CONSTANT("imrelp"), sizeof("imrelp") - 1));
- CHKiRet(prop.ConstructFinalize(pInputName));
-finalize_it:
ENDwillRun
BEGINafterRun
CODESTARTafterRun
/* do cleanup here */
-#if 0
- if(net.pAllowedSenders_TCP != NULL) {
- net.clearAllowedSenders(net.pAllowedSenders_TCP);
- net.pAllowedSenders_TCP = NULL;
- }
-#endif
-
- if(pInputName != NULL)
- prop.Destruct(&pInputName);
ENDafterRun
@@ -161,15 +263,23 @@ CODESTARTmodExit
if(pRelpEngine != NULL)
iRet = relpEngineDestruct(&pRelpEngine);
+ /* global variable cleanup */
+ if(pInputName != NULL)
+ prop.Destruct(&pInputName);
+
/* release objects we used */
+ objRelease(ruleset, CORE_COMPONENT);
objRelease(prop, CORE_COMPONENT);
objRelease(net, LM_NET_FILENAME);
+ objRelease(errmsg, CORE_COMPONENT);
ENDmodExit
static rsRetVal
resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
+ free(cs.pszBindRuleset);
+ cs.pszBindRuleset = NULL;
return RS_RET_OK;
}
@@ -178,6 +288,8 @@ resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unus
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
+CODEqueryEtryPt_STD_CONF2_QUERIES
+CODEqueryEtryPt_STD_CONF2_PREPRIVDROP_QUERIES
ENDqueryEtryPt
@@ -188,13 +300,22 @@ CODEmodInit_QueryRegCFSLineHdlr
pRelpEngine = NULL;
/* request objects we use */
CHKiRet(objUse(prop, CORE_COMPONENT));
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
CHKiRet(objUse(net, LM_NET_FILENAME));
+ CHKiRet(objUse(ruleset, CORE_COMPONENT));
/* register config file handlers */
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputrelpserverbindruleset", 0, eCmdHdlrGetWord,
+ NULL, &cs.pszBindRuleset, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputrelpserverrun", 0, eCmdHdlrGetWord,
- addListener, NULL, STD_LOADABLE_MODULE_ID));
+ addInstance, NULL, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+
+ /* we need to create the inputName property (only once during our lifetime) */
+ CHKiRet(prop.Construct(&pInputName));
+ CHKiRet(prop.SetString(pInputName, UCHAR_CONSTANT("imrelp"), sizeof("imrelp") - 1));
+ CHKiRet(prop.ConstructFinalize(pInputName));
ENDmodInit
diff --git a/plugins/imsolaris/imsolaris.c b/plugins/imsolaris/imsolaris.c
index ee9ec5c..8b607a8 100644
--- a/plugins/imsolaris/imsolaris.c
+++ b/plugins/imsolaris/imsolaris.c
@@ -86,6 +86,7 @@
MODULE_TYPE_INPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("imsolaris")
/* defines */
#define PATH_LOG "/dev/log"
@@ -99,6 +100,10 @@ DEFobjCurrIf(prop)
/* config settings */
+struct modConfData_s {
+ EMPTY_STRUCT;
+};
+
static prop_t *pInputName = NULL; /* our inputName currently is always "imuxsock", and this will hold it */
static char *LogName = NULL; /* the log socket name TODO: make configurable! */
@@ -302,6 +307,31 @@ finalize_it:
}
+BEGINbeginCnfLoad
+CODESTARTbeginCnfLoad
+ENDbeginCnfLoad
+
+
+BEGINendCnfLoad
+CODESTARTendCnfLoad
+ENDendCnfLoad
+
+
+BEGINcheckCnf
+CODESTARTcheckCnf
+ENDcheckCnf
+
+
+BEGINactivateCnf
+CODESTARTactivateCnf
+ENDactivateCnf
+
+
+BEGINfreeCnf
+CODESTARTfreeCnf
+ENDfreeCnf
+
+
/* This function is called to gather input. */
BEGINrunInput
CODESTARTrunInput
diff --git a/plugins/imtcp/imtcp.c b/plugins/imtcp/imtcp.c
index b537cbb..33404fe 100644
--- a/plugins/imtcp/imtcp.c
+++ b/plugins/imtcp/imtcp.c
@@ -66,6 +66,7 @@
MODULE_TYPE_INPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("imtcp")
/* static data */
DEF_IMOD_STATIC_DATA
@@ -76,23 +77,58 @@ DEFobjCurrIf(netstrm)
DEFobjCurrIf(errmsg)
DEFobjCurrIf(ruleset)
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal);
+
/* Module static data */
static tcpsrv_t *pOurTcpsrv = NULL; /* our TCP server(listener) TODO: change for multiple instances */
static permittedPeers_t *pPermPeersRoot = NULL;
/* config settings */
-static int iTCPSessMax = 200; /* max number of sessions */
-static int iTCPLstnMax = 20; /* max number of sessions */
-static int iStrmDrvrMode = 0; /* mode for stream driver, driver-dependent (0 mostly means plain tcp) */
-static int bEmitMsgOnClose = 0; /* emit an informational message on close by remote peer */
-static int iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER; /* addtl frame delimiter, e.g. for netscreen, default none */
-static int bDisableLFDelim = 0; /* disbale standard LF delimiter */
-static int bUseFlowControl = 1; /* use flow control, what means indicate ourselfs a "light delayable" */
-static uchar *pszStrmDrvrAuthMode = NULL; /* authentication mode to use */
-static uchar *pszInputName = NULL; /* value for inputname property, NULL is OK and handled by core engine */
-static ruleset_t *pBindRuleset = NULL; /* ruleset to bind listener to (use system default if unspecified) */
-
+static struct configSettings_s {
+ int iTCPSessMax;
+ int iTCPLstnMax;
+ int bSuppOctetFram;
+ int iStrmDrvrMode;
+ int bKeepAlive;
+ int bEmitMsgOnClose;
+ int iAddtlFrameDelim;
+ int bDisableLFDelim;
+ int bUseFlowControl;
+ uchar *pszStrmDrvrAuthMode;
+ uchar *pszInputName;
+ uchar *pszBindRuleset;
+} cs;
+
+struct instanceConf_s {
+ uchar *pszBindPort; /* port to bind to */
+ uchar *pszBindRuleset; /* name of ruleset to bind to */
+ ruleset_t *pBindRuleset; /* ruleset to bind listener to (use system default if unspecified) */
+ uchar *pszInputName; /* value for inputname property, NULL is OK and handled by core engine */
+ int bSuppOctetFram;
+ struct instanceConf_s *next;
+};
+
+
+struct modConfData_s {
+ rsconf_t *pConf; /* our overall config object */
+ instanceConf_t *root, *tail;
+ int iTCPSessMax; /* max number of sessions */
+ int iTCPLstnMax; /* max number of sessions */
+ int iStrmDrvrMode; /* mode for stream driver, driver-dependent (0 mostly means plain tcp) */
+ int iAddtlFrameDelim; /* addtl frame delimiter, e.g. for netscreen, default none */
+ int bSuppOctetFram;
+ sbool bDisableLFDelim; /* disable standard LF delimiter */
+ sbool bUseFlowControl; /* use flow control, what means indicate ourselfs a "light delayable" */
+ sbool bKeepAlive;
+ sbool bEmitMsgOnClose; /* emit an informational message on close by remote peer */
+ uchar *pszStrmDrvrAuthMode; /* authentication mode to use */
+};
+
+static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
+static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current load process */
+
+#include "im-helper.h" /* must be included AFTER the type definitions! */
/* callbacks */
/* this shall go into a specific ACL module! */
@@ -165,48 +201,72 @@ finalize_it:
}
-/* accept a new ruleset to bind. Checks if it exists and complains, if not */
-static rsRetVal setRuleset(void __attribute__((unused)) *pVal, uchar *pszName)
+/* This function is called when a new listener instace shall be added to
+ * the current config object via the legacy config system. It just shuffles
+ * all parameters to the listener in-memory instance.
+ * rgerhards, 2011-05-04
+ */
+static rsRetVal addInstance(void __attribute__((unused)) *pVal, uchar *pNewVal)
{
- ruleset_t *pRuleset;
- rsRetVal localRet;
+ instanceConf_t *inst;
DEFiRet;
- localRet = ruleset.GetRuleset(&pRuleset, pszName);
- if(localRet == RS_RET_NOT_FOUND) {
- errmsg.LogError(0, RS_RET_RULESET_NOT_FOUND, "error: ruleset '%s' not found - ignored", pszName);
+ CHKmalloc(inst = MALLOC(sizeof(instanceConf_t)));
+
+ CHKmalloc(inst->pszBindPort = ustrdup((pNewVal == NULL || *pNewVal == '\0')
+ ? (uchar*) "10514" : pNewVal));
+ if((cs.pszBindRuleset == NULL) || (cs.pszBindRuleset[0] == '\0')) {
+ inst->pszBindRuleset = NULL;
+ } else {
+ CHKmalloc(inst->pszBindRuleset = ustrdup(cs.pszBindRuleset));
+ }
+ if((cs.pszInputName == NULL) || (cs.pszInputName[0] == '\0')) {
+ inst->pszInputName = NULL;
+ } else {
+ CHKmalloc(inst->pszInputName = ustrdup(cs.pszInputName));
+ }
+ inst->bSuppOctetFram = cs.bSuppOctetFram;
+ inst->next = NULL;
+
+ /* node created, let's add to config */
+ if(loadModConf->tail == NULL) {
+ loadModConf->tail = loadModConf->root = inst;
+ } else {
+ loadModConf->tail->next = inst;
+ loadModConf->tail = inst;
}
- CHKiRet(localRet);
- pBindRuleset = pRuleset;
- DBGPRINTF("imtcp current bind ruleset %p: '%s'\n", pRuleset, pszName);
finalize_it:
- free(pszName); /* no longer needed */
+ free(pNewVal);
RETiRet;
}
-static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVal)
+static rsRetVal
+addListner(modConfData_t *modConf, instanceConf_t *inst)
{
DEFiRet;
if(pOurTcpsrv == NULL) {
CHKiRet(tcpsrv.Construct(&pOurTcpsrv));
- CHKiRet(tcpsrv.SetSessMax(pOurTcpsrv, iTCPSessMax));
- CHKiRet(tcpsrv.SetLstnMax(pOurTcpsrv, iTCPLstnMax));
+ /* callbacks */
CHKiRet(tcpsrv.SetCBIsPermittedHost(pOurTcpsrv, isPermittedHost));
CHKiRet(tcpsrv.SetCBRcvData(pOurTcpsrv, doRcvData));
CHKiRet(tcpsrv.SetCBOpenLstnSocks(pOurTcpsrv, doOpenLstnSocks));
CHKiRet(tcpsrv.SetCBOnRegularClose(pOurTcpsrv, onRegularClose));
CHKiRet(tcpsrv.SetCBOnErrClose(pOurTcpsrv, onErrClose));
- CHKiRet(tcpsrv.SetDrvrMode(pOurTcpsrv, iStrmDrvrMode));
- CHKiRet(tcpsrv.SetUseFlowControl(pOurTcpsrv, bUseFlowControl));
- CHKiRet(tcpsrv.SetAddtlFrameDelim(pOurTcpsrv, iAddtlFrameDelim));
- CHKiRet(tcpsrv.SetbDisableLFDelim(pOurTcpsrv, bDisableLFDelim));
- CHKiRet(tcpsrv.SetNotificationOnRemoteClose(pOurTcpsrv, bEmitMsgOnClose));
+ /* params */
+ CHKiRet(tcpsrv.SetKeepAlive(pOurTcpsrv, modConf->bKeepAlive));
+ CHKiRet(tcpsrv.SetSessMax(pOurTcpsrv, modConf->iTCPSessMax));
+ CHKiRet(tcpsrv.SetLstnMax(pOurTcpsrv, modConf->iTCPLstnMax));
+ CHKiRet(tcpsrv.SetDrvrMode(pOurTcpsrv, modConf->iStrmDrvrMode));
+ CHKiRet(tcpsrv.SetUseFlowControl(pOurTcpsrv, modConf->bUseFlowControl));
+ CHKiRet(tcpsrv.SetAddtlFrameDelim(pOurTcpsrv, modConf->iAddtlFrameDelim));
+ CHKiRet(tcpsrv.SetbDisableLFDelim(pOurTcpsrv, modConf->bDisableLFDelim));
+ CHKiRet(tcpsrv.SetNotificationOnRemoteClose(pOurTcpsrv, modConf->bEmitMsgOnClose));
/* now set optional params, but only if they were actually configured */
- if(pszStrmDrvrAuthMode != NULL) {
- CHKiRet(tcpsrv.SetDrvrAuthMode(pOurTcpsrv, pszStrmDrvrAuthMode));
+ if(modConf->pszStrmDrvrAuthMode != NULL) {
+ CHKiRet(tcpsrv.SetDrvrAuthMode(pOurTcpsrv, modConf->pszStrmDrvrAuthMode));
}
if(pPermPeersRoot != NULL) {
CHKiRet(tcpsrv.SetDrvrPermPeers(pOurTcpsrv, pPermPeersRoot));
@@ -214,41 +274,113 @@ static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVa
}
/* initialized, now add socket and listener params */
- CHKiRet(tcpsrv.SetRuleset(pOurTcpsrv, pBindRuleset));
- CHKiRet(tcpsrv.SetInputName(pOurTcpsrv, pszInputName == NULL ?
- UCHAR_CONSTANT("imtcp") : pszInputName));
- tcpsrv.configureTCPListen(pOurTcpsrv, pNewVal);
+ DBGPRINTF("imtcp: trying to add port *:%s\n", inst->pszBindPort);
+ CHKiRet(tcpsrv.SetRuleset(pOurTcpsrv, inst->pBindRuleset));
+ CHKiRet(tcpsrv.SetInputName(pOurTcpsrv, inst->pszInputName == NULL ?
+ UCHAR_CONSTANT("imtcp") : inst->pszInputName));
+ tcpsrv.configureTCPListen(pOurTcpsrv, inst->pszBindPort, inst->bSuppOctetFram);
finalize_it:
if(iRet != RS_RET_OK) {
- errmsg.LogError(0, NO_ERRCODE, "error %d trying to add listener", iRet);
- if(pOurTcpsrv != NULL)
- tcpsrv.Destruct(&pOurTcpsrv);
+ errmsg.LogError(0, NO_ERRCODE, "imtcp: error %d trying to add listener", iRet);
}
RETiRet;
}
+
+BEGINbeginCnfLoad
+CODESTARTbeginCnfLoad
+ loadModConf = pModConf;
+ pModConf->pConf = pConf;
+ /* init legacy config variables */
+ cs.pszStrmDrvrAuthMode = NULL;
+ resetConfigVariables(NULL, NULL); /* dummy parameters just to fulfill interface def */
+ENDbeginCnfLoad
+
+
+BEGINendCnfLoad
+CODESTARTendCnfLoad
+ /* persist module-specific settings from legacy config system */
+ pModConf->iTCPSessMax = cs.iTCPSessMax;
+ pModConf->iTCPLstnMax = cs.iTCPLstnMax;
+ pModConf->iStrmDrvrMode = cs.iStrmDrvrMode;
+ pModConf->bEmitMsgOnClose = cs.bEmitMsgOnClose;
+ pModConf->bSuppOctetFram = cs.bSuppOctetFram;
+ pModConf->iAddtlFrameDelim = cs.iAddtlFrameDelim;
+ pModConf->bDisableLFDelim = cs.bDisableLFDelim;
+ pModConf->bUseFlowControl = cs.bUseFlowControl;
+ pModConf->bKeepAlive = cs.bKeepAlive;
+ if((cs.pszStrmDrvrAuthMode == NULL) || (cs.pszStrmDrvrAuthMode[0] == '\0')) {
+ loadModConf->pszStrmDrvrAuthMode = NULL;
+ free(cs.pszStrmDrvrAuthMode);
+ } else {
+ loadModConf->pszStrmDrvrAuthMode = cs.pszStrmDrvrAuthMode;
+ }
+ cs.pszStrmDrvrAuthMode = NULL;
+
+ loadModConf = NULL; /* done loading */
+ENDendCnfLoad
+
+
+/* function to generate error message if framework does not find requested ruleset */
+static inline void
+std_checkRuleset_genErrMsg(__attribute__((unused)) modConfData_t *modConf, instanceConf_t *inst)
+{
+ errmsg.LogError(0, NO_ERRCODE, "imtcp: ruleset '%s' for port %s not found - "
+ "using default ruleset instead", inst->pszBindRuleset,
+ inst->pszBindPort);
+}
+
+BEGINcheckCnf
+ instanceConf_t *inst;
+CODESTARTcheckCnf
+ for(inst = pModConf->root ; inst != NULL ; inst = inst->next) {
+ std_checkRuleset(pModConf, inst);
+ }
+ if(pModConf->root == NULL) {
+ errmsg.LogError(0, RS_RET_NO_LISTNERS , "imtcp: module loaded, but "
+ "no listeners defined - no input will be gathered");
+ iRet = RS_RET_NO_LISTNERS;
+ }
+ENDcheckCnf
+
+
+BEGINactivateCnfPrePrivDrop
+ instanceConf_t *inst;
+CODESTARTactivateCnfPrePrivDrop
+ runModConf = pModConf;
+ for(inst = runModConf->root ; inst != NULL ; inst = inst->next) {
+ addListner(pModConf, inst);
+ }
+ if(pOurTcpsrv == NULL)
+ ABORT_FINALIZE(RS_RET_NO_RUN);
+ CHKiRet(tcpsrv.ConstructFinalize(pOurTcpsrv));
+finalize_it:
+ENDactivateCnfPrePrivDrop
+
+
+BEGINactivateCnf
+CODESTARTactivateCnf
+ /* sorry, nothing to do here... */
+ENDactivateCnf
+
+
+BEGINfreeCnf
+CODESTARTfreeCnf
+ENDfreeCnf
+
/* This function is called to gather input.
*/
BEGINrunInput
CODESTARTrunInput
- /* TODO: we must be careful to start the listener here. Currently, tcpsrv.c seems to
- * do that in ConstructFinalize
- */
- CHKiRet(tcpsrv.ConstructFinalize(pOurTcpsrv));
iRet = tcpsrv.Run(pOurTcpsrv);
-finalize_it:
ENDrunInput
/* initialize and return if will run or not */
BEGINwillRun
CODESTARTwillRun
- /* first apply some config settings */
net.PrintAllowedSenders(2); /* TCP */
- if(pOurTcpsrv == NULL)
- ABORT_FINALIZE(RS_RET_NO_RUN);
-finalize_it:
ENDwillRun
@@ -288,17 +420,19 @@ ENDmodExit
static rsRetVal
resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
- iTCPSessMax = 200;
- iTCPLstnMax = 20;
- iStrmDrvrMode = 0;
- bUseFlowControl = 0;
- bEmitMsgOnClose = 0;
- iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER;
- bDisableLFDelim = 0;
- free(pszInputName);
- pszInputName = NULL;
- free(pszStrmDrvrAuthMode);
- pszStrmDrvrAuthMode = NULL;
+ cs.iTCPSessMax = 200;
+ cs.iTCPLstnMax = 20;
+ cs.bSuppOctetFram = 1;
+ cs.iStrmDrvrMode = 0;
+ cs.bUseFlowControl = 0;
+ cs.bKeepAlive = 0;
+ cs.bEmitMsgOnClose = 0;
+ cs.iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER;
+ cs.bDisableLFDelim = 0;
+ free(cs.pszInputName);
+ cs.pszInputName = NULL;
+ free(cs.pszStrmDrvrAuthMode);
+ cs.pszStrmDrvrAuthMode = NULL;
return RS_RET_OK;
}
@@ -307,6 +441,8 @@ resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unus
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
+CODEqueryEtryPt_STD_CONF2_QUERIES
+CODEqueryEtryPt_STD_CONF2_PREPRIVDROP_QUERIES
CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
ENDqueryEtryPt
@@ -326,31 +462,35 @@ CODEmodInit_QueryRegCFSLineHdlr
/* register config file handlers */
CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverrun"), 0, eCmdHdlrGetWord,
- addTCPListener, NULL, STD_LOADABLE_MODULE_ID));
+ addInstance, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverkeepalive"), 0, eCmdHdlrBinary,
+ NULL, &cs.bKeepAlive, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserversupportoctetcountedframing"), 0, eCmdHdlrBinary,
+ NULL, &cs.bSuppOctetFram, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpmaxsessions"), 0, eCmdHdlrInt,
- NULL, &iTCPSessMax, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.iTCPSessMax, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpmaxlisteners"), 0, eCmdHdlrInt,
- NULL, &iTCPLstnMax, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpservernotifyonconnectionclose"), 0,
- eCmdHdlrBinary, NULL, &bEmitMsgOnClose, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverstreamdrivermode"), 0,
- eCmdHdlrInt, NULL, &iStrmDrvrMode, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverstreamdriverauthmode"), 0,
- eCmdHdlrGetWord, NULL, &pszStrmDrvrAuthMode, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverstreamdriverpermittedpeer"), 0,
- eCmdHdlrGetWord, setPermittedPeer, NULL, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.iTCPLstnMax, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpservernotifyonconnectionclose"), 0, eCmdHdlrBinary,
+ NULL, &cs.bEmitMsgOnClose, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverstreamdrivermode"), 0, eCmdHdlrInt,
+ NULL, &cs.iStrmDrvrMode, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverstreamdriverauthmode"), 0, eCmdHdlrGetWord,
+ NULL, &cs.pszStrmDrvrAuthMode, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverstreamdriverpermittedpeer"), 0, eCmdHdlrGetWord,
+ setPermittedPeer, NULL, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserveraddtlframedelimiter"), 0, eCmdHdlrInt,
- NULL, &iAddtlFrameDelim, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.iAddtlFrameDelim, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverdisablelfdelimiter"), 0, eCmdHdlrBinary,
- NULL, &bDisableLFDelim, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverinputname"), 0,
- eCmdHdlrGetWord, NULL, &pszInputName, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverbindruleset"), 0,
- eCmdHdlrGetWord, setRuleset, NULL, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpflowcontrol"), 0,
- eCmdHdlrBinary, NULL, &bUseFlowControl, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.bDisableLFDelim, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverinputname"), 0, eCmdHdlrGetWord,
+ NULL, &cs.pszInputName, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverbindruleset"), 0, eCmdHdlrGetWord,
+ NULL, &cs.pszBindRuleset, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpflowcontrol"), 0, eCmdHdlrBinary,
+ NULL, &cs.bUseFlowControl, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("resetconfigvariables"), 1, eCmdHdlrCustomHandler,
- resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
diff --git a/plugins/imtemplate/Makefile.am b/plugins/imtemplate/Makefile.am
deleted file mode 100644
index 1825b5b..0000000
--- a/plugins/imtemplate/Makefile.am
+++ /dev/null
@@ -1,6 +0,0 @@
-pkglib_LTLIBRARIES = imtemplate.la
-
-imtemplate_la_SOURCES = imtemplate.c
-imtemplate_la_CPPFLAGS = -I$(top_srcdir) $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
-imtemplate_la_LDFLAGS = -module -avoid-version
-imtemplate_la_LIBADD =
diff --git a/plugins/imtemplate/imtemplate.c b/plugins/imtemplate/imtemplate.c
deleted file mode 100644
index 0e2cac1..0000000
--- a/plugins/imtemplate/imtemplate.c
+++ /dev/null
@@ -1,436 +0,0 @@
-/* imtemplate.c
- *
- * This is NOT a real input module but a (copy)-template to create one. Please
- * do NOT edit this file directly. Rather, copy it, together with the rest of
- * the directory, to a new location ./plugins/im<yourname>, then replace
- * all references to imtemplate in Makefile.am to im<yourname>. Be sure to
- * fix the copyright notices to gain proper credit ;) Any derived version,
- * however, needs to be placed under GPLv3 (see GPLv3 for details). If you
- * do not like that policy, do not use this template or any of the header
- * files. The rsyslog project greatly appreciates module contributions, so
- * please consider contributing your work - even if you may think it only
- * server a single very special purpose. It has turned out that at least some
- * folks have similiar special purposes ;)
- *
- * IMPORTANT
- * The comments in this file are actually the interface specification. I decided
- * not to put it into a separate file as it is much simpler to keep it up to
- * date when it is part of the actual template module.
- *
- * NAMING
- * All input modules shall be named im<something>. While this is not a hard
- * requirement, it helps keeping track of things.
- *
- * Global variables and functions should have a prefix - use as somewhat
- * longer one to prevent conflicts with rsyslog itself and other modules
- * (OK, hopefully I'll have some more precise advise in the future...).
- *
- * INCLUDE MODULE IN THE MAIN MAKE SCRIPT
- * If the module shall be provided as part of rsyslog (or simply as a build aid,
- * you need to add it to the main autoconf files). To do so, you need to edit
- * Makefile.am and configure.ac in the main directory. Search for imtemplate
- * and copy/modify the relevant code for your plugin.
- *
- * DEBUGGING
- * While you develop your code, you may want to add
- * --enable-debug --enable-rtinst
- * to your ./configure settings. These enable extra run-time checks, which cost
- * a lot of performance but can help detect some of the most frequently made
- * bugs. These settings will also provide you with a nice stack dump if something
- * goes really wrong.
- *
- * MORE SAMPLES
- * Remember that rsyslog ships with a number of input modules (./plugins/im*). It
- * is always a good idea to have a look at them before starting your own. imudp
- * may be a good, relatively trivial, sample.
- *
- * --------------------------------------------------------------------------------
- *
- * This template was cretead on 2008-02-01 by Rainer Gerhards.
- *
- * 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" /* this is for autotools and always must be the first include */
-#include <stdlib.h>
-#include <assert.h>
-#include <string.h>
-#include <errno.h>
-#include <pthread.h> /* do NOT remove: will soon be done by the module generation macros */
-#include "rsyslog.h" /* error codes etc... */
-#include "cfsysline.h" /* access to config file objects */
-#include "module-template.h" /* generic module interface code - very important, read it! */
-#include "srUtils.h" /* some utility functions */
-#include "debug.h" /* some debug helper functions */
-
-MODULE_TYPE_INPUT /* must be present for input modules, do not remove */
-MODULE_TYPE_NOKEEP
-
-/* defines */
-
-/* Module static data */
-DEF_IMOD_STATIC_DATA /* must be present, starts static data */
-
-/* Here, define whatever static data is needed. Is it suggested that static variables only are
- * used (not externally visible). If you need externally visible variables, make sure you use a
- * prefix in order not to conflict with other modules or rsyslogd itself (also see comment
- * at file header).
- */
-/* static int imtemplateWhateverVar = 0; */
-
-/* config settings */
-
-
-/* You may add any functions that you feel are useful for your needs. No specific restrictions
- * apply, but we suggest that you use the "iRet" call order, which enables you to use debug
- * support for your own functions and which also makes it easy to communicate exceptions back
- * to the upstream caller (rsyslog framework, for example.
- *
- * The function below is a sample of how one of your functions may look like. Again, the sample
- * below is *not* needed to be present in order to meet the interface requirements.
- *
- * Be sure to use static functions (suggested) or prefixes to prevent name conflicts -- see file
- * header for more information.
- */
-static rsRetVal /* rsRetVal is our generic error-reporting return type */
-imtemplateMyFunc(int iMyParam)
-{
- DEFiRet; /* define iRet, the return code and other plumbing */
- /* define your local variables here */
-
- /* code whatever you need to code here. The "iRet" system can be helpful:
- *
- * CHKiRet(function(param1, param2, ...));
- * calls a function and checks if it returns RS_RET_OK. If so, work
- * proceeds. If some other code is returned, the function is aborted
- * and control transferred to finalize_it (which you need to define)
- *
- * CHKiRet_Hdlr(function(param1, param2, ...))
- * much like CHKiRet, but allows you to specify your own code that is
- * executed if the function does not return RS_RET_OK, e.g.:
- * CHKiRet_Hdlr(function(a, b)) {
- * ... some error handling here ...
- * }
- * control is not transferred to finalize_it, except if you use one
- * of the relevant macros (described below)
- *
- * FINALIZE
- * immediately transfers control to finalize_it, using the current
- * value of iRet, e.g.
- * if(bDone)
- * FINALIZE;
- *
- * ABORT_FINALIZE(retcode)
- * just like FINALIZE, except that iRet is set to the provided error
- * code before control is transferred, e.g.
- * if((ptr = MALLOC(20)) == NULL)
- * ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- *
- * In order for all this to work, you need to define finalize_it, e.g.
- *
- * finalize_it:
- * RETiRet;
- *
- * RETiRet does some housekeeping and then does a "return iRet" to transfer
- * control back to the caller. There shall only be one function exit and
- * it shall be via RETiRet, preferrably at the end of the function code.
- *
- */
-
-finalize_it:
- /* clean up anything that needs to be cleaned up if processing did not
- * go well, for example:
- */
- if(iRet != RS_RET_OK) {
- /* cleanup, e.g.
- * free(somePtr);
- */
- }
-
- RETiRet;
-}
-
-
-/* This function is the cancel cleanup handler. It is called when rsyslog decides the
- * module must be stopped, what most probably happens during shutdown of rsyslogd. When
- * this function is called, the runInput() function (below) is already terminated - somewhere
- * in the middle of what it was doing. The cancel cleanup handler below should take
- * care of any locked mutexes and such, things that really need to be cleaned up
- * before processing continues. In general, many plugins do not need to provide
- * any code at all here.
- *
- * IMPORTANT: the calling interface of this function can NOT be modified. It actually is
- * called by pthreads. The provided argument is currently not being used.
- */
-/* ------------------------------------------------------------------------------------------ *
- * DO NOT TOUCH the following code - it will soon be part of the module generation macros! */
-static void
-inputModuleCleanup(void *arg)
-{
- BEGINfunc
-/* END no-touch zone *
- * ------------------------------------------------------------------------------------------ */
-
-
-
- /* your code here */
-
-
-
-/* ------------------------------------------------------------------------------------------ *
- * DO NOT TOUCH the following code - it will soon be part of the module generation macros! */
- ENDfunc
-}
-/* END no-touch zone *
- * ------------------------------------------------------------------------------------------ */
-
-
-/* This function is called by the framework to gather the input. The module stays
- * most of its lifetime inside this function. It MUST NEVER exit this function. Doing
- * so would end module processing and rsyslog would NOT reschedule the module. If
- * you exit from this function, you violate the interface specification!
- *
- * So how is it terminated? When it is time to terminate, rsyslog actually cancels
- * the threads. This may sound scary, but is not. There is a cancel cleanup handler
- * defined (the function directly above). See comments there for specifics.
- *
- * runInput is always called on a single thread. If the module neees multiple threads,
- * it is free to create them. HOWEVER, it must make sure that any threads created
- * are killed and joined in the cancel cleanup handler.
- */
-BEGINrunInput
- /* define any local variables you need here */
-CODESTARTrunInput
- /* ------------------------------------------------------------------------------------------ *
- * DO NOT TOUCH the following code - it will soon be part of the module generation macros! */
- pthread_cleanup_push(inputModuleCleanup, NULL);
- while(1) { /* endless loop - do NOT break; out of it! */
- /* END no-touch zone *
- * ------------------------------------------------------------------------------------------ */
-
- /* your code here */
-
- /* All rsyslog objects (see other modules, e.g. msg.c) are available
- * to your here. Some useful things are:
- *
- * errmsg.LogError(NO_ERRCODE, format-string, ... params ...);
- * logs an error message as syslogd, just as printf, e.g.
- * errmsg.LogError(NO_ERRCODE, "Error %d occured during %s", 1, "test");
- *
- * To submit the message to the queue engine, we must create the message
- * object and fill it with data. If it contains a syslog message that must
- * be parsed, we can add a flag that requests parsing. Otherwise, we must
- * fill the properties ourselves. That is appropriate if the message
- * does not need to be parsed, for example when reading text (log) files. In that way,
- * we can set the message properties as of our liking. This is how it works:
- *
- msg_t *pMsg;
- CHKiRet(msgConstruct(&pMsg));
- MsgSetRawMsg(pMsg, msg);
- MsgSetHOSTNAME(pMsg, LocalHostName);
- MsgSetTAG(pMsg, "rsyslogd:");
- pMsg->iFacility = LOG_FAC(pri);
- pMsg->iSeverity = LOG_PRI(pri);
- flags |= INTERNAL_MSG;
- logmsg(pMsg, flags); / * some time, CHKiRet() will work here, too [today NOT!] * /
- *
- * NOTE: for up-to-date usage samples, see the other provided input modules.
- * A good starting point is probably imuxsock.
- *
- * This example probably does not set all message properties (but the ones
- * that are of practical importance). If you need all, check msg.h. Use
- * method access functions whereever possible, unfortunately not all structure
- * members are currently exposed in that clean way - so you sometimes need
- * to access them directly (it goes without saying that we will fix that
- * over time ;)).
- */
-
- /* ------------------------------------------------------------------------------------------ *
- * DO NOT TOUCH the following code - it will soon be part of the module generation macros! */
- }
- /*NOTREACHED*/
-
- pthread_cleanup_pop(0); /* just for completeness, but never called... */
- RETiRet; /* use it to make sure the housekeeping is done! */
-ENDrunInput
- /* END no-touch zone *
- * ------------------------------------------------------------------------------------------ */
-
-
-/* The function is called by rsyslog before runInput() is called. It is a last chance
- * to set up anything specific. Most importantly, it can be used to tell rsyslog if the
- * input shall run or not. The idea is that if some config settings (or similiar things)
- * are not OK, the input can tell rsyslog it will not execute. To do so, return
- * RS_RET_NO_RUN or a specific error code. If RS_RET_OK is returned, rsyslog will
- * proceed and call the runInput() entry point. If you do not return anything
- * specific, RS_RET_OK is automatically returned (as in all functions).
- */
-BEGINwillRun
- /* place any variables needed here */
-CODESTARTwillRun
-
- /* ... your code here ... */
-
- /* Just to give you an idea, here are some samples (from the actual imudp module:
- *
- if(udpLstnSocks == NULL)
- ABORT_FINALIZE(RS_RET_NO_RUN);
-
- if((pRcvBuf = MALLOC(glbl.GetMaxLine * sizeof(char))) == NULL) {
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- }
- *
- */
-finalize_it:
-ENDwillRun
-
-
-/* This function is called by the framework after runInput() has been terminated. It
- * shall free any resources and prepare the module for unload.
- *
- * So it is important that runInput() keeps track of what needs to be cleaned up.
- * Objects to think about are files (must be closed), network connections, threads (must
- * be stopped and joined) and memory (must be freed). Of course, there are a myriad
- * of other things, so use your own judgement what you need to do.
- *
- * Another important chore of this function is to persist whatever state the module
- * needs to persist. Unfortunately, there is currently no standard way of doing that.
- * Future version of the module interface will probably support it, but that doesn't
- * help you right at the moment. In general, it is suggested that anything that needs
- * to be persisted is saved in a file, whose name and location is passed in by a
- * module-specific config directive.
- */
-BEGINafterRun
- /* place any variables needed here */
-CODESTARTafterRun
-
- /* ... do cleanup here ... */
-
- /* if you have a string config variable, remember to free its content:
- *
- if(pszStr != NULL) {
- free(pszStr);
- pszStr = NULL;
- }
- */
-ENDafterRun
-
-
-/* The following entry points are defined in module-template.h.
- * In general, they need to be present, but you do NOT need to provide
- * any code here.
- */
-BEGINmodExit
-CODESTARTmodExit
-ENDmodExit
-
-
-BEGINqueryEtryPt
-CODESTARTqueryEtryPt
-CODEqueryEtryPt_STD_IMOD_QUERIES
-ENDqueryEtryPt
-
-
-/* The following function shall reset all configuration variables to their
- * default values. The code provided in modInit() below registers it to be
- * called on "$ResetConfigVariables". You may also call it from other places,
- * but in general this is not necessary. Once runInput() has been called, this
- * function here is never again called.
- */
-static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
-{
- DEFiRet;
-
- /* if you have string variables in you config settings, you need to do this:
- if(pszStr != NULL) {
- free(pszStr);
- pszStr = NULL;
- }
- * Note that it is vitally important that the pointer is set to NULL, because
- * otherwise the framework handler will try to free it a second time when
- * a new value is set!
- */
-
-
- /* ... your code here ... */
-
-
- RETiRet;
-}
-
-
-/* modInit() is called once the module is loaded. It must perform all module-wide
- * initialization tasks. There are also a number of housekeeping tasks that the
- * framework requires. These are handled by the macros. Please note that the
- * complexity of processing is depending on the actual module. However, only
- * thing absolutely necessary should be done here. Actual app-level processing
- * is to be performed in runInput(). A good sample of what to do here may be to
- * set some variable defaults. The most important thing probably is registration
- * of config command handlers.
- */
-BEGINmodInit()
-CODESTARTmodInit
- *ipIFVersProvided = 1; /* interface spec version this module is written to (currently always 1) */
-CODEmodInit_QueryRegCFSLineHdlr
- /* register config file handlers
- * For details, see cfsysline.c/.h. The config file is automatically handled. In general,
- * a pointer to a variable receiving the value and the config directive is to be supplied.
- * A custom function pointer can only be provided, which then is called when the config
- * directive appears. Limit this to cases where it is absolutely necessary. The
- * STD_LOADABLE_MODULE_ID is a value that identifies the module. It is use to automatically
- * unregister the module's config file handlers upon module unload. Do NOT use any other
- * value for this parameter! Available Syntaxes (supported types) can be seen in cfsysline.h,
- * the ecslCmdHdrlType enum has all that are currently defined.
- *
- * Config file directives should always be along the lines of
- *
- * $Input<moduleobject>ObjObjName
- *
- * An example would be $InputImtemplateRetriesMax. This is currently not enforced,
- * but when we get to our new config file format and reader, this becomes quite
- * important.
- *
- * Please note that config directives must be provided in lower case. The engine
- * makes the mapping (what currently means case-insensitive comparison). The dollar
- * sign is NOT part of the directive and thus not specified.
- *
- * Some samples:
- *
- * A hypothetical integer variable:
- * CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputimtemplatemessagenumber", 0, eCmdHdlrInt,
- NULL, &intVariable, STD_LOADABLE_MODULE_ID));
- *
- * and a hypothetical string variable:
- * CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputimtemplatemessagetext", 0, eCmdHdlrGetWord,
- * NULL, &pszBindAddr, STD_LOADABLE_MODULE_ID));
- */
-
- /* whenever config variables exist, they should be resettable via $ResetConfigVariables.
- * The following line adds our handler for that. Note that if you do not have any config
- * variables at all (unlikely, I think...), you can remove this handler.
- */
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
- resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
-
- /* ... do whatever else you need to do, but keep it brief ... */
-
-ENDmodInit
-/*
- * vim:set ai:
- */
diff --git a/plugins/imttcp/imttcp.c b/plugins/imttcp/imttcp.c
index c7f546c..c72886b 100644
--- a/plugins/imttcp/imttcp.c
+++ b/plugins/imttcp/imttcp.c
@@ -122,6 +122,7 @@
MODULE_TYPE_INPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("imttcp")
/* static data */
DEF_IMOD_STATIC_DATA
@@ -135,6 +136,10 @@ DEFobjCurrIf(ruleset)
/* config settings */
+struct modConfData_s {
+ EMPTY_STRUCT;
+};
+
typedef struct configSettings_s {
int bEmitMsgOnClose; /* emit an informational message on close by remote peer */
int iAddtlFrameDelim; /* addtl frame delimiter, e.g. for netscreen, default none */
@@ -808,7 +813,7 @@ static rsRetVal setRuleset(void __attribute__((unused)) *pVal, uchar *pszName)
rsRetVal localRet;
DEFiRet;
- localRet = ruleset.GetRuleset(&pRuleset, pszName);
+ localRet = ruleset.GetRuleset(ourConf, &pRuleset, pszName);
if(localRet == RS_RET_NOT_FOUND) {
errmsg.LogError(0, NO_ERRCODE, "error: ruleset '%s' not found - ignored", pszName);
}
@@ -984,6 +989,7 @@ startupListeners()
RETiRet;
}
+
/* This function is called to gather input.
*/
BEGINrunInput
@@ -1121,7 +1127,7 @@ CODEmodInit_QueryRegCFSLineHdlr
/* initialize "read-only" thread attributes */
pthread_attr_init(&sessThrdAttr);
pthread_attr_setdetachstate(&sessThrdAttr, PTHREAD_CREATE_DETACHED);
- pthread_attr_setstacksize(&sessThrdAttr, 200*1024);
+ pthread_attr_setstacksize(&sessThrdAttr, 4096*1024);
/* register config file handlers */
CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputttcpserverrun"), 0, eCmdHdlrGetWord,
diff --git a/plugins/imudp/imudp.c b/plugins/imudp/imudp.c
index e68bcf3..6abeab0 100644
--- a/plugins/imudp/imudp.c
+++ b/plugins/imudp/imudp.c
@@ -6,7 +6,7 @@
*
* File begun on 2007-12-21 by RGerhards (extracted from syslogd.c)
*
- * Copyright 2007-2009 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007-2011 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -26,6 +26,7 @@
* A copy of the GPL can be found in the file "COPYING" in this distribution.
*/
#include "config.h"
+#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
@@ -51,10 +52,12 @@
#include "datetime.h"
#include "prop.h"
#include "ruleset.h"
+#include "statsobj.h"
#include "unicode-helper.h"
MODULE_TYPE_INPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("imudp")
/* defines */
@@ -66,6 +69,16 @@ DEFobjCurrIf(net)
DEFobjCurrIf(datetime)
DEFobjCurrIf(prop)
DEFobjCurrIf(ruleset)
+DEFobjCurrIf(statsobj)
+
+
+static struct lstn_s {
+ struct lstn_s *next;
+ int sock; /* socket */
+ ruleset_t *pRuleset; /* bound ruleset */
+ statsobj_t *stats; /* listener stats */
+ STATSCOUNTER_DEF(ctrSubmit, mutCtrSubmit)
+} *lcnfRoot = NULL, *lcnfLast = NULL;
static int bDoACLCheck; /* are ACL checks neeed? Cached once immediately before listener startup */
static int iMaxLine; /* maximum UDP message size supported */
@@ -73,141 +86,111 @@ static time_t ttLastDiscard = 0; /* timestamp when a message from a non-permitte
* This shall prevent remote DoS when the "discard on disallowed sender"
* message is configured to be logged on occurance of such a case.
*/
-static int *udpLstnSocks = NULL; /* Internet datagram sockets, first element is nbr of elements
- * read-only after init(), but beware of restart! */
-static ruleset_t **udpRulesets = NULL; /* ruleset to be used with sockets in question (entry 0 is empty) */
-static uchar *pszBindAddr = NULL; /* IP to bind socket to */
static uchar *pRcvBuf = NULL; /* receive buffer (for a single packet). We use a global and alloc
* it so that we can check available memory in willRun() and request
* termination if we can not get it. -- rgerhards, 2007-12-27
*/
static prop_t *pInputName = NULL; /* our inputName currently is always "imudp", and this will hold it */
-static uchar *pszSchedPolicy = NULL; /* scheduling policy string */
-static int iSchedPolicy; /* scheduling policy as SCHED_xxx */
-static int iSchedPrio; /* scheduling priority */
-static int seen_iSchedPrio = 0; /* have we seen scheduling priority in the config file? */
-static ruleset_t *pBindRuleset = NULL; /* ruleset to bind listener to (use system default if unspecified) */
-#define TIME_REQUERY_DFLT 2
-static int iTimeRequery = TIME_REQUERY_DFLT;/* how often is time to be queried inside tight recv loop? 0=always */
-
-/* config settings */
-
-static rsRetVal check_scheduling_priority(int report_error)
-{
- DEFiRet;
-
-#ifdef HAVE_SCHED_GET_PRIORITY_MAX
- if (iSchedPrio < sched_get_priority_min(iSchedPolicy) ||
- iSchedPrio > sched_get_priority_max(iSchedPolicy)) {
- if (report_error)
- errmsg.LogError(errno, NO_ERRCODE,
- "imudp: scheduling priority %d out of range (%d - %d)"
- " for scheduling policy '%s' - ignoring settings",
- iSchedPrio,
- sched_get_priority_min(iSchedPolicy),
- sched_get_priority_max(iSchedPolicy),
- pszSchedPolicy);
- ABORT_FINALIZE(RS_RET_VALIDATION_RUN);
- }
-#endif
-
-finalize_it:
- RETiRet;
-}
-/* Set scheduling priority in the supplied variable (will be iSchedPrio)
- * and record that we have seen the directive (in seen_iSchedPrio).
+#define TIME_REQUERY_DFLT 2
+#define SCHED_PRIO_UNSET -12345678 /* a value that indicates that the scheduling priority has not been set */
+/* config vars for legacy config system */
+static struct configSettings_s {
+ uchar *pszBindAddr; /* IP to bind socket to */
+ uchar *pszSchedPolicy; /* scheduling policy string */
+ uchar *pszBindRuleset; /* name of Ruleset to bind to */
+ int iSchedPrio; /* scheduling priority */
+ int iTimeRequery; /* how often is time to be queried inside tight recv loop? 0=always */
+} cs;
+
+struct instanceConf_s {
+ uchar *pszBindAddr; /* IP to bind socket to */
+ uchar *pszBindPort; /* Port to bind socket to */
+ uchar *pszBindRuleset; /* name of ruleset to bind to */
+ ruleset_t *pBindRuleset; /* ruleset to bind listener to (use system default if unspecified) */
+ struct instanceConf_s *next;
+};
+
+struct modConfData_s {
+ rsconf_t *pConf; /* our overall config object */
+ instanceConf_t *root, *tail;
+ uchar *pszSchedPolicy; /* scheduling policy string */
+ int iSchedPolicy; /* scheduling policy as SCHED_xxx */
+ int iSchedPrio; /* scheduling priority */
+ int iTimeRequery; /* how often is time to be queried inside tight recv loop? 0=always */
+};
+static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
+static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current load process */
+
+#include "im-helper.h" /* must be included AFTER the type definitions! */
+
+
+
+/* This function is called when a new listener instace shall be added to
+ * the current config object via the legacy config system. It just shuffles
+ * all parameters to the listener in-memory instance.
+ * rgerhards, 2011-05-04
*/
-static rsRetVal set_scheduling_priority(void *pVal, int value)
+static rsRetVal addInstance(void __attribute__((unused)) *pVal, uchar *pNewVal)
{
+ instanceConf_t *inst;
DEFiRet;
- if (seen_iSchedPrio) {
- errmsg.LogError(0, NO_ERRCODE, "directive already seen");
- ABORT_FINALIZE(RS_RET_VALIDATION_RUN);
- }
- *(int *)pVal = value;
- seen_iSchedPrio = 1;
- if (pszSchedPolicy != NULL)
- CHKiRet(check_scheduling_priority(1));
-
-finalize_it:
- RETiRet;
-}
-
-/* Set scheduling policy in iSchedPolicy */
-static rsRetVal set_scheduling_policy(void *pVal, uchar *pNewVal)
-{
- int have_sched_policy = 0;
- DEFiRet;
-
- if (pszSchedPolicy != NULL) {
- errmsg.LogError(0, NO_ERRCODE, "directive already seen");
- ABORT_FINALIZE(RS_RET_VALIDATION_RUN);
+ CHKmalloc(inst = MALLOC(sizeof(instanceConf_t)));
+ CHKmalloc(inst->pszBindPort = ustrdup((pNewVal == NULL || *pNewVal == '\0')
+ ? (uchar*) "514" : pNewVal));
+ if((cs.pszBindAddr == NULL) || (cs.pszBindAddr[0] == '\0')) {
+ inst->pszBindAddr = NULL;
+ } else {
+ CHKmalloc(inst->pszBindAddr = ustrdup(cs.pszBindAddr));
}
- *((uchar**)pVal) = pNewVal; /* pVal is pszSchedPolicy */
- if (0) { /* trick to use conditional compilation */
-#ifdef SCHED_FIFO
- } else if (!strcasecmp((char*)pszSchedPolicy, "fifo")) {
- iSchedPolicy = SCHED_FIFO;
- have_sched_policy = 1;
-#endif
-#ifdef SCHED_RR
- } else if (!strcasecmp((char*)pszSchedPolicy, "rr")) {
- iSchedPolicy = SCHED_RR;
- have_sched_policy = 1;
-#endif
-#ifdef SCHED_OTHER
- } else if (!strcasecmp((char*)pszSchedPolicy, "other")) {
- iSchedPolicy = SCHED_OTHER;
- have_sched_policy = 1;
-#endif
+ if((cs.pszBindRuleset == NULL) || (cs.pszBindRuleset[0] == '\0')) {
+ inst->pszBindRuleset = NULL;
} else {
- errmsg.LogError(errno, NO_ERRCODE,
- "imudp: invalid scheduling policy '%s' "
- "- ignoring setting", pszSchedPolicy);
+ CHKmalloc(inst->pszBindRuleset = ustrdup(cs.pszBindRuleset));
}
- if (have_sched_policy == 0) {
- free(pszSchedPolicy);
- pszSchedPolicy = NULL;
- ABORT_FINALIZE(RS_RET_VALIDATION_RUN);
+ inst->pBindRuleset = NULL;
+ inst->next = NULL;
+
+ /* node created, let's add to config */
+ if(loadModConf->tail == NULL) {
+ loadModConf->tail = loadModConf->root = inst;
+ } else {
+ loadModConf->tail->next = inst;
+ loadModConf->tail = inst;
}
- if (seen_iSchedPrio)
- CHKiRet(check_scheduling_priority(1));
finalize_it:
+ free(pNewVal);
RETiRet;
}
/* This function is called when a new listener shall be added. It takes
- * the configured parameters, tries to bind the socket and, if that
+ * the instance config description, tries to bind the socket and, if that
* succeeds, adds it to the list of existing listen sockets.
- * rgerhards, 2007-12-27
*/
-static rsRetVal addListner(void __attribute__((unused)) *pVal, uchar *pNewVal)
+static inline rsRetVal
+addListner(instanceConf_t *inst)
{
DEFiRet;
uchar *bindAddr;
int *newSocks;
- int *tmpSocks;
- int iSrc, iDst;
- ruleset_t **tmpRulesets;
+ int iSrc;
+ struct lstn_s *newlcnfinfo;
+ uchar *bindName;
+ uchar *port;
+ uchar statname[64];
/* check which address to bind to. We could do this more compact, but have not
* done so in order to make the code more readable. -- rgerhards, 2007-12-27
*/
- if(pszBindAddr == NULL)
- bindAddr = NULL;
- else if(pszBindAddr[0] == '*' && pszBindAddr[1] == '\0')
- bindAddr = NULL;
- else
- bindAddr = pszBindAddr;
+#if 0 //<<<<<<< HEAD
- DBGPRINTF("Trying to open syslog UDP ports at %s:%s.\n",
- (bindAddr == NULL) ? (uchar*)"*" : bindAddr, pNewVal);
+ DBGPRINTF("imudp: trying to open port at %s:%s.\n",
+ (inst->pszBindAddr == NULL) ? (uchar*)"*" : inst->pszBindAddr, inst->pszBindPort);
- newSocks = net.create_udp_socket(bindAddr, (pNewVal == NULL || *pNewVal == '\0') ? (uchar*) "514" : pNewVal, 1);
+ newSocks = net.create_udp_socket(inst->pszBindAddr, inst->pszBindPort, 1);
if(newSocks != NULL) {
/* we now need to add the new sockets to the existing set */
if(udpLstnSocks == NULL) {
@@ -215,7 +198,7 @@ static rsRetVal addListner(void __attribute__((unused)) *pVal, uchar *pNewVal)
udpLstnSocks = newSocks;
CHKmalloc(udpRulesets = (ruleset_t**) MALLOC(sizeof(ruleset_t*) * (newSocks[0] + 1)));
for(iDst = 1 ; iDst <= newSocks[0] ; ++iDst)
- udpRulesets[iDst] = pBindRuleset;
+ udpRulesets[iDst] = inst->pBindRuleset;
} else {
/* we need to add them */
tmpSocks = (int*) MALLOC(sizeof(int) * (1 + newSocks[0] + udpLstnSocks[0]));
@@ -238,7 +221,7 @@ static rsRetVal addListner(void __attribute__((unused)) *pVal, uchar *pNewVal)
}
for(iSrc = 1 ; iSrc <= newSocks[0] ; ++iSrc, ++iDst) {
tmpSocks[iDst] = newSocks[iSrc];
- tmpRulesets[iDst] = pBindRuleset;
+ tmpRulesets[iDst] = inst->pBindRuleset;
}
tmpSocks[0] = udpLstnSocks[0] + newSocks[0];
free(newSocks);
@@ -247,35 +230,64 @@ static rsRetVal addListner(void __attribute__((unused)) *pVal, uchar *pNewVal)
free(udpRulesets);
udpRulesets = tmpRulesets;
}
+#else //=======
+ if(inst->pszBindAddr == NULL)
+ bindAddr = NULL;
+ else if(inst->pszBindAddr[0] == '*' && inst->pszBindAddr[1] == '\0')
+ bindAddr = NULL;
+ else
+ bindAddr = inst->pszBindAddr;
+ bindName = (bindAddr == NULL) ? (uchar*)"*" : bindAddr;
+ port = (inst->pszBindPort == NULL || *inst->pszBindPort == '\0') ? (uchar*) "514" : inst->pszBindPort;
+
+ DBGPRINTF("Trying to open syslog UDP ports at %s:%s.\n", bindName, inst->pszBindPort);
+
+ newSocks = net.create_udp_socket(bindAddr, port, 1);
+ if(newSocks != NULL) {
+ /* we now need to add the new sockets to the existing set */
+ /* ready to copy */
+ for(iSrc = 1 ; iSrc <= newSocks[0] ; ++iSrc) {
+ CHKmalloc(newlcnfinfo = (struct lstn_s*) MALLOC(sizeof(struct lstn_s)));
+ newlcnfinfo->next = NULL;
+ newlcnfinfo->sock = newSocks[iSrc];
+ newlcnfinfo->pRuleset = inst->pBindRuleset;
+ /* support statistics gathering */
+ CHKiRet(statsobj.Construct(&(newlcnfinfo->stats)));
+ snprintf((char*)statname, sizeof(statname), "imudp(%s:%s)", bindName, port);
+ statname[sizeof(statname)-1] = '\0'; /* just to be on the save side... */
+ CHKiRet(statsobj.SetName(newlcnfinfo->stats, statname));
+ STATSCOUNTER_INIT(newlcnfinfo->ctrSubmit, newlcnfinfo->mutCtrSubmit);
+ CHKiRet(statsobj.AddCounter(newlcnfinfo->stats, UCHAR_CONSTANT("submitted"),
+ ctrType_IntCtr, &(newlcnfinfo->ctrSubmit)));
+ CHKiRet(statsobj.ConstructFinalize(newlcnfinfo->stats));
+ /* link to list. Order must be preserved to take care for
+ * conflicting matches.
+ */
+ if(lcnfRoot == NULL)
+ lcnfRoot = newlcnfinfo;
+ if(lcnfLast == NULL)
+ lcnfLast = newlcnfinfo;
+ else {
+ lcnfLast->next = newlcnfinfo;
+ lcnfLast = newlcnfinfo;
+#endif //>>>>>>> ef34821a2737799f48c3032b9616418e4f7fa34f
+ }
}
}
finalize_it:
- free(pNewVal); /* in any case, this is no longer needed */
-
+ free(newSocks);
RETiRet;
}
-/* accept a new ruleset to bind. Checks if it exists and complains, if not */
-static rsRetVal
-setRuleset(void __attribute__((unused)) *pVal, uchar *pszName)
+static inline void
+std_checkRuleset_genErrMsg(__attribute__((unused)) modConfData_t *modConf, instanceConf_t *inst)
{
- ruleset_t *pRuleset;
- rsRetVal localRet;
- DEFiRet;
-
- localRet = ruleset.GetRuleset(&pRuleset, pszName);
- if(localRet == RS_RET_NOT_FOUND) {
- errmsg.LogError(0, NO_ERRCODE, "error: ruleset '%s' not found - ignored", pszName);
- }
- CHKiRet(localRet);
- pBindRuleset = pRuleset;
- DBGPRINTF("imudp current bind ruleset %p: '%s'\n", pRuleset, pszName);
-
-finalize_it:
- free(pszName); /* no longer needed */
- RETiRet;
+ errmsg.LogError(0, NO_ERRCODE, "imudp: ruleset '%s' for %s:%s not found - "
+ "using default ruleset instead", inst->pszBindRuleset,
+ inst->pszBindAddr == NULL ? "*" : (char*) inst->pszBindAddr,
+ inst->pszBindPort);
}
@@ -294,8 +306,7 @@ finalize_it:
* on scheduling order. -- rgerhards, 2008-10-02
*/
static inline rsRetVal
-processSocket(thrdInfo_t *pThrd, int fd, struct sockaddr_storage *frominetPrev, int *pbIsPermitted,
- ruleset_t *pRuleset)
+processSocket(thrdInfo_t *pThrd, struct lstn_s *lstn, struct sockaddr_storage *frominetPrev, int *pbIsPermitted)
{
DEFiRet;
int iNbrTimeUsed;
@@ -315,7 +326,7 @@ processSocket(thrdInfo_t *pThrd, int fd, struct sockaddr_storage *frominetPrev,
if(pThrd->bShallStop == TRUE)
ABORT_FINALIZE(RS_RET_FORCE_TERM);
socklen = sizeof(struct sockaddr_storage);
- lenRcvBuf = recvfrom(fd, (char*) pRcvBuf, iMaxLine, 0, (struct sockaddr *)&frominet, &socklen);
+ lenRcvBuf = recvfrom(lstn->sock, (char*) pRcvBuf, iMaxLine, 0, (struct sockaddr *)&frominet, &socklen);
if(lenRcvBuf < 0) {
if(errno != EINTR && errno != EAGAIN) {
rs_strerror_r(errno, errStr, sizeof(errStr));
@@ -360,23 +371,24 @@ processSocket(thrdInfo_t *pThrd, int fd, struct sockaddr_storage *frominetPrev,
*pbIsPermitted = 1; /* no check -> everything permitted */
}
- DBGPRINTF("recv(%d,%d),acl:%d,msg:%s\n", fd, (int) lenRcvBuf, *pbIsPermitted, pRcvBuf);
+ DBGPRINTF("recv(%d,%d),acl:%d,msg:%s\n", lstn->sock, (int) lenRcvBuf, *pbIsPermitted, pRcvBuf);
if(*pbIsPermitted != 0) {
- if((iTimeRequery == 0) || (iNbrTimeUsed++ % iTimeRequery) == 0) {
+ if((runModConf->iTimeRequery == 0) || (iNbrTimeUsed++ % runModConf->iTimeRequery) == 0) {
datetime.getCurrTime(&stTime, &ttGenTime);
}
/* we now create our own message object and submit it to the queue */
CHKiRet(msgConstructWithTime(&pMsg, &stTime, ttGenTime));
MsgSetRawMsg(pMsg, (char*)pRcvBuf, lenRcvBuf);
MsgSetInputName(pMsg, pInputName);
- MsgSetRuleset(pMsg, pRuleset);
+ MsgSetRuleset(pMsg, lstn->pRuleset);
MsgSetFlowControlType(pMsg, eFLOWCTL_NO_DELAY);
pMsg->msgFlags = NEEDS_PARSING | PARSE_HOSTNAME | NEEDS_DNSRESOL;
if(*pbIsPermitted == 2)
pMsg->msgFlags |= NEEDS_ACLCHK_U; /* request ACL check after resolution */
CHKiRet(msgSetFromSockinfo(pMsg, &frominet));
CHKiRet(submitMsg(pMsg));
+ STATSCOUNTER_INC(lstn->ctrSubmit, lstn->mutCtrSubmit);
}
}
@@ -389,42 +401,128 @@ finalize_it:
RETiRet;
}
-static void set_thread_schedparam(void)
+
+/* check configured scheduling priority.
+ * Precondition: iSchedPolicy must have been set
+ */
+static inline rsRetVal
+checkSchedulingPriority(modConfData_t *modConf)
{
- struct sched_param sparam;
+ DEFiRet;
- if (pszSchedPolicy != NULL && seen_iSchedPrio == 0) {
+#ifdef HAVE_SCHED_GET_PRIORITY_MAX
+ if( modConf->iSchedPrio < sched_get_priority_min(modConf->iSchedPolicy)
+ || modConf->iSchedPrio > sched_get_priority_max(modConf->iSchedPolicy)) {
errmsg.LogError(0, NO_ERRCODE,
+ "imudp: scheduling priority %d out of range (%d - %d)"
+ " for scheduling policy '%s' - ignoring settings",
+ modConf->iSchedPrio,
+ sched_get_priority_min(modConf->iSchedPolicy),
+ sched_get_priority_max(modConf->iSchedPolicy),
+ modConf->pszSchedPolicy);
+ ABORT_FINALIZE(RS_RET_VALIDATION_RUN);
+ }
+#endif
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* check scheduling policy string and, if valid, set its
+ * numeric equivalent in current load config
+ */
+static rsRetVal
+checkSchedulingPolicy(modConfData_t *modConf)
+{
+ DEFiRet;
+
+ if (0) { /* trick to use conditional compilation */
+#ifdef SCHED_FIFO
+ } else if (!strcasecmp((char*)modConf->pszSchedPolicy, "fifo")) {
+ modConf->iSchedPolicy = SCHED_FIFO;
+#endif
+#ifdef SCHED_RR
+ } else if (!strcasecmp((char*)modConf->pszSchedPolicy, "rr")) {
+ modConf->iSchedPolicy = SCHED_RR;
+#endif
+#ifdef SCHED_OTHER
+ } else if (!strcasecmp((char*)modConf->pszSchedPolicy, "other")) {
+ modConf->iSchedPolicy = SCHED_OTHER;
+#endif
+ } else {
+ errmsg.LogError(errno, NO_ERRCODE,
+ "imudp: invalid scheduling policy '%s' "
+ "- ignoring setting", modConf->pszSchedPolicy);
+ ABORT_FINALIZE(RS_RET_ERR_SCHED_PARAMS);
+ }
+finalize_it:
+ RETiRet;
+}
+
+/* checks scheduling parameters during config check phase */
+static rsRetVal
+checkSchedParam(modConfData_t *modConf)
+{
+ DEFiRet;
+
+ if(modConf->pszSchedPolicy != NULL && modConf->iSchedPrio == SCHED_PRIO_UNSET) {
+ errmsg.LogError(0, RS_RET_ERR_SCHED_PARAMS,
"imudp: scheduling policy set, but without priority - ignoring settings");
- } else if (pszSchedPolicy == NULL && seen_iSchedPrio != 0) {
- errmsg.LogError(0, NO_ERRCODE,
+ ABORT_FINALIZE(RS_RET_ERR_SCHED_PARAMS);
+ } else if(modConf->pszSchedPolicy == NULL && modConf->iSchedPrio != SCHED_PRIO_UNSET) {
+ errmsg.LogError(0, RS_RET_ERR_SCHED_PARAMS,
"imudp: scheduling priority set, but without policy - ignoring settings");
- } else if (pszSchedPolicy != NULL && seen_iSchedPrio != 0 &&
- check_scheduling_priority(0) == 0) {
+ ABORT_FINALIZE(RS_RET_ERR_SCHED_PARAMS);
+ } else if(modConf->pszSchedPolicy != NULL && modConf->iSchedPrio != SCHED_PRIO_UNSET) {
+ /* we have parameters set, so check them */
+ CHKiRet(checkSchedulingPolicy(modConf));
+ CHKiRet(checkSchedulingPriority(modConf));
+ } else { /* nothing set */
+ modConf->iSchedPrio = SCHED_PRIO_UNSET; /* prevents doing the activation call */
+ }
#ifndef HAVE_PTHREAD_SETSCHEDPARAM
- errmsg.LogError(0, NO_ERRCODE,
- "imudp: cannot set thread scheduling policy, "
- "pthread_setschedparam() not available");
-#else
- int err;
-
- memset(&sparam, 0, sizeof sparam);
- sparam.sched_priority = iSchedPrio;
- dbgprintf("imudp trying to set sched policy to '%s', prio %d\n",
- pszSchedPolicy, iSchedPrio);
- err = pthread_setschedparam(pthread_self(), iSchedPolicy, &sparam);
- if (err != 0) {
- errmsg.LogError(err, NO_ERRCODE, "imudp: pthread_setschedparam() failed");
- }
+ errmsg.LogError(0, NO_ERRCODE,
+ "imudp: cannot set thread scheduling policy, "
+ "pthread_setschedparam() not available");
+ ABORT_FINALIZE(RS_RET_ERR_SCHED_PARAMS);
#endif
- }
- if (pszSchedPolicy != NULL) {
- free(pszSchedPolicy);
- pszSchedPolicy = NULL;
+finalize_it:
+ if(iRet != RS_RET_OK)
+ modConf->iSchedPrio = SCHED_PRIO_UNSET; /* prevents doing the activation call */
+
+ RETiRet;
+}
+
+/* set the configured scheduling policy (if possible) */
+static rsRetVal
+setSchedParams(modConfData_t *modConf)
+{
+ DEFiRet;
+
+# ifdef HAVE_PTHREAD_SETSCHEDPARAM
+ int err;
+ struct sched_param sparam;
+
+ if(modConf->iSchedPrio == SCHED_PRIO_UNSET)
+ FINALIZE;
+
+ memset(&sparam, 0, sizeof sparam);
+ sparam.sched_priority = modConf->iSchedPrio;
+ dbgprintf("imudp trying to set sched policy to '%s', prio %d\n",
+ modConf->pszSchedPolicy, modConf->iSchedPrio);
+ err = pthread_setschedparam(pthread_self(), modConf->iSchedPolicy, &sparam);
+ if(err != 0) {
+ errmsg.LogError(err, NO_ERRCODE, "imudp: pthread_setschedparam() failed - ignoring");
}
+# endif
+
+finalize_it:
+ RETiRet;
}
+
/* This function implements the main reception loop. Depending on the environment,
* we either use the traditional (but slower) select() or the Linux-specific epoll()
* interface. ./configure settings control which one is used.
@@ -443,15 +541,20 @@ rsRetVal rcvMainLoop(thrdInfo_t *pThrd)
struct epoll_event *udpEPollEvt = NULL;
struct epoll_event currEvt[NUM_EPOLL_EVENTS];
char errStr[1024];
+ struct lstn_s *lstn;
+ int nLstn;
/* start "name caching" algo by making sure the previous system indicator
* is invalidated.
*/
- set_thread_schedparam();
bIsPermitted = 0;
memset(&frominetPrev, 0, sizeof(frominetPrev));
- CHKmalloc(udpEPollEvt = calloc(udpLstnSocks[0], sizeof(struct epoll_event)));
+ /* count num listeners -- do it here in order to avoid inconsistency */
+ nLstn = 0;
+ for(lstn = lcnfRoot ; lstn != NULL ; lstn = lstn->next)
+ ++nLstn;
+ CHKmalloc(udpEPollEvt = calloc(nLstn, sizeof(struct epoll_event)));
#if defined(EPOLL_CLOEXEC) && defined(HAVE_EPOLL_CREATE1)
DBGPRINTF("imudp uses epoll_create1()\n");
@@ -471,16 +574,18 @@ rsRetVal rcvMainLoop(thrdInfo_t *pThrd)
/* fill the epoll set - we need to do this only once, as the set
* can not change dyamically.
*/
- for (i = 0; i < *udpLstnSocks; i++) {
- if (udpLstnSocks[i+1] != -1) {
+ i = 0;
+ for(lstn = lcnfRoot ; lstn != NULL ; lstn = lstn->next) {
+ if(lstn->sock != -1) {
udpEPollEvt[i].events = EPOLLIN | EPOLLET;
- udpEPollEvt[i].data.u64 = i+1;
- if(epoll_ctl(efd, EPOLL_CTL_ADD, udpLstnSocks[i+1], &(udpEPollEvt[i])) < 0) {
+ udpEPollEvt[i].data.u64 = (long long unsigned) lstn;
+ if(epoll_ctl(efd, EPOLL_CTL_ADD, lstn->sock, &(udpEPollEvt[i])) < 0) {
rs_strerror_r(errno, errStr, sizeof(errStr));
errmsg.LogError(errno, NO_ERRCODE, "epoll_ctrl failed on fd %d with %s\n",
- udpLstnSocks[i+1], errStr);
+ lstn->sock, errStr);
}
}
+ i++;
}
while(1) {
@@ -492,8 +597,7 @@ rsRetVal rcvMainLoop(thrdInfo_t *pThrd)
break; /* terminate input! */
for(i = 0 ; i < nfds ; ++i) {
- processSocket(pThrd, udpLstnSocks[currEvt[i].data.u64], &frominetPrev, &bIsPermitted,
- udpRulesets[currEvt[i].data.u64]);
+ processSocket(pThrd, (struct lstn_s*)currEvt[i].data.u64, &frominetPrev, &bIsPermitted);
}
}
@@ -510,36 +614,31 @@ rsRetVal rcvMainLoop(thrdInfo_t *pThrd)
DEFiRet;
int maxfds;
int nfds;
- int i;
fd_set readfds;
struct sockaddr_storage frominetPrev;
int bIsPermitted;
+ struct lstn_s *lstn;
/* start "name caching" algo by making sure the previous system indicator
* is invalidated.
*/
- set_thread_schedparam();
bIsPermitted = 0;
memset(&frominetPrev, 0, sizeof(frominetPrev));
DBGPRINTF("imudp uses select()\n");
while(1) {
- /* Add the Unix Domain Sockets to the list of read
- * descriptors.
- * rgerhards 2005-08-01: we must now check if there are
- * any local sockets to listen to at all. If the -o option
- * is given without -a, we do not need to listen at all..
+ /* Add the Unix Domain Sockets to the list of read descriptors.
*/
maxfds = 0;
FD_ZERO(&readfds);
/* Add the UDP listen sockets to the list of read descriptors. */
- for (i = 0; i < *udpLstnSocks; i++) {
- if (udpLstnSocks[i+1] != -1) {
+ for(lstn = lcnfRoot ; lstn != NULL ; lstn = lstn->next) {
+ if (lstn->sock != -1) {
if(Debug)
- net.debugListenInfo(udpLstnSocks[i+1], "UDP");
- FD_SET(udpLstnSocks[i+1], &readfds);
- if(udpLstnSocks[i+1]>maxfds) maxfds=udpLstnSocks[i+1];
+ net.debugListenInfo(lstn->sock, "UDP");
+ FD_SET(lstn->sock, &readfds);
+ if(lstn->sock>maxfds) maxfds=lstn->sock;
}
}
if(Debug) {
@@ -555,10 +654,9 @@ rsRetVal rcvMainLoop(thrdInfo_t *pThrd)
if(glbl.GetGlobalInputTermState() == 1)
break; /* terminate input! */
- for(i = 0; nfds && i < *udpLstnSocks; i++) {
- if(FD_ISSET(udpLstnSocks[i+1], &readfds)) {
- processSocket(pThrd, udpLstnSocks[i+1], &frominetPrev, &bIsPermitted,
- udpRulesets[i+1]);
+ for(lstn = lcnfRoot ; nfds && lstn != NULL ; lstn = lstn->next) {
+ if(FD_ISSET(lstn->sock, &readfds)) {
+ processSocket(pThrd, lstn, &frominetPrev, &bIsPermitted);
--nfds; /* indicate we have processed one descriptor */
}
}
@@ -569,8 +667,95 @@ rsRetVal rcvMainLoop(thrdInfo_t *pThrd)
}
#endif /* #if HAVE_EPOLL_CREATE1 */
+
+BEGINbeginCnfLoad
+CODESTARTbeginCnfLoad
+ loadModConf = pModConf;
+ pModConf->pConf = pConf;
+ /* init legacy config vars */
+ cs.pszBindRuleset = NULL;
+ cs.pszSchedPolicy = NULL;
+ cs.pszBindAddr = NULL;
+ cs.iSchedPrio = SCHED_PRIO_UNSET;
+ cs.iTimeRequery = TIME_REQUERY_DFLT;
+ENDbeginCnfLoad
+
+
+BEGINendCnfLoad
+CODESTARTendCnfLoad
+ /* persist module-specific settings from legacy config system
+ * TODO: when we add the new config system, we must decide on priority
+ * already-set module options should not be overwritable by the legacy
+ * system (though this is debatable and should at least trigger an error
+ * message if the equivalent legacy option is selected as well)
+ * rgerhards, 2011-05-04
+ */
+ loadModConf->iSchedPrio = cs.iSchedPrio;
+ loadModConf->iTimeRequery = cs.iTimeRequery;
+ if((cs.pszSchedPolicy == NULL) || (cs.pszSchedPolicy[0] == '\0')) {
+ loadModConf->pszSchedPolicy = NULL;
+ } else {
+ CHKmalloc(loadModConf->pszSchedPolicy = ustrdup(cs.pszSchedPolicy));
+ }
+
+finalize_it:
+ loadModConf = NULL; /* done loading */
+ /* free legacy config vars */
+ free(cs.pszBindRuleset);
+ free(cs.pszSchedPolicy);
+ free(cs.pszBindAddr);
+ENDendCnfLoad
+
+
+BEGINcheckCnf
+ instanceConf_t *inst;
+CODESTARTcheckCnf
+ checkSchedParam(pModConf); /* this can not cause fatal errors */
+ for(inst = pModConf->root ; inst != NULL ; inst = inst->next) {
+ std_checkRuleset(pModConf, inst);
+ }
+ if(pModConf->root == NULL) {
+ errmsg.LogError(0, RS_RET_NO_LISTNERS , "imudp: module loaded, but "
+ "no listeners defined - no input will be gathered");
+ iRet = RS_RET_NO_LISTNERS;
+ }
+ENDcheckCnf
+
+
+BEGINactivateCnfPrePrivDrop
+ instanceConf_t *inst;
+CODESTARTactivateCnfPrePrivDrop
+ runModConf = pModConf;
+ for(inst = runModConf->root ; inst != NULL ; inst = inst->next) {
+ addListner(inst);
+ }
+ /* if we could not set up any listners, there is no point in running... */
+ if(lcnfRoot == NULL) {
+ errmsg.LogError(0, NO_ERRCODE, "imudp: no listeners could be started, "
+ "input not activated.\n");
+ ABORT_FINALIZE(RS_RET_NO_RUN);
+ }
+
+ setSchedParams(pModConf);
+finalize_it:
+ENDactivateCnfPrePrivDrop
+
+
+BEGINactivateCnf
+CODESTARTactivateCnf
+ /* caching various settings */
+ iMaxLine = glbl.GetMaxLine();
+ CHKmalloc(pRcvBuf = MALLOC((iMaxLine + 1) * sizeof(char)));
+finalize_it:
+ENDactivateCnf
+
+
+BEGINfreeCnf
+CODESTARTfreeCnf
+ENDfreeCnf
+
/* This function is called to gather input.
- * Note that udpLstnSocks must be non-NULL because otherwise we would not have
+ * Note that sock must be non-NULL because otherwise we would not have
* indicated that we want to run (or we have a programming error ;)). -- rgerhards, 2008-10-02
*/
BEGINrunInput
@@ -582,49 +767,40 @@ ENDrunInput
/* initialize and return if will run or not */
BEGINwillRun
CODESTARTwillRun
- /* we need to create the inputName property (only once during our lifetime) */
- CHKiRet(prop.Construct(&pInputName));
- CHKiRet(prop.SetString(pInputName, UCHAR_CONSTANT("imudp"), sizeof("imudp") - 1));
- CHKiRet(prop.ConstructFinalize(pInputName));
-
net.PrintAllowedSenders(1); /* UDP */
net.HasRestrictions(UCHAR_CONSTANT("UDP"), &bDoACLCheck); /* UDP */
-
- /* if we could not set up any listners, there is no point in running... */
- if(udpLstnSocks == NULL)
- ABORT_FINALIZE(RS_RET_NO_RUN);
-
- iMaxLine = glbl.GetMaxLine();
-
- CHKmalloc(pRcvBuf = MALLOC((iMaxLine + 1) * sizeof(char)));
-finalize_it:
ENDwillRun
BEGINafterRun
+ struct lstn_s *lstn, *lstnDel;
CODESTARTafterRun
/* do cleanup here */
net.clearAllowedSenders((uchar*)"UDP");
- if(udpLstnSocks != NULL) {
- net.closeUDPListenSockets(udpLstnSocks);
- udpLstnSocks = NULL;
- free(udpRulesets);
- udpRulesets = NULL;
+ for(lstn = lcnfRoot ; lstn != NULL ; ) {
+ statsobj.Destruct(&(lstn->stats));
+ close(lstn->sock);
+ lstnDel = lstn;
+ lstn = lstn->next;
+ free(lstnDel);
}
+ lcnfRoot = lcnfLast = NULL;
if(pRcvBuf != NULL) {
free(pRcvBuf);
pRcvBuf = NULL;
}
- if(pInputName != NULL)
- prop.Destruct(&pInputName);
ENDafterRun
BEGINmodExit
CODESTARTmodExit
+ if(pInputName != NULL)
+ prop.Destruct(&pInputName);
+
/* release what we no longer need */
objRelease(errmsg, CORE_COMPONENT);
objRelease(glbl, CORE_COMPONENT);
+ objRelease(statsobj, CORE_COMPONENT);
objRelease(datetime, CORE_COMPONENT);
objRelease(prop, CORE_COMPONENT);
objRelease(ruleset, CORE_COMPONENT);
@@ -642,16 +818,27 @@ ENDisCompatibleWithFeature
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
+CODEqueryEtryPt_STD_CONF2_QUERIES
+CODEqueryEtryPt_STD_CONF2_PREPRIVDROP_QUERIES
CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
ENDqueryEtryPt
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
- if(pszBindAddr != NULL) {
- free(pszBindAddr);
- pszBindAddr = NULL;
+ if(cs.pszBindAddr != NULL) {
+ free(cs.pszBindAddr);
+ cs.pszBindAddr = NULL;
+ }
+ if(cs.pszSchedPolicy != NULL) {
+ free(cs.pszSchedPolicy);
+ cs.pszSchedPolicy = NULL;
+ }
+ if(cs.pszBindRuleset != NULL) {
+ free(cs.pszBindRuleset);
+ cs.pszBindRuleset = NULL;
}
- iTimeRequery = TIME_REQUERY_DFLT;/* the default is to query only every second time */
+ cs.iSchedPrio = SCHED_PRIO_UNSET;
+ cs.iTimeRequery = TIME_REQUERY_DFLT;/* the default is to query only every second time */
return RS_RET_OK;
}
@@ -662,24 +849,30 @@ CODESTARTmodInit
CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(errmsg, CORE_COMPONENT));
CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(statsobj, CORE_COMPONENT));
CHKiRet(objUse(datetime, CORE_COMPONENT));
CHKiRet(objUse(prop, CORE_COMPONENT));
CHKiRet(objUse(ruleset, CORE_COMPONENT));
CHKiRet(objUse(net, LM_NET_FILENAME));
+ /* we need to create the inputName property (only once during our lifetime) */
+ CHKiRet(prop.Construct(&pInputName));
+ CHKiRet(prop.SetString(pInputName, UCHAR_CONSTANT("imudp"), sizeof("imudp") - 1));
+ CHKiRet(prop.ConstructFinalize(pInputName));
+
/* register config file handlers */
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputudpserverbindruleset", 0, eCmdHdlrGetWord,
- setRuleset, NULL, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.pszBindRuleset, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"udpserverrun", 0, eCmdHdlrGetWord,
- addListner, NULL, STD_LOADABLE_MODULE_ID));
+ addInstance, NULL, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"udpserveraddress", 0, eCmdHdlrGetWord,
- NULL, &pszBindAddr, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.pszBindAddr, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"imudpschedulingpolicy", 0, eCmdHdlrGetWord,
- &set_scheduling_policy, NULL, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.pszSchedPolicy, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"imudpschedulingpriority", 0, eCmdHdlrInt,
- &set_scheduling_priority, NULL, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.iSchedPrio, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"udpservertimerequery", 0, eCmdHdlrInt,
- NULL, &iTimeRequery, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.iTimeRequery, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
diff --git a/plugins/imuxsock/imuxsock.c b/plugins/imuxsock/imuxsock.c
index 251f478..fe04c8f 100644
--- a/plugins/imuxsock/imuxsock.c
+++ b/plugins/imuxsock/imuxsock.c
@@ -34,6 +34,7 @@
#include <string.h>
#include <errno.h>
#include <unistd.h>
+#include <fcntl.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <sys/socket.h>
@@ -57,6 +58,7 @@
MODULE_TYPE_INPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("imuxsock")
/* defines */
#define MAXFUNIX 50
@@ -74,6 +76,9 @@ MODULE_TYPE_NOKEEP
#define SYSTEMD_PATH_LOG SYSTEMD_JOURNAL "/syslog"
#endif
+/* forward definitions */
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal);
+
/* emulate struct ucred for platforms that do not have it */
#ifndef HAVE_SCM_CREDENTIALS
struct ucred { int pid; };
@@ -89,10 +94,12 @@ DEF_IMOD_STATIC_DATA
DEFobjCurrIf(errmsg)
DEFobjCurrIf(glbl)
DEFobjCurrIf(prop)
+DEFobjCurrIf(net)
DEFobjCurrIf(parser)
DEFobjCurrIf(datetime)
DEFobjCurrIf(statsobj)
+
statsobj_t *modStats;
STATSCOUNTER_DEF(ctrSubmit, mutCtrSubmit)
STATSCOUNTER_DEF(ctrLostRatelimit, mutCtrLostRatelimit)
@@ -141,7 +148,9 @@ typedef struct lstn_s {
sbool bParseHost; /* should parser parse host name? read-only after startup */
sbool bCreatePath; /* auto-creation of socket directory? */
sbool bUseCreds; /* pull original creator credentials from socket */
+ sbool bAnnotate; /* annotate events with trusted properties */
sbool bWritePid; /* write original PID into tag */
+ sbool bUseSysTimeStamp; /* use timestamp from system (instead of from message) */
} lstn_t;
static lstn_t listeners[MAXFUNIX];
@@ -154,26 +163,64 @@ static int startIndexUxLocalSockets; /* process fd from that index on (used to
static int nfd = 1; /* number of Unix sockets open / read-only after startup */
static int sd_fds = 0; /* number of systemd activated sockets */
-/* config settings */
-static int bOmitLocalLogging = 0;
-static uchar *pLogSockName = NULL;
-static uchar *pLogHostName = NULL; /* host name to use with this socket */
-static int bUseFlowCtl = 0; /* use flow control or not (if yes, only LIGHT is used! */
-static int bIgnoreTimestamp = 1; /* ignore timestamps present in the incoming message? */
-static int bWritePid = 0; /* use credentials from recvmsg() and fixup PID in TAG */
-static int bWritePidSysSock = 0; /* use credentials from recvmsg() and fixup PID in TAG */
+/* config vars for legacy config system */
#define DFLT_bCreatePath 0
-static int bCreatePath = DFLT_bCreatePath; /* auto-create socket path? */
-#define DFLT_ratelimitInterval 5
-static int ratelimitInterval = DFLT_ratelimitInterval; /* interval in seconds, 0 = off */
-static int ratelimitIntervalSysSock = DFLT_ratelimitInterval;
+#define DFLT_ratelimitInterval 0
#define DFLT_ratelimitBurst 200
-static int ratelimitBurst = DFLT_ratelimitBurst; /* max nbr of messages in interval */
-static int ratelimitBurstSysSock = DFLT_ratelimitBurst; /* max nbr of messages in interval */
#define DFLT_ratelimitSeverity 1 /* do not rate-limit emergency messages */
-static int ratelimitSeverity = DFLT_ratelimitSeverity;
-static int ratelimitSeveritySysSock = DFLT_ratelimitSeverity;
+static struct configSettings_s {
+ int bOmitLocalLogging;
+ uchar *pLogSockName;
+ uchar *pLogHostName; /* host name to use with this socket */
+ int bUseFlowCtl; /* use flow control or not (if yes, only LIGHT is used! */
+ int bIgnoreTimestamp; /* ignore timestamps present in the incoming message? */
+ int bUseSysTimeStamp; /* use timestamp from system (rather than from message) */
+ int bUseSysTimeStampSysSock; /* same, for system log socket */
+ int bWritePid; /* use credentials from recvmsg() and fixup PID in TAG */
+ int bWritePidSysSock; /* use credentials from recvmsg() and fixup PID in TAG */
+ int bCreatePath; /* auto-create socket path? */
+ int ratelimitInterval; /* interval in seconds, 0 = off */
+ int ratelimitIntervalSysSock;
+ int ratelimitBurst; /* max nbr of messages in interval */
+ int ratelimitBurstSysSock;
+ int ratelimitSeverity;
+ int ratelimitSeveritySysSock;
+ int bAnnotate; /* annotate trusted properties */
+ int bAnnotateSysSock; /* same, for system log socket */
+} cs;
+
+struct instanceConf_s {
+ uchar *sockName;
+ uchar *pLogHostName; /* host name to use with this socket */
+ sbool bUseFlowCtl; /* use flow control or not (if yes, only LIGHT is used! */
+ sbool bIgnoreTimestamp; /* ignore timestamps present in the incoming message? */
+ sbool bWritePid; /* use credentials from recvmsg() and fixup PID in TAG */
+ sbool bUseSysTimeStamp; /* use timestamp from system (instead of from message) */
+ int bCreatePath; /* auto-create socket path? */
+ int ratelimitInterval; /* interval in seconds, 0 = off */
+ int ratelimitBurst; /* max nbr of messages in interval */
+ int ratelimitSeverity;
+ int bAnnotate; /* annotate trusted properties */
+ struct instanceConf_s *next;
+};
+
+struct modConfData_s {
+ rsconf_t *pConf; /* our overall config object */
+ instanceConf_t *root, *tail;
+ uchar *pLogSockName;
+ int ratelimitIntervalSysSock;
+ int ratelimitBurstSysSock;
+ int ratelimitSeveritySysSock;
+ sbool bOmitLocalLogging;
+ sbool bWritePidSysSock;
+ int bAnnotateSysSock;
+ sbool bUseSysTimeStamp;
+};
+static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
+static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current load process */
+/* we do not use this, because we do not bind to a ruleset so far
+ * enable when this is changed: #include "im-helper.h" */ /* must be included AFTER the type definitions! */
static void
@@ -262,6 +309,56 @@ static rsRetVal setSystemLogFlowControl(void __attribute__((unused)) *pVal, int
RETiRet;
}
+
+/* This function is called when a new listen socket instace shall be added to
+ * the current config object via the legacy config system. It just shuffles
+ * all parameters to the listener in-memory instance.
+ * rgerhards, 2011-05-12
+ */
+static rsRetVal addInstance(void __attribute__((unused)) *pVal, uchar *pNewVal)
+{
+ instanceConf_t *inst;
+ DEFiRet;
+
+ if(pNewVal == NULL || pNewVal[0] == '\0') {
+ errmsg.LogError(0, RS_RET_SOCKNAME_MISSING , "imuxsock: socket name must be specified, "
+ "but is not - listener not created\n");
+ if(pNewVal != NULL)
+ free(pNewVal);
+ ABORT_FINALIZE(RS_RET_SOCKNAME_MISSING);
+ }
+
+ CHKmalloc(inst = MALLOC(sizeof(instanceConf_t)));
+ inst->sockName = pNewVal;
+ inst->ratelimitInterval = cs.ratelimitInterval;
+ inst->pLogHostName = cs.pLogHostName;
+ inst->ratelimitBurst = cs.ratelimitBurst;
+ inst->ratelimitSeverity = cs.ratelimitSeverity;
+ inst->bUseFlowCtl = cs.bUseFlowCtl;
+ inst->bIgnoreTimestamp = cs.bIgnoreTimestamp;
+ inst->bCreatePath = cs.bCreatePath;
+ inst->bUseSysTimeStamp = cs.bUseSysTimeStamp;
+ inst->bWritePid = cs.bWritePid;
+ inst->bAnnotate = cs.bAnnotate;
+ inst->next = NULL;
+
+ /* node created, let's add to config */
+ if(loadModConf->tail == NULL) {
+ loadModConf->tail = loadModConf->root = inst;
+ } else {
+ loadModConf->tail->next = inst;
+ loadModConf->tail = inst;
+ }
+
+ /* some legacy conf processing */
+ free(cs.pLogHostName); /* reset hostname for next socket */
+ cs.pLogHostName = NULL;
+
+finalize_it:
+ RETiRet;
+}
+
+
/* add an additional listen socket. Socket names are added
* until the array is filled up. It is never reset, only at
* module unload.
@@ -271,47 +368,46 @@ static rsRetVal setSystemLogFlowControl(void __attribute__((unused)) *pVal, int
* added capability to specify hostname for socket -- rgerhards, 2008-08-01
*/
static rsRetVal
-addLstnSocketName(void __attribute__((unused)) *pVal, uchar *pNewVal)
+addListner(instanceConf_t *inst)
{
DEFiRet;
if(nfd < MAXFUNIX) {
- if(*pNewVal == ':') {
+ if(*inst->sockName == ':') {
listeners[nfd].bParseHost = 1;
} else {
listeners[nfd].bParseHost = 0;
}
- if(pLogHostName == NULL) {
+ if(inst->pLogHostName == NULL) {
listeners[nfd].hostName = NULL;
} else {
CHKiRet(prop.Construct(&(listeners[nfd].hostName)));
- CHKiRet(prop.SetString(listeners[nfd].hostName, pLogHostName, ustrlen(pLogHostName)));
+ CHKiRet(prop.SetString(listeners[nfd].hostName, inst->pLogHostName, ustrlen(inst->pLogHostName)));
CHKiRet(prop.ConstructFinalize(listeners[nfd].hostName));
- /* reset hostname for next socket */
- free(pLogHostName);
- pLogHostName = NULL;
}
- if(ratelimitInterval > 0) {
+ if(inst->ratelimitInterval > 0) {
if((listeners[nfd].ht = create_hashtable(100, hash_from_key_fn, key_equals_fn, NULL)) == NULL) {
- /* in this case, we simply turn of rate-limiting */
+ /* in this case, we simply turn off rate-limiting */
dbgprintf("imuxsock: turning off rate limiting because we could not "
"create hash table\n");
- ratelimitInterval = 0;
+ inst->ratelimitInterval = 0;
}
}
- listeners[nfd].ratelimitInterval = ratelimitInterval;
- listeners[nfd].ratelimitBurst = ratelimitBurst;
- listeners[nfd].ratelimitSev = ratelimitSeverity;
- listeners[nfd].flowCtl = bUseFlowCtl ? eFLOWCTL_LIGHT_DELAY : eFLOWCTL_NO_DELAY;
- listeners[nfd].flags = bIgnoreTimestamp ? IGNDATE : NOFLAG;
- listeners[nfd].bCreatePath = bCreatePath;
- listeners[nfd].sockName = pNewVal;
- listeners[nfd].bUseCreds = (bWritePid || ratelimitInterval) ? 1 : 0;
- listeners[nfd].bWritePid = bWritePid;
+ listeners[nfd].ratelimitInterval = inst->ratelimitInterval;
+ listeners[nfd].ratelimitBurst = inst->ratelimitBurst;
+ listeners[nfd].ratelimitSev = inst->ratelimitSeverity;
+ listeners[nfd].flowCtl = inst->bUseFlowCtl ? eFLOWCTL_LIGHT_DELAY : eFLOWCTL_NO_DELAY;
+ listeners[nfd].flags = inst->bIgnoreTimestamp ? IGNDATE : NOFLAG;
+ listeners[nfd].bCreatePath = inst->bCreatePath;
+ listeners[nfd].sockName = ustrdup(inst->sockName);
+ listeners[nfd].bUseCreds = (inst->bWritePid || inst->ratelimitInterval || inst->bAnnotate) ? 1 : 0;
+ listeners[nfd].bAnnotate = inst->bAnnotate;
+ listeners[nfd].bWritePid = inst->bWritePid;
+ listeners[nfd].bUseSysTimeStamp = inst->bUseSysTimeStamp;
nfd++;
} else {
errmsg.LogError(0, NO_ERRCODE, "Out of unix socket name descriptors, ignoring %s\n",
- pNewVal);
+ inst->sockName);
}
finalize_it:
@@ -363,7 +459,8 @@ createLogSocket(lstn_t *pLstn)
chmod((char*)pLstn->sockName, 0666) < 0) {
errmsg.LogError(errno, NO_ERRCODE, "cannot create '%s'", pLstn->sockName);
dbgprintf("cannot create %s (%d).\n", pLstn->sockName, errno);
- close(pLstn->fd);
+ if(pLstn->fd != -1)
+ close(pLstn->fd);
pLstn->fd = -1;
ABORT_FINALIZE(RS_RET_ERR_CRE_AFUX);
}
@@ -421,15 +518,22 @@ openLogSocket(lstn_t *pLstn)
errmsg.LogError(errno, NO_ERRCODE, "set SCM_CREDENTIALS failed on '%s'", pLstn->sockName);
pLstn->bUseCreds = 0;
}
+// TODO: move to its own #if
+ if(setsockopt(pLstn->fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) != 0) {
+ errmsg.LogError(errno, NO_ERRCODE, "set SO_TIMESTAMP failed on '%s'", pLstn->sockName);
+ }
}
# else /* HAVE_SCM_CREDENTIALS */
pLstn->bUseCreds = 0;
+ pLstn->bAnnotate = 0;
# endif /* HAVE_SCM_CREDENTIALS */
finalize_it:
if(iRet != RS_RET_OK) {
- close(pLstn->fd);
- pLstn->fd = -1;
+ if(pLstn->fd != -1) {
+ close(pLstn->fd);
+ pLstn->fd = -1;
+ }
}
RETiRet;
@@ -506,12 +610,109 @@ fixPID(uchar *bufTAG, int *lenTag, struct ucred *cred)
}
+/* Get an "trusted property" from the system. Returns an empty string if the
+ * property can not be obtained. Inspired by similiar functionality inside
+ * journald. Currently works with Linux /proc filesystem, only.
+ */
+static rsRetVal
+getTrustedProp(struct ucred *cred, char *propName, uchar *buf, size_t lenBuf, int *lenProp)
+{
+ int fd;
+ int i;
+ int lenRead;
+ char namebuf[1024];
+ DEFiRet;
+
+ if(snprintf(namebuf, sizeof(namebuf), "/proc/%lu/%s", (long unsigned) cred->pid,
+ propName) >= (int) sizeof(namebuf)) {
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+ if((fd = open(namebuf, O_RDONLY)) == -1) {
+ DBGPRINTF("error reading '%s'\n", namebuf);
+ *lenProp = 0;
+ FINALIZE;
+ }
+ if((lenRead = read(fd, buf, lenBuf - 1)) == -1) {
+ DBGPRINTF("error reading file data for '%s'\n", namebuf);
+ *lenProp = 0;
+ close(fd);
+ FINALIZE;
+ }
+
+ /* we strip after the first \n */
+ for(i = 0 ; i < lenRead ; ++i) {
+ if(buf[i] == '\n')
+ break;
+ else if(iscntrl(buf[i]))
+ buf[i] = ' ';
+ }
+ buf[i] = '\0';
+ *lenProp = i;
+
+ close(fd);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* read the exe trusted property path (so far, /proc fs only)
+ */
+static rsRetVal
+getTrustedExe(struct ucred *cred, uchar *buf, size_t lenBuf, int* lenProp)
+{
+ int lenRead;
+ char namebuf[1024];
+ DEFiRet;
+
+ if(snprintf(namebuf, sizeof(namebuf), "/proc/%lu/exe", (long unsigned) cred->pid)
+ >= (int) sizeof(namebuf)) {
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+ if((lenRead = readlink(namebuf, (char*)buf, lenBuf - 1)) == -1) {
+ DBGPRINTF("error reading link '%s'\n", namebuf);
+ *lenProp = 0;
+ FINALIZE;
+ }
+
+ buf[lenRead] = '\0';
+ *lenProp = lenRead;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* copy a trusted property in escaped mode. That is, the property can contain
+ * any character and so it must be properly quoted AND escaped.
+ * It is assumed the output buffer is large enough. Returns the number of
+ * characters added.
+ */
+static inline int
+copyescaped(uchar *dstbuf, uchar *inbuf, int inlen)
+{
+ int iDst, iSrc;
+
+ *dstbuf = '"';
+ for(iDst=1, iSrc=0 ; iSrc < inlen ; ++iDst, ++iSrc) {
+ if(inbuf[iSrc] == '"' || inbuf[iSrc] == '\\') {
+ dstbuf[iDst++] = '\\';
+ }
+ dstbuf[iDst] = inbuf[iSrc];
+ }
+ dstbuf[iDst++] = '"';
+ return iDst;
+}
+
+
/* submit received message to the queue engine
* We now parse the message according to expected format so that we
* can also mangle it if necessary.
*/
static inline rsRetVal
-SubmitMsg(uchar *pRcv, int lenRcv, lstn_t *pLstn, struct ucred *cred)
+SubmitMsg(uchar *pRcv, int lenRcv, lstn_t *pLstn, struct ucred *cred, struct timeval *ts)
{
msg_t *pMsg;
int lenMsg;
@@ -525,6 +726,12 @@ SubmitMsg(uchar *pRcv, int lenRcv, lstn_t *pLstn, struct ucred *cred)
struct syslogTime st;
time_t tt;
rs_ratelimit_state_t *ratelimiter = NULL;
+ int lenProp;
+ uchar propBuf[1024];
+ uchar msgbuf[8192];
+ uchar *pmsgbuf;
+ int toffs; /* offset for trusted properties */
+ struct syslogTime dummyTS;
DEFiRet;
/* TODO: handle format errors?? */
@@ -550,12 +757,58 @@ SubmitMsg(uchar *pRcv, int lenRcv, lstn_t *pLstn, struct ucred *cred)
findRatelimiter(pLstn, cred, &ratelimiter); /* ignore error, better so than others... */
}
- datetime.getCurrTime(&st, &tt);
+ if(ts == NULL) {
+ datetime.getCurrTime(&st, &tt);
+ } else {
+ datetime.timeval2syslogTime(ts, &st);
+ tt = ts->tv_sec;
+ }
+
if(ratelimiter != NULL && !withinRatelimit(ratelimiter, tt, cred->pid)) {
STATSCOUNTER_INC(ctrLostRatelimit, mutCtrLostRatelimit);
FINALIZE;
}
+ /* created trusted properties */
+ if(cred != NULL && pLstn->bAnnotate) {
+ if((unsigned) (lenRcv + 4096) < sizeof(msgbuf)) {
+ pmsgbuf = msgbuf;
+ } else {
+ CHKmalloc(pmsgbuf = malloc(lenRcv+4096));
+ }
+ memcpy(pmsgbuf, pRcv, lenRcv);
+ memcpy(pmsgbuf+lenRcv, " @[", 3);
+ toffs = lenRcv + 3; /* next free location */
+ lenProp = snprintf((char*)propBuf, sizeof(propBuf), "_PID=%lu _UID=%lu _GID=%lu",
+ (long unsigned) cred->pid, (long unsigned) cred->uid,
+ (long unsigned) cred->gid);
+ memcpy(pmsgbuf+toffs, propBuf, lenProp);
+ toffs = toffs + lenProp;
+ getTrustedProp(cred, "comm", propBuf, sizeof(propBuf), &lenProp);
+ if(lenProp) {
+ memcpy(pmsgbuf+toffs, " _COMM=", 7);
+ memcpy(pmsgbuf+toffs+7, propBuf, lenProp);
+ toffs = toffs + 7 + lenProp;
+ }
+ getTrustedExe(cred, propBuf, sizeof(propBuf), &lenProp);
+ if(lenProp) {
+ memcpy(pmsgbuf+toffs, " _EXE=", 6);
+ memcpy(pmsgbuf+toffs+6, propBuf, lenProp);
+ toffs = toffs + 6 + lenProp;
+ }
+ getTrustedProp(cred, "cmdline", propBuf, sizeof(propBuf), &lenProp);
+ if(lenProp) {
+ memcpy(pmsgbuf+toffs, " _CMDLINE=", 10);
+ toffs = toffs + 10 +
+ copyescaped(pmsgbuf+toffs+10, propBuf, lenProp);
+ }
+ /* finalize string */
+ pmsgbuf[toffs] = ']';
+ pmsgbuf[toffs+1] = '\0';
+ pRcv = pmsgbuf;
+ lenRcv = toffs + 1;
+ }
+
/* we now create our own message object and submit it to the queue */
CHKiRet(msgConstructWithTime(&pMsg, &st, tt));
MsgSetRawMsg(pMsg, (char*)pRcv, lenRcv);
@@ -570,15 +823,27 @@ SubmitMsg(uchar *pRcv, int lenRcv, lstn_t *pLstn, struct ucred *cred)
parse++; lenMsg--; /* '>' */
- if((pLstn->flags & IGNDATE)) {
- /* in this case, we still need to find out if we have a valid
- * datestamp or not .. and advance the parse pointer accordingly.
- */
- struct syslogTime dummy;
- datetime.ParseTIMESTAMP3164(&dummy, &parse, &lenMsg);
- } else {
- if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &parse, &lenMsg) != RS_RET_OK) {
- DBGPRINTF("we have a problem, invalid timestamp in msg!\n");
+ if(ts == NULL) {
+ if((pLstn->flags & IGNDATE)) {
+ /* in this case, we still need to find out if we have a valid
+ * datestamp or not .. and advance the parse pointer accordingly.
+ */
+ datetime.ParseTIMESTAMP3164(&dummyTS, &parse, &lenMsg);
+ } else {
+ if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &parse, &lenMsg) != RS_RET_OK) {
+ DBGPRINTF("we have a problem, invalid timestamp in msg!\n");
+ }
+ }
+ } else { /* if we pulled the time from the system, we need to update the message text */
+ uchar *tmpParse = parse; /* just to check correctness of TS */
+ if(datetime.ParseTIMESTAMP3164(&dummyTS, &tmpParse, &lenMsg) == RS_RET_OK) {
+ /* We modify the message only if it contained a valid timestamp,
+ * otherwise we do not touch it at all. */
+ datetime.formatTimestamp3164(&st, (char*)parse, 0);
+ parse[15] = ' '; /* re-write \0 from fromatTimestamp3164 by SP */
+ /* update "counters" to reflect processed timestamp */
+ parse += 16;
+ lenMsg -= 16;
}
}
@@ -594,7 +859,11 @@ SubmitMsg(uchar *pRcv, int lenRcv, lstn_t *pLstn, struct ucred *cred)
fixPID(bufParseTAG, &i, cred);
MsgSetTAG(pMsg, bufParseTAG, i);
- MsgSetMSGoffs(pMsg, pMsg->iLenRawMsg - lenMsg);
+ if (pLstn->bAnnotate) {
+ MsgSetMSGoffs(pMsg, pMsg->iLenRawMsg - lenMsg - 16);
+ } else {
+ MsgSetMSGoffs(pMsg, pMsg->iLenRawMsg - lenMsg);
+ }
if(pLstn->bParseHost) {
pMsg->msgFlags = pLstn->flags | PARSE_HOSTNAME;
@@ -630,6 +899,7 @@ static rsRetVal readSocket(lstn_t *pLstn)
struct cmsghdr *cm;
# endif
struct ucred *cred;
+ struct timeval *ts;
uchar bufRcv[4096+1];
char aux[128];
uchar *pRcv = NULL; /* receive buffer */
@@ -668,21 +938,28 @@ static rsRetVal readSocket(lstn_t *pLstn)
dbgprintf("Message from UNIX socket: #%d\n", pLstn->fd);
if(iRcvd > 0) {
cred = NULL;
-# if HAVE_SCM_CREDENTIALS
- if(pLstn->bUseCreds) {
- dbgprintf("XXX: pre CM loop, length of control message %d\n", (int) msgh.msg_controllen);
- for (cm = CMSG_FIRSTHDR(&msgh); cm; cm = CMSG_NXTHDR(&msgh, cm)) {
- dbgprintf("XXX: in CM loop, %d, %d\n", cm->cmsg_level, cm->cmsg_type);
- if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SCM_CREDENTIALS) {
+ ts = NULL;
+ if(pLstn->bUseCreds || pLstn->bUseSysTimeStamp) {
+ for(cm = CMSG_FIRSTHDR(&msgh); cm; cm = CMSG_NXTHDR(&msgh, cm)) {
+# if HAVE_SCM_CREDENTIALS
+ if( pLstn->bUseCreds
+ && cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SCM_CREDENTIALS) {
cred = (struct ucred*) CMSG_DATA(cm);
- dbgprintf("XXX: got credentials pid %d\n", (int) cred->pid);
break;
}
+# endif /* HAVE_SCM_CREDENTIALS */
+# if HAVE_SO_TIMESTAMP
+ if( pLstn->bUseSysTimeStamp
+ && cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP) {
+ ts = (struct timeval *)CMSG_DATA(cm);
+ dbgprintf("XXX: got timestamp %ld.%ld\n",
+ (long) ts->tv_sec, (long) ts->tv_usec);
+ break;
+ }
+# endif /* HAVE_SO_TIMESTAMP */
}
- dbgprintf("XXX: post CM loop\n");
}
-# endif /* HAVE_SCM_CREDENTIALS */
- CHKiRet(SubmitMsg(pRcv, iRcvd, pLstn, cred));
+ CHKiRet(SubmitMsg(pRcv, iRcvd, pLstn, cred, ts));
} else if(iRcvd < 0 && errno != EINTR) {
char errStr[1024];
rs_strerror_r(errno, errStr, sizeof(errStr));
@@ -698,6 +975,127 @@ finalize_it:
}
+/* activate current listeners */
+static inline rsRetVal
+activateListeners()
+{
+ register int i;
+ int actSocks;
+ DEFiRet;
+
+ /* first apply some config settings */
+# ifdef OS_SOLARIS
+ /* under solaris, we must NEVER process the local log socket, because
+ * it is implemented there differently. If we used it, we would actually
+ * delete it and render the system partly unusable. So don't do that.
+ * rgerhards, 2010-03-26
+ */
+ startIndexUxLocalSockets = 1;
+# else
+ startIndexUxLocalSockets = runModConf->bOmitLocalLogging ? 1 : 0;
+# endif
+ if(runModConf->pLogSockName != NULL)
+ listeners[0].sockName = runModConf->pLogSockName;
+ else if(sd_booted()) {
+ struct stat st;
+ if(stat(SYSTEMD_PATH_LOG, &st) != -1 && S_ISSOCK(st.st_mode)) {
+ listeners[0].sockName = (uchar*) SYSTEMD_PATH_LOG;
+ }
+ }
+ if(runModConf->ratelimitIntervalSysSock > 0) {
+ if((listeners[0].ht = create_hashtable(100, hash_from_key_fn, key_equals_fn, NULL)) == NULL) {
+ /* in this case, we simply turn of rate-limiting */
+ errmsg.LogError(0, NO_ERRCODE, "imuxsock: turning off rate limiting because we could not "
+ "create hash table\n");
+ runModConf->ratelimitIntervalSysSock = 0;
+ }
+ }
+ listeners[0].ratelimitInterval = runModConf->ratelimitIntervalSysSock;
+ listeners[0].ratelimitBurst = runModConf->ratelimitBurstSysSock;
+ listeners[0].ratelimitSev = runModConf->ratelimitSeveritySysSock;
+ listeners[0].bUseCreds = (runModConf->bWritePidSysSock || runModConf->ratelimitIntervalSysSock) ? 1 : 0;
+ listeners[0].bWritePid = runModConf->bWritePidSysSock;
+ listeners[0].bAnnotate = runModConf->bAnnotateSysSock;
+ listeners[0].bUseSysTimeStamp = runModConf->bUseSysTimeStamp;
+
+ sd_fds = sd_listen_fds(0);
+ if(sd_fds < 0) {
+ errmsg.LogError(-sd_fds, NO_ERRCODE, "imuxsock: Failed to acquire systemd socket");
+ ABORT_FINALIZE(RS_RET_ERR_CRE_AFUX);
+ }
+
+ /* initialize and return if will run or not */
+ actSocks = 0;
+ for (i = startIndexUxLocalSockets ; i < nfd ; i++) {
+ if(openLogSocket(&(listeners[i])) == RS_RET_OK) {
+ ++actSocks;
+ dbgprintf("imuxsock: Opened UNIX socket '%s' (fd %d).\n",
+ listeners[i].sockName, listeners[i].fd);
+ }
+ }
+
+ if(actSocks == 0) {
+ errmsg.LogError(0, NO_ERRCODE, "imuxsock does not run because we could not aquire any socket\n");
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+
+BEGINbeginCnfLoad
+CODESTARTbeginCnfLoad
+ loadModConf = pModConf;
+ pModConf->pConf = pConf;
+ /* reset legacy config vars */
+ resetConfigVariables(NULL, NULL);
+ENDbeginCnfLoad
+
+
+BEGINendCnfLoad
+CODESTARTendCnfLoad
+ /* persist module-specific settings from legacy config system */
+ loadModConf->bOmitLocalLogging = cs.bOmitLocalLogging;
+ loadModConf->pLogSockName = cs.pLogSockName;
+
+ loadModConf = NULL; /* done loading */
+ /* free legacy config vars */
+ free(cs.pLogHostName);
+ cs.pLogSockName = NULL;
+ cs.pLogHostName = NULL;
+ENDendCnfLoad
+
+
+BEGINcheckCnf
+CODESTARTcheckCnf
+ENDcheckCnf
+
+
+BEGINactivateCnfPrePrivDrop
+ instanceConf_t *inst;
+CODESTARTactivateCnfPrePrivDrop
+ runModConf = pModConf;
+ for(inst = runModConf->root ; inst != NULL ; inst = inst->next) {
+ addListner(inst);
+ }
+ CHKiRet(activateListeners());
+finalize_it:
+ENDactivateCnfPrePrivDrop
+
+
+BEGINactivateCnf
+CODESTARTactivateCnf
+ENDactivateCnf
+
+
+BEGINfreeCnf
+CODESTARTfreeCnf
+ free(pModConf->pLogSockName);
+ENDfreeCnf
+
+
/* This function is called to gather input. */
BEGINrunInput
int maxfds;
@@ -765,74 +1163,12 @@ ENDrunInput
BEGINwillRun
CODESTARTwillRun
- register int i;
- int actSocks;
-
- /* first apply some config settings */
-# ifdef OS_SOLARIS
- /* under solaris, we must NEVER process the local log socket, because
- * it is implemented there differently. If we used it, we would actually
- * delete it and render the system partly unusable. So don't do that.
- * rgerhards, 2010-03-26
- */
- startIndexUxLocalSockets = 1;
-# else
- startIndexUxLocalSockets = bOmitLocalLogging ? 1 : 0;
-# endif
- if(pLogSockName != NULL)
- listeners[0].sockName = pLogSockName;
- else if(sd_booted()) {
- struct stat st;
- if(stat(SYSTEMD_JOURNAL, &st) != -1 && S_ISDIR(st.st_mode)) {
- listeners[0].sockName = (uchar*) SYSTEMD_PATH_LOG;
- }
- }
- if(ratelimitIntervalSysSock > 0) {
- if((listeners[0].ht = create_hashtable(100, hash_from_key_fn, key_equals_fn, NULL)) == NULL) {
- /* in this case, we simply turn of rate-limiting */
- dbgprintf("imuxsock: turning off rate limiting because we could not "
- "create hash table\n");
- ratelimitIntervalSysSock = 0;
- }
- }
- listeners[0].ratelimitInterval = ratelimitIntervalSysSock;
- listeners[0].ratelimitBurst = ratelimitBurstSysSock;
- listeners[0].ratelimitSev = ratelimitSeveritySysSock;
- listeners[0].bUseCreds = (bWritePidSysSock || ratelimitIntervalSysSock) ? 1 : 0;
- listeners[0].bWritePid = bWritePidSysSock;
-
- sd_fds = sd_listen_fds(0);
- if (sd_fds < 0) {
- errmsg.LogError(-sd_fds, NO_ERRCODE, "imuxsock: Failed to acquire systemd socket");
- ABORT_FINALIZE(RS_RET_ERR_CRE_AFUX);
- }
-
- /* initialize and return if will run or not */
- actSocks = 0;
- for (i = startIndexUxLocalSockets ; i < nfd ; i++) {
- if(openLogSocket(&(listeners[i])) == RS_RET_OK) {
- ++actSocks;
- dbgprintf("imuxsock: Opened UNIX socket '%s' (fd %d).\n", listeners[i].sockName, listeners[i].fd);
- }
- }
-
- if(actSocks == 0) {
- errmsg.LogError(0, NO_ERRCODE, "imuxsock does not run because we could not aquire any socket\n");
- ABORT_FINALIZE(RS_RET_ERR);
- }
-
- /* we need to create the inputName property (only once during our lifetime) */
- CHKiRet(prop.Construct(&pInputName));
- CHKiRet(prop.SetString(pInputName, UCHAR_CONSTANT("imuxsock"), sizeof("imuxsock") - 1));
- CHKiRet(prop.ConstructFinalize(pInputName));
-
-finalize_it:
ENDwillRun
BEGINafterRun
-CODESTARTafterRun
int i;
+CODESTARTafterRun
/* do cleanup here */
/* Close the UNIX sockets. */
for (i = 0; i < nfd; i++)
@@ -842,7 +1178,6 @@ CODESTARTafterRun
/* Clean-up files. */
for(i = startIndexUxLocalSockets; i < nfd; i++)
if (listeners[i].sockName && listeners[i].fd != -1) {
-
/* If systemd passed us a socket it is systemd's job to clean it up.
* Do not unlink it -- we will get same socket (node) from systemd
* e.g. on restart again.
@@ -855,20 +1190,17 @@ CODESTARTafterRun
DBGPRINTF("imuxsock: unlinking unix socket file[%d] %s\n", i, listeners[i].sockName);
unlink((char*) listeners[i].sockName);
}
- /* free no longer needed string */
- free(pLogSockName);
- free(pLogHostName);
discardLogSockets();
nfd = 1;
-
- if(pInputName != NULL)
- prop.Destruct(&pInputName);
ENDafterRun
BEGINmodExit
CODESTARTmodExit
+ if(pInputName != NULL)
+ prop.Destruct(&pInputName);
+
statsobj.Destruct(&modStats);
objRelease(parser, CORE_COMPONENT);
@@ -890,34 +1222,33 @@ ENDisCompatibleWithFeature
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
+CODEqueryEtryPt_STD_CONF2_QUERIES
+CODEqueryEtryPt_STD_CONF2_PREPRIVDROP_QUERIES
CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
ENDqueryEtryPt
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
- bOmitLocalLogging = 0;
- if(pLogSockName != NULL) {
- free(pLogSockName);
- pLogSockName = NULL;
- }
- if(pLogHostName != NULL) {
- free(pLogHostName);
- pLogHostName = NULL;
- }
-
- discardLogSockets();
- nfd = 1;
- bIgnoreTimestamp = 1;
- bUseFlowCtl = 0;
- bWritePid = 0;
- bWritePidSysSock = 0;
- bCreatePath = DFLT_bCreatePath;
- ratelimitInterval = DFLT_ratelimitInterval;
- ratelimitIntervalSysSock = DFLT_ratelimitInterval;
- ratelimitBurst = DFLT_ratelimitBurst;
- ratelimitBurstSysSock = DFLT_ratelimitBurst;
- ratelimitSeverity = DFLT_ratelimitSeverity;
- ratelimitSeveritySysSock = DFLT_ratelimitSeverity;
+ free(cs.pLogSockName);
+ cs.pLogSockName = NULL;
+ free(cs.pLogHostName);
+ cs.bOmitLocalLogging = 0;
+ cs.pLogHostName = NULL;
+ cs.bIgnoreTimestamp = 1;
+ cs.bUseFlowCtl = 0;
+ cs.bUseSysTimeStamp = 1;
+ cs.bUseSysTimeStampSysSock = 1;
+ cs.bWritePid = 0;
+ cs.bWritePidSysSock = 0;
+ cs.bAnnotate = 0;
+ cs.bAnnotateSysSock = 0;
+ cs.bCreatePath = DFLT_bCreatePath;
+ cs.ratelimitInterval = DFLT_ratelimitInterval;
+ cs.ratelimitIntervalSysSock = DFLT_ratelimitInterval;
+ cs.ratelimitBurst = DFLT_ratelimitBurst;
+ cs.ratelimitBurstSysSock = DFLT_ratelimitBurst;
+ cs.ratelimitSeverity = DFLT_ratelimitSeverity;
+ cs.ratelimitSeveritySysSock = DFLT_ratelimitSeverity;
return RS_RET_OK;
}
@@ -930,6 +1261,7 @@ CODESTARTmodInit
CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(errmsg, CORE_COMPONENT));
CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(net, CORE_COMPONENT));
CHKiRet(objUse(prop, CORE_COMPONENT));
CHKiRet(objUse(statsobj, CORE_COMPONENT));
CHKiRet(objUse(datetime, CORE_COMPONENT));
@@ -937,6 +1269,22 @@ CODEmodInit_QueryRegCFSLineHdlr
dbgprintf("imuxsock version %s initializing\n", PACKAGE_VERSION);
+ /* init legacy config vars */
+ cs.pLogSockName = NULL;
+ cs.pLogHostName = NULL; /* host name to use with this socket */
+
+ /* we need to create the inputName property (only once during our lifetime) */
+ CHKiRet(prop.Construct(&pInputName));
+ CHKiRet(prop.SetString(pInputName, UCHAR_CONSTANT("imuxsock"), sizeof("imuxsock") - 1));
+ CHKiRet(prop.ConstructFinalize(pInputName));
+
+ /* right now, glbl does not permit per-instance IP address notation. As long as this
+ * is the case, it is OK to query the HostIP once here at this location. HOWEVER, the
+ * whole concept is not 100% clean and needs to be addressed on a higher layer.
+ * TODO / rgerhards, 2012-04-11
+ */
+ pLocalHostIP = glbl.GetLocalHostIP();
+
/* init system log socket settings */
listeners[0].flags = IGNDATE;
listeners[0].sockName = UCHAR_CONSTANT(_PATH_LOG);
@@ -945,7 +1293,9 @@ CODEmodInit_QueryRegCFSLineHdlr
listeners[0].fd = -1;
listeners[0].bParseHost = 0;
listeners[0].bUseCreds = 0;
+ listeners[0].bAnnotate = 0;
listeners[0].bCreatePath = 0;
+ listeners[0].bUseSysTimeStamp = 1;
/* initialize socket names */
for(i = 1 ; i < MAXFUNIX ; ++i) {
@@ -953,33 +1303,38 @@ CODEmodInit_QueryRegCFSLineHdlr
listeners[i].fd = -1;
}
+ /* now init listen socket zero, the local log socket */
CHKiRet(prop.Construct(&pLocalHostIP));
CHKiRet(prop.SetString(pLocalHostIP, UCHAR_CONSTANT("127.0.0.1"), sizeof("127.0.0.1") - 1));
CHKiRet(prop.ConstructFinalize(pLocalHostIP));
/* register config file handlers */
CHKiRet(omsdRegCFSLineHdlr((uchar *)"omitlocallogging", 0, eCmdHdlrBinary,
- NULL, &bOmitLocalLogging, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.bOmitLocalLogging, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketignoremsgtimestamp", 0, eCmdHdlrBinary,
- NULL, &bIgnoreTimestamp, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.bIgnoreTimestamp, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogsocketname", 0, eCmdHdlrGetWord,
- NULL, &pLogSockName, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.pLogSockName, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensockethostname", 0, eCmdHdlrGetWord,
- NULL, &pLogHostName, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.pLogHostName, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketflowcontrol", 0, eCmdHdlrBinary,
- NULL, &bUseFlowCtl, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.bUseFlowCtl, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketannotate", 0, eCmdHdlrBinary,
+ NULL, &cs.bAnnotate, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketcreatepath", 0, eCmdHdlrBinary,
- NULL, &bCreatePath, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.bCreatePath, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketusesystimestamp", 0, eCmdHdlrBinary,
+ NULL, &cs.bUseSysTimeStamp, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"addunixlistensocket", 0, eCmdHdlrGetWord,
- addLstnSocketName, NULL, STD_LOADABLE_MODULE_ID));
+ addInstance, NULL, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketusepidfromsystem", 0, eCmdHdlrBinary,
- NULL, &bWritePid, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.bWritePid, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"imuxsockratelimitinterval", 0, eCmdHdlrInt,
- NULL, &ratelimitInterval, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.ratelimitInterval, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"imuxsockratelimitburst", 0, eCmdHdlrInt,
- NULL, &ratelimitBurst, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.ratelimitBurst, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"imuxsockratelimitseverity", 0, eCmdHdlrInt,
- NULL, &ratelimitSeverity, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.ratelimitSeverity, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
/* the following one is a (dirty) trick: the system log socket is not added via
@@ -992,14 +1347,18 @@ CODEmodInit_QueryRegCFSLineHdlr
setSystemLogTimestampIgnore, NULL, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogsocketflowcontrol", 0, eCmdHdlrBinary,
setSystemLogFlowControl, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogusesystimestamp", 0, eCmdHdlrBinary,
+ NULL, &cs.bUseSysTimeStampSysSock, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogsocketannotate", 0, eCmdHdlrBinary,
+ NULL, &cs.bAnnotateSysSock, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogusepidfromsystem", 0, eCmdHdlrBinary,
- NULL, &bWritePidSysSock, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.bWritePidSysSock, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogratelimitinterval", 0, eCmdHdlrInt,
- NULL, &ratelimitIntervalSysSock, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.ratelimitIntervalSysSock, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogratelimitburst", 0, eCmdHdlrInt,
- NULL, &ratelimitBurstSysSock, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.ratelimitBurstSysSock, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogratelimitseverity", 0, eCmdHdlrInt,
- NULL, &ratelimitSeveritySysSock, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.ratelimitSeveritySysSock, STD_LOADABLE_MODULE_ID));
/* support statistics gathering */
CHKiRet(statsobj.Construct(&modStats));
diff --git a/plugins/mmaudit/Makefile.am b/plugins/mmaudit/Makefile.am
new file mode 100644
index 0000000..c64d082
--- /dev/null
+++ b/plugins/mmaudit/Makefile.am
@@ -0,0 +1,8 @@
+pkglib_LTLIBRARIES = mmaudit.la
+
+mmaudit_la_SOURCES = mmaudit.c
+mmaudit_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) $(LIBLOGNORM_CFLAGS) $(LIBEE_CFLAGS)
+mmaudit_la_LDFLAGS = -module -avoid-version $(LIBLOGNORM_LIBS) $(LIBEE_LIBS)
+mmaudit_la_LIBADD =
+
+EXTRA_DIST =
diff --git a/plugins/mmaudit/mmaudit.c b/plugins/mmaudit/mmaudit.c
new file mode 100644
index 0000000..fcefd01
--- /dev/null
+++ b/plugins/mmaudit/mmaudit.c
@@ -0,0 +1,390 @@
+/* mmaudit.c
+ * This is a message modification module supporting Linux audit format
+ * in various settings. The module tries to identify the provided
+ * message as being a Linux audit record and, if so, converts it into
+ * cee-enhanced syslog format.
+ *
+ * NOTE WELL:
+ * Right now, we do not do any trust checks. So it is possible that a
+ * malicous user emits something that looks like an audit record and
+ * tries to fool the system with that. Solving this trust issue is NOT
+ * an easy thing to do. This will be worked on, as the lumberjack effort
+ * continues. Please consider the module in its current state as a proof
+ * of concept.
+ *
+ * File begun on 2012-02-23 by RGerhards
+ *
+ * Copyright 2012 Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <libestr.h>
+#include <libee/libee.h>
+#include "conf.h"
+#include "syslogd-types.h"
+#include "template.h"
+#include "module-template.h"
+#include "errmsg.h"
+#include "cfsysline.h"
+#include "dirty.h"
+
+MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("mmaudit")
+
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal);
+
+/* static data */
+DEFobjCurrIf(errmsg);
+
+/* internal structures
+ */
+DEF_OMOD_STATIC_DATA
+
+typedef struct _instanceData {
+ ee_ctx ctxee; /**< context to be used for libee */
+} instanceData;
+
+typedef struct configSettings_s {
+ int dummy; /* remove when the first real parameter is needed */
+} configSettings_t;
+static configSettings_t cs;
+
+BEGINinitConfVars /* (re)set config variables to default values */
+CODESTARTinitConfVars
+ resetConfigVariables(NULL, NULL);
+ENDinitConfVars
+
+
+BEGINcreateInstance
+CODESTARTcreateInstance
+ENDcreateInstance
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ENDisCompatibleWithFeature
+
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ ee_exitCtx(pData->ctxee);
+ENDfreeInstance
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ dbgprintf("mmaudit\n");
+ENDdbgPrintInstInfo
+
+
+BEGINtryResume
+CODESTARTtryResume
+ENDtryResume
+
+
+static inline void
+skipWhitespace(uchar **buf)
+{
+ while(**buf && isspace(**buf))
+ ++(*buf);
+}
+
+
+static inline rsRetVal
+parseName(uchar **buf, char *name, unsigned lenName)
+{
+ unsigned i;
+ skipWhitespace(buf);
+ --lenName; /* reserve space for '\0' */
+ i = 0;
+ while(**buf && **buf != '=' && lenName) {
+//dbgprintf("parseNAme, buf: %s\n", *buf);
+ name[i++] = **buf;
+ ++(*buf), --lenName;
+ }
+ name[i] = '\0';
+ return RS_RET_OK;
+}
+
+
+static inline rsRetVal
+parseValue(uchar **buf, char *val, unsigned lenval)
+{
+ char termc;
+ unsigned i;
+ DEFiRet;
+
+ --lenval; /* reserve space for '\0' */
+ i = 0;
+ if(**buf == '\0') {
+ FINALIZE;
+ } else if(**buf == '\'') {
+ termc = '\'';
+ ++(*buf);
+ } else if(**buf == '"') {
+ termc = '"';
+ ++(*buf);
+ } else {
+ termc = ' ';
+ }
+
+ while(**buf && **buf != termc && lenval) {
+//dbgprintf("parseValue, termc '%c', buf: %s\n", termc, *buf);
+ val[i++] = **buf;
+ ++(*buf), --lenval;
+ }
+ val[i] = '\0';
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* parse the audit record and create libee structure
+ */
+static rsRetVal
+audit_parse(instanceData *pData, uchar *buf, struct ee_event **event)
+{
+ es_str_t *estr;
+ char name[1024];
+ char val[1024];
+ DEFiRet;
+
+ *event = ee_newEvent(pData->ctxee);
+ if(event == NULL) {
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+ while(*buf) {
+//dbgprintf("audit_parse, buf: '%s'\n", buf);
+ CHKiRet(parseName(&buf, name, sizeof(name)));
+ if(*buf != '=') {
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ ++buf;
+ CHKiRet(parseValue(&buf, val, sizeof(val)));
+
+ estr = es_newStrFromCStr(val, strlen(val));
+ ee_addStrFieldToEvent(*event, name, estr);
+ es_deleteStr(estr);
+dbgprintf("mmaudit: parsed %s=%s\n", name, val);
+ }
+
+
+finalize_it:
+ RETiRet;
+}
+
+
+BEGINdoAction
+ msg_t *pMsg;
+ uchar *buf;
+ int typeID;
+ struct ee_event *event;
+ int i;
+ es_str_t *estr;
+ char auditID[1024];
+ int bSuccess = 0;
+CODESTARTdoAction
+ pMsg = (msg_t*) ppString[0];
+ /* note that we can performance-optimize the interface, but this also
+ * requires changes to the libraries. For now, we accept message
+ * duplication. -- rgerhards, 2010-12-01
+ */
+ buf = getMSG(pMsg);
+
+dbgprintf("mmaudit: msg is '%s'\n", buf);
+ while(*buf && isspace(*buf)) {
+ ++buf;
+ }
+
+ if(*buf == '\0' || strncmp((char*)buf, "type=", 5)) {
+ DBGPRINTF("mmaudit: type= undetected: '%s'\n", buf);
+ FINALIZE;
+ }
+ buf += 5;
+
+ typeID = 0;
+ while(*buf && isdigit(*buf)) {
+ typeID = typeID * 10 + *buf - '0';
+ ++buf;
+ }
+
+ if(*buf == '\0' || strncmp((char*)buf, " audit(", sizeof(" audit(")-1)) {
+ DBGPRINTF("mmaudit: audit( header not found: %s'\n", buf);
+ FINALIZE;
+ }
+ buf += sizeof(" audit(");
+
+ for(i = 0 ; i < (int) (sizeof(auditID)-2) && *buf && *buf != ')' ; ++i) {
+ auditID[i] = *buf++;
+ }
+ auditID[i] = '\0';
+ if(*buf != ')' || *(buf+1) != ':') {
+ DBGPRINTF("mmaudit: trailer '):' not found, no audit record: %s'\n", buf);
+ FINALIZE;
+ }
+ buf += 2;
+
+dbgprintf("mmaudit: cookie found, type %d, auditID '%s', rest of message: '%s'\n", typeID, auditID, buf);
+ audit_parse(pData, buf, &event);
+ if(event == NULL) {
+ DBGPRINTF("mmaudit: audit parse error, assuming no "
+ "audit message: '%s'\n", buf);
+ FINALIZE;
+ }
+
+ /* we now need to shuffle the "outer" properties into that stream */
+ estr = es_newStrFromCStr(auditID, strlen(auditID));
+ ee_addStrFieldToEvent(event, "audithdr.auditid", estr);
+ es_deleteStr(estr);
+
+ /* we abuse auditID a bit to save space... (TODO: change!) */
+ snprintf(auditID, sizeof(auditID), "%d", typeID);
+ estr = es_newStrFromCStr(auditID, strlen(auditID));
+ ee_addStrFieldToEvent(event, "audithdr.type", estr);
+ es_deleteStr(estr);
+
+ /* TODO: in the long term, we need to think about merging & different
+ name spaces (probably best to add the newly-obtained event as a child to
+ the existing event...)
+ */
+ if(pMsg->event != NULL) {
+ ee_deleteEvent(pMsg->event);
+ }
+ pMsg->event = event;
+ bSuccess = 1;
+
+#if 1
+ /***DEBUG***/ // TODO: remove after initial testing - 2010-12-01
+ {
+ char *cstr;
+ es_str_t *str;
+ ee_fmtEventToJSON(pMsg->event, &str);
+ cstr = es_str2cstr(str, NULL);
+ dbgprintf("mmaudit generated: %s\n", cstr);
+ free(cstr);
+ es_deleteStr(str);
+ }
+ /***END DEBUG***/
+#endif
+finalize_it:
+ MsgSetParseSuccess(pMsg, bSuccess);
+ENDdoAction
+
+
+BEGINparseSelectorAct
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(1)
+ /* first check if this config line is actually for us */
+ if(strncmp((char*) p, ":mmaudit:", sizeof(":mmaudit:") - 1)) {
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+ }
+
+ /* ok, if we reach this point, we have something for us */
+ p += sizeof(":mmaudit:") - 1; /* eat indicator sequence (-1 because of '\0'!) */
+ CHKiRet(createInstance(&pData));
+
+ /* check if a non-standard template is to be applied */
+ if(*(p-1) == ';')
+ --p;
+ /* we call the function below because we need to call it via our interface definition. However,
+ * the format specified (if any) is always ignored.
+ */
+ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_TPL_AS_MSG, (uchar*) "RSYSLOG_FileFormat"));
+
+ /* finally build the instance */
+ if((pData->ctxee = ee_initCtx()) == NULL) {
+ errmsg.LogError(0, RS_RET_NO_RULESET, "error: could not initialize libee ctx, cannot "
+ "activate action");
+ ABORT_FINALIZE(RS_RET_ERR_LIBEE_INIT);
+ }
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+
+BEGINmodExit
+CODESTARTmodExit
+ 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;
+ RETiRet;
+}
+
+
+BEGINmodInit()
+ rsRetVal localRet;
+ rsRetVal (*pomsrGetSupportedTplOpts)(unsigned long *pOpts);
+ unsigned long opts;
+ int bMsgPassingSupported;
+CODESTARTmodInit
+INITLegCnfVars
+ *ipIFVersProvided = CURR_MOD_IF_VERSION;
+ /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ /* check if the rsyslog core supports parameter passing code */
+ bMsgPassingSupported = 0;
+ localRet = pHostQueryEtryPt((uchar*)"OMSRgetSupportedTplOpts",
+ &pomsrGetSupportedTplOpts);
+ if(localRet == RS_RET_OK) {
+ /* found entry point, so let's see if core supports msg passing */
+ CHKiRet((*pomsrGetSupportedTplOpts)(&opts));
+ if(opts & OMSR_TPL_AS_MSG)
+ bMsgPassingSupported = 1;
+ } else if(localRet != RS_RET_ENTRY_POINT_NOT_FOUND) {
+ ABORT_FINALIZE(localRet); /* Something else went wrong, not acceptable */
+ }
+
+ if(!bMsgPassingSupported) {
+ DBGPRINTF("mmaudit: msg-passing is not supported by rsyslog core, "
+ "can not continue.\n");
+ ABORT_FINALIZE(RS_RET_NO_MSG_PASSING);
+ }
+
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
+ resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+
+/* vi:set ai:
+ */
diff --git a/plugins/mmjsonparse/Makefile.am b/plugins/mmjsonparse/Makefile.am
new file mode 100644
index 0000000..5175fe8
--- /dev/null
+++ b/plugins/mmjsonparse/Makefile.am
@@ -0,0 +1,8 @@
+pkglib_LTLIBRARIES = mmjsonparse.la
+
+mmjsonparse_la_SOURCES = mmjsonparse.c
+mmjsonparse_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) $(LIBLOGNORM_CFLAGS) $(LIBEE_CFLAGS)
+mmjsonparse_la_LDFLAGS = -module -avoid-version $(LIBLOGNORM_LIBS) $(LIBEE_LIBS)
+mmjsonparse_la_LIBADD =
+
+EXTRA_DIST =
diff --git a/plugins/mmjsonparse/mmjsonparse.c b/plugins/mmjsonparse/mmjsonparse.c
new file mode 100644
index 0000000..03147b5
--- /dev/null
+++ b/plugins/mmjsonparse/mmjsonparse.c
@@ -0,0 +1,250 @@
+/* mmjsonparse.c
+ * This is a message modification module. If give, it extracts JSON data
+ * and populates the EE event structure with it.
+ *
+ * NOTE: read comments in module-template.h for details on the calling interface!
+ *
+ * File begun on 2012-02-20 by RGerhards
+ *
+ * Copyright 2012 Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <libestr.h>
+#include <libee/libee.h>
+#include "conf.h"
+#include "syslogd-types.h"
+#include "template.h"
+#include "module-template.h"
+#include "errmsg.h"
+#include "cfsysline.h"
+#include "dirty.h"
+
+MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("mmjsonparse")
+
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal);
+
+/* static data */
+DEFobjCurrIf(errmsg);
+
+/* internal structures
+ */
+DEF_OMOD_STATIC_DATA
+
+typedef struct _instanceData {
+ ee_ctx ctxee; /**< context to be used for libee */
+} instanceData;
+
+typedef struct configSettings_s {
+ int dummy; /* remove when the first real parameter is needed */
+} configSettings_t;
+static configSettings_t cs;
+
+BEGINinitConfVars /* (re)set config variables to default values */
+CODESTARTinitConfVars
+ resetConfigVariables(NULL, NULL);
+ENDinitConfVars
+
+
+BEGINcreateInstance
+CODESTARTcreateInstance
+ENDcreateInstance
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ENDisCompatibleWithFeature
+
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ ee_exitCtx(pData->ctxee);
+ENDfreeInstance
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ dbgprintf("mmjsonparse\n");
+ENDdbgPrintInstInfo
+
+
+BEGINtryResume
+CODESTARTtryResume
+ENDtryResume
+
+#define COOKIE "@cee: "
+#define LEN_COOKIE (sizeof(COOKIE)-1)
+BEGINdoAction
+ msg_t *pMsg;
+ uchar *buf;
+ struct ee_event *event;
+ int bSuccess = 0;
+CODESTARTdoAction
+ pMsg = (msg_t*) ppString[0];
+ /* note that we can performance-optimize the interface, but this also
+ * requires changes to the libraries. For now, we accept message
+ * duplication. -- rgerhards, 2010-12-01
+ */
+ buf = getMSG(pMsg);
+
+dbgprintf("mmjsonparse: msg is '%s'\n", buf);
+ while(*buf && isspace(*buf)) {
+ ++buf;
+ }
+
+ if(*buf == '\0' || strncmp((char*)buf, COOKIE, LEN_COOKIE)) {
+ DBGPRINTF("mmjsonparse: no JSON cookie: '%s'\n", buf);
+ FINALIZE;
+ }
+ buf += LEN_COOKIE;
+dbgprintf("mmjsonparse: cookie found, rest of message: '%s'\n", buf);
+ event = ee_newEventFromJSON(pData->ctxee, (char*)buf);
+ if(event == NULL) {
+ DBGPRINTF("mmjsonparse: JSON parse error, assuming no "
+ "JSON-enhanced message: '%s'\n", buf);
+ FINALIZE;
+ }
+ /* TODO: in the long term, we need to think about merging & different
+ name spaces (probably best to add the newly-obtained event as a child to
+ the existing event...)
+ */
+ if(pMsg->event != NULL) {
+ ee_deleteEvent(pMsg->event);
+ }
+ pMsg->event = event;
+ bSuccess = 1;
+
+#if 1
+ /***DEBUG***/ // TODO: remove after initial testing - 2010-12-01
+ {
+ char *cstr;
+ es_str_t *str;
+ ee_fmtEventToJSON(pMsg->event, &str);
+ cstr = es_str2cstr(str, NULL);
+ dbgprintf("mmjsonparse generated: %s\n", cstr);
+ free(cstr);
+ es_deleteStr(str);
+ }
+ /***END DEBUG***/
+#endif
+finalize_it:
+ MsgSetParseSuccess(pMsg, bSuccess);
+ENDdoAction
+
+
+BEGINparseSelectorAct
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(1)
+ /* first check if this config line is actually for us */
+ if(strncmp((char*) p, ":mmjsonparse:", sizeof(":mmjsonparse:") - 1)) {
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+ }
+
+ /* ok, if we reach this point, we have something for us */
+ p += sizeof(":mmjsonparse:") - 1; /* eat indicator sequence (-1 because of '\0'!) */
+ CHKiRet(createInstance(&pData));
+
+ /* check if a non-standard template is to be applied */
+ if(*(p-1) == ';')
+ --p;
+ /* we call the function below because we need to call it via our interface definition. However,
+ * the format specified (if any) is always ignored.
+ */
+ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_TPL_AS_MSG, (uchar*) "RSYSLOG_FileFormat"));
+
+ /* finally build the instance */
+ if((pData->ctxee = ee_initCtx()) == NULL) {
+ errmsg.LogError(0, RS_RET_NO_RULESET, "error: could not initialize libee ctx, cannot "
+ "activate action");
+ ABORT_FINALIZE(RS_RET_ERR_LIBEE_INIT);
+ }
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+
+BEGINmodExit
+CODESTARTmodExit
+ 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;
+ RETiRet;
+}
+
+
+BEGINmodInit()
+ rsRetVal localRet;
+ rsRetVal (*pomsrGetSupportedTplOpts)(unsigned long *pOpts);
+ unsigned long opts;
+ int bMsgPassingSupported;
+CODESTARTmodInit
+INITLegCnfVars
+ *ipIFVersProvided = CURR_MOD_IF_VERSION;
+ /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ /* check if the rsyslog core supports parameter passing code */
+ bMsgPassingSupported = 0;
+ localRet = pHostQueryEtryPt((uchar*)"OMSRgetSupportedTplOpts",
+ &pomsrGetSupportedTplOpts);
+ if(localRet == RS_RET_OK) {
+ /* found entry point, so let's see if core supports msg passing */
+ CHKiRet((*pomsrGetSupportedTplOpts)(&opts));
+ if(opts & OMSR_TPL_AS_MSG)
+ bMsgPassingSupported = 1;
+ } else if(localRet != RS_RET_ENTRY_POINT_NOT_FOUND) {
+ ABORT_FINALIZE(localRet); /* Something else went wrong, not acceptable */
+ }
+
+ if(!bMsgPassingSupported) {
+ DBGPRINTF("mmjsonparse: msg-passing is not supported by rsyslog core, "
+ "can not continue.\n");
+ ABORT_FINALIZE(RS_RET_NO_MSG_PASSING);
+ }
+
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
+ resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+
+/* vi:set ai:
+ */
diff --git a/plugins/mmnormalize/mmnormalize.c b/plugins/mmnormalize/mmnormalize.c
index adba0ef..c5b290f 100644
--- a/plugins/mmnormalize/mmnormalize.c
+++ b/plugins/mmnormalize/mmnormalize.c
@@ -48,6 +48,7 @@
MODULE_TYPE_OUTPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("mmnormalize")
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal);
@@ -68,8 +69,7 @@ typedef struct configSettings_s {
uchar *rulebase; /**< name of normalization rulebase to use */
sbool bUseRawMsg; /**< use %rawmsg% instead of %msg% */
} configSettings_t;
-
-SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */
+static configSettings_t cs;
BEGINinitConfVars /* (re)set config variables to default values */
CODESTARTinitConfVars
@@ -126,6 +126,9 @@ CODESTARTdoAction
r = ln_normalize(pData->ctxln, str, &pMsg->event);
if(r != 0) {
DBGPRINTF("error %d during ln_normalize\n", r);
+ MsgSetParseSuccess(pMsg, 0);
+ } else {
+ MsgSetParseSuccess(pMsg, 1);
}
es_deleteStr(str);
/***DEBUG***/ // TODO: remove after initial testing - 2010-12-01
@@ -237,7 +240,7 @@ BEGINmodInit()
unsigned long opts;
int bMsgPassingSupported;
CODESTARTmodInit
-SCOPINGmodInit
+INITLegCnfVars
*ipIFVersProvided = CURR_MOD_IF_VERSION;
/* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
diff --git a/plugins/mmsnmptrapd/mmsnmptrapd.c b/plugins/mmsnmptrapd/mmsnmptrapd.c
index bbe2619..b1ac2f6 100644
--- a/plugins/mmsnmptrapd/mmsnmptrapd.c
+++ b/plugins/mmsnmptrapd/mmsnmptrapd.c
@@ -49,6 +49,7 @@
MODULE_TYPE_OUTPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("mmsnmptrapd")
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal);
@@ -76,8 +77,7 @@ typedef struct configSettings_s {
uchar *pszTagName; /**< name of tag start value that indicates snmptrapd initiated message */
uchar *pszSeverityMapping; /**< severitystring to numerical code mapping for snmptrapd string */
} configSettings_t;
-
-SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */
+static configSettings_t cs;
BEGINinitConfVars /* (re)set config variables to default values */
CODESTARTinitConfVars
@@ -385,7 +385,7 @@ BEGINmodInit()
unsigned long opts;
int bMsgPassingSupported;
CODESTARTmodInit
-SCOPINGmodInit
+INITLegCnfVars
*ipIFVersProvided = CURR_MOD_IF_VERSION;
/* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
@@ -414,7 +414,7 @@ CODEmodInit_QueryRegCFSLineHdlr
cs.pszTagName = NULL;
cs.pszSeverityMapping = NULL;
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"mmsnmptrapdtag", 0, eCmdHdlrInt,
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"mmsnmptrapdtag", 0, eCmdHdlrGetWord,
NULL, &cs.pszTagName, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"mmsnmptrapdseveritymapping", 0, eCmdHdlrGetWord,
NULL, &cs.pszSeverityMapping, STD_LOADABLE_MODULE_ID));
diff --git a/plugins/omdbalerting/Makefile.am b/plugins/omdbalerting/Makefile.am
deleted file mode 100644
index becf29b..0000000
--- a/plugins/omdbalerting/Makefile.am
+++ /dev/null
@@ -1,8 +0,0 @@
-pkglib_LTLIBRARIES = omdbalerting.la
-
-omdbalerting_la_SOURCES = omdbalerting.c
-omdbalerting_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS)
-omdbalerting_la_LDFLAGS = -module -avoid-version
-omdbalerting_la_LIBADD =
-
-EXTRA_DIST =
diff --git a/plugins/omdbalerting/omdbalerting.c b/plugins/omdbalerting/omdbalerting.c
deleted file mode 100644
index 35de581..0000000
--- a/plugins/omdbalerting/omdbalerting.c
+++ /dev/null
@@ -1,145 +0,0 @@
-/* omdbalerting.c
- * generate alerts based on database contents - so far a skeleton
- * left for implementation by somebody else (skeleton created on request).
- *
- * NOTE: read comments in module-template.h for more specifics!
- *
- * File begun on 2009-11-17 by RGerhards
- *
- * Copyright 2009 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 <signal.h>
-#include <errno.h>
-#include <unistd.h>
-#include "conf.h"
-#include "syslogd-types.h"
-#include "srUtils.h"
-#include "template.h"
-#include "module-template.h"
-#include "errmsg.h"
-#include "cfsysline.h"
-
-MODULE_TYPE_OUTPUT
-MODULE_TYPE_NOKEEP
-
-/* internal structures
- */
-DEF_OMOD_STATIC_DATA
-
-/* config variables */
-
-
-typedef struct _instanceData {
-} instanceData;
-
-BEGINcreateInstance
-CODESTARTcreateInstance
-ENDcreateInstance
-
-
-BEGINisCompatibleWithFeature
-CODESTARTisCompatibleWithFeature
- if(eFeat == sFEATURERepeatedMsgReduction)
- iRet = RS_RET_OK;
-ENDisCompatibleWithFeature
-
-
-BEGINfreeInstance
-CODESTARTfreeInstance
-ENDfreeInstance
-
-
-BEGINdbgPrintInstInfo
-CODESTARTdbgPrintInstInfo
-ENDdbgPrintInstInfo
-
-
-BEGINtryResume
-CODESTARTtryResume
-ENDtryResume
-
-BEGINdoAction
-CODESTARTdoAction
-ENDdoAction
-
-
-BEGINparseSelectorAct
-CODESTARTparseSelectorAct
-CODE_STD_STRING_REQUESTparseSelectorAct(1)
- /* first check if this config line is actually for us */
- if(strncmp((char*) p, ":omdbalerting:", sizeof(":dbalerting:") - 1)) {
- ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
- }
-
- /* ok, if we reach this point, we have something for us */
- p += sizeof(":omdbalerting:") - 1; /* eat indicator sequence (-1 because of '\0'!) */
- CHKiRet(createInstance(&pData));
-
- /* check if a non-standard template is to be applied */
- if(*(p-1) == ';')
- --p;
- /* we request the standard interface via template, others may be more useful
- * here.
- */
- CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, 0, (uchar*) "RSYSLOG_FileFormat"));
-CODE_STD_FINALIZERparseSelectorAct
-ENDparseSelectorAct
-
-
-BEGINmodExit
-CODESTARTmodExit
-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;
- RETiRet;
-}
-
-
-BEGINmodInit()
-CODESTARTmodInit
- *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
-CODEmodInit_QueryRegCFSLineHdlr
- // SAMPLE! CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionomdbalertingensurelfending", 0, eCmdHdlrBinary, NULL,
- // &bEnsureLFEnding, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
- resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
-ENDmodInit
-
-/* vi:set ai:
- */
diff --git a/plugins/omelasticsearch/Makefile.am b/plugins/omelasticsearch/Makefile.am
new file mode 100644
index 0000000..a574c72
--- /dev/null
+++ b/plugins/omelasticsearch/Makefile.am
@@ -0,0 +1,8 @@
+pkglib_LTLIBRARIES = omelasticsearch.la
+
+omelasticsearch_la_SOURCES = omelasticsearch.c
+omelasticsearch_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS)
+omelasticsearch_la_LDFLAGS = -module -avoid-version
+omelasticsearch_la_LIBADD = $(CURL_LIBS)
+
+EXTRA_DIST =
diff --git a/plugins/omelasticsearch/omelasticsearch.c b/plugins/omelasticsearch/omelasticsearch.c
new file mode 100644
index 0000000..f77caec
--- /dev/null
+++ b/plugins/omelasticsearch/omelasticsearch.c
@@ -0,0 +1,741 @@
+/* omelasticsearch.c
+ * This is the http://www.elasticsearch.org/ output module.
+ *
+ * NOTE: read comments in module-template.h for more specifics!
+ *
+ * Copyright 2011 Nathan Scott.
+ * Copyright 2009-2012 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <curl/curl.h>
+#include <curl/easy.h>
+#include <assert.h>
+#include <signal.h>
+#include <errno.h>
+#include <time.h>
+#include "conf.h"
+#include "syslogd-types.h"
+#include "srUtils.h"
+#include "template.h"
+#include "module-template.h"
+#include "errmsg.h"
+#include "statsobj.h"
+#include "cfsysline.h"
+#include "unicode-helper.h"
+
+MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("omelasticsearch")
+
+/* internal structures */
+DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(statsobj)
+
+statsobj_t *indexStats;
+STATSCOUNTER_DEF(indexConFail, mutIndexConFail)
+STATSCOUNTER_DEF(indexSubmit, mutIndexSubmit)
+STATSCOUNTER_DEF(indexFailed, mutIndexFailed)
+STATSCOUNTER_DEF(indexSuccess, mutIndexSuccess)
+
+/* REST API for elasticsearch hits this URL:
+ * http://<hostName>:<restPort>/<searchIndex>/<searchType>
+ */
+typedef struct curl_slist HEADER;
+typedef struct _instanceData {
+ uchar *server;
+ int port;
+ uchar *uid;
+ uchar *pwd;
+ uchar *searchIndex;
+ uchar *searchType;
+ uchar *parent;
+ uchar *tplName;
+ uchar *timeout;
+ sbool dynSrchIdx;
+ sbool dynSrchType;
+ sbool dynParent;
+ sbool bulkmode;
+ sbool asyncRepl;
+ struct {
+ es_str_t *data;
+ uchar *currTpl1;
+ uchar *currTpl2;
+ } batch;
+ CURL *curlHandle; /* libcurl session handle */
+ HEADER *postHeader; /* json POST request info */
+} instanceData;
+
+
+/* tables for interfacing with the v6 config system */
+/* action (instance) parameters */
+static struct cnfparamdescr actpdescr[] = {
+ { "server", eCmdHdlrGetWord, 0 },
+ { "serverport", eCmdHdlrInt, 0 },
+ { "uid", eCmdHdlrGetWord, 0 },
+ { "pwd", eCmdHdlrGetWord, 0 },
+ { "searchindex", eCmdHdlrGetWord, 0 },
+ { "searchtype", eCmdHdlrGetWord, 0 },
+ { "parent", eCmdHdlrGetWord, 0 },
+ { "dynsearchindex", eCmdHdlrBinary, 0 },
+ { "dynsearchtype", eCmdHdlrBinary, 0 },
+ { "dynparent", eCmdHdlrBinary, 0 },
+ { "bulkmode", eCmdHdlrBinary, 0 },
+ { "asyncrepl", eCmdHdlrBinary, 0 },
+ { "timeout", eCmdHdlrGetWord, 0 },
+ { "template", eCmdHdlrGetWord, 1 }
+};
+static struct cnfparamblk actpblk =
+ { CNFPARAMBLK_VERSION,
+ sizeof(actpdescr)/sizeof(struct cnfparamdescr),
+ actpdescr
+ };
+
+BEGINcreateInstance
+CODESTARTcreateInstance
+ENDcreateInstance
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURERepeatedMsgReduction)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ if (pData->postHeader) {
+ curl_slist_free_all(pData->postHeader);
+ pData->postHeader = NULL;
+ }
+ if (pData->curlHandle) {
+ curl_easy_cleanup(pData->curlHandle);
+ pData->curlHandle = NULL;
+ }
+ free(pData->server);
+ free(pData->uid);
+ free(pData->pwd);
+ free(pData->searchIndex);
+ free(pData->searchType);
+ free(pData->parent);
+ free(pData->tplName);
+ENDfreeInstance
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ dbgprintf("omelasticsearch\n");
+ dbgprintf("\ttemplate='%s'\n", pData->tplName);
+ dbgprintf("\tserver='%s'\n", pData->server);
+ dbgprintf("\tserverport=%d\n", pData->port);
+ dbgprintf("\tuid='%s'\n", pData->uid == NULL ? (uchar*)"(not configured)" : pData->uid);
+ dbgprintf("\tpwd=(%sconfigured)\n", pData->pwd == NULL ? "not " : "");
+ dbgprintf("\tsearch index='%s'\n", pData->searchIndex);
+ dbgprintf("\tsearch index='%s'\n", pData->searchType);
+ dbgprintf("\tparent='%s'\n", pData->parent);
+ dbgprintf("\ttimeout='%s'\n", pData->timeout);
+ dbgprintf("\tdynamic search index=%d\n", pData->dynSrchIdx);
+ dbgprintf("\tdynamic search type=%d\n", pData->dynSrchType);
+ dbgprintf("\tdynamic parent=%d\n", pData->dynParent);
+ dbgprintf("\tasync replication=%d\n", pData->asyncRepl);
+ dbgprintf("\tbulkmode=%d\n", pData->bulkmode);
+ENDdbgPrintInstInfo
+
+
+/* Build basic URL part, which includes hostname and port as follows:
+ * http://hostname:port/
+ * Newly creates an estr for this purpose.
+ */
+static rsRetVal
+setBaseURL(instanceData *pData, es_str_t **url)
+{
+ char portBuf[64];
+ int r;
+ DEFiRet;
+
+ *url = es_newStr(128);
+ snprintf(portBuf, sizeof(portBuf), "%d", pData->port);
+ r = es_addBuf(url, "http://", sizeof("http://")-1);
+ if(r == 0) r = es_addBuf(url, (char*)pData->server, strlen((char*)pData->server));
+ if(r == 0) r = es_addChar(url, ':');
+ if(r == 0) r = es_addBuf(url, portBuf, strlen(portBuf));
+ if(r == 0) r = es_addChar(url, '/');
+ RETiRet;
+}
+
+
+static inline rsRetVal
+checkConn(instanceData *pData)
+{
+ es_str_t *url;
+ CURL *curl = NULL;
+ CURLcode res;
+ char *cstr;
+ DEFiRet;
+
+ setBaseURL(pData, &url);
+ curl = curl_easy_init();
+ if(curl == NULL) {
+ DBGPRINTF("omelasticsearch: checkConn() curl_easy_init() failed\n");
+ ABORT_FINALIZE(RS_RET_SUSPENDED);
+ }
+ cstr = es_str2cstr(url, NULL);
+ curl_easy_setopt(curl, CURLOPT_URL, cstr);
+ free(cstr);
+
+ res = curl_easy_perform(curl);
+ if(res != CURLE_OK) {
+ DBGPRINTF("omelasticsearch: checkConn() curl_easy_perform() "
+ "failed: %s\n", curl_easy_strerror(res));
+ ABORT_FINALIZE(RS_RET_SUSPENDED);
+ }
+ DBGPRINTF("omelasticsearch: checkConn() completed with success\n");
+
+finalize_it:
+ if(curl != NULL)
+ curl_easy_cleanup(curl);
+ RETiRet;
+}
+
+
+BEGINtryResume
+CODESTARTtryResume
+ DBGPRINTF("omelasticsearch: tryResume called\n");
+ iRet = checkConn(pData);
+ENDtryResume
+
+
+/* get the current index and type for this message */
+static inline void
+getIndexTypeAndParent(instanceData *pData, uchar **tpls,
+ uchar **srchIndex, uchar **srchType, uchar **parent)
+{
+ if(pData->dynSrchIdx) {
+ *srchIndex = tpls[1];
+ if(pData->dynSrchType) {
+ *srchType = tpls[2];
+ if(pData->dynParent) {
+ *parent = tpls[3];
+ } else {
+ *parent = pData->parent;
+ }
+ } else {
+ *srchType = pData->searchType;
+ if(pData->dynParent) {
+ *parent = tpls[2];
+ } else {
+ *parent = pData->parent;
+ }
+ }
+ } else {
+ *srchIndex = pData->searchIndex;
+ if(pData->dynSrchType) {
+ *srchType = tpls[1];
+ if(pData->dynParent) {
+ *parent = tpls[2];
+ } else {
+ *parent = pData->parent;
+ }
+ } else {
+ *srchType = pData->searchType;
+ if(pData->dynParent) {
+ *parent = tpls[1];
+ } else {
+ *parent = pData->parent;
+ }
+ }
+ }
+}
+
+
+static rsRetVal
+setCurlURL(instanceData *pData, uchar **tpls)
+{
+ char authBuf[1024];
+ char *restURL;
+ uchar *searchIndex;
+ uchar *searchType;
+ uchar *parent;
+ es_str_t *url;
+ int rLocal;
+ int r;
+ DEFiRet;
+
+ setBaseURL(pData, &url);
+
+ if(pData->bulkmode) {
+ r = es_addBuf(&url, "_bulk", sizeof("_bulk")-1);
+ parent = NULL;
+ } else {
+ getIndexTypeAndParent(pData, tpls, &searchIndex, &searchType, &parent);
+ r = es_addBuf(&url, (char*)searchIndex, ustrlen(searchIndex));
+ if(r == 0) r = es_addChar(&url, '/');
+ if(r == 0) r = es_addBuf(&url, (char*)searchType, ustrlen(searchType));
+ }
+ if(r == 0) r = es_addChar(&url, '?');
+ if(pData->asyncRepl) {
+ if(r == 0) r = es_addBuf(&url, "replication=async&",
+ sizeof("replication=async&")-1);
+ }
+ if(pData->timeout != NULL) {
+ if(r == 0) r = es_addBuf(&url, "timeout=", sizeof("timeout=")-1);
+ if(r == 0) r = es_addBuf(&url, (char*)pData->timeout, ustrlen(pData->timeout));
+ if(r == 0) r = es_addChar(&url, '&');
+ }
+ if(parent != NULL) {
+ if(r == 0) r = es_addBuf(&url, "parent=", sizeof("parent=")-1);
+ if(r == 0) r = es_addBuf(&url, (char*)parent, ustrlen(parent));
+ }
+ restURL = es_str2cstr(url, NULL);
+ curl_easy_setopt(pData->curlHandle, CURLOPT_URL, restURL);
+ es_deleteStr(url);
+ DBGPRINTF("omelasticsearch: using REST URL: '%s'\n", restURL);
+ free(restURL);
+
+ if(pData->uid != NULL) {
+ rLocal = snprintf(authBuf, sizeof(authBuf), "%s:%s", pData->uid,
+ (pData->pwd == NULL) ? "" : (char*)pData->pwd);
+ if(rLocal != (int) es_strlen(url)) {
+ errmsg.LogError(0, RS_RET_ERR, "omelasticsearch: snprintf failed "
+ "when trying to build auth string (return %d)\n",
+ rLocal);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ curl_easy_setopt(pData->curlHandle, CURLOPT_USERPWD, authBuf);
+ curl_easy_setopt(pData->curlHandle, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
+ }
+finalize_it:
+ RETiRet;
+}
+
+
+/* this method does not directly submit but builds a batch instead. It
+ * may submit, if we have dynamic index/type and the current type or
+ * index changes.
+ */
+static rsRetVal
+buildBatch(instanceData *pData, uchar *message, uchar **tpls)
+{
+ int length = strlen((char *)message);
+ int r;
+ uchar *searchIndex;
+ uchar *searchType;
+ uchar *parent;
+ DEFiRet;
+# define META_STRT "{\"index\":{\"_index\": \""
+# define META_TYPE "\",\"_type\":\""
+# define META_PARENT "\",\"_parent\":\""
+# define META_END "\"}}\n"
+
+ getIndexTypeAndParent(pData, tpls, &searchIndex, &searchType, &parent);
+dbgprintf("AAA: searchIndex: '%s'\n", searchIndex);
+dbgprintf("AAA: searchType: '%s'\n", searchType);
+dbgprintf("AAA: parent: '%s'\n", parent);
+ r = es_addBuf(&pData->batch.data, META_STRT, sizeof(META_STRT)-1);
+ if(r == 0) r = es_addBuf(&pData->batch.data, (char*)searchIndex,
+ ustrlen(searchIndex));
+ if(r == 0) r = es_addBuf(&pData->batch.data, META_TYPE, sizeof(META_TYPE)-1);
+ if(r == 0) r = es_addBuf(&pData->batch.data, (char*)searchType,
+ ustrlen(searchType));
+ if(parent != NULL) {
+ if(r == 0) r = es_addBuf(&pData->batch.data, META_PARENT, sizeof(META_PARENT)-1);
+ if(r == 0) r = es_addBuf(&pData->batch.data, (char*)parent, ustrlen(parent));
+ }
+ if(r == 0) r = es_addBuf(&pData->batch.data, META_END, sizeof(META_END)-1);
+ if(r == 0) r = es_addBuf(&pData->batch.data, (char*)message, length);
+ if(r == 0) r = es_addBuf(&pData->batch.data, "\n", sizeof("\n")-1);
+ if(r != 0) {
+ DBGPRINTF("omelasticsearch: growing batch failed with code %d\n", r);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ iRet = RS_RET_DEFER_COMMIT;
+
+finalize_it:
+ RETiRet;
+}
+
+static rsRetVal
+curlPost(instanceData *instance, uchar *message, int msglen, uchar **tpls)
+{
+ CURLcode code;
+ CURL *curl = instance->curlHandle;
+ DEFiRet;
+
+ if(instance->dynSrchIdx || instance->dynSrchType || instance->dynParent)
+ CHKiRet(setCurlURL(instance, tpls));
+
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, (char *)message);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, (char *)message);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, msglen);
+dbgprintf("omelasticsearch: do curl_easy_perform()\n");
+ code = curl_easy_perform(curl);
+DBGPRINTF("omelasticsearch: curl_easy_perform() returned %lld\n", (long long) code);
+ switch (code) {
+ case CURLE_COULDNT_RESOLVE_HOST:
+ case CURLE_COULDNT_RESOLVE_PROXY:
+ case CURLE_COULDNT_CONNECT:
+ case CURLE_WRITE_ERROR:
+ STATSCOUNTER_INC(indexConFail, mutIndexConFail);
+ DBGPRINTF("omelasticsearch: we are suspending ourselfs due "
+ "to failure %lld of curl_easy_perform()\n",
+ (long long) code);
+ return RS_RET_SUSPENDED;
+ default:
+ STATSCOUNTER_INC(indexSubmit, mutIndexSubmit);
+ return RS_RET_OK;
+ }
+finalize_it:
+ RETiRet;
+}
+
+BEGINbeginTransaction
+CODESTARTbeginTransaction
+dbgprintf("omelasticsearch: beginTransaction\n");
+ if(!pData->bulkmode) {
+ FINALIZE;
+ }
+
+ es_emptyStr(pData->batch.data);
+finalize_it:
+ENDbeginTransaction
+
+
+BEGINdoAction
+CODESTARTdoAction
+ if(pData->bulkmode) {
+ CHKiRet(buildBatch(pData, ppString[0], ppString));
+ } else {
+dbgprintf("omelasticsearch: doAction calling curlPost\n");
+ CHKiRet(curlPost(pData, ppString[0], strlen((char*)ppString[0]),
+ ppString));
+ }
+finalize_it:
+dbgprintf("omelasticsearch: result doAction: %d (bulkmode %d)\n", iRet, pData->bulkmode);
+ENDdoAction
+
+
+BEGINendTransaction
+ char *cstr;
+CODESTARTendTransaction
+dbgprintf("omelasticsearch: endTransaction init\n");
+ cstr = es_str2cstr(pData->batch.data, NULL);
+ dbgprintf("omelasticsearch: endTransaction, batch: '%s'\n", cstr);
+ CHKiRet(curlPost(pData, (uchar*) cstr, strlen(cstr), NULL));
+finalize_it:
+ free(cstr);
+dbgprintf("omelasticsearch: endTransaction done with %d\n", iRet);
+ENDendTransaction
+
+/* elasticsearch POST result string ... useful for debugging */
+size_t
+curlResult(void *ptr, size_t size, size_t nmemb, void *userdata)
+{
+ unsigned int i;
+ char *p = (char *)ptr;
+ char *jsonData = (char *)userdata;
+ static char ok[] = "{\"ok\":true,";
+
+ ASSERT(size == 1);
+DBGPRINTF("omelasticsearch request: %s\n", jsonData);
+DBGPRINTF("omelasticsearch result: ");
+for (i = 0; i < nmemb; i++)
+ DBGPRINTF("%c", p[i]);
+DBGPRINTF("\n");
+
+ if (size == 1 &&
+ nmemb > sizeof(ok)-1 &&
+ strncmp(p, ok, sizeof(ok)-1) == 0) {
+ STATSCOUNTER_INC(indexSuccess, mutIndexSuccess);
+dbgprintf("omelasticsearch ok\n");
+ } else {
+dbgprintf("omelasticsearch fail\n");
+ STATSCOUNTER_INC(indexFailed, mutIndexFailed);
+ if (Debug) {
+ DBGPRINTF("omelasticsearch (fail) request: %s\n", jsonData);
+ DBGPRINTF("omelasticsearch (fail) result: ");
+ for (i = 0; i < nmemb; i++)
+ DBGPRINTF("%c", p[i]);
+ DBGPRINTF("\n");
+ }
+ }
+ return size * nmemb;
+}
+
+
+static rsRetVal
+curlSetup(instanceData *pData)
+{
+ HEADER *header;
+ CURL *handle;
+
+ handle = curl_easy_init();
+ if (handle == NULL) {
+ return RS_RET_OBJ_CREATION_FAILED;
+ }
+
+ header = curl_slist_append(NULL, "Content-Type: text/json; charset=utf-8");
+ curl_easy_setopt(handle, CURLOPT_HTTPHEADER, header);
+
+ curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, curlResult);
+ curl_easy_setopt(handle, CURLOPT_POST, 1);
+
+ pData->curlHandle = handle;
+ pData->postHeader = header;
+
+ if( pData->bulkmode
+ || (pData->dynSrchIdx == 0 && pData->dynSrchType == 0 && pData->dynParent == 0)) {
+ /* in this case, we know no tpls are involved in the request-->NULL OK! */
+ setCurlURL(pData, NULL);
+ }
+
+ if(Debug) {
+ if(pData->dynSrchIdx == 0 && pData->dynSrchType == 0 && pData->dynParent == 0)
+ dbgprintf("omelasticsearch setup, using static REST URL\n");
+ else
+ dbgprintf("omelasticsearch setup, we have a dynamic REST URL\n");
+ }
+ return RS_RET_OK;
+}
+
+static inline void
+setInstParamDefaults(instanceData *pData)
+{
+ pData->server = NULL;
+ pData->port = 9200;
+ pData->uid = NULL;
+ pData->pwd = NULL;
+ pData->searchIndex = NULL;
+ pData->searchType = NULL;
+ pData->parent = NULL;
+ pData->timeout = NULL;
+ pData->dynSrchIdx = 0;
+ pData->dynSrchType = 0;
+ pData->dynParent = 0;
+ pData->asyncRepl = 0;
+ pData->bulkmode = 0;
+ pData->tplName = NULL;
+}
+
+BEGINnewActInst
+ struct cnfparamvals *pvals;
+ int i;
+ int iNumTpls;
+CODESTARTnewActInst
+ if((pvals = nvlstGetParams(lst, &actpblk, NULL)) == NULL) {
+ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+ }
+
+ CHKiRet(createInstance(&pData));
+ setInstParamDefaults(pData);
+
+ for(i = 0 ; i < actpblk.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(actpblk.descr[i].name, "server")) {
+ pData->server = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(actpblk.descr[i].name, "serverport")) {
+ pData->port = (int) pvals[i].val.d.n, NULL;
+ } else if(!strcmp(actpblk.descr[i].name, "uid")) {
+ pData->uid = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(actpblk.descr[i].name, "pwd")) {
+ pData->pwd = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(actpblk.descr[i].name, "searchindex")) {
+ pData->searchIndex = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(actpblk.descr[i].name, "searchtype")) {
+ pData->searchType = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(actpblk.descr[i].name, "parent")) {
+ pData->parent = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(actpblk.descr[i].name, "dynsearchindex")) {
+ pData->dynSrchIdx = pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "dynsearchtype")) {
+ pData->dynSrchType = pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "dynparent")) {
+ pData->dynParent = pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "bulkmode")) {
+ pData->bulkmode = pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "timeout")) {
+ pData->timeout = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(actpblk.descr[i].name, "asyncrepl")) {
+ pData->asyncRepl = 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("omelasticsearch: program error, non-handled "
+ "param '%s'\n", actpblk.descr[i].name);
+ }
+ }
+
+ if(pData->pwd != NULL && pData->uid == NULL) {
+ errmsg.LogError(0, RS_RET_UID_MISSING,
+ "omelasticsearch: password is provided, but no uid "
+ "- action definition invalid");
+ ABORT_FINALIZE(RS_RET_UID_MISSING);
+ }
+ if(pData->dynSrchIdx && pData->searchIndex == NULL) {
+ errmsg.LogError(0, RS_RET_CONFIG_ERROR,
+ "omelasticsearch: requested dynamic search index, but no "
+ "name for index template given - action definition invalid");
+ ABORT_FINALIZE(RS_RET_CONFIG_ERROR);
+ }
+ if(pData->dynSrchType && pData->searchType == NULL) {
+ errmsg.LogError(0, RS_RET_CONFIG_ERROR,
+ "omelasticsearch: requested dynamic search type, but no "
+ "name for type template given - action definition invalid");
+ ABORT_FINALIZE(RS_RET_CONFIG_ERROR);
+ }
+ if(pData->dynParent && pData->parent == NULL) {
+ errmsg.LogError(0, RS_RET_CONFIG_ERROR,
+ "omelasticsearch: requested dynamic parent, but no "
+ "name for parent template given - action definition invalid");
+ ABORT_FINALIZE(RS_RET_CONFIG_ERROR);
+ }
+
+ if(pData->bulkmode) {
+ pData->batch.currTpl1 = NULL;
+ pData->batch.currTpl2 = NULL;
+ if((pData->batch.data = es_newStr(1024)) == NULL) {
+ DBGPRINTF("omelasticsearch: error creating batch string "
+ "turned off bulk mode\n");
+ pData->bulkmode = 0; /* at least it works */
+ }
+ }
+
+ iNumTpls = 1;
+ if(pData->dynSrchIdx) ++iNumTpls;
+ if(pData->dynSrchType) ++iNumTpls;
+ if(pData->dynParent) ++iNumTpls;
+ DBGPRINTF("omelasticsearch: requesting %d templates\n", iNumTpls);
+ CODE_STD_STRING_REQUESTparseSelectorAct(iNumTpls)
+
+ CHKiRet(OMSRsetEntry(*ppOMSR, 0, (uchar*)strdup((pData->tplName == NULL) ?
+ " StdJSONFmt" : (char*)pData->tplName),
+ OMSR_NO_RQD_TPL_OPTS));
+
+
+ /* we need to request additional templates. If we have a dynamic search index,
+ * it will always be string 1. Type may be 1 or 2, depending on whether search
+ * index is dynamic as well. Rule needs to be followed throughout the module.
+ */
+ if(pData->dynSrchIdx) {
+ CHKiRet(OMSRsetEntry(*ppOMSR, 1, ustrdup(pData->searchIndex),
+ OMSR_NO_RQD_TPL_OPTS));
+ if(pData->dynSrchType) {
+ CHKiRet(OMSRsetEntry(*ppOMSR, 2, ustrdup(pData->searchType),
+ OMSR_NO_RQD_TPL_OPTS));
+ if(pData->dynParent) {
+ CHKiRet(OMSRsetEntry(*ppOMSR, 3, ustrdup(pData->parent),
+ OMSR_NO_RQD_TPL_OPTS));
+ }
+ } else {
+ if(pData->dynParent) {
+ CHKiRet(OMSRsetEntry(*ppOMSR, 2, ustrdup(pData->parent),
+ OMSR_NO_RQD_TPL_OPTS));
+ }
+ }
+ } else {
+ if(pData->dynSrchType) {
+ CHKiRet(OMSRsetEntry(*ppOMSR, 1, ustrdup(pData->searchType),
+ OMSR_NO_RQD_TPL_OPTS));
+ if(pData->dynParent) {
+ CHKiRet(OMSRsetEntry(*ppOMSR, 2, ustrdup(pData->parent),
+ OMSR_NO_RQD_TPL_OPTS));
+ }
+ } else {
+ if(pData->dynParent) {
+ CHKiRet(OMSRsetEntry(*ppOMSR, 1, ustrdup(pData->parent),
+ OMSR_NO_RQD_TPL_OPTS));
+ }
+ }
+ }
+
+ if(pData->server == NULL)
+ pData->server = (uchar*) strdup("localhost");
+ if(pData->searchIndex == NULL)
+ pData->searchIndex = (uchar*) strdup("system");
+ if(pData->searchType == NULL)
+ pData->searchType = (uchar*) strdup("events");
+
+ CHKiRet(curlSetup(pData));
+
+CODE_STD_FINALIZERnewActInst
+ cnfparamvalsDestruct(pvals, &actpblk);
+ENDnewActInst
+
+
+BEGINparseSelectorAct
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(1)
+ if(!strncmp((char*) p, ":omelasticsearch:", sizeof(":omelasticsearch:") - 1)) {
+ errmsg.LogError(0, RS_RET_LEGA_ACT_NOT_SUPPORTED,
+ "omelasticsearch supports only v6 config format, use: "
+ "action(type=\"omelasticsearch\" server=...)");
+ }
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+
+BEGINmodExit
+CODESTARTmodExit
+ curl_global_cleanup();
+ statsobj.Destruct(&indexStats);
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(statsobj, CORE_COMPONENT);
+ENDmodExit
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_OMOD_QUERIES
+CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
+CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES
+CODEqueryEtryPt_TXIF_OMOD_QUERIES /* we support the transactional interface! */
+ENDqueryEtryPt
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(statsobj, CORE_COMPONENT));
+
+ if (curl_global_init(CURL_GLOBAL_ALL) != 0) {
+ errmsg.LogError(0, RS_RET_OBJ_CREATION_FAILED, "CURL fail. -elasticsearch indexing disabled");
+ ABORT_FINALIZE(RS_RET_OBJ_CREATION_FAILED);
+ }
+
+ /* support statistics gathering */
+ CHKiRet(statsobj.Construct(&indexStats));
+ CHKiRet(statsobj.SetName(indexStats, (uchar *)"elasticsearch"));
+ CHKiRet(statsobj.AddCounter(indexStats, (uchar *)"connfail",
+ ctrType_IntCtr, &indexConFail));
+ CHKiRet(statsobj.AddCounter(indexStats, (uchar *)"submits",
+ ctrType_IntCtr, &indexSubmit));
+ CHKiRet(statsobj.AddCounter(indexStats, (uchar *)"failed",
+ ctrType_IntCtr, &indexFailed));
+ CHKiRet(statsobj.AddCounter(indexStats, (uchar *)"success",
+ ctrType_IntCtr, &indexSuccess));
+ CHKiRet(statsobj.ConstructFinalize(indexStats));
+ENDmodInit
+
+/* vi:set ai:
+ */
diff --git a/plugins/omgssapi/omgssapi.c b/plugins/omgssapi/omgssapi.c
index 2599524..818a7cf 100644
--- a/plugins/omgssapi/omgssapi.c
+++ b/plugins/omgssapi/omgssapi.c
@@ -59,6 +59,7 @@
MODULE_TYPE_OUTPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("omgssapi")
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal);
@@ -94,18 +95,12 @@ typedef enum gss_mode_e {
GSSMODE_ENC
} gss_mode_t;
-typedef struct configSettings_s {
+static struct configSettings_s {
uchar *pszTplName; /* name of the default template to use */
char *gss_base_service_name;
gss_mode_t gss_mode;
-} configSettings_t;
+} cs;
-SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */
-
-BEGINinitConfVars /* (re)set config variables to default values */
-CODESTARTinitConfVars
- resetConfigVariables(NULL, NULL);
-ENDinitConfVars
/* get the syslog forward port from selector_t. The passed in
* struct must be one that is setup for forwarding.
@@ -698,7 +693,6 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
BEGINmodInit()
CODESTARTmodInit
-SCOPINGmodInit
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(errmsg, CORE_COMPONENT));
diff --git a/plugins/omhdfs/omhdfs.c b/plugins/omhdfs/omhdfs.c
index 58b78a3..cd14d03 100644
--- a/plugins/omhdfs/omhdfs.c
+++ b/plugins/omhdfs/omhdfs.c
@@ -51,6 +51,7 @@
MODULE_TYPE_OUTPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("omhdfs")
/* internal structures
*/
diff --git a/plugins/omhiredis/COPYING b/plugins/omhiredis/COPYING
new file mode 100644
index 0000000..f44bd49
--- /dev/null
+++ b/plugins/omhiredis/COPYING
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adv