summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog353
-rw-r--r--Makefile.am23
-rw-r--r--action.c577
-rw-r--r--action.h11
-rw-r--r--configure.ac118
-rw-r--r--dirty.h6
-rw-r--r--doc/Makefile.am1
-rw-r--r--doc/design.tex74
-rw-r--r--doc/how2help.html5
-rw-r--r--doc/imptcp.html5
-rw-r--r--doc/imrelp.html9
-rw-r--r--doc/imudp.html8
-rw-r--r--doc/manual.html11
-rw-r--r--doc/mmnormalize.html56
-rw-r--r--doc/pmlastmsg.html7
-rw-r--r--doc/property_replacer.html11
-rw-r--r--doc/rscript_abnf.html53
-rw-r--r--doc/rsyslog_conf.html75
-rw-r--r--doc/rsyslog_conf_basic_structure.html35
-rw-r--r--doc/rsyslog_conf_file_syntax_differences.html32
-rw-r--r--doc/rsyslog_conf_filter.html7
-rw-r--r--doc/rsyslog_conf_lines.html23
-rw-r--r--doc/rsyslog_conf_modules.html6
-rw-r--r--doc/rsyslog_ng_comparison.html235
-rw-r--r--doc/rsyslog_recording_pri.html48
-rw-r--r--doc/v6compatibility.html171
-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.c1614
-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.c7
-rw-r--r--plugins/im3195/im3195.c35
-rw-r--r--plugins/imdiag/imdiag.c32
-rw-r--r--plugins/imfile/imfile.c8
-rw-r--r--plugins/imgssapi/imgssapi.c32
-rw-r--r--plugins/imklog/bsd.c22
-rw-r--r--plugins/imklog/imklog.c198
-rw-r--r--plugins/imklog/imklog.h26
-rw-r--r--plugins/immark/immark.c66
-rw-r--r--plugins/impstats/impstats.c75
-rw-r--r--plugins/imptcp/imptcp.c495
-rw-r--r--plugins/imrelp/imrelp.c195
-rw-r--r--plugins/imsolaris/imsolaris.c30
-rw-r--r--plugins/imtcp/imtcp.c293
-rw-r--r--plugins/imtemplate/Makefile.am6
-rw-r--r--plugins/imtemplate/imtemplate.c436
-rw-r--r--plugins/imttcp/Makefile.am6
-rw-r--r--plugins/imttcp/imttcp.c1151
-rw-r--r--plugins/imudp/imudp.c521
-rw-r--r--plugins/imuxsock/imuxsock.c451
-rw-r--r--plugins/mmnormalize/Makefile.am8
-rw-r--r--plugins/mmnormalize/mmnormalize.c274
-rw-r--r--plugins/mmsnmptrapd/mmsnmptrapd.c10
-rw-r--r--plugins/omdbalerting/omdbalerting.c1
-rw-r--r--plugins/omgssapi/omgssapi.c72
-rw-r--r--plugins/omhdfs/omhdfs.c52
-rw-r--r--plugins/omlibdbi/omlibdbi.c110
-rw-r--r--plugins/ommail/ommail.c90
-rw-r--r--plugins/ommongodb/Makefile.am11
-rw-r--r--plugins/ommongodb/README23
-rw-r--r--plugins/ommongodb/ommongodb.c281
-rw-r--r--plugins/ommysql/ommysql.c49
-rw-r--r--plugins/omoracle/omoracle.c1
-rw-r--r--plugins/ompgsql/ompgsql.c11
-rw-r--r--plugins/omprog/omprog.c31
-rw-r--r--plugins/omrelp/omrelp.c11
-rw-r--r--plugins/omruleset/omruleset.c37
-rw-r--r--plugins/omsnmp/omsnmp.c178
-rw-r--r--plugins/omstdout/omstdout.c31
-rw-r--r--plugins/omtemplate/Makefile.am8
-rw-r--r--plugins/omtemplate/omtemplate.c221
-rw-r--r--plugins/omtesting/omtesting.c17
-rw-r--r--plugins/omudpspoof/omudpspoof.c85
-rw-r--r--plugins/omuxsock/omuxsock.c38
-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--rsyslog.service.in2
-rw-r--r--runtime/Makefile.am31
-rw-r--r--runtime/apc.c402
-rw-r--r--runtime/apc.h56
-rw-r--r--runtime/cfsysline.c26
-rw-r--r--runtime/cfsysline.h18
-rw-r--r--runtime/conf.c588
-rw-r--r--runtime/conf.h29
-rw-r--r--runtime/ctok.c624
-rw-r--r--runtime/ctok.h54
-rw-r--r--runtime/ctok_token.c127
-rw-r--r--runtime/ctok_token.h85
-rw-r--r--runtime/datetime.c1
-rw-r--r--runtime/debug.c40
-rw-r--r--runtime/dnscache.c354
-rw-r--r--runtime/dnscache.h (renamed from runtime/sysvar.h)30
-rw-r--r--runtime/errmsg.c1
-rw-r--r--runtime/expr.c475
-rw-r--r--runtime/expr.h55
-rw-r--r--runtime/glbl.c108
-rw-r--r--runtime/glbl.h7
-rw-r--r--runtime/im-helper.h66
-rw-r--r--runtime/module-template.h251
-rw-r--r--runtime/modules.c279
-rw-r--r--runtime/modules.h47
-rw-r--r--runtime/msg.c217
-rw-r--r--runtime/msg.h10
-rw-r--r--runtime/net.c107
-rw-r--r--runtime/netstrm.c2
-rw-r--r--runtime/netstrms.c1
-rw-r--r--runtime/nsd.h12
-rw-r--r--runtime/nsd_gtls.c26
-rw-r--r--runtime/nsd_ptcp.c46
-rw-r--r--runtime/nsdpoll_ptcp.c50
-rw-r--r--runtime/nsdpoll_ptcp.h1
-rw-r--r--runtime/nspoll.c6
-rw-r--r--runtime/nspoll.h5
-rw-r--r--runtime/obj.c4
-rw-r--r--runtime/parser.c2
-rw-r--r--runtime/queue.c237
-rw-r--r--runtime/queue.h4
-rw-r--r--runtime/rsconf.c1354
-rw-r--r--runtime/rsconf.h182
-rw-r--r--runtime/rsyslog.c26
-rw-r--r--runtime/rsyslog.h35
-rw-r--r--runtime/rule.c87
-rw-r--r--runtime/rule.h6
-rw-r--r--runtime/ruleset.c130
-rw-r--r--runtime/ruleset.h31
-rw-r--r--runtime/statsobj.c1
-rw-r--r--runtime/stream.h3
-rw-r--r--runtime/stringbuf.c29
-rw-r--r--runtime/stringbuf.h2
-rw-r--r--runtime/sync.c55
-rw-r--r--runtime/sync.h48
-rw-r--r--runtime/sysvar.c202
-rw-r--r--runtime/typedefs.h50
-rw-r--r--runtime/var.c326
-rw-r--r--runtime/var.h12
-rw-r--r--runtime/vm.c842
-rw-r--r--runtime/vm.h66
-rw-r--r--runtime/vmop.c305
-rw-r--r--runtime/vmop.h126
-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--tcpclt.c2
-rw-r--r--tcps_sess.c2
-rw-r--r--tcpsrv.c244
-rw-r--r--tcpsrv.h12
-rw-r--r--template.c77
-rw-r--r--template.h27
-rw-r--r--tests/Makefile.am71
-rwxr-xr-xtests/cfg.sh4
-rwxr-xr-xtests/diag.sh6
-rw-r--r--tests/filewriter.c158
-rwxr-xr-xtests/imtcp-tls-basic-vg.sh15
-rwxr-xr-xtests/imtcp-tls-basic.sh14
-rwxr-xr-xtests/imtcp_conndrop.sh1
-rwxr-xr-xtests/imtcp_conndrop_tls-vg.sh17
-rwxr-xr-xtests/imtcp_conndrop_tls.sh16
-rwxr-xr-xtests/manytcp-too-few-tls.sh2
-rw-r--r--tests/rscript.c1
-rw-r--r--tests/rt-init.c1
-rwxr-xr-xtests/sndrcv_drvr.sh1
-rwxr-xr-xtests/sndrcv_tls_anon_rebind.sh5
-rw-r--r--tests/testsuites/imtcp-tls-basic.conf21
-rw-r--r--tests/testsuites/imtcp_conndrop.conf1
-rw-r--r--tests/testsuites/sndrcv_tls_anon_rebind_rcvr.conf22
-rw-r--r--tests/testsuites/sndrcv_tls_anon_rebind_sender.conf20
-rw-r--r--tests/testsuites/udp-msgreduc-orgmsg-vg.conf11
-rw-r--r--tests/testsuites/udp-msgreduc-vg.conf11
-rwxr-xr-xtests/udp-msgreduc-orgmsg-vg.sh18
-rwxr-xr-xtests/udp-msgreduc-vg.sh18
-rwxr-xr-xtests/validation-run.sh3
-rw-r--r--tools/Makefile.am10
-rw-r--r--tools/omdiscard.c2
-rw-r--r--tools/omfile.c359
-rw-r--r--tools/omfwd.c447
-rw-r--r--tools/ompipe.c17
-rw-r--r--tools/omshell.c8
-rw-r--r--tools/omusrmsg.c178
-rw-r--r--tools/syslogd.c1126
-rw-r--r--tools/syslogd.h1
195 files changed, 13630 insertions, 9052 deletions
diff --git a/ChangeLog b/ChangeLog
index fe8ae053..7f6504e9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,351 @@
---------------------------------------------------------------------------
+Version 6.3.7 [DEVEL] 2011-0?-??
+- imported refactored v5.9.6 imklog linux driver, now combined with BSD
+ driver
+- bugfix: bsd klog driver did no longer compile
+- 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. Now also supported by
+ - omfwd
+- 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
+---------------------------------------------------------------------------
+Version 6.2.1 [v6-stable], 2012-01-??
+- change plugin config interface to be compatible with pre-v6.2 system
+ The functionality was already removed (because it is superseeded by the
+ v6.3+ config language), but code was still present. I have now removed
+ those parts that affect interface. Full removal will happen in v6.3, in
+ order to limit potential regressions. However, it was considered useful
+ enough to do the interface change in v6-stable; this also eases merging
+ branches!
+---------------------------------------------------------------------------
+Version 6.2.0 [v6-stable], 2012-01-09
+- bugfix (kind of): removed numerical part from pri-text
+ see v6 compatibility document for reasons
+- bugfix: race condition when extracting program name, APPNAME, structured
+ data and PROCID (RFC5424 fields) could lead to invalid characters e.g.
+ in dynamic file names or during forwarding (general malfunction of these
+ fields in templates, mostly under heavy load)
+- 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
+- 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
+- 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.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.0 [v6-stable], 2011-0?-??
+- bugfix (kind of): removed numerical part from pri-text
+ see v6 compatibility document for reasons
+- bugfix: race condition when extracting program name, APPNAME, structured
+ data and PROCID (RFC5424 fields) could lead to invalid characters e.g.
+ in dynamic file names or during forwarding (general malfunction of these
+ fields in templates, mostly under heavy load)
+- $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.
+---------------------------------------------------------------------------
+Version 6.1.12 [BETA], 2011-09-01
+- bugfix/security: off-by-two bug in legacy syslog parser, CVE-2011-3200
+- bugfix: mark message processing did not work correctly
+- bugfix: potential misadressing in property replacer
+- bugfix: memcpy overflow can occur in allowed sender checkig
+ if a name is resolved to IPv4-mapped-on-IPv6 address
+ Found by Ismail Dönmez at suse
+- bugfix: The NUL-Byte for the syslogtag was not copied in MsgDup (msg.c)
+- bugfix: fixed incorrect state handling for Discard Action (transactions)
+ Note: This caused all messages in a batch to be set to COMMITTED,
+ even if they were discarded.
+---------------------------------------------------------------------------
+Version 6.1.11 [BETA] (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
+---------------------------------------------------------------------------
+Version 6.1.10 [BETA] (rgerhards), 2011-06-22
+- 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.
+---------------------------------------------------------------------------
+Version 6.1.9 [BETA] (rgerhards), 2011-06-14
+- 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/improvement:$WorkDirectory now gracefully handles trailing slashes
+- 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]
+- bugfix: $ActionFileDefaultTemplate did not work
+ closes: http://bugzilla.adiscon.com/show_bug.cgi?id=262
+---------------------------------------------------------------------------
+Version 6.1.8 [BETA] (rgerhards), 2011-05-20
+- official new beta version (note that in a sense 6.1.7 was already beta,
+ so we may release the first stable v6 earlier than usual)
+- new module mmsnmptrapd, a sample message modification module
+- import of minor bug fixes from v4 & v5
+---------------------------------------------------------------------------
+Version 6.1.7 [DEVEL] (rgerhards), 2011-04-15
+- added log classification capabilities (via mmnormalize & tags)
+- speeded up tcp forwarding by reducing number of API calls
+ this especially speeds up TLS processing
+- somewhat improved documentation index
+- bugfix: enhanced imudp config processing code disabled due to wrong
+ merge (affected UDP realtime capabilities)
+- bugfix (kind of): memory leak with tcp reception epoll handler
+ This was an extremely unlikely leak and, if it happend, quite small.
+ Still it is better to handle this border case.
+- 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
+ performance, as we now call the HDFS API with much larger message
+ sizes and far more infrequently
+- improved testbench
+ among others, life tests for ommysql (against a test database) have
+ been added, valgrind-based testing enhanced, ...
+- bugfix: minor memory leak in omlibdbi (< 1k per instance and run)
+- bugfix: (regression) omhdfs did no longer compile
+- bugfix: omlibdbi did not use password from rsyslog.con
+ closes: http://bugzilla.adiscon.com/show_bug.cgi?id=203
+- systemd support somewhat improved (can now take over existing log sockt)
+- bugfix: discard action did not work under some circumstances
+ fixes: http://bugzilla.adiscon.com/show_bug.cgi?id=217
+- bugfix: file descriptor leak in gnutls netstream driver
+ fixes: http://bugzilla.adiscon.com/show_bug.cgi?id=222
+- fixed compile problem in imtemplate
+ fixes: http://bugzilla.adiscon.com/show_bug.cgi?id=235
+---------------------------------------------------------------------------
+Version 6.1.5 [DEVEL] (rgerhards), 2011-03-04
+- improved testbench
+- enhanced imtcp to use a pool of worker threads to process incoming
+ messages. This enables higher processing rates, especially in the TLS
+ case (where more CPU is needed for the crypto functions)
+- added support for TLS (in anon mode) to tcpflood
+- improved TLS error reporting
+- improved TLS startup (Diffie-Hellman bits do not need to be generated,
+ as we do not support full anon key exchange -- we always need certs)
+- bugfix: fixed a memory leak and potential abort condition
+ this could happen if multiple rulesets were used and some output batches
+ contained messages belonging to more than one ruleset.
+ fixes: http://bugzilla.adiscon.com/show_bug.cgi?id=226
+ fixes: http://bugzilla.adiscon.com/show_bug.cgi?id=218
+- bugfix: memory leak when $RepeatedMsgReduction on was used
+ bug tracker: http://bugzilla.adiscon.com/show_bug.cgi?id=225
+- bugfix: potential abort condition when $RepeatedMsgReduction set to on
+ as well as potentially in a number of other places where MsgDup() was
+ used. This only happened when the imudp input module was used and it
+ depended on name resolution not yet had taken place. In other words,
+ this was a strange problem that could lead to hard to diagnose
+ instability. So if you experience instability, chances are good that
+ this fix will help.
+---------------------------------------------------------------------------
+Version 6.1.4 [DEVEL] (rgerhards), 2011-02-18
+- bugfix/omhdfs: directive $OMHDFSFileName rendered unusable
+ due to a search and replace-induced bug ;)
+- bugfix: minor race condition in action.c - considered cosmetic
+ This is considered cosmetic as multiple threads tried to write exactly
+ the same value into the same memory location without sync. The method
+ has been changed so this can no longer happen.
+- added pmsnare parser module (written by David Lang)
+- enhanced imfile to support non-cancel input termination
+- improved systemd socket activation thanks to Marius Tomaschweski
+- improved error reporting for $WorkDirectory
+ non-existance and other detectable problems are now reported,
+ and the work directory is NOT set in this case
+- bugfix: pmsnare causded abort under some conditions
+- bugfix: abort if imfile reads file line of more than 64KiB
+ Thanks to Peter Eisentraut for reporting and analysing this problem.
+ bug tracker: http://bugzilla.adiscon.com/show_bug.cgi?id=221
+- bugfix: queue engine did not properly slow down inputs in FULL_DELAY mode
+ when in disk-assisted mode. This especially affected imfile, which
+ created unnecessarily queue files if a large set of input file data was
+ to process.
+- bugfix: very long running actions could prevent shutdown under some
+ circumstances. This has now been solved, at least for common
+ situations.
+- bugfix: fixed compile problem due to empty structs
+ this occured only on some platforms/compilers. thanks to Dražen Kačar
+ for the fix
+---------------------------------------------------------------------------
+Version 6.1.3 [DEVEL] (rgerhards), 2011-02-01
+- experimental support for monogodb added
+- added $IMUDPSchedulingPolicy and $IMUDPSchedulingPriority config settings
+- added $LocalHostName config directive
+- improved tcpsrv performance by enabling multiple-entry epoll
+ so far, we always pulled a single event from the epoll interface.
+ Now 128, what should result in performance improvement (less API
+ calls) on busy systems. Most importantly affects imtcp.
+- imptcp now supports non-cancel termination mode, a plus in stability
+- imptcp speedup: multiple worker threads can now be used to read data
+- new directive $InputIMPTcpHelperThreads added
+- bugfix: fixed build problems on some platforms
+ namely those that have 32bit atomic operations but not 64 bit ones
+- bugfix: local hostname was pulled too-early, so that some config
+ directives (namely FQDN settings) did not have any effect
+- enhanced tcpflood to support multiple sender threads
+ this is required for some high-throughput scenarios (and necessary to
+ run some performance tests, because otherwise the sender is too slow).
+- added some new custom parsers (snare, aix, some Cisco "specialities")
+ thanks to David Lang
+---------------------------------------------------------------------------
+Version 6.1.2 [DEVEL] (rgerhards), 2010-12-16
+- added experimental support for log normalizaton (via liblognorm)
+ support for normalizing log messages has been added in the form of
+ mmnormalize. The core engine (property replacer, filter engine) has
+ been enhanced to support properties from normalized events.
+ Note: this is EXPERIMENTAL code. It is currently know that
+ there are issues if the functionality is used with
+ - disk-based queues
+ - asynchronous action queues
+ You can not use the new functionality together with these features.
+ This limitation will be removed in later releases. However, we
+ preferred to release early, so that one can experiment with the new
+ feature set and accepted the price that this means the full set of
+ functionality is not yet available. If not used together with
+ these features, log normalizing should be pretty stable.
+- enhanced testing tool tcpflood
+ now supports sending via UDP and the capability to run multiple
+ iterations and generate statistics data records
+- bugfix: potential abort when output modules with different parameter
+ passing modes were used in configured output modules
+---------------------------------------------------------------------------
+Version 6.1.1 [DEVEL] (rgerhards), 2010-11-30
+- bugfix(important): problem in TLS handling could cause rsyslog to loop
+ in a tight loop, effectively disabling functionality and bearing the
+ risk of unresponsiveness of the whole system.
+ Bug tracker: http://bugzilla.adiscon.com/show_bug.cgi?id=194
+- support for omhdfs officially added (import from 5.7.1)
+- merged imuxsock improvements from 5.7.1 (see there)
+- support for systemd officially added (import from 5.7.0)
+- bugfix: a couple of problems that imfile had on some platforms, namely
+ Ubuntu (not their fault, but occured there)
+- bugfix: imfile utilizes 32 bit to track offset. Most importantly,
+ this problem can not experienced on Fedora 64 bit OS (which has
+ 64 bit long's!)
+- a number of other bugfixes from older versions imported
+---------------------------------------------------------------------------
+Version 6.1.0 [DEVEL] (rgerhards), 2010-08-12
+
+*********************************** NOTE **********************************
+The v6 versions of rsyslog feature a greatly redesigned config system
+which, among others, supports scoping. However, the initial version does
+not contain the whole new system. Rather it will evolve. So it is
+expected that interfaces, even new ones, break during the initial
+6.x.y releases.
+*********************************** NOTE **********************************
+
+- added $Begin, $End and $ScriptScoping config scope statments
+ (at this time for actions only).
+- added imptcp, a simplified, Linux-specific and potentielly fast
+ syslog plain tcp input plugin (NOT supporting TLS!)
+ [ported from v4]
+---------------------------------------------------------------------------
Version 5.9.6 [V5-DEVEL], 2012-??-??
- refactored imklog linux driver, now combined with BSD driver
The Linux driver no longer supports outdated kernel symbol resolution,
@@ -49,7 +396,7 @@ Version 5.9.3 [V5-DEVEL], 2011-09-01
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 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
@@ -178,7 +525,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.
@@ -1077,7 +1424,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 fd348a67..ff8a0e86 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -66,7 +66,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
@@ -116,10 +116,6 @@ if ENABLE_SMCUSTBINDCDR
SUBDIRS += plugins/sm_cust_bindcdr
endif
-if ENABLE_IMTEMPLATE
-SUBDIRS += plugins/imtemplate
-endif
-
if ENABLE_OMSTDOUT
SUBDIRS += plugins/omstdout
endif
@@ -164,10 +160,6 @@ if ENABLE_OMHDFS
SUBDIRS += plugins/omhdfs
endif
-if ENABLE_OMTEMPLATE
-SUBDIRS += plugins/omtemplate
-endif
-
if ENABLE_ELASTICSEARCH
SUBDIRS += plugins/omelasticsearch
endif
@@ -184,6 +176,10 @@ if ENABLE_IMPTCP
SUBDIRS += plugins/imptcp
endif
+if ENABLE_IMTTCP
+SUBDIRS += plugins/imttcp
+endif
+
if ENABLE_IMDIAG
SUBDIRS += plugins/imdiag
endif
@@ -200,6 +196,10 @@ if ENABLE_RFC3195
SUBDIRS += plugins/im3195
endif
+if ENABLE_MMNORMALIZE
+SUBDIRS += plugins/mmnormalize
+endif
+
if ENABLE_ORACLE
SUBDIRS += plugins/omoracle
endif
@@ -220,6 +220,7 @@ SUBDIRS += tests
# in a make distcheck is so that we detect code that accidently was not updated
# when some global update happened.
DISTCHECK_CONFIGURE_FLAGS= --enable-gssapi_krb5 \
+ --enable-gnutls \
--enable-imfile \
--enable-snmp \
--enable-pgsql \
@@ -237,16 +238,14 @@ DISTCHECK_CONFIGURE_FLAGS= --enable-gssapi_krb5 \
--enable-omprog \
--enable-imdiag \
--enable-imptcp \
+ --enable-imttcp \
--enable-omuxsock \
--enable-extended-tests \
--enable-impstats \
- --enable-imptcp \
--enable-memcheck \
--enable-pmaixforwardedfrom \
--enable-pmcisconames \
--enable-pmsnare \
- --enable-imtemplate \
- --enable-omtemplate \
--enable-mmsnmptrapd \
--enable-elasticsearch \
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
diff --git a/action.c b/action.c
index 34ca4fc0..d12d182a 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,16 @@
#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 */
@@ -129,39 +130,46 @@ DEFobjCurrIf(datetime)
DEFobjCurrIf(module)
DEFobjCurrIf(errmsg)
DEFobjCurrIf(statsobj)
-
-static int iActExecOnceInterval = 0; /* execute action once every nn seconds */
-static int iActExecEveryNthOccur = 0; /* execute action every n-th occurence (0,1=always) */
-static time_t iActExecEveryNthOccurTO = 0; /* timeout for n-occurence setting (in seconds, 0=never) */
-static int glbliActionResumeInterval = 30;
-int glbliActionResumeRetryCount = 0; /* how often should suspended actions be retried? */
-static int bActionRepMsgHasMsg = 0; /* last messsage repeated... has msg fragment in it */
-
-static int bActionWriteAllMarkMsgs = FALSE; /* should all mark messages be unconditionally written? */
-static uchar *pszActionName; /* short name for the action */
-/* action queue and its configuration parameters */
-static queueType_t ActionQueType = QUEUETYPE_DIRECT; /* type of the main message queue above */
-static int iActionQueueSize = 1000; /* size of the main message queue above */
-static int iActionQueueDeqBatchSize = 16; /* batch size for action queues */
-static int iActionQHighWtrMark = 800; /* high water mark for disk-assisted queues */
-static int iActionQLowWtrMark = 200; /* low water mark for disk-assisted queues */
-static int iActionQDiscardMark = 9800; /* begin to discard messages */
-static int iActionQDiscardSeverity = 8; /* by default, discard nothing to prevent unintentional loss */
-static int iActionQueueNumWorkers = 1; /* number of worker threads for the mm queue above */
-static uchar *pszActionQFName = NULL; /* prefix for the main message queue file */
-static int64 iActionQueMaxFileSize = 1024*1024;
-static int iActionQPersistUpdCnt = 0; /* persist queue info every n updates */
-static int bActionQSyncQeueFiles = 0; /* sync queue files */
-static int iActionQtoQShutdown = 0; /* queue shutdown */
-static int iActionQtoActShutdown = 1000; /* action shutdown (in phase 2) */
-static int iActionQtoEnq = 2000; /* timeout for queue enque */
-static int iActionQtoWrkShutdown = 60000; /* timeout for worker thread shutdown */
-static int iActionQWrkMinMsgs = 100; /* minimum messages per worker needed to start a new one */
-static int bActionQSaveOnShutdown = 1; /* save queue on shutdown (when DA enabled)? */
-static int64 iActionQueMaxDiskSpace = 0; /* max disk space allocated 0 ==> unlimited */
-static int iActionQueueDeqSlowdown = 0; /* dequeue slowdown (simple rate limiting) */
-static int iActionQueueDeqtWinFromHr = 0; /* hour begin of time frame when queue is to be dequeued */
-static int iActionQueueDeqtWinToHr = 25; /* hour begin of time frame when queue is to be dequeued */
+DEFobjCurrIf(ruleset)
+
+
+typedef struct configSettings_s {
+ int bActExecWhenPrevSusp; /* execute action only when previous one was suspended? */
+ int bActionWriteAllMarkMsgs; /* should all mark messages be unconditionally written? */
+ int iActExecOnceInterval; /* execute action once every nn seconds */
+ int iActExecEveryNthOccur; /* execute action every n-th occurence (0,1=always) */
+ time_t iActExecEveryNthOccurTO; /* timeout for n-occurence setting (in seconds, 0=never) */
+ int glbliActionResumeInterval;
+ int glbliActionResumeRetryCount; /* how often should suspended actions be retried? */
+ int bActionRepMsgHasMsg; /* last messsage repeated... has msg fragment in it */
+ uchar *pszActionName; /* short name for the action */
+ /* action queue and its configuration parameters */
+ queueType_t ActionQueType; /* type of the main message queue above */
+ int iActionQueueSize; /* size of the main message queue above */
+ int iActionQueueDeqBatchSize; /* batch size for action queues */
+ int iActionQHighWtrMark; /* high water mark for disk-assisted queues */
+ int iActionQLowWtrMark; /* low water mark for disk-assisted queues */
+ int iActionQDiscardMark; /* begin to discard messages */
+ int iActionQDiscardSeverity; /* by default, discard nothing to prevent unintentional loss */
+ int iActionQueueNumWorkers; /* number of worker threads for the mm queue above */
+ uchar *pszActionQFName; /* prefix for the main message queue file */
+ int64 iActionQueMaxFileSize;
+ int iActionQPersistUpdCnt; /* persist queue info every n updates */
+ int bActionQSyncQeueFiles; /* sync queue files */
+ int iActionQtoQShutdown; /* queue shutdown */
+ int iActionQtoActShutdown; /* action shutdown (in phase 2) */
+ int iActionQtoEnq; /* timeout for queue enque */
+ int iActionQtoWrkShutdown; /* timeout for worker thread shutdown */
+ int iActionQWrkMinMsgs; /* minimum messages per worker needed to start a new one */
+ int bActionQSaveOnShutdown; /* save queue on shutdown (when DA enabled)? */
+ int64 iActionQueMaxDiskSpace; /* max disk space allocated 0 ==> unlimited */
+ int iActionQueueDeqSlowdown; /* dequeue slowdown (simple rate limiting) */
+ int iActionQueueDeqtWinFromHr; /* hour begin of time frame when queue is to be dequeued */
+ int iActionQueueDeqtWinToHr; /* hour begin of time frame when queue is to be dequeued */
+} configSettings_t;
+
+configSettings_t cs; /* our current config settings */
+configSettings_t cs_save; /* our saved (scope!) config settings */
/* the counter below counts actions created. It is used to obtain unique IDs for the action. They
* should not be relied on for any long-term activity (e.g. disk queue names!), but they are nice
@@ -171,6 +179,25 @@ static int iActionQueueDeqtWinToHr = 25; /* hour begin of time frame when queu
*/
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
@@ -222,32 +249,32 @@ actionResetQueueParams(void)
{
DEFiRet;
- ActionQueType = QUEUETYPE_DIRECT; /* type of the main message queue above */
- iActionQueueSize = 1000; /* size of the main message queue above */
- iActionQueueDeqBatchSize = 16; /* default batch size */
- iActionQHighWtrMark = 800; /* high water mark for disk-assisted queues */
- iActionQLowWtrMark = 200; /* low water mark for disk-assisted queues */
- iActionQDiscardMark = 9800; /* begin to discard messages */
- iActionQDiscardSeverity = 8; /* discard warning and above */
- iActionQueueNumWorkers = 1; /* number of worker threads for the mm queue above */
- iActionQueMaxFileSize = 1024*1024;
- iActionQPersistUpdCnt = 0; /* persist queue info every n updates */
- bActionQSyncQeueFiles = 0;
- iActionQtoQShutdown = 0; /* queue shutdown */
- iActionQtoActShutdown = 1000; /* action shutdown (in phase 2) */
- iActionQtoEnq = 2000; /* timeout for queue enque */
- iActionQtoWrkShutdown = 60000; /* timeout for worker thread shutdown */
- iActionQWrkMinMsgs = 100; /* minimum messages per worker needed to start a new one */
- bActionQSaveOnShutdown = 1; /* save queue on shutdown (when DA enabled)? */
- iActionQueMaxDiskSpace = 0;
- iActionQueueDeqSlowdown = 0;
- iActionQueueDeqtWinFromHr = 0;
- iActionQueueDeqtWinToHr = 25; /* 25 disables time windowed dequeuing */
-
- glbliActionResumeRetryCount = 0; /* I guess it is smart to reset this one, too */
-
- d_free(pszActionQFName);
- pszActionQFName = NULL; /* prefix for the main message queue file */
+ cs.ActionQueType = QUEUETYPE_DIRECT; /* type of the main message queue above */
+ cs.iActionQueueSize = 1000; /* size of the main message queue above */
+ cs.iActionQueueDeqBatchSize = 16; /* default batch size */
+ cs.iActionQHighWtrMark = 800; /* high water mark for disk-assisted queues */
+ cs.iActionQLowWtrMark = 200; /* low water mark for disk-assisted queues */
+ cs.iActionQDiscardMark = 9800; /* begin to discard messages */
+ cs.iActionQDiscardSeverity = 8; /* discard warning and above */
+ cs.iActionQueueNumWorkers = 1; /* number of worker threads for the mm queue above */
+ cs.iActionQueMaxFileSize = 1024*1024;
+ cs.iActionQPersistUpdCnt = 0; /* persist queue info every n updates */
+ cs.bActionQSyncQeueFiles = 0;
+ cs.iActionQtoQShutdown = 0; /* queue shutdown */
+ cs.iActionQtoActShutdown = 1000; /* action shutdown (in phase 2) */
+ cs.iActionQtoEnq = 2000; /* timeout for queue enque */
+ cs.iActionQtoWrkShutdown = 60000; /* timeout for worker thread shutdown */
+ cs.iActionQWrkMinMsgs = 100; /* minimum messages per worker needed to start a new one */
+ cs.bActionQSaveOnShutdown = 1; /* save queue on shutdown (when DA enabled)? */
+ cs.iActionQueMaxDiskSpace = 0;
+ cs.iActionQueueDeqSlowdown = 0;
+ cs.iActionQueueDeqtWinFromHr = 0;
+ cs.iActionQueueDeqtWinToHr = 25; /* 25 disables time windowed dequeuing */
+
+ cs.glbliActionResumeRetryCount = 0; /* I guess it is smart to reset this one, too */
+
+ d_free(cs.pszActionQFName);
+ cs.pszActionQFName = NULL; /* prefix for the main message queue file */
RETiRet;
}
@@ -277,7 +304,7 @@ rsRetVal actionDestruct(action_t *pThis)
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);
@@ -290,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)
{
@@ -298,13 +327,24 @@ rsRetVal actionConstruct(action_t **ppThis)
ASSERT(ppThis != NULL);
+ if(cs.pszActionName != NULL) {
+ free(cs.pszActionName);
+ cs.pszActionName = NULL;
+ }
CHKmalloc(pThis = (action_t*) calloc(1, sizeof(action_t)));
- pThis->iResumeInterval = glbliActionResumeInterval;
- pThis->iResumeRetryCount = 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;
@@ -318,7 +358,7 @@ finalize_it:
/* action construction finalizer
*/
rsRetVal
-actionConstructFinalize(action_t *pThis)
+actionConstructFinalize(action_t *pThis, struct cnfparamvals *queueParams)
{
DEFiRet;
uchar pszAName[64]; /* friendly name of our action */
@@ -330,7 +370,7 @@ actionConstructFinalize(action_t *pThis)
snprintf((char*) pszAName, sizeof(pszAName)/sizeof(uchar), "action %d", iActionNbr);
} else {
ustrncpy(pszAName, pThis->pszName, sizeof(pszAName));
- pszAName[63] = '\0'; /* to be on the save side */
+ pszAName[sizeof(pszAName)-1] = '\0'; /* to be on the save side */
}
/* support statistics gathering */
@@ -388,7 +428,7 @@ actionConstructFinalize(action_t *pThis)
* msg object thread safety in this case (this costs a bit performance and thus
* is not enabled by default. -- rgerhards, 2008-02-20
*/
- if(ActionQueType != QUEUETYPE_DIRECT)
+ if(cs.ActionQueType != QUEUETYPE_DIRECT)
MsgEnableThreadSafety();
/* create queue */
@@ -397,49 +437,55 @@ actionConstructFinalize(action_t *pThis)
* to be run on multiple threads. So far, this is forbidden by the interface
* spec. -- rgerhards, 2008-01-30
*/
- CHKiRet(qqueueConstruct(&pThis->pQueue, ActionQueType, 1, iActionQueueSize,
+ CHKiRet(qqueueConstruct(&pThis->pQueue, cs.ActionQueType, 1, cs.iActionQueueSize,
(rsRetVal (*)(void*, batch_t*, int*))processBatchMain));
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", iActionQueMaxDiskSpace);
- setQPROP(qqueueSetiDeqBatchSize, "$ActionQueueDequeueBatchSize", iActionQueueDeqBatchSize);
- setQPROP(qqueueSetMaxFileSize, "$ActionQueueFileSize", iActionQueMaxFileSize);
- setQPROPstr(qqueueSetFilePrefix, "$ActionQueueFileName", pszActionQFName);
- setQPROP(qqueueSetiPersistUpdCnt, "$ActionQueueCheckpointInterval", iActionQPersistUpdCnt);
- setQPROP(qqueueSetbSyncQueueFiles, "$ActionQueueSyncQueueFiles", bActionQSyncQeueFiles);
- setQPROP(qqueueSettoQShutdown, "$ActionQueueTimeoutShutdown", iActionQtoQShutdown );
- setQPROP(qqueueSettoActShutdown, "$ActionQueueTimeoutActionCompletion", iActionQtoActShutdown);
- setQPROP(qqueueSettoWrkShutdown, "$ActionQueueWorkerTimeoutThreadShutdown", iActionQtoWrkShutdown);
- setQPROP(qqueueSettoEnq, "$ActionQueueTimeoutEnqueue", iActionQtoEnq);
- setQPROP(qqueueSetiHighWtrMrk, "$ActionQueueHighWaterMark", iActionQHighWtrMark);
- setQPROP(qqueueSetiLowWtrMrk, "$ActionQueueLowWaterMark", iActionQLowWtrMark);
- setQPROP(qqueueSetiDiscardMrk, "$ActionQueueDiscardMark", iActionQDiscardMark);
- setQPROP(qqueueSetiDiscardSeverity, "$ActionQueueDiscardSeverity", iActionQDiscardSeverity);
- setQPROP(qqueueSetiMinMsgsPerWrkr, "$ActionQueueWorkerThreadMinimumMessages", iActionQWrkMinMsgs);
- setQPROP(qqueueSetbSaveOnShutdown, "$ActionQueueSaveOnShutdown", bActionQSaveOnShutdown);
- setQPROP(qqueueSetiDeqSlowdown, "$ActionQueueDequeueSlowdown", iActionQueueDeqSlowdown);
- setQPROP(qqueueSetiDeqtWinFromHr, "$ActionQueueDequeueTimeBegin", iActionQueueDeqtWinFromHr);
- setQPROP(qqueueSetiDeqtWinToHr, "$ActionQueueDequeueTimeEnd", iActionQueueDeqtWinToHr);
-
# undef setQPROP
# undef setQPROPstr
dbgoprint((obj_t*) pThis->pQueue, "save on shutdown %d, max disk space allowed %lld\n",
- bActionQSaveOnShutdown, iActionQueMaxDiskSpace);
+ cs.bActionQSaveOnShutdown, cs.iActionQueMaxDiskSpace);
- 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!) */
@@ -455,7 +501,7 @@ finalize_it:
*/
rsRetVal actionSetGlobalResumeInterval(int iNewVal)
{
- glbliActionResumeInterval = iNewVal;
+ cs.glbliActionResumeInterval = iNewVal;
return RS_RET_OK;
}
@@ -997,7 +1043,7 @@ tryDoAction(action_t *pAction, batch_t *pBatch, int *pnElem)
i = pBatch->iDoneUpTo; /* all messages below that index are processed */
iElemProcessed = 0;
iCommittedUpTo = i;
-dbgprintf("XXXXX: tryDoAction %p, pnElem %d, nElem %d\n", pAction, *pnElem, pBatch->nElem);
+ DBGPRINTF("tryDoAction %p, pnElem %d, nElem %d\n", pAction, *pnElem, pBatch->nElem);
while(iElemProcessed <= *pnElem && i < pBatch->nElem) {
if(*(pBatch->pbShutdownImmediate))
ABORT_FINALIZE(RS_RET_FORCE_TERM);
@@ -1267,16 +1313,16 @@ static rsRetVal setActionQueType(void __attribute__((unused)) *pVal, uchar *pszT
DEFiRet;
if (!strcasecmp((char *) pszType, "fixedarray")) {
- ActionQueType = QUEUETYPE_FIXED_ARRAY;
+ cs.ActionQueType = QUEUETYPE_FIXED_ARRAY;
DBGPRINTF("action queue type set to FIXED_ARRAY\n");
} else if (!strcasecmp((char *) pszType, "linkedlist")) {
- ActionQueType = QUEUETYPE_LINKEDLIST;
+ cs.ActionQueType = QUEUETYPE_LINKEDLIST;
DBGPRINTF("action queue type set to LINKEDLIST\n");
} else if (!strcasecmp((char *) pszType, "disk")) {
- ActionQueType = QUEUETYPE_DISK;
+ cs.ActionQueType = QUEUETYPE_DISK;
DBGPRINTF("action queue type set to DISK\n");
} else if (!strcasecmp((char *) pszType, "direct")) {
- ActionQueType = QUEUETYPE_DIRECT;
+ cs.ActionQueType = QUEUETYPE_DIRECT;
DBGPRINTF("action queue type set to DIRECT (no queueing at all)\n");
} else {
errmsg.LogError(0, RS_RET_INVALID_PARAMS, "unknown actionqueue parameter: %s", (char *) pszType);
@@ -1505,6 +1551,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
@@ -1726,23 +1799,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
+ */
+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);
+ }
+ }
+ cnfparamvalsDestruct(pvals, &pblk);
+ 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;
@@ -1754,22 +1872,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 = pszActionName;
- pszActionName = NULL; /* free again! */
- pAction->bWriteAllMarkMsgs = bActionWriteAllMarkMsgs;
- bActionWriteAllMarkMsgs = FALSE; /* reset */
- pAction->bExecWhenPrevSusp = bActExecWhenPrevSusp;
- pAction->iSecsExecOnceInterval = iActExecOnceInterval;
- pAction->iExecEveryNthOccur = iActExecEveryNthOccur;
- pAction->iExecEveryNthOccurTO = iActExecEveryNthOccurTO;
- pAction->bRepMsgHasMsg = bActionRepMsgHasMsg;
- iActExecEveryNthOccur = 0; /* auto-reset */
- 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);
@@ -1788,7 +1912,7 @@ 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((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);
@@ -1820,9 +1944,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;
}
@@ -1831,7 +1955,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... */
@@ -1857,11 +1981,147 @@ finalize_it:
static rsRetVal
resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
- iActExecOnceInterval = 0;
+ cs.iActExecOnceInterval = 0;
+ cs.bActExecWhenPrevSusp = 0;
return RS_RET_OK;
}
+/* initialize (current) config variables.
+ * Used at program start and when a new scope is created.
+ */
+static inline void
+initConfigVariables(void)
+{
+ cs.bActionWriteAllMarkMsgs = FALSE;
+ cs.glbliActionResumeRetryCount = 0;
+ cs.bActExecWhenPrevSusp = 0;
+ cs.iActExecOnceInterval = 0;
+ cs.iActExecEveryNthOccur = 0;
+ cs.iActExecEveryNthOccurTO = 0;
+ cs.glbliActionResumeInterval = 30;
+ cs.glbliActionResumeRetryCount = 0;
+ cs.bActionRepMsgHasMsg = 0;
+ if(cs.pszActionName != NULL) {
+ free(cs.pszActionName);
+ cs.pszActionName = NULL;
+ }
+ actionResetQueueParams();
+}
+
+
+/* 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)
+{
+ DEFiRet;
+ memcpy(&cs_save, &cs, sizeof(cs));
+ initConfigVariables();
+ RETiRet;
+}
+
+
+/* restore previously saved scope.
+ * rgerhards, 2010-07-23
+ */
+rsRetVal
+actionRestoreScope(void)
+{
+ DEFiRet;
+ memcpy(&cs, &cs_save, sizeof(cs));
+ RETiRet;
+}
+
+
+
+rsRetVal
+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;
+
+ 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;
+}
+
+
+/* Process a rsyslog v6 action config object (the now-primary config method).
+ * rgerhards, 2011-07-19
+ */
+rsRetVal
+actionProcessCnf(struct cnfobj *o)
+{
+ DEFiRet;
+#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)
@@ -1873,37 +2133,42 @@ rsRetVal actionClassInit(void)
CHKiRet(objUse(module, CORE_COMPONENT));
CHKiRet(objUse(errmsg, CORE_COMPONENT));
CHKiRet(objUse(statsobj, CORE_COMPONENT));
-
- CHKiRet(regCfSysLineHdlr((uchar *)"actionname", 0, eCmdHdlrGetWord, NULL, &pszActionName, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuefilename", 0, eCmdHdlrGetWord, NULL, &pszActionQFName, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuesize", 0, eCmdHdlrInt, NULL, &iActionQueueSize, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionwriteallmarkmessages", 0, eCmdHdlrBinary, NULL, &bActionWriteAllMarkMsgs, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuedequeuebatchsize", 0, eCmdHdlrInt, NULL, &iActionQueueDeqBatchSize, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuemaxdiskspace", 0, eCmdHdlrSize, NULL, &iActionQueMaxDiskSpace, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuehighwatermark", 0, eCmdHdlrInt, NULL, &iActionQHighWtrMark, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuelowwatermark", 0, eCmdHdlrInt, NULL, &iActionQLowWtrMark, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuediscardmark", 0, eCmdHdlrInt, NULL, &iActionQDiscardMark, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuediscardseverity", 0, eCmdHdlrInt, NULL, &iActionQDiscardSeverity, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuecheckpointinterval", 0, eCmdHdlrInt, NULL, &iActionQPersistUpdCnt, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuesyncqueuefiles", 0, eCmdHdlrBinary, NULL, &bActionQSyncQeueFiles, NULL));
+ 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));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuesize", 0, eCmdHdlrInt, NULL, &cs.iActionQueueSize, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionwriteallmarkmessages", 0, eCmdHdlrBinary, NULL, &cs.bActionWriteAllMarkMsgs, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuedequeuebatchsize", 0, eCmdHdlrInt, NULL, &cs.iActionQueueDeqBatchSize, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuemaxdiskspace", 0, eCmdHdlrSize, NULL, &cs.iActionQueMaxDiskSpace, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuehighwatermark", 0, eCmdHdlrInt, NULL, &cs.iActionQHighWtrMark, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuelowwatermark", 0, eCmdHdlrInt, NULL, &cs.iActionQLowWtrMark, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuediscardmark", 0, eCmdHdlrInt, NULL, &cs.iActionQDiscardMark, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuediscardseverity", 0, eCmdHdlrInt, NULL, &cs.iActionQDiscardSeverity, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuecheckpointinterval", 0, eCmdHdlrInt, NULL, &cs.iActionQPersistUpdCnt, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuesyncqueuefiles", 0, eCmdHdlrBinary, NULL, &cs.bActionQSyncQeueFiles, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuetype", 0, eCmdHdlrGetWord, setActionQueType, NULL, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionqueueworkerthreads", 0, eCmdHdlrInt, NULL, &iActionQueueNumWorkers, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuetimeoutshutdown", 0, eCmdHdlrInt, NULL, &iActionQtoQShutdown, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuetimeoutactioncompletion", 0, eCmdHdlrInt, NULL, &iActionQtoActShutdown, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuetimeoutenqueue", 0, eCmdHdlrInt, NULL, &iActionQtoEnq, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionqueueworkertimeoutthreadshutdown", 0, eCmdHdlrInt, NULL, &iActionQtoWrkShutdown, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionqueueworkerthreadminimummessages", 0, eCmdHdlrInt, NULL, &iActionQWrkMinMsgs, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuemaxfilesize", 0, eCmdHdlrSize, NULL, &iActionQueMaxFileSize, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuesaveonshutdown", 0, eCmdHdlrBinary, NULL, &bActionQSaveOnShutdown, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuedequeueslowdown", 0, eCmdHdlrInt, NULL, &iActionQueueDeqSlowdown, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuedequeuetimebegin", 0, eCmdHdlrInt, NULL, &iActionQueueDeqtWinFromHr, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuedequeuetimeend", 0, eCmdHdlrInt, NULL, &iActionQueueDeqtWinToHr, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionexeconlyeverynthtime", 0, eCmdHdlrInt, NULL, &iActExecEveryNthOccur, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionexeconlyeverynthtimetimeout", 0, eCmdHdlrInt, NULL, &iActExecEveryNthOccurTO, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionexeconlyonceeveryinterval", 0, eCmdHdlrInt, NULL, &iActExecOnceInterval, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"repeatedmsgcontainsoriginalmsg", 0, eCmdHdlrBinary, NULL, &bActionRepMsgHasMsg, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionqueueworkerthreads", 0, eCmdHdlrInt, NULL, &cs.iActionQueueNumWorkers, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuetimeoutshutdown", 0, eCmdHdlrInt, NULL, &cs.iActionQtoQShutdown, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuetimeoutactioncompletion", 0, eCmdHdlrInt, NULL, &cs.iActionQtoActShutdown, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuetimeoutenqueue", 0, eCmdHdlrInt, NULL, &cs.iActionQtoEnq, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionqueueworkertimeoutthreadshutdown", 0, eCmdHdlrInt, NULL, &cs.iActionQtoWrkShutdown, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionqueueworkerthreadminimummessages", 0, eCmdHdlrInt, NULL, &cs.iActionQWrkMinMsgs, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuemaxfilesize", 0, eCmdHdlrSize, NULL, &cs.iActionQueMaxFileSize, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuesaveonshutdown", 0, eCmdHdlrBinary, NULL, &cs.bActionQSaveOnShutdown, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuedequeueslowdown", 0, eCmdHdlrInt, NULL, &cs.iActionQueueDeqSlowdown, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuedequeuetimebegin", 0, eCmdHdlrInt, NULL, &cs.iActionQueueDeqtWinFromHr, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuedequeuetimeend", 0, eCmdHdlrInt, NULL, &cs.iActionQueueDeqtWinToHr, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionexeconlyeverynthtime", 0, eCmdHdlrInt, NULL, &cs.iActExecEveryNthOccur, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionexeconlyeverynthtimetimeout", 0, eCmdHdlrInt, NULL, &cs.iActExecEveryNthOccurTO, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionexeconlyonceeveryinterval", 0, eCmdHdlrInt, NULL, &cs.iActExecOnceInterval, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"repeatedmsgcontainsoriginalmsg", 0, eCmdHdlrBinary, NULL, &cs.bActionRepMsgHasMsg, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionexeconlywhenpreviousissuspended", 0, eCmdHdlrBinary, NULL, &cs.bActExecWhenPrevSusp, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionresumeretrycount", 0, eCmdHdlrInt, NULL, &cs.glbliActionResumeRetryCount, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, NULL));
+ initConfigVariables(); /* first-time init of config setings */
+
finalize_it:
RETiRet;
}
diff --git a/action.h b/action.h
index add55a5e..4da0be24 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,7 +84,7 @@ 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);
@@ -99,7 +98,7 @@ struct action_s {
/* 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);
@@ -107,6 +106,10 @@ 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 addAction(action_t **ppAction, modInfo_t *pMod, void *pModData, omodStringRequest_t *pOMSR, struct cnfparamvals *actParams, struct cnfparamvals *queueParams, int bSuspended);
+rsRetVal actionNewScope(void);
+rsRetVal actionRestoreScope(void);
+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 32029409..7aa735e4 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],[5.9.4],[rsyslog@lists.adiscon.com])
+AC_INIT([rsyslog],[6.3.6],[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"
@@ -35,6 +31,10 @@ AC_CANONICAL_HOST
PKG_PROG_PKG_CONFIG
+# modules we require
+PKG_CHECK_MODULES(LIBESTR, libestr >= 0.1.2)
+PKG_CHECK_MODULES(LIBEE, libee >= 0.3.1)
+
case "${host}" in
*-*-linux*)
AC_DEFINE([OS_LINUX], [1], [Indicator for a Linux OS])
@@ -718,6 +718,7 @@ AC_ARG_ENABLE(gnutls,
)
if test "x$enable_gnutls" = "xyes"; then
PKG_CHECK_MODULES(GNUTLS, gnutls >= 1.4.0)
+ AC_DEFINE([ENABLE_GNUTLS], [1], [Indicator that GnuTLS is present])
fi
AM_CONDITIONAL(ENABLE_GNUTLS, test x$enable_gnutls = xyes)
AC_SUBST(GNUTLS_CFLAGS)
@@ -735,8 +736,9 @@ AC_ARG_ENABLE(rsyslogrt,
[enable_rsyslogrt=yes]
)
if test "x$enable_rsyslogrt" = "xyes"; then
- RSRT_CFLAGS="-I\$(top_srcdir)/runtime -I\$(top_srcdir)"
+ RSRT_CFLAGS="-I\$(top_srcdir)/runtime -I\$(top_srcdir) -I\$(top_srcdir)/grammar"
RSRT_LIBS="\$(top_builddir)/runtime/librsyslog.la"
+ #??CNF_LIBS="\$(top_builddir)/grammar/libgrammar.la"
fi
AM_CONDITIONAL(ENABLE_RSYSLOGRT, test x$enable_rsyslogrt = xyes)
AC_SUBST(RSRT_CFLAGS)
@@ -820,6 +822,24 @@ AC_ARG_ENABLE(imdiag,
AM_CONDITIONAL(ENABLE_IMDIAG, test x$enable_imdiag = xyes)
+# mmnormalize
+AC_ARG_ENABLE(mmnormalize,
+ [AS_HELP_STRING([--enable-mmnormalize],[Enable building mmnormalize support @<:@default=no@:>@])],
+ [case "${enableval}" in
+ yes) enable_mmnormalize="yes" ;;
+ no) enable_mmnormalize="no" ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-mmnormalize) ;;
+ esac],
+ [enable_mmnormalize=no]
+)
+if test "x$enable_mmnormalize" = "xyes"; then
+ PKG_CHECK_MODULES(LIBLOGNORM, lognorm >= 0.3.1)
+fi
+AM_CONDITIONAL(ENABLE_MMNORMALIZE, test x$enable_mmnormalize = xyes)
+AC_SUBST(LOGNORM_CFLAGS)
+AC_SUBST(LOGNORM_LIBS)
+
+
# RELP support
AC_ARG_ENABLE(relp,
[AS_HELP_STRING([--enable-relp],[Enable RELP support @<:@default=no@:>@])],
@@ -908,6 +928,19 @@ AC_ARG_ENABLE(imptcp,
AM_CONDITIONAL(ENABLE_IMPTCP, test x$enable_imptcp = xyes)
+# settings for the ttcp input module
+AC_ARG_ENABLE(imttcp,
+ [AS_HELP_STRING([--enable-imttcp],[threaded plain tcp input module enabled @<:@default=no@:>@])],
+ [case "${enableval}" in
+ yes) enable_imttcp="yes" ;;
+ no) enable_imttcp="no" ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-imttcp) ;;
+ esac],
+ [enable_imttcp=no]
+)
+AM_CONDITIONAL(ENABLE_IMTTCP, test x$enable_imttcp = xyes)
+
+
# settings for the pstats input module
AC_ARG_ENABLE(impstats,
[AS_HELP_STRING([--enable-impstats],[periodic statistics module enabled @<:@default=no@:>@])],
@@ -1132,46 +1165,6 @@ AC_ARG_ENABLE(smcustbindcdr,
AM_CONDITIONAL(ENABLE_SMCUSTBINDCDR, test x$enable_smcustbindcdr = 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@:>@])],
@@ -1202,8 +1195,28 @@ AC_ARG_ENABLE(omhdfs,
AM_CONDITIONAL(ENABLE_OMHDFS, test x$enable_omhdfs = xyes)
+#MONGODB SUPPORT
+
+AC_ARG_ENABLE(ommongodb,
+ [AS_HELP_STRING([--enable-ommongodb],[Compiles ommongodb template module @<:@default=no@:>@])],
+ [case "${enableval}" in
+ yes) enable_ommongodb="yes" ;;
+ no) enable_ommongodb="no" ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-ommongodb) ;;
+ esac],
+ [enable_ommongodb=no]
+)
+#
+# you may want to do some library checks here - see snmp, mysql, pgsql modules
+# for samples
+#
+AM_CONDITIONAL(ENABLE_OMMONGODB, test x$enable_ommongodb = xyes)
+# end of mongodb code
+
+
AC_CONFIG_FILES([Makefile \
runtime/Makefile \
+ grammar/Makefile \
tools/Makefile \
doc/Makefile \
plugins/imudp/Makefile \
@@ -1213,8 +1226,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 \
@@ -1229,6 +1240,7 @@ AC_CONFIG_FILES([Makefile \
plugins/imfile/Makefile \
plugins/imsolaris/Makefile \
plugins/imptcp/Makefile \
+ plugins/imttcp/Makefile \
plugins/impstats/Makefile \
plugins/imrelp/Makefile \
plugins/imdiag/Makefile \
@@ -1242,6 +1254,7 @@ AC_CONFIG_FILES([Makefile \
plugins/omsnmp/Makefile \
plugins/omoracle/Makefile \
plugins/omudpspoof/Makefile \
+ plugins/mmnormalize/Makefile \
plugins/omelasticsearch/Makefile \
plugins/sm_cust_bindcdr/Makefile \
plugins/mmsnmptrapd/Makefile \
@@ -1266,15 +1279,16 @@ echo
echo "---{ input plugins }---"
echo " Klog functionality enabled: $enable_klog ($os_type)"
echo " plain tcp input module enabled: $enable_imptcp"
+echo " threaded plain tcp input module enabled: $enable_imttcp"
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"
@@ -1282,7 +1296,6 @@ 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"
@@ -1292,6 +1305,7 @@ echo " pmaixforwardedfrom module w.be compiled: $enable_pmaixforwardedfrom"
echo " pmsnare module will be compiled: $enable_pmsnare"
echo
echo "---{ message modification modules }---"
+echo " mmnormalize module will be compiled: $enable_mmnormalize"
echo " mmsnmptrapd module will be compiled: $enable_mmsnmptrapd"
echo
echo "---{ strgen modules }---"
diff --git a/dirty.h b/dirty.h
index c1e75b44..a3940cb9 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);
@@ -39,11 +39,11 @@ rsRetVal createMainQueue(qqueue_t **ppQueue, uchar *pszQueueName);
* we move to the next interval until we reach the largest.
* TODO: move this to action object! Only action.c and syslogd.c use it.
*/
-extern int bActExecWhenPrevSusp;
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/Makefile.am b/doc/Makefile.am
index 91d92afd..04fda6b3 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -110,6 +110,7 @@ html_files = \
src/tls_cert.dia \
gssapi.html \
licensing.html \
+ mmnormalize.html \
ommail.html \
omuxsock.html \
omrelp.html \
diff --git a/doc/design.tex b/doc/design.tex
index a3ec8f45..1def3fb7 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/how2help.html b/doc/how2help.html
index 4f0bd57a..7fda6949 100644
--- a/doc/how2help.html
+++ b/doc/how2help.html
@@ -14,6 +14,9 @@ wish list, that would be awfully helpful!</p>
<li>spread word about rsyslog in forums and newsgroups</li>
<li>place a link to <a href="http://www.rsyslog.com">www.rsyslog.com</a>
from your home page</li>
+ <li>you may also want to tell others about the
+ <a href="http://loganalyzer.adiscon.com">log analyzer tool
+ created by the same folks as rsyslog</a> - at least, if you like it ;)
</ul>
</li>
<li>let us know about rsyslog - we are eager for feedback<ul>
@@ -54,4 +57,4 @@ wish list, that would be awfully helpful!</p>
might do!</p>
</body>
-</html
+</html>
diff --git a/doc/imptcp.html b/doc/imptcp.html
index 386e691a..a4f43249 100644
--- a/doc/imptcp.html
+++ b/doc/imptcp.html
@@ -71,6 +71,11 @@ name is not strictly necessary, but can be useful to apply filtering based on wh
the message was received from.
<li>$InputPTCPServerBindRuleset &lt;name&gt;<br>
Binds specified ruleset to next server defined.
+<li>$InputPTCPHelperThreads &lt;number&gt;<br>
+Number of helper worker threads to process incoming messages. These
+threads are utilized to pull data off the network. On a busy system, additional
+helper threads (but not more than there are CPUs/Cores) can help improving
+performance. The default value is two.
<li>$InputPTCPServerListenIP &lt;name&gt;<br>
On multi-homed machines, specifies to which local address the next listerner should
be bound.
diff --git a/doc/imrelp.html b/doc/imrelp.html
index 2cf9c1f7..d83b2a15 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/imudp.html b/doc/imudp.html
index f0e86307..ea985b60 100644
--- a/doc/imudp.html
+++ b/doc/imudp.html
@@ -2,7 +2,7 @@
<html>
<head>
<meta http-equiv="Content-Language" content="en">
-<title>TCP Syslog Input Module</title>
+<title>UDP Syslog Input Module (imudp)</title>
</head>
<body>
@@ -35,6 +35,12 @@ You can set this value as high as you like, but do so at your own risk. The high
the value, the less precise the timestamp.
<li>$InputUDPServerBindRuleset &lt;ruleset&gt;<br>
Binds the listener to a specific <a href="multi_ruleset.html">ruleset</a>.</li>
+<li>$IMUDPSchedulingPolicy &lt;rr/fifo/other&gt;<br>
+Can be used the set the scheduler priority, if the necessary functionality
+is provided by the platform. Most useful to select "fifo" for real-time
+processing under Linux (and thus reduce chance of packet loss). Available since 4.7.4+, 5.7.3+, 6.1.3+.
+<li>$IMUDPSchedulingPriority &lt;number&gt;<br>
+Scheduling priority to use. Available since 4.7.4+, 5.7.3+, 6.1.3+.
</ul>
<b>Caveats/Known Bugs:</b>
<ul>
diff --git a/doc/manual.html b/doc/manual.html
index fdeb2980..07de4530 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 5.9.4 (stable branch) of rsyslog.</b>
+<p><b>This documentation is for version 6.3.6 (devel 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,18 @@ 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>.
+<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="rsyslog_conf.html">configuration file syntax (rsyslog.conf)</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>
<li><a href="bugs.html">rsyslog bug list</a></li>
diff --git a/doc/mmnormalize.html b/doc/mmnormalize.html
new file mode 100644
index 00000000..82f9b6a2
--- /dev/null
+++ b/doc/mmnormalize.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head>
+<title>Log Message Normalization Module (mmnormalize)</title>
+</head>
+<body>
+<a href="rsyslog_conf_modules.html">back</a>
+
+<h1>Log Message Normalization Module</h1>
+<p><b>Module Name:&nbsp;&nbsp;&nbsp; mmnormalize</b></p>
+<p><b>Available since: </b>6.1.2+
+<p><b>Author: </b>Rainer Gerhards &lt;rgerhards@adiscon.com&gt;</p>
+<p><b>Description</b>:</p>
+<p>This module provides the capability to normalize log messages via
+<a href="http://www.liblognorm.com">liblognorm</a>. Thanks to libee, unstructured text,
+like usually found in log messages, can very quickly be parsed and put into
+a normal form. This is done so quickly, that it usually should be possible
+to normalize events in realtime.
+<p>This module is implemented via the output module interface. That means that
+mmnormalize should be called just like an action. After it has been called,
+the normalized message properties are avaialable and can be access. These properties
+are called the "CEE" properties, because liblognorm creates a format that is
+inspired by the CEE approach.
+<p>Note that mmnormalize should only be called once on each message. Behaviour is
+undifined if multiple calls to mmnormalize happen for the same message.
+</p>
+<p><b>Configuration Directives</b>:</p>
+<ul>
+<li>$mmnormalizeRuleBase &lt;rulebase-file&gt;<br>
+Specifies which rulebase file is to use. This file is loaded. If there are
+multiple mmnormalize instances, each one can use a different file. However,
+a single instance can use only a single file. This parameter MUST be given,
+because normalization can only happen based on a rulebase.
+<li>$mmnormalizeUseRawMsg &lt;on/off&gt;<br>
+Specifies if the raw message should be used for normalization (on) or just the
+MSG part of the message (off). Default is "off".
+</ul>
+<b>Caveats/Known Bugs:</b>
+<p>None known at this time.
+</ul>
+<p><b>Sample:</b></p>
+<p>This activates the module and applies normalization to all messages:<br>
+</p>
+<textarea rows="8" cols="60">$ModLoad mmnormalize
+$mmnormalizeRuleBase rulebase.rb
+*.* :mmnormalize:
+</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; 2010 by <a href="http://www.gerhards.net/rainer">Rainer
+Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>.
+Released under the GNU GPL version 3 or higher.</font></p>
+</body></html>
diff --git a/doc/pmlastmsg.html b/doc/pmlastmsg.html
index 2abeac6a..fd26dbd5 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 f0153f2a..6456296a 100644
--- a/doc/property_replacer.html
+++ b/doc/property_replacer.html
@@ -65,8 +65,7 @@ BSD syslogd. For example, when TAG is "named[12345]", programname is
</tr>
<tr>
<td><b>pri-text</b></td>
-<td>the PRI part of the message in a textual form with the numerical PRI appended in
-brackes (e.g. "local0.err<133>")</td>
+<td>the PRI part of the message in textual form (e.g. "syslog.info")</td>
</tr>
<tr>
<td><b>iut</b></td>
@@ -201,6 +200,14 @@ range from 0 to 3 (for the four quater hours that are in each hour)</td>
<td>The name of the current host as it knows itself (probably useful
for filtering in a generic way)</td>
</tr>
+<tr>
+<td><b>$!&lt;name&gt;</b></td>
+<td>This is the "bridge" to syslog message normalization (via
+<a href="mmnormalize.html">mmnormalize</a>): name is a name defined
+inside the normalization rule. It has the value selected by the rule
+or none if no rule with this field did match.
+</td>
+</tr>
</tbody>
</table>
<p>Properties starting with a $-sign are so-called system
diff --git a/doc/rscript_abnf.html b/doc/rscript_abnf.html
index d60edb5c..9172d945 100644
--- a/doc/rscript_abnf.html
+++ b/doc/rscript_abnf.html
@@ -21,7 +21,58 @@ and many other languages).</p>
<p>Below is the formal language definitionin ABNF (RFC 2234)
format: <br>
</p>
-<pre>; <span style="font-weight: bold;">all of this is a working document and may change!</span> -- rgerhards, 2008-02-24<br><br>script := *stmt<br>stmt := (if_stmt / block / vardef / run_s / load_s)<br>vardef := "var" ["scope" = ("global" / "event")] <br>block := "begin" stmt "end"<br>load_s := "load" constraint ("module") modpath params ; load mod only if expr is true<br>run_s := "run" constraint ("input") name<br>constraint:= "if" expr ; constrains some one-time commands<br>modpath := expr<br>params := ["params" *1param *("," param) "endparams"]<br>param := paramname) "=" expr<br>paramname := [*(obqualifier ".") name]<br>modpath:= ; path to module<br>?line? := cfsysline / cfli<br>cfsysline:= BOL "$" *char EOL ; how to handle the first line? (no EOL in front!)<br>BOL := ; Begin of Line - implicitely set on file beginning and after each EOL<br>EOL := 0x0a ;LF<br>if_stmt := "if" expr "then"<br>old_filter:= BOL facility "." severity ; no whitespace allowed between BOL and facility!<br>facility := "*" / "auth" / "authpriv" / "cron" / "daemon" / "kern" / "lpr" / <br> "mail" / "mark" / "news" / "security" / "syslog" / "user" / "uucp" / <br> "local0" .. "local7" / "mark"<br> ; The keyword security should not be used anymore<br> ; mark is just internal<br>severity := TBD ; not really relevant in this context<br><br>; and now the actual expression<br>expr := e_and *("or" e_and)<br>e_and := e_cmp *("and" e_cmp)<br>e_cmp := val 0*1(cmp_op val)<br>val := term *(("+" / "-" / "&amp;") term)<br>term := factor *(("*" / "/" / "%") factor)<br>factor := ["not"] ["-"] terminal<br>terminal := var / constant / function / ( "(" expr ")" )<br>function := name "(" *("," expr) ")"<br>var := "$" varname<br>varname := msgvar / sysvar<br>msgvar := name<br>sysvar := "$" name<br>name := alpha *(alnum)<br>constant := string / number<br>string := simpstr / tplstr ; tplstr will be implemented in next phase<br>simpstr := "'" *char "'" ; use your imagination for char ;)<br>tplstr := '"' template '"' ; not initially implemented<br>number := ["-"] 1*digit ; 0nn = octal, 0xnn = hex, nn = decimal<br>cmp_op := "==" / "!=" / "&lt;&gt;" / "&lt;" / "&gt;" / "&lt;=" / "&gt;=" / "contains" / "contains_i" / "startswith" / "startswith_i"<br>digit := %x30-39<br>alpha := "a" ... "z" # all letters<br>alnum :* alpha / digit / "_" /"-" # "-" necessary to cover currently-existing message properties<br></pre>
+<pre>; <span style="font-weight: bold;">all of this is a working document and may change!</span> -- rgerhards, 2008-02-24<br>
+<br>
+script := *stmt<br>
+stmt := (if_stmt / block / vardef / run_s / load_s)<br>
+vardef := "var" ["scope" = ("global" / "event")] <br>
+block := "begin" stmt "end"<br>
+load_s := "load" constraint ("module") modpath params ; load mod only if expr is true<br>
+run_s := "run" constraint ("input") name<br>
+constraint:= "if" expr ; constrains some one-time commands<br>
+modpath := expr<br>
+params := ["params" *1param *("," param) "endparams"]<br>
+param := paramname) "=" expr<br>
+paramname := [*(obqualifier ".") name]<br>
+modpath:= ; path to module<br>
+?line? := cfsysline / cfli<br>
+cfsysline:= BOL "$" *char EOL ; how to handle the first line? (no EOL in front!)<br>
+BOL := ; Begin of Line - implicitely set on file beginning and after each EOL<br>
+EOL := 0x0a ;LF<br>
+if_stmt := "if" expr "then"<br>
+old_filter:= BOL facility "." severity ; no whitespace allowed between BOL and facility!<br>
+facility := "*" / "auth" / "authpriv" / "cron" / "daemon" / "kern" / "lpr" / <br>
+"mail" / "mark" / "news" / "security" / "syslog" / "user" / "uucp" / <br>
+"local0" .. "local7" / "mark"<br>
+; The keyword security should not be used anymore<br>
+; mark is just internal<br>
+severity := TBD ; not really relevant in this context<br>
+<br>
+; and now the actual expression<br>
+expr := e_and *("or" e_and)<br>
+e_and := e_cmp *("and" e_cmp)<br>
+e_cmp := val 0*1(cmp_op val)<br>
+val := term *(("+" / "-" / "&amp;") term)<br>
+term := factor *(("*" / "/" / "%") factor)<br>
+factor := ["not"] ["-"] terminal<br>
+terminal := var / constant / function / ( "(" expr ")" )<br>
+function := name "(" *("," expr) ")"<br>
+var := "$" varname<br>
+varname := msgvar / sysvar / ceevar<br>
+msgvar := name<br>
+ceevar := "!" name<br>
+sysvar := "$" name<br>
+name := alpha *(alnum)<br>
+constant := string / number<br>
+string := simpstr / tplstr ; tplstr will be implemented in next phase<br>
+simpstr := "'" *char "'" ; use your imagination for char ;)<br>
+tplstr := '"' template '"' ; not initially implemented<br>
+number := ["-"] 1*digit ; 0nn = octal, 0xnn = hex, nn = decimal<br>
+cmp_op := "==" / "!=" / "&lt;&gt;" / "&lt;" / "&gt;" / "&lt;=" / "&gt;=" / "contains" / "contains_i" / "startswith" / "startswith_i"<br>
+digit := %x30-39<br>
+alpha := "a" ... "z" # all letters<br>
+alnum :* alpha / digit / "_" /"-" # "-" necessary to cover currently-existing message properties<br>
+</pre>
<h2>Samples</h2>
<p>Some samples of RainerScript:</p><p>define function IsLinux<br>begin<br>&nbsp; &nbsp; if $environ contains "linux" then return true else return false<br>end</p><p>load if IsLinux() 'imklog.so' params name='klog' endparams /* load klog under linux only */<br>run if IsLinux() input 'klog'<br>load 'ommysql.so'</p><p>if $message contains "error" then<br>&nbsp; action<br>&nbsp;&nbsp;&nbsp; type='ommysql.so', queue.mode='disk', queue.highwatermark = 300,<br>&nbsp; &nbsp; action.dbname='events', action.dbuser='uid',<br>&nbsp;
&nbsp; [?action.template='templatename'?] or [?action.sql='insert into
diff --git a/doc/rsyslog_conf.html b/doc/rsyslog_conf.html
index 703e7a6e..6aa2e460 100644
--- a/doc/rsyslog_conf.html
+++ b/doc/rsyslog_conf.html
@@ -1,10 +1,10 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html><head><title>rsyslog.conf file</title></head>
+<html><head><title>rsyslog.conf configuration file</title></head>
<body>
<h1>rsyslog.conf configuration file</h1>
-<p><b>Rsyslogd is configured via the rsyslog.conf file</b>,
+<p><b>Rsyslog is configured via the rsyslog.conf file</b>,
typically found in /etc. By default, rsyslogd reads the file
-/etc/rsyslog.conf. This may be changed by a command line option.</p>
+/etc/rsyslog.conf. This may be changed by command line option "-f".</p>
<p><a href="http://wiki.rsyslog.com/index.php/Configuration_Samples">
Configuration file examples can be found in the rsyslog wiki</a>. Also
keep the
@@ -12,65 +12,26 @@ keep the
on your mind. These are ready-to-use
real building blocks for rsyslog configuration.
</p>
-<p>There is also one sample file provided together with the
-documentation set. If you do not like to read, be sure to have at least
-a quick look at
-<a href="rsyslog-example.conf">rsyslog-example.conf</a>.
-</p>
<p>While rsyslogd contains enhancements over standard syslogd,
efforts have been made to keep the configuration file as compatible as
possible. While, for obvious reasons, <a href="features.html">enhanced
features</a> require a different config file syntax, rsyslogd
should be able to work with a standard syslog.conf file. This is
especially useful while you are migrating from syslogd to rsyslogd.</p>
-<h2><a href="rsyslog_conf_modules.html">Modules</a></h2>
-<h2>Lines</h2>
-Lines can be continued by specifying a backslash ("\") as the last
-character of the line. There is a hard-coded maximum line length of 4K.
-If you need lines larger than that, you need to change compile-time
-settings inside rsyslog and recompile.
-<h2><a href="rsyslog_conf_global.html">Configuration Directives</a></h2>
-<h2>Basic Structure</h2>
-<p>Rsyslog supports standard sysklogd's configuration file format
-and extends it. So in general, you can take a "normal" syslog.conf and
-use it together with rsyslogd. It will understand everything. However,
-to use most of rsyslogd's unique features, you need to add extended
-configuration directives.</p>
-<p>Rsyslogd supports the classical, selector-based rule lines.
-They are still at the heart of it and all actions are initiated via
-rule lines. A rule lines is any line not starting with a $ or the
-comment sign (#). Lines starting with $ carry rsyslog-specific
-directives.</p>
-<p>Every rule line consists of two fields, a selector field and
-an action field. These two fields are separated by one or more spaces
-or tabs. The selector field specifies a pattern of facilities and
-priorities belonging to the specified action.<br>
-<br>
-Lines starting with a hash mark ("#'') and empty lines are ignored.
-</p>
-<h2><a href="rsyslog_conf_templates.html">Templates</a></h2>
-<h2><a href="rsyslog_conf_output.html">Output Channels</a></h2>
-<h2><a href="rsyslog_conf_filter.html">Filter Conditions</a></h2>
-<h2><a href="rsyslog_conf_actions.html">Actions</a></h2>
-<h2><a href="rsyslog_conf_examples.html">Examples</a></h2>
-<p>Here you will find examples for templates and selector lines. I hope
-they are self-explanatory. If not, please see
-www.monitorware.com/rsyslog/ for advise.</p>
-<h2>Configuration File Syntax Differences</h2>
-<p>Rsyslogd uses a slightly different syntax for its
-configuration file than the original BSD sources. Originally all
-messages of a specific priority and above were forwarded to the log
-file. The modifiers "='', "!'' and "!-'' were added to make rsyslogd
-more flexible and to use it in a more intuitive manner.<br>
-<br>
-The original BSD syslogd doesn't understand spaces as separators
-between the selector and the action field.<br>
-<br>
-When compared to syslogd from sysklogd package, rsyslogd offers
-additional
-<a href="features.html">features</a> (like template
-and database support). For obvious reasons, the syntax for defining
-such features is available in rsyslogd, only.</p>
+
+<p><b>Follow the links below to learn more about specific topics:</b></p>
+<ul>
+<li><a href="rsyslog_conf_modules.html">Modules</a></li>
+<li><a href="rsyslog_conf_lines.html">Lines</a></li>
+<li><a href="rsyslog_conf_global.html">Configuration Directives</a></li>
+<li><a href="rsyslog_conf_basic_structure.html">Basic Structure</a></li>
+<li><a href="rsyslog_conf_templates.html">Templates</a></li>
+<li><a href="rsyslog_conf_output.html">Output Channels</a></li>
+<li><a href="rsyslog_conf_filter.html">Filter Conditions</a></li>
+<li><a href="rsyslog_conf_actions.html">Actions</a></li>
+<li><a href="rsyslog_conf_file_syntax_differences.html">Configuration File Syntax Differences</a></li>
+<li><a href="rsyslog_conf_examples.html">Examples</a></li>
+</ul>
<p>[<a href="rsyslog_conf.html">back to top</a>]
[<a href="manual.html">manual index</a>]
@@ -82,4 +43,4 @@ Copyright &copy; 2008-2011 by <a href="http://www.gerhards.net/rainer">Rainer Ge
version 3 or higher.</font></p>
</body>
</html>
->
+
diff --git a/doc/rsyslog_conf_basic_structure.html b/doc/rsyslog_conf_basic_structure.html
new file mode 100644
index 00000000..4ce78de0
--- /dev/null
+++ b/doc/rsyslog_conf_basic_structure.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><title>Basic Structure - rsyslog.conf</title></head>
+<body>
+<p>This is a part of the rsyslog.conf documentation.</p>
+<a href="rsyslog_conf.html">Back to rsyslog.conf manual</a>
+<h1>Basic Structure</h1>
+<p>Rsyslog supports standard sysklogd's configuration file format
+and extends it. So in general, you can take a "normal" syslog.conf and
+use it together with rsyslogd. It will understand everything. However,
+to use most of rsyslogd's unique features, you need to add extended
+configuration directives.</p>
+<p>Rsyslogd supports the classical, selector-based rule lines.
+They are still at the heart of it and all actions are initiated via
+rule lines. A rule lines is any line not starting with a $ or the
+comment sign (#). Lines starting with $ carry rsyslog-specific
+directives.</p>
+<p>Every rule line consists of two fields, a selector field and
+an action field. These two fields are separated by one or more spaces
+or tabs. The selector field specifies a pattern of facilities and
+priorities belonging to the specified action.<br>
+<br>
+Lines starting with a hash mark ("#'') and empty lines are ignored.
+</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>
+<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 Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 3 or higher.</font></p>
+</body>
+</html>
+
diff --git a/doc/rsyslog_conf_file_syntax_differences.html b/doc/rsyslog_conf_file_syntax_differences.html
new file mode 100644
index 00000000..bfac8926
--- /dev/null
+++ b/doc/rsyslog_conf_file_syntax_differences.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><title>Configuration File Syntax Differences - rsyslog.conf</title></head>
+<body>
+<p>This is a part of the rsyslog.conf documentation.</p>
+<a href="rsyslog_conf.html">Back to rsyslog.conf manual</a>
+<h1>Configuration File Syntax Differences</h1>
+<p>Rsyslogd uses a slightly different syntax for its
+configuration file than the original BSD sources. Originally all
+messages of a specific priority and above were forwarded to the log
+file. The modifiers "='', "!'' and "!-'' were added to make rsyslogd
+more flexible and to use it in a more intuitive manner.<br>
+<br>
+The original BSD syslogd doesn't understand spaces as separators
+between the selector and the action field.<br>
+<br>
+When compared to syslogd from sysklogd package, rsyslogd offers
+additional
+<a href="features.html">features</a> (like template
+and database support). For obvious reasons, the syntax for defining
+such features is available in rsyslogd, only.</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>
+<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 Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 3 or higher.</font></p>
+</body>
+</html>
+
diff --git a/doc/rsyslog_conf_filter.html b/doc/rsyslog_conf_filter.html
index 34839616..fbced4a3 100644
--- a/doc/rsyslog_conf_filter.html
+++ b/doc/rsyslog_conf_filter.html
@@ -117,6 +117,13 @@ currently supported:</p>
the property. There must be an exact match, wildcards are not supported.</td>
</tr>
<tr>
+<td>isempty</td>
+<td>Checks if the property is empty. The value is discarded. This is
+especially useful when working with normalized data, where some fields
+may be populated based on normalization result.
+Available since 6.6.2.
+</tr>
+<tr>
<td>isequal</td>
<td>Compares the "value" string provided and the property
contents. These two values must be exactly equal to match. The
diff --git a/doc/rsyslog_conf_lines.html b/doc/rsyslog_conf_lines.html
new file mode 100644
index 00000000..0e6cc0d3
--- /dev/null
+++ b/doc/rsyslog_conf_lines.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><title>Lines - rsyslog.conf</title></head>
+<body>
+<p>This is a part of the rsyslog.conf documentation.</p>
+<a href="rsyslog_conf.html">Back to rsyslog.conf manual</a>
+<h1>Lines</h1>
+<p>Lines can be continued by specifying a backslash ("\") as the last
+character of the line. There is a hard-coded maximum line length of 4K.<br>
+If you need lines larger than that, you need to change compile-time
+settings inside rsyslog and recompile.
+</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>
+<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 Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 3 or higher.</font></p>
+</body>
+</html>
+
diff --git a/doc/rsyslog_conf_modules.html b/doc/rsyslog_conf_modules.html
index 9fa35ccd..650e20ad 100644
--- a/doc/rsyslog_conf_modules.html
+++ b/doc/rsyslog_conf_modules.html
@@ -94,7 +94,7 @@ repated n times" messages emitted by some syslogds.
They can be implemented using either the output module or the parser module interface.
From the rsyslog core's point of view, they actually are output or parser modules, it is their
implementation that makes them special.
-<p>Currently, there do not exist any such modules, but could be written with
+<p>Currently, there exists only a limited set of such modules, but new ones could be written with
the methods the engine provides. They could be used, for example, to:
<ul>
<li>anonymize message content
@@ -103,8 +103,10 @@ the methods the engine provides. They could be used, for example, to:
<p>Message modification modules are usually written for one specific task and thus
usually are not generic enough to be reused. However, existing module's code is
probably an excellent starting base for writing a new module. Currently, the following
-modules existin inside the source tree
+modules exist inside the source tree:
<ul>
+<li><a href="mmnormalize.html">mmnormalize</a> - used to normalize log messages.
+Note that this actually is a <b>generic</b> module.
<li><a href="mmsnmptrapd.html">mmsnmptrapd</a> - uses information provided by snmptrapd inside
the tag to correct the original sender system and priority of messages. Implemented via
the output module interface.
diff --git a/doc/rsyslog_ng_comparison.html b/doc/rsyslog_ng_comparison.html
index 7d12a4a7..44c895f7 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/rsyslog_recording_pri.html b/doc/rsyslog_recording_pri.html
index a092980c..abcadf2a 100644
--- a/doc/rsyslog_recording_pri.html
+++ b/doc/rsyslog_recording_pri.html
@@ -45,7 +45,7 @@ percent signs is literal text, which is simply written as specified.</p>
<p>Thankfully, rsyslog provides message properties for the priority. These are
called &quot;PRI&quot;, &quot;syslogfacility&quot; and &quot;syslogpriority&quot; (case is important!). They are numerical
values. Starting with rsyslog 1.13.4, there is also a property &quot;pri-text&quot;, which
-contains the priority in friendly text format (e.g. &quot;local0.err<133>&quot;). For the rest
+contains the priority in friendly text format (e.g. &quot;syslog.info&quot;). For the rest
of this article, I assume that you run version 1.13.4 or higher.</p>
<p>Recording the priority is now a simple matter of adding the respective field
to the template. It now looks like this:</p>
@@ -83,29 +83,29 @@ A little bit of configuration is required.</p>
<p>Below is some sample data created with the template specified above. Note the
priority recording at the start of each line.</p>
<p>
-<code>kern.info&lt;6&gt;: Jun 15 18:10:38 host kernel: PCI: Sharing IRQ 11 with 00:04.0<br>
-kern.info&lt;6&gt;: Jun 15 18:10:38 host kernel: PCI: Sharing IRQ 11 with 01:00.0<br>
-kern.warn&lt;4&gt;: Jun 15 18:10:38 host kernel: Yenta IRQ list 06b8, PCI irq11<br>
-kern.warn&lt;4&gt;: Jun 15 18:10:38 host kernel: Socket status: 30000006<br>
-kern.warn&lt;4&gt;: Jun 15 18:10:38 host kernel: Yenta IRQ list 06b8, PCI irq11<br>
-kern.warn&lt;4&gt;: Jun 15 18:10:38 host kernel: Socket status: 30000010<br>
-kern.info&lt;6&gt;: Jun 15 18:10:38 host kernel: cs: IO port probe 0x0c00-0x0cff: clean.<br>
-kern.info&lt;6&gt;: Jun 15 18:10:38 host kernel: cs: IO port probe 0x0100-0x04ff: excluding 0x100-0x107 0x378-0x37f 0x4d0-0x4d7<br>
-kern.info&lt;6&gt;: Jun 15 18:10:38 host kernel: cs: IO port probe 0x0a00-0x0aff: clean.<br>
-local7.notice&lt;189&gt;: Jun 15 18:17:24 host dd: 1+0 records out<br>
-local7.notice&lt;189&gt;: Jun 15 18:17:24 host random: Saving random seed: succeeded<br>
-local7.notice&lt;189&gt;: Jun 15 18:17:25 host portmap: portmap shutdown succeeded<br>
-local7.notice&lt;189&gt;: Jun 15 18:17:25 host network: Shutting down interface eth1: succeeded<br>
-local7.notice&lt;189&gt;: Jun 15 18:17:25 host network: Shutting down loopback interface: succeeded<br>
-local7.notice&lt;189&gt;: Jun 15 18:17:25 host pcmcia: Shutting down PCMCIA services: cardmgr<br>
-user.notice&lt;13&gt;: Jun 15 18:17:25 host /etc/hotplug/net.agent: NET unregister event not supported<br>
-local7.notice&lt;189&gt;: Jun 15 18:17:27 host pcmcia: modules.<br>
-local7.notice&lt;189&gt;: Jun 15 18:17:29 host rc: Stopping pcmcia: succeeded<br>
-local7.notice&lt;189&gt;: Jun 15 18:17:30 host rc: Starting killall: succeeded<br>
-syslog.info&lt;46&gt;: Jun 15 18:17:33 host [origin software=&quot;rsyslogd&quot; swVersion=&quot;1.13.3&quot; x-pid=&quot;2464&quot;] exiting on signal 15.<br>
-syslog.info&lt;46&gt;: Jun 18 10:55:47 host [origin software=&quot;rsyslogd&quot; swVersion=&quot;1.13.3&quot; x-pid=&quot;2367&quot;][x-configInfo udpReception=&quot;Yes&quot; udpPort=&quot;514&quot; tcpReception=&quot;Yes&quot; tcpPort=&quot;1470&quot;] restart<br>
-user.notice&lt;13&gt;: Jun 18 10:55:50 host rger: test<br>
-syslog.info&lt;46&gt;: Jun 18 10:55:52 host [origin software=&quot;rsyslogd&quot; swVersion=&quot;1.13.3&quot; x-pid=&quot;2367&quot;] exiting on signal 2.</code></p>
+<code>kern.info: Jun 15 18:10:38 host kernel: PCI: Sharing IRQ 11 with 00:04.0<br>
+kern.info: Jun 15 18:10:38 host kernel: PCI: Sharing IRQ 11 with 01:00.0<br>
+kern.warn: Jun 15 18:10:38 host kernel: Yenta IRQ list 06b8, PCI irq11<br>
+kern.warn: Jun 15 18:10:38 host kernel: Socket status: 30000006<br>
+kern.warn: Jun 15 18:10:38 host kernel: Yenta IRQ list 06b8, PCI irq11<br>
+kern.warn: Jun 15 18:10:38 host kernel: Socket status: 30000010<br>
+kern.info: Jun 15 18:10:38 host kernel: cs: IO port probe 0x0c00-0x0cff: clean.<br>
+kern.info: Jun 15 18:10:38 host kernel: cs: IO port probe 0x0100-0x04ff: excluding 0x100-0x107 0x378-0x37f 0x4d0-0x4d7<br>
+kern.info: Jun 15 18:10:38 host kernel: cs: IO port probe 0x0a00-0x0aff: clean.<br>
+local7.notice: Jun 15 18:17:24 host dd: 1+0 records out<br>
+local7.notice: Jun 15 18:17:24 host random: Saving random seed: succeeded<br>
+local7.notice: Jun 15 18:17:25 host portmap: portmap shutdown succeeded<br>
+local7.notice: Jun 15 18:17:25 host network: Shutting down interface eth1: succeeded<br>
+local7.notice: Jun 15 18:17:25 host network: Shutting down loopback interface: succeeded<br>
+local7.notice: Jun 15 18:17:25 host pcmcia: Shutting down PCMCIA services: cardmgr<br>
+user.notice: Jun 15 18:17:25 host /etc/hotplug/net.agent: NET unregister event not supported<br>
+local7.notice: Jun 15 18:17:27 host pcmcia: modules.<br>
+local7.notice: Jun 15 18:17:29 host rc: Stopping pcmcia: succeeded<br>
+local7.notice: Jun 15 18:17:30 host rc: Starting killall: succeeded<br>
+syslog.info: Jun 15 18:17:33 host [origin software=&quot;rsyslogd&quot; swVersion=&quot;1.13.3&quot; x-pid=&quot;2464&quot;] exiting on signal 15.<br>
+syslog.info: Jun 18 10:55:47 host [origin software=&quot;rsyslogd&quot; swVersion=&quot;1.13.3&quot; x-pid=&quot;2367&quot;][x-configInfo udpReception=&quot;Yes&quot; udpPort=&quot;514&quot; tcpReception=&quot;Yes&quot; tcpPort=&quot;1470&quot;] restart<br>
+user.notice: Jun 18 10:55:50 host rger: test<br>
+syslog.info: Jun 18 10:55:52 host [origin software=&quot;rsyslogd&quot; swVersion=&quot;1.13.3&quot; x-pid=&quot;2367&quot;] exiting on signal 2.</code></p>
<h2>Feedback Requested</h2>
<P>I would appreciate feedback on this paper. If you have additional ideas,
comments or find bugs, please
diff --git a/doc/v6compatibility.html b/doc/v6compatibility.html
new file mode 100644
index 00000000..1f830854
--- /dev/null
+++ b/doc/v6compatibility.html
@@ -0,0 +1,171 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><title>Compatibility notes for rsyslog v6</title>
+</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). 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
+not been changed much in v6 and will not. So once the configuration is loaded,
+the stability of v6 is quite comparable to v5.
+</p>
+<h2>Property "pri-text"</h2>
+<p>Traditionally, this property did not only return the textual form
+of the pri ("local0.err"), but also appended the numerical value to it
+("local0.err<133>"). This sounds odd and was left unnoticed for some years.
+In October 2011, this odd behaviour was brought up on the rsyslog mailing list
+by Gregory K. Ruiz-Ade. Code review showed that the behaviour was intentional,
+but no trace of what the intention was when it was introduced could be found.
+The documentation was also unclear, it said no numerical value was present,
+but the samples had it. We agreed that the additional numerical value is
+of disadvantage. We also guessed that this property is very rarely being used,
+otherwise the problem should have been raised much earlier. However, we
+didn't want to change behaviour in older builds. So v6 was set to clean up
+the situation. In v6, text-pri will always return the textual part only
+("local0.err") and the numerical value will not be contained any longer inside
+the string. If you actually need that value, it can fairly easily be added
+via the template system.
+<p><b>If you have used this property previously and relied on the numerical
+part, you need to update your rsyslog configuration files.</b>
+<h2>Plugin ABI</h2>
+<p>The plugin interface has considerably been changed to support the new
+config language. All plugins need to be upgraded. This usually does not require
+much coding. However, if the new config language shall be supported, more
+changes must be made to plugin code. All project-supported plugins have been
+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/grammar/Makefile.am b/grammar/Makefile.am
new file mode 100644
index 00000000..5911f443
--- /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 00000000..e34ab784
--- /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 00000000..ff7708c5
--- /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 00000000..4dbb5907
--- /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 00000000..402b1a57
--- /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 00000000..a3f8bedd
--- /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);
+ // printf("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 00000000..b998a39d
--- /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 00000000..3bb0de44
--- /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 00000000..597cfe40
--- /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 00000000..22a90cb7
--- /dev/null
+++ b/grammar/rainerscript.c
@@ -0,0 +1,1614 @@
+/* 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);
+ }
+dbgprintf("XXXXX: queue type: %d\n", (int)val->val.d.n);
+ 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->next = NULL;
+ cflst->line = line;
+ if(actlst->syslines == NULL) {
+ actlst->syslines = cflst;
+ } 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 {
+ *bSuccess = 1;
+ }
+ return r->d.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;
+ }
+ FREE_BOTH_RET;
+ 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;
+ } else {
+ ret->d.n = 0ll;
+ }
+ FREE_BOTH_RET;
+ 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);
+}
+
+void
+cnfcfsyslinelstDestruct(struct cnfcfsyslinelst *cfslst)
+{
+ struct cnfcfsyslinelst *toDel;
+ while(cfslst != NULL) {
+ toDel = cfslst;
+ cfslst = cfslst->next;
+ free(toDel->line);
+ 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 00000000..e11ae62f
--- /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 00000000..91d475b0
--- /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 00000000..b29626d4
--- /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 84a9c08a..c97d220d 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 aa5f2a52..659d49cd 100644
--- a/parse.c
+++ b/parse.c
@@ -239,7 +239,8 @@ rsRetVal parsSkipWhitespace(rsParsObj *pThis, sbool bRequireOne)
/* Parse string up to a delimiter.
*
* Input:
- * cDelim - the delimiter
+ * cDelim - the delimiter. Note that SP within a value always is a delimiter,
+ * so cDelim is actually an *additional* delimiter.
* The following two are for whitespace stripping,
* 0 means "no", 1 "yes"
* - bTrimLeading
@@ -264,13 +265,13 @@ rsRetVal parsDelimCStr(rsParsObj *pThis, cstr_t **ppCStr, char cDelim, int bTrim
pC = rsCStrGetBufBeg(pThis->pCStr) + pThis->iCurrPos;
- while(pThis->iCurrPos < rsCStrLen(pThis->pCStr) && *pC != cDelim) {
+ while(pThis->iCurrPos < rsCStrLen(pThis->pCStr) && *pC != cDelim && *pC != ' ') {
CHKiRet(cstrAppendChar(pCStr, bConvLower ? tolower(*pC) : *pC));
++pThis->iCurrPos;
++pC;
}
- if(*pC == cDelim) {
+ if(pThis->iCurrPos < cstrLen(pThis->pCStr)) { //BUGFIX!!
++pThis->iCurrPos; /* eat delimiter */
}
diff --git a/plugins/im3195/im3195.c b/plugins/im3195/im3195.c
index 28ca8856..c75e0e34 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 b3468921..48573853 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 */
@@ -387,6 +392,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 632bf390..440f5991 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 */
@@ -85,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;
@@ -335,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)
@@ -561,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);
}
diff --git a/plugins/imgssapi/imgssapi.c b/plugins/imgssapi/imgssapi.c
index 446795d6..9668d1ac 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? */
@@ -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/bsd.c b/plugins/imklog/bsd.c
index eaf8e5ca..80ff9494 100644
--- a/plugins/imklog/bsd.c
+++ b/plugins/imklog/bsd.c
@@ -151,37 +151,37 @@ submitSyslog(int pri, uchar *buf)
#endif /* #ifdef LINUX */
-static uchar *GetPath(void)
+static uchar *GetPath(modConfData_t *pModConf)
{
- return pszPath ? pszPath : (uchar*) _PATH_KLOG;
+ 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) {
imklogLogIntMsg(RS_RET_ERR_OPEN_KLOG, "imklog: cannot open kernel log(%s): %s.",
- GetPath(), rs_strerror_r(errno, errmsg, sizeof(errmsg)));
+ 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(console_log_level != -1) {
- r = klogctl(8, NULL, console_log_level);
+ 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! */
- console_log_level = -1;
+ pModConf->console_log_level = -1;
}
}
# endif /* #ifdef OS_LINUX */
@@ -257,14 +257,14 @@ 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(console_log_level != -1)
+ if(pModConf->console_log_level != -1)
klogctl(7, NULL, 0);
# endif
RETiRet;
@@ -276,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 40249273..b52b67af 100644
--- a/plugins/imklog/imklog.c
+++ b/plugins/imklog/imklog.c
@@ -62,6 +62,7 @@
MODULE_TYPE_INPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("imklog")
/* Module static data */
DEF_IMOD_STATIC_DATA
@@ -69,27 +70,43 @@ DEFobjCurrIf(datetime)
DEFobjCurrIf(glbl)
DEFobjCurrIf(prop)
-/* 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;
+ char *symfile; /* TODO: actually unsued currently! */
+} 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.symfile = 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.
@@ -182,8 +199,7 @@ rsRetVal imklogLogIntMsg(int priority, char *fmt, ...)
pLogMsg = msgBuf;
va_end(ap);
- iRet = enqMsg((uchar*)pLogMsg, (uchar*) ((iFacilIntMsg == LOG_KERN) ? "kernel:" : "imklog:"),
- iFacilIntMsg, LOG_PRI(priority), NULL);
+ logmsgInternal(NO_ERRCODE ,priority, msgBuf, 0);
RETiRet;
}
@@ -220,7 +236,7 @@ rsRetVal Syslog(int priority, uchar *pMsg, struct timeval *tp)
/* 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), tp);
@@ -252,63 +268,123 @@ 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;
+ if((cs.symfile == NULL) || (cs.symfile[0] == '\0')) {
+ loadModConf->symfile = NULL;
+ if(cs.symfile != NULL)
+ free(cs.symfile);
+ } else {
+ loadModConf->symfile = cs.symfile;
+ }
+ cs.symfile = NULL;
+
+ loadModConf = NULL; /* done loading */
+ /* free legacy config vars */
+ free(cs.pszPath);
+ cs.pszPath = NULL;
+ free(cs.symfile);
+ cs.symfile = NULL;
+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(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.symfile = NULL;
+ 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;
}
@@ -320,17 +396,31 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(glbl, CORE_COMPONENT));
CHKiRet(objUse(prop, CORE_COMPONENT));
- 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));
+ /* 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));
+
+ /* 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 39bdeb5c..b0772711 100644
--- a/plugins/imklog/imklog.h
+++ b/plugins/imklog/imklog.h
@@ -30,22 +30,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;
+ char *symfile;
+ 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);
+rsRetVal klogLogKMsg(modConfData_t *pModConf);
+rsRetVal klogWillRun(modConfData_t *pModConf);
+rsRetVal klogAfterRun(modConfData_t *pModConf);
+int klogFacilIntMsg();
/* 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
*/
+#if 0
extern int symbols_twice;
extern int use_syscall;
extern int symbol_lookup;
@@ -53,6 +68,7 @@ extern char *symfile;
extern int console_log_level;
extern int dbgPrintSymbols;
extern uchar *pszPath;
+#endif
/* the functions below may be called by the drivers */
rsRetVal imklogLogIntMsg(int priority, char *fmt, ...) __attribute__((format(printf,2, 3)));
@@ -60,7 +76,7 @@ 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/immark/immark.c b/plugins/immark/immark.c
index 358b3b18..273af021 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 3012136c..66c5fee6 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)
@@ -59,6 +60,16 @@ typedef struct configSettings_s {
int iSeverity;
} configSettings_t;
+struct modConfData_s {
+ rsconf_t *pConf; /* our overall config object */
+ int iStatsInterval;
+ int iFacility;
+ int iSeverity;
+};
+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;
@@ -95,8 +106,8 @@ doSubmitMsg(uchar *line)
MsgSetRcvFromIP(pMsg, pLocalHostIP);
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);
@@ -129,6 +140,53 @@ generateStatsMsgs(void)
}
+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;
+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 +194,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 +205,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
@@ -182,6 +230,7 @@ ENDmodExit
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
+CODEqueryEtryPt_STD_CONF2_QUERIES
CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
ENDqueryEtryPt
diff --git a/plugins/imptcp/imptcp.c b/plugins/imptcp/imptcp.c
index a735b0b2..ec6836c1 100644
--- a/plugins/imptcp/imptcp.c
+++ b/plugins/imptcp/imptcp.c
@@ -75,6 +75,7 @@
MODULE_TYPE_INPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("imptcp")
/* static data */
DEF_IMOD_STATIC_DATA
@@ -86,6 +87,8 @@ DEFobjCurrIf(errmsg)
DEFobjCurrIf(ruleset)
DEFobjCurrIf(statsobj)
+/* forward references */
+static void * wrkr(void *myself);
/* config settings */
typedef struct configSettings_s {
@@ -97,11 +100,37 @@ typedef struct configSettings_s {
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 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;
@@ -125,6 +154,7 @@ struct ptcpsrv_s {
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;
};
@@ -167,6 +197,20 @@ struct ptcplstn_s {
};
+/* The following structure controls the worker threads. Global data is
+ * needed for their access.
+ */
+static struct wrkrInfo_s {
+ pthread_t tid; /* the worker's thread ID */
+ pthread_cond_t run;
+ struct epoll_event *event; /* event == NULL -> idle */
+ long long unsigned numCalled; /* how often was this called */
+} wrkrInfo[16];
+static pthread_mutex_t wrkrMut;
+static pthread_cond_t wrkrIdle;
+static int wrkrRunning;
+
+
/* type of object stored in epoll descriptor */
typedef enum {
epolld_lstn,
@@ -184,20 +228,10 @@ struct epolld_s {
/* global data */
-//static permittedPeers_t *pPermPeersRoot = NULL;
+pthread_attr_t wrkrThrdAttr; /* Attribute for session threads; read only after startup */
static ptcpsrv_t *pSrvRoot = NULL;
static int epollfd = -1; /* (sole) descriptor for epoll */
static int iMaxLine; /* maximum size of a single message */
-/* we use a single static receive buffer, as this module is not multi-threaded. Keeping
- * the buffer in the data segment is probably a little bit more efficient than on the stack
- * (but at least I can't believe it will ever be less efficient ;) -- rgerhards, 2010-08-10
- * Note that we do NOT (yet?) provide a config setting to set the buffer size. For usual
- * syslog traffic, it should be large enough. Also keep in mind that we run under a virtual
- * memory system, so if we do not use large parts of the buffer, that's no issue at
- * all -- it'll just use up address space. On the other hand, it would be silly to page in
- * or page out some data just to get space for the IO buffer.
- */
-static char rcvBuf[128*1024];
/* forward definitions */
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal);
@@ -222,6 +256,7 @@ static void
destructSrv(ptcpsrv_t *pSrv)
{
prop.Destruct(&pSrv->pInputName);
+ pthread_mutex_destroy(&pSrv->mutSessLst);
free(pSrv->port);
free(pSrv);
}
@@ -250,7 +285,7 @@ startupSrv(ptcpsrv_t *pSrv)
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;
@@ -769,9 +804,11 @@ static inline void
initConfigSettings(void)
{
cs.bEmitMsgOnClose = 0;
+ cs.wrkrMax = 2;
cs.iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER;
cs.pszInputName = NULL;
- cs.pRuleset = NULL;
+ cs.pszBindRuleset = NULL;
+ cs.pszInputName = NULL;
cs.lstnIP = NULL;
}
@@ -893,10 +930,12 @@ addSess(ptcplstn_t *pLstn, int sock, prop_t *peerName, prop_t *peerIP)
/* add to start of server's listener list */
pSess->prev = NULL;
+ pthread_mutex_lock(&pSrv->mutSessLst);
pSess->next = pSrv->pSess;
if(pSrv->pSess != NULL)
pSrv->pSess->prev = pSess;
pSrv->pSess = pSess;
+ pthread_mutex_unlock(&pSrv->mutSessLst);
iRet = addEPollSock(epolld_sess, pSess, sock, &pSess->epd);
@@ -919,10 +958,8 @@ closeSess(ptcpsess_t *pSess)
CHKiRet(removeEPollSock(sock, pSess->epd));
close(sock);
+ pthread_mutex_lock(&pSess->pLstn->pSrv->mutSessLst);
/* finally unlink session from structures */
-//fprintf(stderr, "closing session %d next %p, prev %p\n", pSess->sock, pSess->next, pSess->prev);
-//DBGPRINTF("imptcp: pSess->next %p\n", pSess->next);
-//DBGPRINTF("imptcp: pSess->prev %p\n", pSess->prev);
if(pSess->next != NULL)
pSess->next->prev = pSess->prev;
if(pSess->prev == NULL) {
@@ -931,6 +968,7 @@ closeSess(ptcpsess_t *pSess)
} else {
pSess->prev->next = pSess->next;
}
+ pthread_mutex_unlock(&pSess->pLstn->pSrv->mutSessLst);
/* unlinked, now remove structure */
destructSess(pSess);
@@ -941,62 +979,86 @@ finalize_it:
}
-#if 0
-/* set permitted peer -- rgerhards, 2008-05-19
+/* 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
-setPermittedPeer(void __attribute__((unused)) *pVal, uchar *pszID)
-{
- DEFiRet;
- CHKiRet(net.AddPermittedPeer(&pPermPeersRoot, pszID));
- free(pszID); /* no longer needed, but we need to free as of interface def */
-finalize_it:
- RETiRet;
-}
-#endif
-
-
-/* accept a new ruleset to bind. Checks if it exists and complains, if not */
-static rsRetVal setRuleset(void __attribute__((unused)) *pVal, uchar *pszName)
+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->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->bKeepAlive = cs.bKeepAlive;
- pSrv->iKeepAliveIntvl = cs.iKeepAliveTime;
- pSrv->iKeepAliveProbes = cs.iKeepAliveProbes;
- pSrv->iKeepAliveTime = cs.iKeepAliveTime;
- pSrv->bEmitMsgOnClose = cs.bEmitMsgOnClose;
- pSrv->port = pNewVal;
- pSrv->iAddtlFrameDelim = cs.iAddtlFrameDelim;
- cs.pszInputName = NULL; /* moved over to pSrv, we do not own */
- pSrv->lstnIP = cs.lstnIP;
- cs.lstnIP = NULL; /* moved over to pSrv, we do not own */
- pSrv->pRuleset = cs.pRuleset;
- pSrv->pszInputName = (cs.pszInputName == NULL) ? UCHAR_CONSTANT("imptcp") : cs.pszInputName;
+ 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 = (inst->pszInputName == NULL) ? UCHAR_CONSTANT("imptcp") : ustrdup(inst->pszInputName);
CHKiRet(prop.Construct(&pSrv->pInputName));
CHKiRet(prop.SetString(pSrv->pInputName, pSrv->pszInputName, ustrlen(pSrv->pszInputName)));
CHKiRet(prop.ConstructFinalize(pSrv->pInputName));
@@ -1018,6 +1080,46 @@ finalize_it:
}
+/* destroy worker pool structures and wait for workers to terminate
+ */
+static inline void
+startWorkerPool(void)
+{
+ int i;
+ wrkrRunning = 0;
+ if(runModConf->wrkrMax > 16)
+ runModConf->wrkrMax = 16; /* TODO: make dynamic? */
+ pthread_mutex_init(&wrkrMut, NULL);
+ pthread_cond_init(&wrkrIdle, NULL);
+ for(i = 0 ; i < runModConf->wrkrMax ; ++i) {
+ /* init worker info structure! */
+ pthread_cond_init(&wrkrInfo[i].run, NULL);
+ wrkrInfo[i].event = NULL;
+ wrkrInfo[i].numCalled = 0;
+ pthread_create(&wrkrInfo[i].tid, &wrkrThrdAttr, wrkr, &(wrkrInfo[i]));
+ }
+
+}
+
+/* destroy worker pool structures and wait for workers to terminate
+ */
+static inline void
+stopWorkerPool(void)
+{
+ int 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);
+ pthread_cond_destroy(&wrkrInfo[i].run);
+ }
+ pthread_cond_destroy(&wrkrIdle);
+ pthread_mutex_destroy(&wrkrMut);
+
+}
+
+
+
/* start up all listeners
* This is a one-time stop once the module is set to start.
*/
@@ -1025,15 +1127,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("Starting up ptcp server for port %s, name '%s'\n", pSrv->port, pSrv->pszInputName);
- startupSrv(pSrv);
+ DBGPRINTF("imptcp: starting up server for port %s, name '%s'\n", pSrv->port, pSrv->pszInputName);
+ 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;
}
@@ -1051,9 +1167,9 @@ lstnActivity(ptcplstn_t *pLstn)
DEFiRet;
DBGPRINTF("imptcp: new connection on listen socket %d\n", pLstn->sock);
- while(1) {
+ while(glbl.GetGlobalInputTermState() == 0) {
localRet = AcceptConnReq(pLstn, &newSock, &peerName, &peerIP);
- if(localRet == RS_RET_NO_MORE_DATA)
+ if(localRet == RS_RET_NO_MORE_DATA || glbl.GetGlobalInputTermState() == 1)
break;
CHKiRet(localRet);
CHKiRet(addSess(pLstn, newSock, peerName, peerIP));
@@ -1072,6 +1188,7 @@ sessActivity(ptcpsess_t *pSess)
{
int lenRcv;
int lenBuf;
+ char rcvBuf[128*1024];
DEFiRet;
DBGPRINTF("imptcp: new activity on session socket %d\n", pSess->sock);
@@ -1109,47 +1226,160 @@ finalize_it:
}
-/* This function is called to gather input.
+/* This function is called to process a single request. This may
+ * be carried out by the main worker or a helper. It can be run
+ * concurrently.
*/
-BEGINrunInput
- int i;
- int nfds;
- struct epoll_event events[1];
+static inline void
+processWorkItem(struct epoll_event *event)
+{
epolld_t *epd;
-CODESTARTrunInput
- DBGPRINTF("imptcp now beginning to process input data\n");
- /* v5 TODO: consentual termination mode */
- while(1) {
- DBGPRINTF("imptcp going on epoll_wait\n");
- nfds = epoll_wait(epollfd, events, sizeof(events)/sizeof(struct epoll_event), -1);
- for(i = 0 ; i < nfds ; ++i) { /* support for larger batches (later, TODO) */
- epd = (epolld_t*) events[i].data.ptr;
- switch(epd->typ) {
- case epolld_lstn:
- lstnActivity((ptcplstn_t *) epd->ptr);
- break;
- case epolld_sess:
- sessActivity((ptcpsess_t *) epd->ptr);
- break;
- default:
- errmsg.LogError(0, RS_RET_INTERNAL_ERROR,
- "error: invalid epolld_type_t %d after epoll", epd->typ);
- break;
+
+ epd = (epolld_t*) event->data.ptr;
+ switch(epd->typ) {
+ case epolld_lstn:
+ lstnActivity((ptcplstn_t *) epd->ptr);
+ break;
+ case epolld_sess:
+ sessActivity((ptcpsess_t *) epd->ptr);
+ break;
+ default:
+ errmsg.LogError(0, RS_RET_INTERNAL_ERROR,
+ "error: invalid epolld_type_t %d after epoll", epd->typ);
+ break;
+ }
+}
+
+
+/* This function is called to process a complete workset, that
+ * is a set of events returned from epoll.
+ */
+static inline void
+processWorkSet(int nEvents, struct epoll_event events[])
+{
+ int iEvt;
+ int i;
+ int remainEvents;
+
+ remainEvents = nEvents;
+ for(iEvt = 0 ; (iEvt < nEvents) && (glbl.GetGlobalInputTermState() == 0) ; ++iEvt) {
+ if(remainEvents == 1) {
+ /* process self, save context switch */
+ processWorkItem(events+iEvt);
+ } else {
+ pthread_mutex_lock(&wrkrMut);
+ /* check if there is a free worker */
+ for(i = 0 ; (i < runModConf->wrkrMax) && (wrkrInfo[i].event != NULL) ; ++i)
+ /*do search*/;
+ if(i < runModConf->wrkrMax) {
+ /* worker free -> use it! */
+ wrkrInfo[i].event = events+iEvt;
+ ++wrkrRunning;
+ pthread_cond_signal(&wrkrInfo[i].run);
+ pthread_mutex_unlock(&wrkrMut);
+ } else {
+ pthread_mutex_unlock(&wrkrMut);
+ /* no free worker, so we process this one ourselfs */
+ processWorkItem(events+iEvt);
}
}
+ --remainEvents;
}
-ENDrunInput
+ if(nEvents > 1) {
+ /* we now need to wait until all workers finish. This is because the
+ * rest of this module can not handle the concurrency introduced
+ * by workers running during the epoll call.
+ */
+ pthread_mutex_lock(&wrkrMut);
+ while(wrkrRunning > 0) {
+ pthread_cond_wait(&wrkrIdle, &wrkrMut);
+ }
+ pthread_mutex_unlock(&wrkrMut);
+ }
-/* initialize and return if will run or not */
-BEGINwillRun
-CODESTARTwillRun
- /* first apply some config settings */
- //net.PrintAllowedSenders(2); /* TCP */
+}
+
+
+/* worker to process incoming requests
+ */
+static void *
+wrkr(void *myself)
+{
+ struct wrkrInfo_s *me = (struct wrkrInfo_s*) myself;
+
+ pthread_mutex_lock(&wrkrMut);
+ while(1) {
+ while(me->event == NULL && glbl.GetGlobalInputTermState() == 0) {
+ pthread_cond_wait(&me->run, &wrkrMut);
+ }
+ if(glbl.GetGlobalInputTermState() == 1)
+ break;
+ pthread_mutex_unlock(&wrkrMut);
+
+ ++me->numCalled;
+ processWorkItem(me->event);
+
+ pthread_mutex_lock(&wrkrMut);
+ me->event = NULL; /* indicate we are free again */
+ --wrkrRunning;
+ pthread_cond_signal(&wrkrIdle);
+ }
+ pthread_mutex_unlock(&wrkrMut);
+
+ return NULL;
+}
+
+
+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);
+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);
+ }
+ENDcheckCnf
+
+
+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);
}
@@ -1176,6 +1406,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
@@ -1216,8 +1492,8 @@ shutdownSrv(ptcpsrv_t *pSrv)
BEGINafterRun
ptcpsrv_t *pSrv, *srvDel;
CODESTARTafterRun
- /* do cleanup here */
- //net.clearAllowedSenders(UCHAR_CONSTANT("TCP"));
+ stopWorkerPool();
+
/* we need to close everything that is still open */
pSrv = pSrvRoot;
while(pSrv != NULL) {
@@ -1233,12 +1509,7 @@ ENDafterRun
BEGINmodExit
CODESTARTmodExit
-#if 0
- if(pPermPeersRoot != NULL) {
- net.DestructPermittedPeers(&pPermPeersRoot);
- }
-#endif
-
+ pthread_attr_destroy(&wrkrThrdAttr);
/* release objects we used */
objRelease(glbl, CORE_COMPONENT);
objRelease(statsobj, CORE_COMPONENT);
@@ -1254,6 +1525,7 @@ static rsRetVal
resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
cs.bEmitMsgOnClose = 0;
+ cs.wrkrMax = 2;
cs.bKeepAlive = 0;
cs.iKeepAliveProbes = 0;
cs.iKeepAliveTime = 0;
@@ -1267,10 +1539,19 @@ resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unus
}
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURENonCancelInputTermination)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
+CODEqueryEtryPt_STD_CONF2_QUERIES
+CODEqueryEtryPt_STD_CONF2_PREPRIVDROP_QUERIES
+CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
ENDqueryEtryPt
@@ -1278,7 +1559,6 @@ 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));
@@ -1288,9 +1568,16 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(datetime, CORE_COMPONENT));
CHKiRet(objUse(ruleset, CORE_COMPONENT));
+ /* initialize "read-only" thread attributes */
+ pthread_attr_init(&wrkrThrdAttr);
+ pthread_attr_setstacksize(&wrkrThrdAttr, 2048*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,
@@ -1303,12 +1590,14 @@ CODEmodInit_QueryRegCFSLineHdlr
eCmdHdlrBinary, NULL, &cs.bEmitMsgOnClose, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserveraddtlframedelimiter"), 0, eCmdHdlrInt,
NULL, &cs.iAddtlFrameDelim, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverhelperthreads"), 0, eCmdHdlrInt,
+ NULL, &cs.wrkrMax, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverinputname"), 0,
eCmdHdlrGetWord, NULL, &cs.pszInputName, STD_LOADABLE_MODULE_ID));
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 602809ff..99fabd18 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 ee9ec5c6..8b607a84 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 e9961af7..15f9fa72 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,24 +77,55 @@ 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 bKeepAlive = 0; /* support keep-alive packets */
-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 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 */
+ 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 */
+ 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! */
@@ -166,49 +198,71 @@ 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->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.SetKeepAlive(pOurTcpsrv, bKeepAlive));
- 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));
@@ -216,41 +270,112 @@ 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);
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->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
@@ -290,18 +415,18 @@ ENDmodExit
static rsRetVal
resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
- iTCPSessMax = 200;
- bKeepAlive = 0;
- 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.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;
}
@@ -310,6 +435,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
@@ -329,33 +456,33 @@ 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, &bKeepAlive, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.bKeepAlive, 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 1825b5bc..00000000
--- 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 0e2cac11..00000000
--- 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/Makefile.am b/plugins/imttcp/Makefile.am
new file mode 100644
index 00000000..9b09b4bf
--- /dev/null
+++ b/plugins/imttcp/Makefile.am
@@ -0,0 +1,6 @@
+pkglib_LTLIBRARIES = imttcp.la
+
+imttcp_la_SOURCES = imttcp.c
+imttcp_la_CPPFLAGS = -I$(top_srcdir) $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
+imttcp_la_LDFLAGS = -module -avoid-version
+imttcp_la_LIBADD =
diff --git a/plugins/imttcp/imttcp.c b/plugins/imttcp/imttcp.c
new file mode 100644
index 00000000..5ed714fa
--- /dev/null
+++ b/plugins/imttcp/imttcp.c
@@ -0,0 +1,1151 @@
+/* imttcp.c
+ * This is an experimental plain tcp input module which follows the
+ * multiple thread paradigm.
+ *
+ * WARNING
+ * This module is unfinished. It seems to work, but there also seems to be a problem
+ * if it is under large stress (e.g. tcpflood with more than 500 to 1000 concurrent
+ * connections). I quickly put together this module after I worked on a larger paper
+ * and read [1], which claims that using massively threaded applications is
+ * preferrable to an event driven approach. So I put this to test, especially as
+ * that would also lead to a much simpler programming paradigm. Unfortuantely, the
+ * performance results are devastive: while there is a very slight speedup with
+ * a low connection number (close to the number of cores on the system), there
+ * is a dramatic negative speedup if running with many threads. Even at only 50
+ * connections, rsyslog is dramatically slower (80 seconds for the same workload
+ * which was processed in 60 seconds with traditional imtcp or when running on
+ * a single connection). At 1,000 connections, the run was *extremely* slow. So
+ * this is definitely a dead-end. To be honest, Behren, condit and Brewer claim
+ * that the problem lies in the current implementation of thread libraries.
+ * As one cure, they propose user-level threads. However, as far as I could
+ * find out, User-Level threads seem not to be much faster under Linux than
+ * Kernel-Level threads (which I used in my approach).
+ *
+ * Even more convincing is, from the rsyslog PoV, that there are clear reasons
+ * why the highly threaded input must be slower:
+ * o batch sizes are smaller, leading to much more overhead
+ * o many more context switches are needed to switch between the various
+ * i/o handlers
+ * o more OS API calls are required because in this model we get more
+ * frequent wakeups on new incoming data, so we have less data available
+ * to read at each instant
+ * o more lock contention because many more threads compete on the
+ * main queue mutex
+ *
+ * All in all, this means that the approach is not the right one, at least
+ * not for rsyslog (it may work better if the input can be processed
+ * totally independent, but I have note evaluated this). So I will look into
+ * an enhanced event-based model with a small set of input workers pulling
+ * off data (I assume this is useful for e.g. TLS, as TLS transport is much
+ * more computebound than other inputs, and this computation becomes a
+ * limiting factor for the overall processing speed under some
+ * circumstances - see [2]).
+ *
+ * For obvious reasons, I will not try to finish imttcp. However, I have
+ * decided to leave it included in the source tree, so that
+ * a) someone else can build on it, if he sees value in that
+ * b) I may use it for some other tests in the future
+ *
+ * But if you intend to actually use this module unmodified, be prepared
+ * for problems.
+ *
+ * [1] R. Von Behren, J. Condit, and E. Brewer. Why events are a bad idea
+ * (for high-concurrency servers). In Proceedings of the 9th conference on Hot
+ * Topics in Operating Systems-Volume 9, page 4. USENIX Association, 2003.
+ *
+ * [2] http://kb.monitorware.com/tls-limited-17800-messages-per-second-t10598.html
+ *
+ * File begun on 2011-01-24 by RGerhards
+ *
+ * Copyright 2007-2011 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"
+#if !defined(HAVE_EPOLL_CREATE)
+# error imttcp requires OS support for epoll - can not build
+ /* imttcp gains speed by using modern Linux capabilities. As such,
+ * it can only be build on platforms supporting the epoll API.
+ */
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/epoll.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include "rsyslog.h"
+#include "cfsysline.h"
+#include "prop.h"
+#include "dirty.h"
+#include "module-template.h"
+#include "unicode-helper.h"
+#include "glbl.h"
+#include "prop.h"
+#include "errmsg.h"
+#include "srUtils.h"
+#include "datetime.h"
+#include "ruleset.h"
+#include "msg.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 ... */
+#define TCPSRV_NO_ADDTL_DELIMITER -1 /* specifies that no additional delimiter is to be used in TCP framing */
+
+
+MODULE_TYPE_INPUT
+MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("imttcp")
+
+/* static data */
+DEF_IMOD_STATIC_DATA
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(net)
+DEFobjCurrIf(prop)
+DEFobjCurrIf(datetime)
+DEFobjCurrIf(errmsg)
+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 */
+ 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) */
+} configSettings_t;
+
+static configSettings_t cs;
+
+/* data elements describing our running config */
+typedef struct ttcpsrv_s ttcpsrv_t;
+typedef struct ttcplstn_s ttcplstn_t;
+typedef struct ttcpsess_s ttcpsess_t;
+typedef struct epolld_s epolld_t;
+
+/* the ttcp server (listener) object
+ * Note that the object contains support for forming a linked list
+ * of them. It does not make sense to do this seperately.
+ */
+struct ttcpsrv_s {
+ ttcpsrv_t *pNext; /* linked list maintenance */
+ uchar *port; /* Port to listen to */
+ uchar *lstnIP; /* which IP we should listen on? */
+ int bEmitMsgOnClose;
+ int iAddtlFrameDelim;
+ uchar *pszInputName;
+ prop_t *pInputName; /* InputName in (fast to process) property format */
+ ruleset_t *pRuleset;
+ ttcplstn_t *pLstn; /* root of our listeners */
+ ttcpsess_t *pSess; /* root of our sessions */
+ pthread_mutex_t mutSess; /* mutex for session list updates */
+};
+
+/* the ttcp session object. Describes a single active session.
+ * includes support for doubly-linked list.
+ */
+struct ttcpsess_s {
+ ttcpsrv_t *pSrv; /* our server */
+ ttcpsess_t *prev, *next;
+ int sock;
+ pthread_t tid;
+//--- 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? */
+ enum {
+ eAtStrtFram,
+ eInOctetCnt,
+ eInMsg
+ } inputState; /* our current state */
+ int iOctetsRemain; /* Number of Octets remaining in message */
+ TCPFRAMINGMODE eFraming;
+ uchar *pMsg; /* message (fragment) received */
+ prop_t *peerName; /* host name we received messages from */
+ prop_t *peerIP;
+//--- END from tcps_sess.h
+};
+
+
+/* the ttcp listener object. Describes a single active listener.
+ */
+struct ttcplstn_s {
+ ttcpsrv_t *pSrv; /* our server */
+ ttcplstn_t *prev, *next;
+ int sock;
+ pthread_t tid; /* ID of our listener thread */
+};
+
+
+/* type of object stored in epoll descriptor */
+typedef enum {
+ epolld_lstn,
+ epolld_sess
+} epolld_type_t;
+
+/* an epoll descriptor. contains all information necessary to process
+ * the result of epoll.
+ */
+struct epolld_s {
+ epolld_type_t typ;
+ void *ptr;
+ struct epoll_event ev;
+};
+
+
+/* global data */
+static ttcpsrv_t *pSrvRoot = NULL;
+static int iMaxLine; /* maximum size of a single message */
+pthread_attr_t sessThrdAttr; /* Attribute for session threads; read only after startup */
+
+/* forward definitions */
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal);
+static rsRetVal addLstn(ttcpsrv_t *pSrv, int sock);
+static void * sessThrd(void *arg);
+
+
+/* some simple constructors/destructors */
+static void
+destructSess(ttcpsess_t *pSess)
+{
+ free(pSess->pMsg);
+ prop.Destruct(&pSess->peerName);
+ prop.Destruct(&pSess->peerIP);
+ /* TODO: make these inits compile-time switch depending: */
+ pSess->pMsg = NULL;
+ free(pSess);
+}
+
+static void
+destructSrv(ttcpsrv_t *pSrv)
+{
+ prop.Destruct(&pSrv->pInputName);
+ free(pSrv->port);
+ free(pSrv);
+}
+
+
+/* common initialisation for new threads */
+static inline void
+initThrd(void)
+{
+ /* block all signals */
+ sigset_t sigSet;
+ sigfillset(&sigSet);
+ pthread_sigmask(SIG_BLOCK, &sigSet, NULL);
+
+ /* but ignore SIGTTN, which we (ab)use to signal the thread to shutdown -- rgerhards, 2009-07-20 */
+ sigemptyset(&sigSet);
+ sigaddset(&sigSet, SIGTTIN);
+ pthread_sigmask(SIG_UNBLOCK, &sigSet, NULL);
+
+}
+
+
+
+/****************************************** TCP SUPPORT FUNCTIONS ***********************************/
+/* We may later think about moving this into a helper library again. But the whole point
+ * so far was to keep everything related close togehter. -- rgerhards, 2010-08-10
+ */
+
+
+/* Start up a server. That means all of its listeners are created.
+ * Does NOT yet accept/process any incoming data (but binds ports). Hint: this
+ * code is to be executed before dropping privileges.
+ */
+static rsRetVal
+createSrv(ttcpsrv_t *pSrv)
+{
+ DEFiRet;
+ int error, maxs, on = 1;
+ int sock = -1;
+ int numSocks;
+ struct addrinfo hints, *res = NULL, *r;
+ uchar *lstnIP;
+
+ lstnIP = pSrv->lstnIP == NULL ? UCHAR_CONSTANT("") : pSrv->lstnIP;
+
+ DBGPRINTF("imttcp creating listen socket on server '%s', port %s\n", lstnIP, pSrv->port);
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = glbl.GetDefPFFamily();
+ hints.ai_socktype = SOCK_STREAM;
+
+ error = getaddrinfo((char*)pSrv->lstnIP, (char*) pSrv->port, &hints, &res);
+ if(error) {
+ DBGPRINTF("error %d querying server '%s', port '%s'\n", error, pSrv->lstnIP, pSrv->port);
+ ABORT_FINALIZE(RS_RET_INVALID_PORT);
+ }
+
+ /* Count max number of sockets we may open */
+ for(maxs = 0, r = res; r != NULL ; r = r->ai_next, maxs++)
+ /* EMPTY */;
+
+ numSocks = 0; /* num of sockets counter at start of array */
+ for(r = res; r != NULL ; r = r->ai_next) {
+ sock = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
+ if(sock < 0) {
+ if(!(r->ai_family == PF_INET6 && errno == EAFNOSUPPORT))
+ DBGPRINTF("error %d creating tcp listen socket", errno);
+ /* it is debatable if PF_INET with EAFNOSUPPORT should
+ * also be ignored...
+ */
+ continue;
+ }
+
+#ifdef IPV6_V6ONLY
+ if(r->ai_family == AF_INET6) {
+ int iOn = 1;
+ if(setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
+ (char *)&iOn, sizeof (iOn)) < 0) {
+ close(sock);
+ sock = -1;
+ continue;
+ }
+ }
+#endif
+ if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0 ) {
+ DBGPRINTF("error %d setting tcp socket option\n", errno);
+ close(sock);
+ sock = -1;
+ continue;
+ }
+
+ /* We need to enable BSD compatibility. Otherwise an attacker
+ * could flood our log files by sending us tons of ICMP errors.
+ */
+#ifndef BSD
+ if(net.should_use_so_bsdcompat()) {
+ if (setsockopt(sock, SOL_SOCKET, SO_BSDCOMPAT,
+ (char *) &on, sizeof(on)) < 0) {
+ errmsg.LogError(errno, NO_ERRCODE, "TCP setsockopt(BSDCOMPAT)");
+ close(sock);
+ sock = -1;
+ continue;
+ }
+ }
+#endif
+
+ if( (bind(sock, r->ai_addr, r->ai_addrlen) < 0)
+#ifndef IPV6_V6ONLY
+ && (errno != EADDRINUSE)
+#endif
+ ) {
+ /* TODO: check if *we* bound the socket - else we *have* an error! */
+ DBGPRINTF("error %d while binding tcp socket", errno);
+ close(sock);
+ sock = -1;
+ continue;
+ }
+
+ if(listen(sock, 511) < 0) {
+ DBGPRINTF("tcp listen error %d, suspending\n", errno);
+ close(sock);
+ sock = -1;
+ continue;
+ }
+
+ /* 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));
+ ++numSocks;
+ }
+
+ if(numSocks != maxs)
+ DBGPRINTF("We could initialize %d TCP listen sockets out of %d we received "
+ "- this may or may not be an error indication.\n", numSocks, maxs);
+
+ if(numSocks == 0) {
+ DBGPRINTF("No TCP listen sockets could successfully be initialized");
+ ABORT_FINALIZE(RS_RET_COULD_NOT_BIND);
+ }
+
+finalize_it:
+ if(res != NULL)
+ freeaddrinfo(res);
+
+ if(iRet != RS_RET_OK) {
+ if(sock != -1)
+ close(sock);
+ }
+
+ RETiRet;
+}
+
+
+/* Set pRemHost based on the address provided. This is to be called upon accept()ing
+ * a connection request. It must be provided by the socket we received the
+ * message on as well as a NI_MAXHOST size large character buffer for the FQDN.
+ * Please see http://www.hmug.org/man/3/getnameinfo.php (under Caveats)
+ * for some explanation of the code found below. If we detect a malicious
+ * hostname, we return RS_RET_MALICIOUS_HNAME and let the caller decide
+ * on how to deal with that.
+ * rgerhards, 2008-03-31
+ */
+static rsRetVal
+getPeerNames(prop_t **peerName, prop_t **peerIP, struct sockaddr *pAddr)
+{
+ int error;
+ uchar szIP[NI_MAXHOST] = "";
+ uchar szHname[NI_MAXHOST] = "";
+ struct addrinfo hints, *res;
+
+ DEFiRet;
+
+ error = getnameinfo(pAddr, SALEN(pAddr), (char*)szIP, sizeof(szIP), NULL, 0, NI_NUMERICHOST);
+
+ if(error) {
+ DBGPRINTF("Malformed from address %s\n", gai_strerror(error));
+ strcpy((char*)szHname, "???");
+ strcpy((char*)szIP, "???");
+ ABORT_FINALIZE(RS_RET_INVALID_HNAME);
+ }
+
+ if(!glbl.GetDisableDNS()) {
+ error = getnameinfo(pAddr, SALEN(pAddr), (char*)szHname, NI_MAXHOST, NULL, 0, NI_NAMEREQD);
+ if(error == 0) {
+ memset (&hints, 0, sizeof (struct addrinfo));
+ hints.ai_flags = AI_NUMERICHOST;
+ hints.ai_socktype = SOCK_STREAM;
+ /* we now do a lookup once again. This one should fail,
+ * because we should not have obtained a non-numeric address. If
+ * we got a numeric one, someone messed with DNS!
+ */
+ if(getaddrinfo((char*)szHname, NULL, &hints, &res) == 0) {
+ freeaddrinfo (res);
+ /* OK, we know we have evil, so let's indicate this to our caller */
+ snprintf((char*)szHname, NI_MAXHOST, "[MALICIOUS:IP=%s]", szIP);
+ DBGPRINTF("Malicious PTR record, IP = \"%s\" HOST = \"%s\"", szIP, szHname);
+ iRet = RS_RET_MALICIOUS_HNAME;
+ }
+ } else {
+ strcpy((char*)szHname, (char*)szIP);
+ }
+ } else {
+ strcpy((char*)szHname, (char*)szIP);
+ }
+
+ /* We now have the names, so now let's allocate memory and store them permanently. */
+ CHKiRet(prop.Construct(peerName));
+ CHKiRet(prop.SetString(*peerName, szHname, ustrlen(szHname)));
+ CHKiRet(prop.ConstructFinalize(*peerName));
+ CHKiRet(prop.Construct(peerIP));
+ CHKiRet(prop.SetString(*peerIP, szIP, ustrlen(szIP)));
+ CHKiRet(prop.ConstructFinalize(*peerIP));
+
+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)
+{
+ struct sockaddr_storage addr;
+ socklen_t addrlen = sizeof(addr);
+ int iNewSock = -1;
+
+ DEFiRet;
+
+ iNewSock = accept(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);
+ }
+
+ CHKiRet(getPeerNames(peerName, peerIP, (struct sockaddr*) &addr));
+
+ *newSock = iNewSock;
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ /* the close may be redundant, but that doesn't hurt... */
+ if(iNewSock != -1)
+ close(iNewSock);
+ }
+
+ RETiRet;
+}
+
+
+/* This is a helper for submitting the message to the rsyslog core.
+ * It does some common processing, including resetting the various
+ * state variables to a "processed" state.
+ * Note that this function is also called if we had a buffer overflow
+ * due to a too-long message. So far, there is no indication this
+ * happened and it may be worth thinking about different handling
+ * of this case (what obviously would require a change to this
+ * function or some related code).
+ * rgerhards, 2009-04-23
+ * EXTRACT from tcps_sess.c
+ */
+static rsRetVal
+doSubmitMsg(ttcpsess_t *pThis, struct syslogTime *stTime, time_t ttGenTime, multi_submit_t *pMultiSub)
+{
+ msg_t *pMsg;
+ DEFiRet;
+
+ if(pThis->iMsg == 0) {
+ DBGPRINTF("discarding zero-sized message\n");
+ FINALIZE;
+ }
+
+ /* 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);
+ 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);
+
+ if(pMultiSub == NULL) {
+ CHKiRet(submitMsg(pMsg));
+ } else {
+ pMultiSub->ppMsgs[pMultiSub->nElem++] = pMsg;
+ if(pMultiSub->nElem == pMultiSub->maxElem)
+ CHKiRet(multiSubmitMsg(pMultiSub));
+ }
+
+
+finalize_it:
+ /* reset status variables */
+ pThis->bAtStrtOfFram = 1;
+ pThis->iMsg = 0;
+
+ RETiRet;
+}
+
+
+/* process the data received. As TCP is stream based, we need to process the
+ * data inside a state machine. The actual data received is passed in byte-by-byte
+ * from DataRcvd, and this function here compiles messages from them and submits
+ * the end result to the queue. Introducing this function fixes a long-term bug ;)
+ * rgerhards, 2008-03-14
+ * EXTRACT from tcps_sess.c
+ */
+static inline rsRetVal
+processDataRcvd(ttcpsess_t *pThis, char c, struct syslogTime *stTime, time_t ttGenTime, multi_submit_t *pMultiSub)
+{
+ DEFiRet;
+
+ if(pThis->inputState == eAtStrtFram) {
+ if(isdigit((int) c)) {
+ pThis->inputState = eInOctetCnt;
+ pThis->iOctetsRemain = 0;
+ pThis->eFraming = TCP_FRAMING_OCTET_COUNTING;
+ } else {
+ pThis->inputState = eInMsg;
+ pThis->eFraming = TCP_FRAMING_OCTET_STUFFING;
+ }
+ }
+
+ if(pThis->inputState == eInOctetCnt) {
+ if(isdigit(c)) {
+ pThis->iOctetsRemain = pThis->iOctetsRemain * 10 + c - '0';
+ } else { /* done with the octet count, so this must be the SP terminator */
+ DBGPRINTF("TCP Message with octet-counter, size %d.\n", pThis->iOctetsRemain);
+ if(c != ' ') {
+ errmsg.LogError(0, NO_ERRCODE, "Framing Error in received TCP message: "
+ "delimiter is not SP but has ASCII value %d.\n", c);
+ }
+ if(pThis->iOctetsRemain < 1) {
+ /* TODO: handle the case where the octet count is 0! */
+ DBGPRINTF("Framing Error: invalid octet count\n");
+ errmsg.LogError(0, NO_ERRCODE, "Framing Error in received TCP message: "
+ "invalid octet count %d.\n", pThis->iOctetsRemain);
+ } else if(pThis->iOctetsRemain > iMaxLine) {
+ /* while we can not do anything against it, we can at least log an indication
+ * that something went wrong) -- rgerhards, 2008-03-14
+ */
+ DBGPRINTF("truncating message with %d octets - max msg size is %d\n",
+ pThis->iOctetsRemain, iMaxLine);
+ errmsg.LogError(0, NO_ERRCODE, "received oversize message: size is %d bytes, "
+ "max msg size is %d, truncating...\n", pThis->iOctetsRemain, iMaxLine);
+ }
+ pThis->inputState = eInMsg;
+ }
+ } else {
+ assert(pThis->inputState == eInMsg);
+ if(pThis->iMsg >= iMaxLine) {
+ /* emergency, we now need to flush, no matter if we are at end of message or not... */
+ DBGPRINTF("error: message received is larger than max msg size, we split it\n");
+ doSubmitMsg(pThis, stTime, ttGenTime, pMultiSub);
+ /* we might think if it is better to ignore the rest of the
+ * message than to treat it as a new one. Maybe this is a good
+ * candidate for a configuration parameter...
+ * rgerhards, 2006-12-04
+ */
+ }
+
+ if(( (c == '\n')
+ || ((pThis->pSrv->iAddtlFrameDelim != TCPSRV_NO_ADDTL_DELIMITER) && (c == pThis->pSrv->iAddtlFrameDelim))
+ ) && pThis->eFraming == TCP_FRAMING_OCTET_STUFFING) { /* record delimiter? */
+ doSubmitMsg(pThis, stTime, ttGenTime, pMultiSub);
+ pThis->inputState = eAtStrtFram;
+ } else {
+ /* IMPORTANT: here we copy the actual frame content to the message - for BOTH framing modes!
+ * If we have a message that is larger than the max msg size, we truncate it. This is the best
+ * we can do in light of what the engine supports. -- rgerhards, 2008-03-14
+ */
+ if(pThis->iMsg < iMaxLine) {
+ *(pThis->pMsg + pThis->iMsg++) = c;
+ }
+ }
+
+ if(pThis->eFraming == TCP_FRAMING_OCTET_COUNTING) {
+ /* do we need to find end-of-frame via octet counting? */
+ pThis->iOctetsRemain--;
+ if(pThis->iOctetsRemain < 1) {
+ /* we have end of frame! */
+ doSubmitMsg(pThis, stTime, ttGenTime, pMultiSub);
+ pThis->inputState = eAtStrtFram;
+ }
+ }
+ }
+
+ RETiRet;
+}
+
+
+/* Processes the data received via a TCP session. If there
+ * is no other way to handle it, data is discarded.
+ * Input parameter data is the data received, iLen is its
+ * len as returned from recv(). iLen must be 1 or more (that
+ * is errors must be handled by caller!). iTCPSess must be
+ * the index of the TCP session that received the data.
+ * rgerhards 2005-07-04
+ * And another change while generalizing. We now return either
+ * RS_RET_OK, which means the session should be kept open
+ * or anything else, which means it must be closed.
+ * rgerhards, 2008-03-01
+ * As a performance optimization, we pick up the timestamp here. Acutally,
+ * this *is* the *correct* reception step for all the data we received, because
+ * we have just received a bunch of data! -- rgerhards, 2009-06-16
+ * EXTRACT from tcps_sess.c
+ */
+#define NUM_MULTISUB 1024
+static rsRetVal
+DataRcvd(ttcpsess_t *pThis, char *pData, size_t iLen)
+{
+ multi_submit_t multiSub;
+ msg_t *pMsgs[NUM_MULTISUB];
+ struct syslogTime stTime;
+ time_t ttGenTime;
+ char *pEnd;
+ DEFiRet;
+
+ assert(pData != NULL);
+ assert(iLen > 0);
+
+ datetime.getCurrTime(&stTime, &ttGenTime);
+ multiSub.ppMsgs = pMsgs;
+ multiSub.maxElem = NUM_MULTISUB;
+ multiSub.nElem = 0;
+
+ /* We now copy the message to the session buffer. */
+ pEnd = pData + iLen; /* this is one off, which is intensional */
+
+ while(pData < pEnd) {
+ CHKiRet(processDataRcvd(pThis, *pData++, &stTime, ttGenTime, &multiSub));
+ }
+
+ if(multiSub.nElem > 0) {
+ /* submit anything that was not yet submitted */
+ CHKiRet(multiSubmitMsg(&multiSub));
+ }
+
+finalize_it:
+ RETiRet;
+}
+#undef NUM_MULTISUB
+
+
+/****************************************** --END-- TCP SUPPORT FUNCTIONS ***********************************/
+
+
+static inline void
+initConfigSettings(void)
+{
+ cs.bEmitMsgOnClose = 0;
+ cs.iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER;
+ cs.pszInputName = NULL;
+ cs.pRuleset = NULL;
+ cs.lstnIP = NULL;
+}
+
+
+/* add a listener to the server
+ */
+static rsRetVal
+addLstn(ttcpsrv_t *pSrv, int sock)
+{
+ DEFiRet;
+ ttcplstn_t *pLstn;
+
+ CHKmalloc(pLstn = malloc(sizeof(ttcplstn_t)));
+ pLstn->pSrv = pSrv;
+ pLstn->sock = sock;
+
+ /* add to start of server's listener list */
+ pLstn->prev = NULL;
+ pLstn->next = pSrv->pLstn;
+ if(pSrv->pLstn != NULL)
+ pSrv->pLstn->prev = pLstn;
+ pSrv->pLstn = pLstn;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* add a session to the server
+ */
+static rsRetVal
+addSess(ttcpsrv_t *pSrv, int sock, prop_t *peerName, prop_t *peerIP)
+{
+ DEFiRet;
+ ttcpsess_t *pSess = NULL;
+
+ CHKmalloc(pSess = malloc(sizeof(ttcpsess_t)));
+ CHKmalloc(pSess->pMsg = malloc(iMaxLine * sizeof(uchar)));
+ pSess->pSrv = pSrv;
+ pSess->sock = sock;
+ pSess->inputState = eAtStrtFram;
+ pSess->iMsg = 0;
+ pSess->bAtStrtOfFram = 1;
+ pSess->peerName = peerName;
+ pSess->peerIP = peerIP;
+
+ /* add to start of server's listener list */
+ pSess->prev = NULL;
+ pthread_mutex_lock(&pSrv->mutSess);
+ pSess->next = pSrv->pSess;
+ if(pSrv->pSess != NULL)
+ pSrv->pSess->prev = pSess;
+ pSrv->pSess = pSess;
+ pthread_mutex_unlock(&pSrv->mutSess);
+
+ /* finally run session handler */
+ pthread_create(&pSess->tid, &sessThrdAttr, sessThrd, (void*) pSess);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* close/remove a session
+ * NOTE: we must first remove the fd from the epoll set and then close it -- else we
+ * get an error "bad file descriptor" from epoll.
+ */
+static inline rsRetVal
+closeSess(ttcpsess_t *pSess)
+{
+ int sock;
+ DEFiRet;
+
+ sock = pSess->sock;
+ close(sock);
+
+ /* finally unlink session from structures */
+ pthread_mutex_lock(&pSess->pSrv->mutSess);
+ if(pSess->next != NULL)
+ pSess->next->prev = pSess->prev;
+ if(pSess->prev == NULL) {
+ /* need to update root! */
+ pSess->pSrv->pSess = pSess->next;
+ } else {
+ pSess->prev->next = pSess->next;
+ }
+ pthread_mutex_unlock(&pSess->pSrv->mutSess);
+
+ /* unlinked, now remove structure */
+ destructSess(pSess);
+
+ RETiRet;
+}
+
+
+/* accept a new ruleset to bind. Checks if it exists and complains, if not */
+static rsRetVal setRuleset(void __attribute__((unused)) *pVal, uchar *pszName)
+{
+ ruleset_t *pRuleset;
+ rsRetVal localRet;
+ DEFiRet;
+
+ localRet = ruleset.GetRuleset(ourConf, &pRuleset, pszName);
+ if(localRet == RS_RET_NOT_FOUND) {
+ errmsg.LogError(0, NO_ERRCODE, "error: ruleset '%s' not found - ignored", pszName);
+ }
+ CHKiRet(localRet);
+ cs.pRuleset = pRuleset;
+ DBGPRINTF("imttcp current bind ruleset %p: '%s'\n", pRuleset, pszName);
+
+finalize_it:
+ free(pszName); /* no longer needed */
+ RETiRet;
+}
+
+
+static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVal)
+{
+ DEFiRet;
+ ttcpsrv_t *pSrv;
+
+ CHKmalloc(pSrv = malloc(sizeof(ttcpsrv_t)));
+ pthread_mutex_init(&pSrv->mutSess, NULL);
+ pSrv->pSess = NULL;
+ pSrv->pLstn = NULL;
+ pSrv->bEmitMsgOnClose = cs.bEmitMsgOnClose;
+ pSrv->port = pNewVal;
+ pSrv->iAddtlFrameDelim = cs.iAddtlFrameDelim;
+ cs.pszInputName = NULL; /* moved over to pSrv, we do not own */
+ pSrv->lstnIP = cs.lstnIP;
+ cs.lstnIP = NULL; /* moved over to pSrv, we do not own */
+ pSrv->pRuleset = cs.pRuleset;
+ pSrv->pszInputName = (cs.pszInputName == NULL) ? UCHAR_CONSTANT("imttcp") : cs.pszInputName;
+ CHKiRet(prop.Construct(&pSrv->pInputName));
+ CHKiRet(prop.SetString(pSrv->pInputName, pSrv->pszInputName, ustrlen(pSrv->pszInputName)));
+ CHKiRet(prop.ConstructFinalize(pSrv->pInputName));
+
+ /* add to linked list */
+ pSrv->pNext = pSrvRoot;
+ pSrvRoot = pSrv;
+
+ /* all config vars are auto-reset -- this also is very useful with the
+ * new config format effort (v6).
+ */
+ resetConfigVariables(NULL, NULL);
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ errmsg.LogError(0, NO_ERRCODE, "error %d trying to add listener", iRet);
+ }
+ RETiRet;
+}
+
+
+/* create up all listeners
+ * This is a one-time stop once the module is set to start.
+ */
+static inline rsRetVal
+createServers()
+{
+ DEFiRet;
+ ttcpsrv_t *pSrv;
+
+ pSrv = pSrvRoot;
+ while(pSrv != NULL) {
+ DBGPRINTF("Starting up ttcp server for port %s, name '%s'\n", pSrv->port, pSrv->pszInputName);
+ createSrv(pSrv);
+ pSrv = pSrv->pNext;
+ }
+
+ RETiRet;
+}
+
+
+/* This function implements the thread to be used for listeners.
+ * The function terminates if shutdown is required.
+ */
+static void *
+lstnThrd(void *arg)
+{
+ ttcplstn_t *pLstn = (ttcplstn_t *) arg;
+ rsRetVal iRet = RS_RET_OK;
+ int newSock;
+ prop_t *peerName;
+ prop_t *peerIP;
+ rsRetVal localRet;
+
+ initThrd();
+
+ while(glbl.GetGlobalInputTermState() == 0) {
+ localRet = AcceptConnReq(pLstn->sock, &newSock, &peerName, &peerIP);
+ if(glbl.GetGlobalInputTermState() == 1)
+ break; /* terminate input! */
+ CHKiRet(localRet);
+ DBGPRINTF("imttcp: new connection %d on listen socket %d\n", newSock, pLstn->sock);
+ CHKiRet(addSess(pLstn->pSrv, newSock, peerName, peerIP));
+ }
+
+finalize_it:
+ close(pLstn->sock);
+ DBGPRINTF("imttcp shutdown listen socket %d\n", pLstn->sock);
+ /* Note: we do NOT unlink the deleted session. While this sounds not 100% clean,
+ * it is fine with the current implementation as we will never reuse these elements.
+ * However, it make sense (and not cost notable performance) to do it "right"...
+ */
+ return NULL;
+}
+
+
+/* This function implements the thread to be used for a session
+ * The function terminates if shutdown is required.
+ */
+static void *
+sessThrd(void *arg)
+{
+ ttcpsess_t *pSess = (ttcpsess_t*) arg;
+ rsRetVal iRet = RS_RET_OK;
+ int lenRcv;
+ int lenBuf;
+ char rcvBuf[64*1024];
+
+ initThrd();
+
+ while(glbl.GetGlobalInputTermState() == 0) {
+ lenBuf = sizeof(rcvBuf);
+ lenRcv = recv(pSess->sock, rcvBuf, lenBuf, 0);
+
+ if(glbl.GetGlobalInputTermState() == 1)
+ ABORT_FINALIZE(RS_RET_FORCE_TERM);
+
+ if(lenRcv > 0) {
+ /* have data, process it */
+ DBGPRINTF("imttcp: data(%d) on socket %d: %s\n", lenRcv, pSess->sock, rcvBuf);
+ CHKiRet(DataRcvd(pSess, rcvBuf, lenRcv));
+ } else if (lenRcv == 0) {
+ /* session was closed, do clean-up */
+ if(pSess->pSrv->bEmitMsgOnClose) {
+ uchar *peerName;
+ int lenPeer;
+ prop.GetString(pSess->peerName, &peerName, &lenPeer);
+ errmsg.LogError(0, RS_RET_PEER_CLOSED_CONN, "imttcp session %d closed by remote peer %s.\n",
+ pSess->sock, peerName);
+ }
+ break;
+ } else {
+ if(errno == EAGAIN)
+ break;
+ DBGPRINTF("imttcp: error on session socket %d - closing.\n", pSess->sock);
+ break;
+ }
+ }
+
+finalize_it:
+ DBGPRINTF("imttcp: session thread terminates, socket %d\n", pSess->sock);
+ closeSess(pSess); /* try clean-up by dropping session */
+ return NULL;
+}
+
+/* startup all listeners
+ */
+static inline rsRetVal
+startupListeners()
+{
+ DEFiRet;
+ ttcpsrv_t *pSrv;
+ ttcplstn_t *pLstn;
+
+ pSrv = pSrvRoot;
+ while(pSrv != NULL) {
+ for(pLstn = pSrv->pLstn ; pLstn != NULL ; pLstn = pLstn->next) {
+ pthread_create(&pLstn->tid, NULL, lstnThrd, (void*) pLstn);
+ }
+ pSrv = pSrv->pNext;
+ }
+
+ RETiRet;
+}
+
+
+/* This function is called to gather input.
+ */
+BEGINrunInput
+ struct timeval tvSelectTimeout;
+CODESTARTrunInput
+ DBGPRINTF("imttcp: now beginning to process input data\n");
+ CHKiRet(startupListeners());
+
+ // TODO: this loop is a quick hack, do it right!
+ while(glbl.GetGlobalInputTermState() == 0) {
+ tvSelectTimeout.tv_sec = 86400 /*1 day*/;
+ tvSelectTimeout.tv_usec = 0;
+ select(1, NULL, NULL, NULL, &tvSelectTimeout);
+ }
+finalize_it: ;
+ENDrunInput
+
+
+/* initialize and return if will run or not */
+BEGINwillRun
+CODESTARTwillRun
+ /* first apply some config settings */
+ iMaxLine = glbl.GetMaxLine(); /* get maximum size we currently support */
+
+ if(pSrvRoot == NULL) {
+ errmsg.LogError(0, RS_RET_NO_LSTN_DEFINED, "error: no ttcp server defined, module can not run.");
+ ABORT_FINALIZE(RS_RET_NO_RUN);
+ }
+
+ /* start up servers, but do not yet read input data */
+ CHKiRet(createServers());
+ DBGPRINTF("imttcp started up, but not yet receiving data\n");
+finalize_it:
+ENDwillRun
+
+
+/* completely shut down a server. All we need to do is unblock the
+ * various session and listerner threads as they then check the termination
+ * praedicate themselves.
+ */
+static inline void
+shutdownSrv(ttcpsrv_t *pSrv)
+{
+ ttcplstn_t *pLstn;
+ ttcplstn_t *pLstnDel;
+ ttcpsess_t *pSess;
+ pthread_t tid;
+
+ pLstn = pSrv->pLstn;
+ while(pLstn != NULL) {
+ tid = pLstn->tid; /* pSess will be destructed! */
+ pthread_kill(tid, SIGTTIN);
+ DBGPRINTF("imttcp: termination request for listen thread %x\n", (unsigned) tid);
+ pthread_join(tid, NULL);
+ DBGPRINTF("imttcp: listen thread %x terminated \n", (unsigned) tid);
+ pLstnDel = pLstn;
+ pLstn = pLstn->next;
+ free(pLstnDel);
+ }
+
+ pSess = pSrv->pSess;
+ while(pSess != NULL) {
+ tid = pSess->tid; /* pSess will be destructed! */
+ pSess = pSess->next;
+ pthread_kill(tid, SIGTTIN);
+ DBGPRINTF("imttcp: termination request for session thread %x\n", (unsigned) tid);
+ //pthread_join(tid, NULL);
+ DBGPRINTF("imttcp: session thread %x terminated \n", (unsigned) tid);
+ }
+}
+
+
+BEGINafterRun
+ ttcpsrv_t *pSrv, *srvDel;
+CODESTARTafterRun
+ /* do cleanup here */
+ /* we need to close everything that is still open */
+ pSrv = pSrvRoot;
+ while(pSrv != NULL) {
+ srvDel = pSrv;
+ pSrv = pSrv->pNext;
+ shutdownSrv(srvDel);
+ destructSrv(srvDel);
+ }
+ENDafterRun
+
+
+BEGINmodExit
+CODESTARTmodExit
+ pthread_attr_destroy(&sessThrdAttr);
+
+ /* release objects we used */
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(prop, CORE_COMPONENT);
+ objRelease(net, LM_NET_FILENAME);
+ objRelease(datetime, CORE_COMPONENT);
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(ruleset, CORE_COMPONENT);
+ENDmodExit
+
+
+static rsRetVal
+resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
+{
+ cs.bEmitMsgOnClose = 0;
+ cs.iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER;
+ free(cs.pszInputName);
+ cs.pszInputName = NULL;
+ free(cs.lstnIP);
+ cs.lstnIP = NULL;
+ return RS_RET_OK;
+}
+
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_IMOD_QUERIES
+ENDqueryEtryPt
+
+
+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(prop, CORE_COMPONENT));
+ CHKiRet(objUse(net, LM_NET_FILENAME));
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
+ CHKiRet(objUse(ruleset, CORE_COMPONENT));
+
+ /* initialize "read-only" thread attributes */
+ pthread_attr_init(&sessThrdAttr);
+ pthread_attr_setdetachstate(&sessThrdAttr, PTHREAD_CREATE_DETACHED);
+ pthread_attr_setstacksize(&sessThrdAttr, 200*1024);
+
+ /* register config file handlers */
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputttcpserverrun"), 0, eCmdHdlrGetWord,
+ addTCPListener, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputttcpservernotifyonconnectionclose"), 0,
+ eCmdHdlrBinary, NULL, &cs.bEmitMsgOnClose, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputttcpserveraddtlframedelimiter"), 0, eCmdHdlrInt,
+ NULL, &cs.iAddtlFrameDelim, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputttcpserverinputname"), 0,
+ eCmdHdlrGetWord, NULL, &cs.pszInputName, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputttcpserverlistenip"), 0,
+ eCmdHdlrGetWord, NULL, &cs.lstnIP, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputttcpserverbindruleset"), 0,
+ eCmdHdlrGetWord, setRuleset, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("resetconfigvariables"), 1, eCmdHdlrCustomHandler,
+ resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+
+
+/* vim:set ai:
+ */
diff --git a/plugins/imudp/imudp.c b/plugins/imudp/imudp.c
index 0db6bf9a..48cb2774 100644
--- a/plugins/imudp/imudp.c
+++ b/plugins/imudp/imudp.c
@@ -57,6 +57,7 @@
MODULE_TYPE_INPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("imudp")
/* defines */
@@ -85,117 +86,92 @@ 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 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;
@@ -209,17 +185,63 @@ static rsRetVal addListner(void __attribute__((unused)) *pVal, uchar *pNewVal)
/* 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)
+#if 0 //<<<<<<< HEAD
+
+ DBGPRINTF("imudp: trying to open port at %s:%s.\n",
+ (inst->pszBindAddr == NULL) ? (uchar*)"*" : inst->pszBindAddr, inst->pszBindPort);
+
+ 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) {
+ /* esay, we can just replace it */
+ udpLstnSocks = newSocks;
+ CHKmalloc(udpRulesets = (ruleset_t**) MALLOC(sizeof(ruleset_t*) * (newSocks[0] + 1)));
+ for(iDst = 1 ; iDst <= newSocks[0] ; ++iDst)
+ udpRulesets[iDst] = inst->pBindRuleset;
+ } else {
+ /* we need to add them */
+ tmpSocks = (int*) MALLOC(sizeof(int) * (1 + newSocks[0] + udpLstnSocks[0]));
+ tmpRulesets = (ruleset_t**) MALLOC(sizeof(ruleset_t*) * (1 + newSocks[0] + udpLstnSocks[0]));
+ if(tmpSocks == NULL || tmpRulesets == NULL) {
+ DBGPRINTF("out of memory trying to allocate udp listen socket array\n");
+ /* in this case, we discard the new sockets but continue with what we
+ * already have
+ */
+ free(newSocks);
+ free(tmpSocks);
+ free(tmpRulesets);
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ } else {
+ /* ready to copy */
+ iDst = 1;
+ for(iSrc = 1 ; iSrc <= udpLstnSocks[0] ; ++iSrc, ++iDst) {
+ tmpSocks[iDst] = udpLstnSocks[iSrc];
+ tmpRulesets[iDst] = udpRulesets[iSrc];
+ }
+ for(iSrc = 1 ; iSrc <= newSocks[0] ; ++iSrc, ++iDst) {
+ tmpSocks[iDst] = newSocks[iSrc];
+ tmpRulesets[iDst] = inst->pBindRuleset;
+ }
+ tmpSocks[0] = udpLstnSocks[0] + newSocks[0];
+ free(newSocks);
+ free(udpLstnSocks);
+ udpLstnSocks = tmpSocks;
+ free(udpRulesets);
+ udpRulesets = tmpRulesets;
+ }
+#else //=======
+ if(inst->pszBindAddr == NULL)
bindAddr = NULL;
- else if(pszBindAddr[0] == '*' && pszBindAddr[1] == '\0')
+ else if(inst->pszBindAddr[0] == '*' && inst->pszBindAddr[1] == '\0')
bindAddr = NULL;
else
- bindAddr = pszBindAddr;
+ 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, pNewVal);
+ DBGPRINTF("Trying to open syslog UDP ports at %s:%s.\n", bindName, inst->pszBindPort);
- port = (pNewVal == NULL || *pNewVal == '\0') ? (uchar*) "514" : pNewVal;
newSocks = net.create_udp_socket(bindAddr, port, 1);
if(newSocks != NULL) {
/* we now need to add the new sockets to the existing set */
@@ -228,7 +250,7 @@ static rsRetVal addListner(void __attribute__((unused)) *pVal, uchar *pNewVal)
CHKmalloc(newlcnfinfo = (struct lstn_s*) MALLOC(sizeof(struct lstn_s)));
newlcnfinfo->next = NULL;
newlcnfinfo->sock = newSocks[iSrc];
- newlcnfinfo->pRuleset = pBindRuleset;
+ newlcnfinfo->pRuleset = inst->pBindRuleset;
/* support statistics gathering */
CHKiRet(statsobj.Construct(&(newlcnfinfo->stats)));
snprintf((char*)statname, sizeof(statname), "imudp(%s:%s)", bindName, port);
@@ -246,36 +268,23 @@ static rsRetVal addListner(void __attribute__((unused)) *pVal, uchar *pNewVal)
lcnfLast = newlcnfinfo;
else
lcnfLast->next = 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);
}
@@ -362,7 +371,7 @@ processSocket(thrdInfo_t *pThrd, struct lstn_s *lstn, struct sockaddr_storage *f
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 */
@@ -389,42 +398,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.
@@ -449,7 +544,6 @@ rsRetVal rcvMainLoop(thrdInfo_t *pThrd)
/* start "name caching" algo by making sure the previous system indicator
* is invalidated.
*/
- set_thread_schedparam();
bIsPermitted = 0;
memset(&frominetPrev, 0, sizeof(frominetPrev));
@@ -525,7 +619,6 @@ rsRetVal rcvMainLoop(thrdInfo_t *pThrd)
/* 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");
@@ -571,6 +664,93 @@ 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 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
@@ -584,24 +764,8 @@ 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(lcnfRoot == NULL) {
- DBGPRINTF("imudp: no listeners configured, will not run\n");
- ABORT_FINALIZE(RS_RET_NO_RUN);
- }
-
- iMaxLine = glbl.GetMaxLine();
-
- CHKmalloc(pRcvBuf = MALLOC((iMaxLine + 1) * sizeof(char)));
-finalize_it:
ENDwillRun
@@ -622,13 +786,14 @@ CODESTARTafterRun
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);
@@ -650,16 +815,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;
}
@@ -676,19 +852,24 @@ CODEmodInit_QueryRegCFSLineHdlr
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, &pszSchedPolicy, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.pszSchedPolicy, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"imudpschedulingpriority", 0, eCmdHdlrInt,
- &set_scheduling_priority, &iSchedPrio, 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 403173e1..3e477ea0 100644
--- a/plugins/imuxsock/imuxsock.c
+++ b/plugins/imuxsock/imuxsock.c
@@ -58,6 +58,7 @@
MODULE_TYPE_INPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("imuxsock")
/* defines */
#define MAXFUNIX 50
@@ -69,6 +70,9 @@ MODULE_TYPE_NOKEEP
#endif
#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; };
@@ -88,6 +92,7 @@ DEFobjCurrIf(parser)
DEFobjCurrIf(datetime)
DEFobjCurrIf(statsobj)
+
statsobj_t *modStats;
STATSCOUNTER_DEF(ctrSubmit, mutCtrSubmit)
STATSCOUNTER_DEF(ctrLostRatelimit, mutCtrLostRatelimit)
@@ -151,30 +156,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 */
-static int bUseSysTimeStamp = 1; /* use timestamp from system (rather than from message) */
-static int bUseSysTimeStampSysSock = 1; /* same, for system log socket */
-static int bAnnotate = 0; /* annotate trusted properties */
-static int bAnnotateSysSock = 0; /* same, for system log socket */
+/* config vars for legacy config system */
#define DFLT_bCreatePath 0
-static int bCreatePath = DFLT_bCreatePath; /* auto-create socket path? */
#define DFLT_ratelimitInterval 0
-static int ratelimitInterval = DFLT_ratelimitInterval; /* interval in seconds, 0 = off */
-static int ratelimitIntervalSysSock = DFLT_ratelimitInterval;
#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
@@ -263,6 +302,55 @@ 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->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.
@@ -272,49 +360,47 @@ 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;
}
CHKiRet(prop.Construct(&(listeners[nfd].hostName)));
- if(pLogHostName == NULL) {
- CHKiRet(prop.SetString(listeners[nfd].hostName, glbl.GetLocalHostName(), ustrlen(glbl.GetLocalHostName())));
+ if(inst->pLogHostName == NULL) {
+ CHKiRet(prop.SetString(listeners[nfd].hostName, glbl.GetLocalHostName(),
+ ustrlen(glbl.GetLocalHostName())));
} else {
- CHKiRet(prop.SetString(listeners[nfd].hostName, pLogHostName, ustrlen(pLogHostName)));
- /* reset hostname for next socket */
- free(pLogHostName);
- pLogHostName = NULL;
+ CHKiRet(prop.SetString(listeners[nfd].hostName, inst->pLogHostName, ustrlen(inst->pLogHostName)));
}
CHKiRet(prop.ConstructFinalize(listeners[nfd].hostName));
- 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 || bAnnotate) ? 1 : 0;
- listeners[nfd].bAnnotate = bAnnotate;
- listeners[nfd].bWritePid = bWritePid;
- listeners[nfd].bUseSysTimeStamp = bUseSysTimeStamp;
+ 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:
@@ -875,6 +961,121 @@ 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;
+ 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;
@@ -942,70 +1143,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;
- 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 || bAnnotateSysSock) ? 1 : 0;
- listeners[0].bWritePid = bWritePidSysSock;
- listeners[0].bAnnotate = bAnnotateSysSock;
- listeners[0].bUseSysTimeStamp = bUseSysTimeStampSysSock;
-
- 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++)
@@ -1027,21 +1170,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);
@@ -1063,36 +1202,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;
- bUseSysTimeStamp = 1;
- bUseSysTimeStampSysSock = 1;
- 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;
}
@@ -1112,6 +1248,15 @@ 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));
+
/* init system log socket settings */
listeners[0].flags = IGNDATE;
listeners[0].sockName = UCHAR_CONSTANT(_PATH_LOG);
@@ -1141,31 +1286,31 @@ CODEmodInit_QueryRegCFSLineHdlr
/* 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, &bAnnotate, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.bAnnotate, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketcreatepath", 0, eCmdHdlrBinary,
- NULL, &bCreatePath, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketusepidfromsystem", 0, eCmdHdlrBinary,
- NULL, &bWritePid, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.bCreatePath, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketusesystimestamp", 0, eCmdHdlrBinary,
- NULL, &bUseSysTimeStamp, STD_LOADABLE_MODULE_ID));
+ 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, &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
@@ -1179,17 +1324,17 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogsocketflowcontrol", 0, eCmdHdlrBinary,
setSystemLogFlowControl, NULL, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogusesystimestamp", 0, eCmdHdlrBinary,
- NULL, &bUseSysTimeStampSysSock, STD_LOADABLE_MODULE_ID));
+ NULL, &cs.bUseSysTimeStampSysSock, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogsocketannotate", 0, eCmdHdlrBinary,
- NULL, &bAnnotateSysSock, STD_LOADABLE_MODULE_ID));
+ 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/mmnormalize/Makefile.am b/plugins/mmnormalize/Makefile.am
new file mode 100644
index 00000000..0a3b5ba5
--- /dev/null
+++ b/plugins/mmnormalize/Makefile.am
@@ -0,0 +1,8 @@
+pkglib_LTLIBRARIES = mmnormalize.la
+
+mmnormalize_la_SOURCES = mmnormalize.c
+mmnormalize_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) $(LIBLOGNORM_CFLAGS) $(LIBEE_CFLAGS)
+mmnormalize_la_LDFLAGS = -module -avoid-version $(LIBLOGNORM_LIBS) $(LIBEE_LIBS)
+mmnormalize_la_LIBADD =
+
+EXTRA_DIST =
diff --git a/plugins/mmnormalize/mmnormalize.c b/plugins/mmnormalize/mmnormalize.c
new file mode 100644
index 00000000..7b92d820
--- /dev/null
+++ b/plugins/mmnormalize/mmnormalize.c
@@ -0,0 +1,274 @@
+/* mmnormalize.c
+ * This is a message modification module. It normalizes the input message with
+ * the help of liblognorm. The messages EE event structure is updated.
+ *
+ * NOTE: read comments in module-template.h for details on the calling interface!
+ *
+ * File begun on 2010-01-01 by RGerhards
+ *
+ * Copyright 2010 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 <libestr.h>
+#include <libee/libee.h>
+#include <liblognorm.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("mmnormalize")
+
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal);
+
+/* static data */
+DEFobjCurrIf(errmsg);
+
+/* internal structures
+ */
+DEF_OMOD_STATIC_DATA
+
+typedef struct _instanceData {
+ sbool bUseRawMsg; /**< use %rawmsg% instead of %msg% */
+ ln_ctx ctxln; /**< context to be used for liblognorm */
+ ee_ctx ctxee; /**< context to be used for libee */
+} instanceData;
+
+typedef struct configSettings_s {
+ uchar *rulebase; /**< name of normalization rulebase to use */
+ sbool bUseRawMsg; /**< use %rawmsg% instead of %msg% */
+} 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);
+ ln_exitCtx(pData->ctxln);
+ENDfreeInstance
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ dbgprintf("mmnormalize\n");
+ENDdbgPrintInstInfo
+
+
+BEGINtryResume
+CODESTARTtryResume
+ENDtryResume
+
+BEGINdoAction
+ msg_t *pMsg;
+ es_str_t *str;
+ uchar *buf;
+ int len;
+ int r;
+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
+ */
+ if(pData->bUseRawMsg) {
+ getRawMsg(pMsg, &buf, &len);
+ } else {
+ buf = getMSG(pMsg);
+ len = getMSGLen(pMsg);
+ }
+ str = es_newStrFromCStr((char*)buf, len);
+ r = ln_normalize(pData->ctxln, str, &pMsg->event);
+ if(r != 0) {
+ DBGPRINTF("error %d during ln_normalize\n", r);
+ }
+ es_deleteStr(str);
+ /***DEBUG***/ // TODO: remove after initial testing - 2010-12-01
+ {
+ char *cstr;
+ ee_fmtEventToJSON(pMsg->event, &str);
+ cstr = es_str2cstr(str, NULL);
+ dbgprintf("mmnormalize generated: %s\n", cstr);
+ free(cstr);
+ es_deleteStr(str);
+ }
+ /***END DEBUG***/
+ENDdoAction
+
+
+BEGINparseSelectorAct
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(1)
+ /* first check if this config line is actually for us */
+ if(strncmp((char*) p, ":mmnormalize:", sizeof(":mmnormalize:") - 1)) {
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+ }
+
+ if(cs.rulebase == NULL) {
+ errmsg.LogError(0, RS_RET_NO_RULESET, "error: no normalization rulebase was specified, use "
+ "$MMNormalizeSampleDB directive first!");
+ ABORT_FINALIZE(RS_RET_NO_RULESET);
+ }
+
+ /* ok, if we reach this point, we have something for us */
+ p += sizeof(":mmnormalize:") - 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);
+ }
+
+ if((pData->ctxln = ln_initCtx()) == NULL) {
+ errmsg.LogError(0, RS_RET_NO_RULESET, "error: could not initialize liblognorm ctx, cannot "
+ "activate action");
+ ee_exitCtx(pData->ctxee);
+ ABORT_FINALIZE(RS_RET_ERR_LIBLOGNORM_INIT);
+ }
+ ln_setEECtx(pData->ctxln, pData->ctxee);
+ if(ln_loadSamples(pData->ctxln, (char*) cs.rulebase) != 0) {
+ errmsg.LogError(0, RS_RET_NO_RULESET, "error: normalization rulebase '%s' could not be loaded "
+ "cannot activate action", cs.rulebase);
+ ee_exitCtx(pData->ctxee);
+ ln_exitCtx(pData->ctxln);
+ ABORT_FINALIZE(RS_RET_ERR_LIBLOGNORM_SAMPDB_LOAD);
+ }
+ pData->bUseRawMsg = cs.bUseRawMsg;
+
+ /* all config vars auto-reset! */
+ cs.bUseRawMsg = 0;
+ free(cs.rulebase);
+ cs.rulebase = NULL;
+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;
+ cs.rulebase = NULL;
+ cs.bUseRawMsg = 0;
+ RETiRet;
+}
+
+/* set the rulebase name */
+static rsRetVal
+setRuleBase(void __attribute__((unused)) *pVal, uchar *pszName)
+{
+ DEFiRet;
+ cs.rulebase = pszName;
+ pszName = NULL;
+ 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("mmnormalize: 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 *)"mmnormalizerulebase", 0, eCmdHdlrGetWord,
+ setRuleBase, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"mmnormalizeuserawmsg", 0, eCmdHdlrInt,
+ NULL, &cs.bUseRawMsg, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
+ resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+
+/* vi:set ai:
+ */
diff --git a/plugins/mmsnmptrapd/mmsnmptrapd.c b/plugins/mmsnmptrapd/mmsnmptrapd.c
index b78046ee..b1ac2f64 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,11 +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;
-configSettings_t cs;
-
-//TODO: enable for v6
-#if 0
-SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */
+static configSettings_t cs;
BEGINinitConfVars /* (re)set config variables to default values */
CODESTARTinitConfVars
@@ -88,7 +85,6 @@ CODESTARTinitConfVars
cs.pszSeverityMapping = NULL;
resetConfigVariables(NULL, NULL);
ENDinitConfVars
-#endif
BEGINcreateInstance
@@ -389,7 +385,7 @@ BEGINmodInit()
unsigned long opts;
int bMsgPassingSupported;
CODESTARTmodInit
-//TODO v6: add SCOPINGmodInit
+INITLegCnfVars
*ipIFVersProvided = CURR_MOD_IF_VERSION;
/* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
diff --git a/plugins/omdbalerting/omdbalerting.c b/plugins/omdbalerting/omdbalerting.c
index 35de5818..ec9cf346 100644
--- a/plugins/omdbalerting/omdbalerting.c
+++ b/plugins/omdbalerting/omdbalerting.c
@@ -45,6 +45,7 @@
MODULE_TYPE_OUTPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("omdbalerting")
/* internal structures
*/
diff --git a/plugins/omgssapi/omgssapi.c b/plugins/omgssapi/omgssapi.c
index 21c540b7..089e8b41 100644
--- a/plugins/omgssapi/omgssapi.c
+++ b/plugins/omgssapi/omgssapi.c
@@ -59,8 +59,11 @@
MODULE_TYPE_OUTPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("omgssapi")
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal);
+
/* internal structures
*/
DEF_OMOD_STATIC_DATA
@@ -86,12 +89,24 @@ typedef struct _instanceData {
} instanceData;
/* config data */
-static uchar *pszTplName = NULL; /* name of the default template to use */
-static char *gss_base_service_name = NULL;
-static enum gss_mode_t {
+
+typedef enum gss_mode_e {
GSSMODE_MIC,
GSSMODE_ENC
-} gss_mode = GSSMODE_ENC;
+} gss_mode_t;
+
+typedef struct configSettings_s {
+ uchar *pszTplName; /* name of the default template to use */
+ char *gss_base_service_name;
+ gss_mode_t gss_mode;
+} configSettings_t;
+
+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.
@@ -142,10 +157,8 @@ CODESTARTfreeInstance
/* this is meant to be done when module is unloaded,
but since this module is static...
*/
- if (gss_base_service_name != NULL) {
- free(gss_base_service_name);
- gss_base_service_name = NULL;
- }
+ free(cs.gss_base_service_name);
+ cs.gss_base_service_name = NULL;
/* final cleanup */
tcpclt.Destruct(&pData->pTCPClt);
@@ -192,7 +205,7 @@ static rsRetVal TCPSendGSSInit(void *pvData)
if(pData->sock > 0)
ABORT_FINALIZE(RS_RET_OK);
- base = (gss_base_service_name == NULL) ? "host" : gss_base_service_name;
+ base = (cs.gss_base_service_name == NULL) ? "host" : cs.gss_base_service_name;
out_tok.length = strlen(pData->f_hname) + strlen(base) + 2;
CHKmalloc(out_tok.value = MALLOC(out_tok.length));
strcpy(out_tok.value, base);
@@ -216,10 +229,10 @@ static rsRetVal TCPSendGSSInit(void *pvData)
sess_flags = &pData->gss_flags;
*sess_flags = GSS_C_MUTUAL_FLAG;
- if (gss_mode == GSSMODE_MIC) {
+ if (cs.gss_mode == GSSMODE_MIC) {
*sess_flags |= GSS_C_INTEG_FLAG;
}
- if (gss_mode == GSSMODE_ENC) {
+ if (cs.gss_mode == GSSMODE_ENC) {
*sess_flags |= GSS_C_CONF_FLAG;
}
dbgprintf("GSS-API requested context flags:\n");
@@ -300,7 +313,7 @@ static rsRetVal TCPSendGSSSend(void *pvData, char *msg, size_t len)
context = &pData->gss_context;
in_buf.value = msg;
in_buf.length = len;
- maj_stat = gss_wrap(&min_stat, *context, (gss_mode == GSSMODE_ENC) ? 1 : 0, GSS_C_QOP_DEFAULT,
+ maj_stat = gss_wrap(&min_stat, *context, (cs.gss_mode == GSSMODE_ENC) ? 1 : 0, GSS_C_QOP_DEFAULT,
&in_buf, NULL, &out_buf);
if (maj_stat != GSS_S_COMPLETE) {
gssutil.display_status("wrapping message", maj_stat, min_stat);
@@ -602,7 +615,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
/* process template */
CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
- (pszTplName == NULL) ? (uchar*)"RSYSLOG_TraditionalForwardFormat" : pszTplName));
+ (cs.pszTplName == NULL) ? (uchar*)"RSYSLOG_TraditionalForwardFormat" : cs.pszTplName));
/* first set the pData->eDestState */
memset(&hints, 0, sizeof(hints));
@@ -639,9 +652,9 @@ CODESTARTmodExit
objRelease(gssutil, LM_GSSUTIL_FILENAME);
objRelease(tcpclt, LM_TCPCLT_FILENAME);
- if(pszTplName != NULL) {
- free(pszTplName);
- pszTplName = NULL;
+ if(cs.pszTplName != NULL) {
+ free(cs.pszTplName);
+ cs.pszTplName = NULL;
}
ENDmodExit
@@ -658,10 +671,10 @@ static rsRetVal setGSSMode(void __attribute__((unused)) *pVal, uchar *mode)
DEFiRet;
if (!strcmp((char *) mode, "integrity")) {
- gss_mode = GSSMODE_MIC;
+ cs.gss_mode = GSSMODE_MIC;
dbgprintf("GSS-API gssmode set to GSSMODE_MIC\n");
} else if (!strcmp((char *) mode, "encryption")) {
- gss_mode = GSSMODE_ENC;
+ cs.gss_mode = GSSMODE_ENC;
dbgprintf("GSS-API gssmode set to GSSMODE_ENC\n");
} else {
errmsg.LogError(0, RS_RET_INVALID_PARAMS, "unknown gssmode parameter: %s", (char *) mode);
@@ -675,21 +688,18 @@ static rsRetVal setGSSMode(void __attribute__((unused)) *pVal, uchar *mode)
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
- gss_mode = GSSMODE_ENC;
- if (gss_base_service_name != NULL) {
- free(gss_base_service_name);
- gss_base_service_name = NULL;
- }
- if(pszTplName != NULL) {
- free(pszTplName);
- pszTplName = NULL;
- }
+ cs.gss_mode = GSSMODE_ENC;
+ free(cs.gss_base_service_name);
+ cs.gss_base_service_name = NULL;
+ free(cs.pszTplName);
+ cs.pszTplName = NULL;
return RS_RET_OK;
}
BEGINmodInit()
CODESTARTmodInit
+SCOPINGmodInit
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(errmsg, CORE_COMPONENT));
@@ -697,10 +707,10 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(gssutil, LM_GSSUTIL_FILENAME));
CHKiRet(objUse(tcpclt, LM_TCPCLT_FILENAME));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"gssforwardservicename", 0, eCmdHdlrGetWord, NULL, &gss_base_service_name, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"gssmode", 0, eCmdHdlrGetWord, setGSSMode, &gss_mode, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"actiongssforwarddefaulttemplate", 0, eCmdHdlrGetWord, NULL, &pszTplName, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"gssforwardservicename", 0, eCmdHdlrGetWord, NULL, &cs.gss_base_service_name, STD_LOADABLE_MODULE_ID, eConfObjAction));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"gssmode", 0, eCmdHdlrGetWord, setGSSMode, &cs.gss_mode, STD_LOADABLE_MODULE_ID, eConfObjAction));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"actiongssforwarddefaulttemplate", 0, eCmdHdlrGetWord, NULL, &cs.pszTplName, STD_LOADABLE_MODULE_ID, eConfObjAction));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID, eConfObjAction));
ENDmodInit
#endif /* #ifdef USE_GSSAPI */
diff --git a/plugins/omhdfs/omhdfs.c b/plugins/omhdfs/omhdfs.c
index 76128a4e..cd14d03c 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
*/
@@ -60,12 +61,18 @@ DEFobjCurrIf(errmsg)
/* global data */
static struct hashtable *files; /* holds all file objects that we know */
-/* globals for default values */
-static uchar *fileName = NULL;
-static uchar *hdfsHost = NULL;
-static uchar *dfltTplName = NULL; /* default template name to use */
-int hdfsPort = 0;
-/* end globals for default values */
+typedef struct configSettings_s {
+ uchar *fileName;
+ uchar *hdfsHost;
+ uchar *dfltTplName; /* default template name to use */
+ int hdfsPort;
+} configSettings_t;
+
+SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */
+
+BEGINinitConfVars /* (re)set config variables to default values */
+CODESTARTinitConfVars
+ENDinitConfVars
typedef struct {
uchar *name;
@@ -439,22 +446,22 @@ CODESTARTparseSelectorAct
CHKiRet(createInstance(&pData));
CODE_STD_STRING_REQUESTparseSelectorAct(1)
CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, 0,
- (dfltTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : dfltTplName));
+ (cs.dfltTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : cs.dfltTplName));
- if(fileName == NULL) {
+ if(cs.fileName == NULL) {
errmsg.LogError(0, RS_RET_ERR_HDFS_OPEN, "omhdfs: no file name specified, can not continue");
ABORT_FINALIZE(RS_RET_FILE_NOT_SPECIFIED);
}
- pFile = hashtable_search(files, fileName);
+ pFile = hashtable_search(files, cs.fileName);
if(pFile == NULL) {
/* we need a new file object, this one not seen before */
CHKiRet(fileObjConstruct(&pFile));
- CHKmalloc(pFile->name = fileName);
- CHKmalloc(keybuf = ustrdup(fileName));
- fileName = NULL; /* re-set, data passed to file object */
- CHKmalloc(pFile->hdfsHost = strdup((hdfsHost == NULL) ? "default" : (char*) hdfsHost));
- pFile->hdfsPort = hdfsPort;
+ CHKmalloc(pFile->name = cs.fileName);
+ CHKmalloc(keybuf = ustrdup(cs.fileName));
+ cs.fileName = NULL; /* re-set, data passed to file object */
+ CHKmalloc(pFile->hdfsHost = strdup((cs.hdfsHost == NULL) ? "default" : (char*) cs.hdfsHost));
+ pFile->hdfsPort = cs.hdfsPort;
fileOpen(pFile);
if(pFile->fh == NULL){
errmsg.LogError(0, RS_RET_ERR_HDFS_OPEN, "omhdfs: failed to open %s - "
@@ -497,8 +504,12 @@ ENDdoHUP
*/
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
- hdfsHost = NULL;
- hdfsPort = 0;
+ cs.hdfsHost = NULL;
+ cs.hdfsPort = 0;
+ free(cs.fileName);
+ cs.fileName = NULL;
+ free(cs.dfltTplName);
+ cs.dfltTplName = NULL;
return RS_RET_OK;
}
@@ -519,6 +530,7 @@ CODEqueryEtryPt_doHUP
ENDqueryEtryPt
+
BEGINmodInit()
CODESTARTmodInit
*ipIFVersProvided = CURR_MOD_IF_VERSION;
@@ -527,10 +539,10 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKmalloc(files = create_hashtable(20, hash_from_string, key_equals_string,
fileObjDestruct4Hashtable));
- CHKiRet(regCfSysLineHdlr((uchar *)"omhdfsfilename", 0, eCmdHdlrGetWord, NULL, &fileName, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"omhdfshost", 0, eCmdHdlrGetWord, NULL, &hdfsHost, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"omhdfsport", 0, eCmdHdlrInt, NULL, &hdfsPort, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"omhdfsdefaulttemplate", 0, eCmdHdlrGetWord, NULL, &dfltTplName, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"omhdfsfilename", 0, eCmdHdlrGetWord, NULL, &cs.fileName, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"omhdfshost", 0, eCmdHdlrGetWord, NULL, &cs.hdfsHost, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"omhdfsport", 0, eCmdHdlrInt, NULL, &cs.hdfsPort, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"omhdfsdefaulttemplate", 0, eCmdHdlrGetWord, NULL, &cs.dfltTplName, NULL));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
DBGPRINTF("omhdfs: module compiled with rsyslog version %s.\n", VERSION);
CODEmodInit_QueryRegCFSLineHdlr
diff --git a/plugins/omlibdbi/omlibdbi.c b/plugins/omlibdbi/omlibdbi.c
index 4b97da86..44e635c1 100644
--- a/plugins/omlibdbi/omlibdbi.c
+++ b/plugins/omlibdbi/omlibdbi.c
@@ -48,9 +48,11 @@
#include "module-template.h"
#include "debug.h"
#include "errmsg.h"
+#include "conf.h"
MODULE_TYPE_OUTPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("omlibdbi")
/* internal structures
*/
@@ -68,14 +70,28 @@ typedef struct _instanceData {
unsigned uLastDBErrno; /* last errno returned by libdbi or 0 if all is well */
} instanceData;
+typedef struct configSettings_s {
+ uchar *dbiDrvrDir; /* global: where do the dbi drivers reside? */
+ uchar *drvrName; /* driver to use */
+ uchar *host; /* host to connect to */
+ uchar *usrName; /* user name for connect */
+ uchar *pwd; /* password for connect */
+ uchar *dbName; /* database to use */
+} configSettings_t;
+static configSettings_t cs;
+
+BEGINinitConfVars /* (re)set config variables to default values */
+CODESTARTinitConfVars
+ cs.dbiDrvrDir = NULL;
+ cs.drvrName = NULL;
+ cs.host = NULL;
+ cs.usrName = NULL;
+ cs.pwd = NULL;
+ cs.dbName = NULL;
+ENDinitConfVars
+
/* config settings */
-static uchar *dbiDrvrDir = NULL;/* global: where do the dbi drivers reside? */
-static uchar *drvrName = NULL; /* driver to use */
-static uchar *host = NULL; /* host to connect to */
-static uchar *usrName = NULL; /* user name for connect */
-static uchar *pwd = NULL; /* password for connect */
-static uchar *dbName = NULL; /* database to use */
#ifdef HAVE_DBI_R
static dbi_inst dbiInst;
#endif
@@ -168,9 +184,9 @@ static rsRetVal initConn(instanceData *pData, int bSilent)
if(bDbiInitialized == 0) {
/* we need to init libdbi first */
# ifdef HAVE_DBI_R
- iDrvrsLoaded = dbi_initialize_r((char*) dbiDrvrDir, &dbiInst);
+ iDrvrsLoaded = dbi_initialize_r((char*) cs.dbiDrvrDir, &dbiInst);
# else
- iDrvrsLoaded = dbi_initialize((char*) dbiDrvrDir);
+ iDrvrsLoaded = dbi_initialize((char*) cs.dbiDrvrDir);
# endif
if(iDrvrsLoaded == 0) {
errmsg.LogError(0, RS_RET_SUSPENDED, "libdbi error: libdbi or libdbi drivers not present on this system - suspending.");
@@ -278,23 +294,23 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
CHKiRet(createInstance(&pData));
/* no create the instance based on what we currently have */
- if(drvrName == NULL) {
+ if(cs.drvrName == NULL) {
errmsg.LogError(0, RS_RET_NO_DRIVERNAME, "omlibdbi: no db driver name given - action can not be created");
ABORT_FINALIZE(RS_RET_NO_DRIVERNAME);
}
- if((pData->drvrName = (uchar*) strdup((char*)drvrName)) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ if((pData->drvrName = (uchar*) strdup((char*)cs.drvrName)) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
/* NULL values are supported because drivers have different needs.
* They will err out on connect. -- rgerhards, 2008-02-15
*/
- if(host != NULL)
- if((pData->host = (uchar*) strdup((char*)host)) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- if(usrName != NULL)
- if((pData->usrName = (uchar*) strdup((char*)usrName)) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- if(dbName != NULL)
- if((pData->dbName = (uchar*) strdup((char*)dbName)) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- if(pwd != NULL)
- if((pData->pwd = (uchar*) strdup((char*)pwd)) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ if(cs.host != NULL)
+ if((pData->host = (uchar*) strdup((char*)cs.host)) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ if(cs.usrName != NULL)
+ if((pData->usrName = (uchar*) strdup((char*)cs.usrName)) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ if(cs.dbName != NULL)
+ if((pData->dbName = (uchar*) strdup((char*)cs.dbName)) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ if(cs.pwd != NULL)
+ if((pData->pwd = (uchar*) strdup((char*)cs.pwd)) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_RQD_TPL_OPT_SQL, (uchar*) " StdDBFmt"));
@@ -326,53 +342,35 @@ ENDqueryEtryPt
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
DEFiRet;
-
- if(dbiDrvrDir != NULL) {
- free(dbiDrvrDir);
- dbiDrvrDir = NULL;
- }
-
- if(drvrName != NULL) {
- free(drvrName);
- drvrName = NULL;
- }
-
- if(host != NULL) {
- free(host);
- host = NULL;
- }
-
- if(usrName != NULL) {
- free(usrName);
- usrName = NULL;
- }
-
- if(pwd != NULL) {
- free(pwd);
- pwd = NULL;
- }
-
- if(dbName != NULL) {
- free(dbName);
- dbName = NULL;
- }
-
+ free(cs.dbiDrvrDir);
+ cs.dbiDrvrDir = NULL;
+ free(cs.drvrName);
+ cs.drvrName = NULL;
+ free(cs.host);
+ cs.host = NULL;
+ free(cs.usrName);
+ cs.usrName = NULL;
+ free(cs.pwd);
+ cs.pwd = NULL;
+ free(cs.dbName);
+ cs.dbName = NULL;
RETiRet;
}
BEGINmodInit()
CODESTARTmodInit
+INITLegCnfVars
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(errmsg, CORE_COMPONENT));
- CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionlibdbidriverdirectory", 0, eCmdHdlrGetWord, NULL, &dbiDrvrDir, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionlibdbidriver", 0, eCmdHdlrGetWord, NULL, &drvrName, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionlibdbihost", 0, eCmdHdlrGetWord, NULL, &host, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionlibdbiusername", 0, eCmdHdlrGetWord, NULL, &usrName, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionlibdbipassword", 0, eCmdHdlrGetWord, NULL, &pwd, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionlibdbidbname", 0, eCmdHdlrGetWord, NULL, &dbName, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr( (uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionlibdbidriverdirectory", 0, eCmdHdlrGetWord, NULL, &cs.dbiDrvrDir, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionlibdbidriver", 0, eCmdHdlrGetWord, NULL, &cs.drvrName, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionlibdbihost", 0, eCmdHdlrGetWord, NULL, &cs.host, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionlibdbiusername", 0, eCmdHdlrGetWord, NULL, &cs.usrName, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionlibdbipassword", 0, eCmdHdlrGetWord, NULL, &cs.pwd, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionlibdbidbname", 0, eCmdHdlrGetWord, NULL, &cs.dbName, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
DBGPRINTF("omlibdbi compiled with version %s loaded, libdbi version %s\n", VERSION, dbi_version());
ENDmodInit
diff --git a/plugins/ommail/ommail.c b/plugins/ommail/ommail.c
index 468d8db2..d70fa30a 100644
--- a/plugins/ommail/ommail.c
+++ b/plugins/ommail/ommail.c
@@ -54,6 +54,7 @@
MODULE_TYPE_OUTPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("ommail")
/* internal structures
*/
@@ -70,12 +71,6 @@ struct toRcpt_s {
uchar *pszTo;
toRcpt_t *pNext;
};
-static toRcpt_t *lstRcpt = NULL;
-static uchar *pszSrv = NULL;
-static uchar *pszSrvPort = NULL;
-static uchar *pszFrom = NULL;
-static uchar *pszSubject = NULL;
-static int bEnableBody = 1; /* should a mail body be generated? (set to 0 eg for SMS gateways) */
typedef struct _instanceData {
int iMode; /* 0 - smtp, 1 - sendmail */
@@ -95,6 +90,26 @@ typedef struct _instanceData {
} md; /* mode-specific data */
} instanceData;
+typedef struct configSettings_s {
+ toRcpt_t *lstRcpt;
+ uchar *pszSrv;
+ uchar *pszSrvPort;
+ uchar *pszFrom;
+ uchar *pszSubject;
+ int bEnableBody; /* should a mail body be generated? (set to 0 eg for SMS gateways) */
+} configSettings_t;
+static configSettings_t cs;
+
+BEGINinitConfVars /* (re)set config variables to default values */
+CODESTARTinitConfVars
+ cs.lstRcpt = NULL;
+ cs.pszSrv = NULL;
+ cs.pszSrvPort = NULL;
+ cs.pszFrom = NULL;
+ cs.pszSubject = NULL;
+ cs.bEnableBody = 1; /* should a mail body be generated? (set to 0 eg for SMS gateways) */
+ENDinitConfVars
+
/* forward definitions (as few as possible) */
static rsRetVal Send(int sock, char *msg, size_t len);
static rsRetVal readResponse(instanceData *pData, int *piState, int iExpected);
@@ -128,8 +143,8 @@ addRcpt(void __attribute__((unused)) *pVal, uchar *pNewVal)
CHKmalloc(pNew = calloc(1, sizeof(toRcpt_t)));
pNew->pszTo = pNewVal;
- pNew->pNext = lstRcpt;
- lstRcpt = pNew;
+ pNew->pNext = cs.lstRcpt;
+ cs.lstRcpt = pNew;
dbgprintf("ommail::addRcpt adds recipient %s\n", pNewVal);
@@ -608,32 +623,32 @@ CODESTARTparseSelectorAct
/* TODO: check strdup() result */
- if(pszFrom == NULL) {
+ if(cs.pszFrom == NULL) {
errmsg.LogError(0, RS_RET_MAIL_NO_FROM, "no sender address given - specify $ActionMailFrom");
ABORT_FINALIZE(RS_RET_MAIL_NO_FROM);
}
- if(lstRcpt == NULL) {
+ if(cs.lstRcpt == NULL) {
errmsg.LogError(0, RS_RET_MAIL_NO_TO, "no recipient address given - specify $ActionMailTo");
ABORT_FINALIZE(RS_RET_MAIL_NO_TO);
}
- pData->md.smtp.pszFrom = (uchar*) strdup((char*)pszFrom);
- pData->md.smtp.lstRcpt = lstRcpt; /* we "hand over" this memory */
- lstRcpt = NULL; /* note: this is different from pre-3.21.2 versions! */
+ pData->md.smtp.pszFrom = (uchar*) strdup((char*)cs.pszFrom);
+ pData->md.smtp.lstRcpt = cs.lstRcpt; /* we "hand over" this memory */
+ cs.lstRcpt = NULL; /* note: this is different from pre-3.21.2 versions! */
- if(pszSubject == NULL) {
+ if(cs.pszSubject == NULL) {
/* if no subject is configured, we need just one template string */
CODE_STD_STRING_REQUESTparseSelectorAct(1)
} else {
CODE_STD_STRING_REQUESTparseSelectorAct(2)
pData->bHaveSubject = 1;
- CHKiRet(OMSRsetEntry(*ppOMSR, 1, (uchar*)strdup((char*) pszSubject), OMSR_NO_RQD_TPL_OPTS));
+ CHKiRet(OMSRsetEntry(*ppOMSR, 1, (uchar*)strdup((char*) cs.pszSubject), OMSR_NO_RQD_TPL_OPTS));
}
- if(pszSrv != NULL)
- pData->md.smtp.pszSrv = (uchar*) strdup((char*)pszSrv);
- if(pszSrvPort != NULL)
- pData->md.smtp.pszSrvPort = (uchar*) strdup((char*)pszSrvPort);
- pData->bEnableBody = bEnableBody;
+ if(cs.pszSrv != NULL)
+ pData->md.smtp.pszSrv = (uchar*) strdup((char*)cs.pszSrv);
+ if(cs.pszSrvPort != NULL)
+ pData->md.smtp.pszSrvPort = (uchar*) strdup((char*)cs.pszSrvPort);
+ pData->bEnableBody = cs.bEnableBody;
/* process template */
CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar*) "RSYSLOG_FileFormat"));
@@ -646,20 +661,14 @@ static rsRetVal freeConfigVariables(void)
{
DEFiRet;
- if(pszSrv != NULL) {
- free(pszSrv);
- pszSrv = NULL;
- }
- if(pszSrvPort != NULL) {
- free(pszSrvPort);
- pszSrvPort = NULL;
- }
- if(pszFrom != NULL) {
- free(pszFrom);
- pszFrom = NULL;
- }
- lstRcptDestruct(lstRcpt);
- lstRcpt = NULL;
+ free(cs.pszSrv);
+ cs.pszSrv = NULL;
+ free(cs.pszSrvPort);
+ cs.pszSrvPort = NULL;
+ free(cs.pszFrom);
+ cs.pszFrom = NULL;
+ lstRcptDestruct(cs.lstRcpt);
+ cs.lstRcpt = NULL;
RETiRet;
}
@@ -688,7 +697,7 @@ ENDqueryEtryPt
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
DEFiRet;
- bEnableBody = 1;
+ cs.bEnableBody = 1;
iRet = freeConfigVariables();
RETiRet;
}
@@ -696,6 +705,7 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
BEGINmodInit()
CODESTARTmodInit
+INITLegCnfVars
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
/* tell which objects we need */
@@ -705,12 +715,12 @@ CODEmodInit_QueryRegCFSLineHdlr
dbgprintf("ommail version %s initializing\n", VERSION);
- CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionmailsmtpserver", 0, eCmdHdlrGetWord, NULL, &pszSrv, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionmailsmtpport", 0, eCmdHdlrGetWord, NULL, &pszSrvPort, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionmailfrom", 0, eCmdHdlrGetWord, NULL, &pszFrom, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionmailsmtpserver", 0, eCmdHdlrGetWord, NULL, &cs.pszSrv, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionmailsmtpport", 0, eCmdHdlrGetWord, NULL, &cs.pszSrvPort, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionmailfrom", 0, eCmdHdlrGetWord, NULL, &cs.pszFrom, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionmailto", 0, eCmdHdlrGetWord, addRcpt, NULL, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionmailsubject", 0, eCmdHdlrGetWord, NULL, &pszSubject, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionmailenablebody", 0, eCmdHdlrBinary, NULL, &bEnableBody, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionmailsubject", 0, eCmdHdlrGetWord, NULL, &cs.pszSubject, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionmailenablebody", 0, eCmdHdlrBinary, NULL, &cs.bEnableBody, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr( (uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
diff --git a/plugins/ommongodb/Makefile.am b/plugins/ommongodb/Makefile.am
new file mode 100644
index 00000000..1b0e23a1
--- /dev/null
+++ b/plugins/ommongodb/Makefile.am
@@ -0,0 +1,11 @@
+mongodir = ./mongo-c-driver/src
+pkglib_LTLIBRARIES = ommongodb.la
+
+ommongodb_la_SOURCES = ommongodb.c
+ommongodb_la_SOURCES += $(mongodir)/bson.c $(mongodir)/mongo.c $(mongodir)/md5.c $(mongodir)/numbers.c
+
+ommongodb_la_CPPFLAGS = -DMONGO_HAVE_STDINT -Imongo-c-driver/src $(RSRT_CFLAGS) $(PTHREADS_CFLAGS)
+ommongodb_la_LDFLAGS = -module -avoid-version
+ommongodb_la_LIBADD =
+
+EXTRA_DIST =
diff --git a/plugins/ommongodb/README b/plugins/ommongodb/README
new file mode 100644
index 00000000..cea3f3bc
--- /dev/null
+++ b/plugins/ommongodb/README
@@ -0,0 +1,23 @@
+plugin to use MongoDB as backend.
+
+tested in ubuntu 10.04 and ubuntu 10.10
+
+configuration:
+
+in your /etc/rsyslog.conf, together with other modules:
+$ModLoad ommongodb # provides mongodb support
+
+then in your /etc/rsyslog.d (check your distribution way to organize the configuration..) you create a file 10-mongodb.conf with the following content:
+
+#the format for the driver is :ommongodb:ip:db:collection;StdMongoDBFmt
+#if you want to change what is logged in the db, the template, you must change the source code since the keys are hardcoded
+$template StdMongoDBFmt,"%msg%%syslogfacility%%HOSTNAME%%syslogpriority%"
+*.* :ommongodb:127.0.0.1,syslog,logs;StdMongoDBFmt
+
+
+TODO
+we must ensure that the collection is a capped collection
+refactor my code :-)
+
+email Victor Pereira <victor.pereira@bigrails.com>
+twitter twitter.com/vpereira
diff --git a/plugins/ommongodb/ommongodb.c b/plugins/ommongodb/ommongodb.c
new file mode 100644
index 00000000..07050c14
--- /dev/null
+++ b/plugins/ommongodb/ommongodb.c
@@ -0,0 +1,281 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <assert.h>
+#include <signal.h>
+#include <time.h>
+#include "bson.h"
+#include "mongo.h"
+#include "config.h"
+#include "rsyslog.h"
+#include "conf.h"
+#include "syslogd-types.h"
+#include "srUtils.h"
+#include "template.h"
+#include "module-template.h"
+#include "errmsg.h"
+#include "cfsysline.h"
+#include "mongo-c-driver/src/mongo.h"
+
+#define countof(X) ( (size_t) ( sizeof(X)/sizeof*(X) ) )
+
+#define DEFAULT_SERVER "127.0.0.1"
+#define DEFAULT_DATABASE "syslog"
+#define DEFAULT_COLLECTION "log"
+#define DEFAULT_DB_COLLECTION "syslog.log"
+
+//i just defined some constants, i couldt not find the limit
+#define MONGO_DB_NAME_SIZE 128
+#define MONGO_COLLECTION_NAME_SIZE 128
+
+MODULE_TYPE_OUTPUT
+MODULE_CNFNAME("ommongodb")
+/* internal structures
+ */
+DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+
+typedef struct _instanceData {
+ mongo_connection conn[1]; /* ptr */
+ mongo_connection_options opts[1];
+ mongo_conn_return status;
+ char db[MONGO_DB_NAME_SIZE];
+ char collection[MONGO_COLLECTION_NAME_SIZE];
+ char dbcollection[MONGO_DB_NAME_SIZE + MONGO_COLLECTION_NAME_SIZE + 1];
+ unsigned uLastMongoDBErrno;
+ //unsigned iSrvPort; /* sample: server port */
+} instanceData;
+
+char db[_DB_MAXDBLEN+2];
+static int iSrvPort = 27017;
+BEGINcreateInstance
+CODESTARTcreateInstance
+ENDcreateInstance
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ /* use this to specify if select features are supported by this
+ * plugin. If not, the framework will handle that. Currently, only
+ * RepeatedMsgReduction ("last message repeated n times") is optional.
+ */
+ if(eFeat == sFEATURERepeatedMsgReduction)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+static void closeMongoDB(instanceData *pData)
+{
+ ASSERT(pData != NULL);
+
+ if(pData->conn != NULL) {
+ mongo_destroy( pData->conn );
+ memset(pData->conn,0x00,sizeof(mongo_connection));
+ }
+}
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ closeMongoDB(pData);
+ENDfreeInstance
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ /* nothing special here */
+ENDdbgPrintInstInfo
+
+/* log a database error with descriptive message.
+ * We check if we have a valid MongoDB handle. If not, we simply
+ * report an error
+ */
+static void reportDBError(instanceData *pData, int bSilent)
+{
+ char errMsg[512];
+ bson ErrObj;
+
+ ASSERT(pData != NULL);
+
+ /* output log message */
+ errno = 0;
+ if(pData->conn == NULL) {
+ errmsg.LogError(0, NO_ERRCODE, "unknown DB error occured - could not obtain MongoDB handle");
+ } else { /* we can ask mysql for the error description... */
+ //we should handle the error. if bSilent is set then we should print as debug
+ mongo_cmd_get_last_error(pData->conn, pData->db, &ErrObj);
+ bson_destroy(&ErrObj);
+ }
+
+ return;
+}
+
+/* The following function is responsible for initializing a
+ * MySQL connection.
+ * Initially added 2004-10-28 mmeckelein
+ */
+static rsRetVal initMongoDB(instanceData *pData, int bSilent)
+{
+ DEFiRet;
+
+ ASSERT(pData != NULL);
+ ASSERT(pData->conn == NULL);
+
+ //I'm trying to fallback to a default here
+ if(pData->opts->port == 0)
+ pData->opts->port = 27017;
+
+ if(pData->opts->host == 0x00)
+ strcpy(pData->opts->host,DEFAULT_SERVER);
+
+ if(pData->dbcollection == 0x00)
+ strcpy(pData->dbcollection,DEFAULT_DB_COLLECTION);
+
+ pData->status = mongo_connect(pData->conn, pData->opts );
+
+ switch (pData->status) {
+ case mongo_conn_success:
+ fprintf(stderr, "connection succeeded\n" );
+ iRet = RS_RET_OK;
+ break;
+ case mongo_conn_bad_arg:
+ errmsg.LogError(0, RS_RET_SUSPENDED, "can not initialize MongoDB handle");
+ fprintf(stderr, "bad arguments\n" );
+ iRet = RS_RET_SUSPENDED;
+ break;
+ case mongo_conn_no_socket:
+ errmsg.LogError(0, RS_RET_SUSPENDED, "can not initialize MongoDB handle");
+ fprintf(stderr, "no socket\n" );
+ iRet = RS_RET_SUSPENDED;
+ break;
+ case mongo_conn_fail:
+ errmsg.LogError(0, RS_RET_SUSPENDED, "can not initialize MongoDB handle");
+ fprintf(stderr, "connection failed\n" );
+ iRet = RS_RET_SUSPENDED;
+ break;
+ case mongo_conn_not_master:
+ errmsg.LogError(0, RS_RET_SUSPENDED, "can not initialize MongoDB handle");
+ fprintf(stderr, "not master\n" );
+ iRet = RS_RET_SUSPENDED;
+ break;
+ }
+ RETiRet;
+}
+
+//we must implement it
+rsRetVal writeMongoDB(uchar *psz, instanceData *pData)
+{
+ char mydate[32];
+ char **szParams;
+ bson b[1];
+ bson_buffer buf[1];
+ bson_buffer_init( buf );
+ bson_append_new_oid(buf, "_id" );
+ memset(mydate,0x00,32);
+
+
+ DEFiRet;
+
+ ASSERT(psz != NULL);
+ ASSERT(pData != NULL);
+
+
+ /* see if we are ready to proceed */
+ if(pData->conn == NULL) {
+ CHKiRet(initMongoDB(pData, 0));
+ }
+
+szParams = (char**)(void*) psz;
+//We can make it beter
+//if you change the fields in your template, we must update it here
+//there is any C_metaprogramming_ninja there? :-)
+if(countof(szParams) > 0)
+{
+ bson_append_string( buf, "msg", szParams[0]);
+ bson_append_string( buf, "facility",szParams[1]);
+ bson_append_string( buf, "hostname", szParams[2] );
+ bson_append_string(buf, "priority",szParams[3]);
+ bson_append_int(buf,"count",countof(szParams));
+ bson_from_buffer( b, buf );
+ mongo_insert(pData->conn, pData->dbcollection, b );
+}
+
+if(b)
+ bson_destroy(b);
+
+
+ finalize_it:
+ if(iRet == RS_RET_OK) {
+ pData->uLastMongoDBErrno = 0; /* reset error for error supression */
+ }
+
+
+ RETiRet;
+}
+
+BEGINtryResume
+CODESTARTtryResume
+ if(pData->conn == NULL) {
+ iRet = initMongoDB(pData, 1);
+ }
+ENDtryResume
+
+BEGINdoAction
+CODESTARTdoAction
+ iRet = writeMongoDB(ppString[0], pData);
+ENDdoAction
+
+BEGINparseSelectorAct
+ //int iMongoDBPropErr = 0;
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(1)
+
+ if(!strncmp((char*) p, ":ommongodb:", sizeof(":ommongodb:") - 1)) {
+ p += sizeof(":ommongodb:") - 1; /* eat indicator sequence (-1 because of '\0'!) */
+ } else {
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+ }
+
+ CHKiRet(createInstance(&pData));
+
+ if(getSubString(&p, pData->opts->host, MAXHOSTNAMELEN+1, ','))
+ strcpy(pData->opts->host,DEFAULT_SERVER);
+
+ //we must define the max db name
+ if(getSubString(&p,pData->db,255,','))
+ strcpy(pData->db,DEFAULT_DATABASE);
+ if(getSubString(&p,pData->collection,255,';'))
+ strcpy(pData->collection,DEFAULT_COLLECTION);
+ if(*(p-1) == ';')
+ --p;
+
+
+ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_TPL_AS_ARRAY, (uchar*) " StdMongoDBFmt"));
+
+
+ pData->opts->port = (unsigned) iSrvPort; /* set configured port */
+ sprintf(pData->dbcollection,"%s.%s",pData->db,pData->collection);
+ CHKiRet(initMongoDB(pData, 0));
+
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+
+BEGINmodExit
+CODESTARTmodExit
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_OMOD_QUERIES
+ENDqueryEtryPt
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ INITChkCoreFeature(bCoreSupportsBatching, CORE_FEATURE_BATCHING);
+ DBGPRINTF("ompgsql: module compiled with rsyslog version %s.\n", VERSION);
+ DBGPRINTF("ompgsql: %susing transactional output interface.\n", bCoreSupportsBatching ? "" : "not ");
+ENDmodInit
diff --git a/plugins/ommysql/ommysql.c b/plugins/ommysql/ommysql.c
index f8bb4aa6..50ebf3a3 100644
--- a/plugins/ommysql/ommysql.c
+++ b/plugins/ommysql/ommysql.c
@@ -47,6 +47,9 @@
MODULE_TYPE_OUTPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("ommysql")
+
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal);
/* internal structures
*/
@@ -54,21 +57,28 @@ DEF_OMOD_STATIC_DATA
DEFobjCurrIf(errmsg)
typedef struct _instanceData {
- MYSQL *f_hmysql; /* handle to MySQL */
+ MYSQL *f_hmysql; /* handle to MySQL */
char f_dbsrv[MAXHOSTNAMELEN+1]; /* IP or hostname of DB server*/
unsigned int f_dbsrvPort; /* port of MySQL server */
char f_dbname[_DB_MAXDBLEN+1]; /* DB name */
char f_dbuid[_DB_MAXUNAMELEN+1]; /* DB user */
char f_dbpwd[_DB_MAXPWDLEN+1]; /* DB user's password */
- unsigned uLastMySQLErrno; /* last errno returned by MySQL or 0 if all is well */
- uchar * f_configfile; /* MySQL Client Configuration File */
- uchar * f_configsection; /* MySQL Client Configuration Section */
+ unsigned uLastMySQLErrno; /* last errno returned by MySQL or 0 if all is well */
+ uchar * f_configfile; /* MySQL Client Configuration File */
+ uchar * f_configsection; /* MySQL Client Configuration Section */
} instanceData;
-/* config variables */
-static uchar * pszMySQLConfigFile = NULL; /* MySQL Client Configuration File */
-static uchar * pszMySQLConfigSection = NULL; /* MySQL Client Configuration Section */
-static int iSrvPort = 0; /* database server port */
+typedef struct configSettings_s {
+ int iSrvPort; /* database server port */
+ uchar *pszMySQLConfigFile; /* MySQL Client Configuration File */
+ uchar *pszMySQLConfigSection; /* MySQL Client Configuration Section */
+} configSettings_t;
+static configSettings_t cs;
+
+BEGINinitConfVars /* (re)set config variables to default values */
+CODESTARTinitConfVars
+ resetConfigVariables(NULL, NULL);
+ENDinitConfVars
BEGINcreateInstance
@@ -309,9 +319,9 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
errmsg.LogError(0, RS_RET_INVALID_PARAMS, "Trouble with MySQL connection properties. -MySQL logging disabled");
ABORT_FINALIZE(RS_RET_INVALID_PARAMS);
} else {
- pData->f_dbsrvPort = (unsigned) iSrvPort; /* set configured port */
- pData->f_configfile = pszMySQLConfigFile;
- pData->f_configsection = pszMySQLConfigSection;
+ pData->f_dbsrvPort = (unsigned) cs.iSrvPort; /* set configured port */
+ pData->f_configfile = cs.pszMySQLConfigFile;
+ pData->f_configsection = cs.pszMySQLConfigSection;
pData->f_hmysql = NULL; /* initialize, but connect only on first message (important for queued mode!) */
}
@@ -335,24 +345,25 @@ ENDqueryEtryPt
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
DEFiRet;
- iSrvPort = 0; /* zero is the default port */
- free(pszMySQLConfigFile);
- pszMySQLConfigFile = NULL;
- free(pszMySQLConfigSection);
- pszMySQLConfigSection = NULL;
+ cs.iSrvPort = 0; /* zero is the default port */
+ free(cs.pszMySQLConfigFile);
+ cs.pszMySQLConfigFile = NULL;
+ free(cs.pszMySQLConfigSection);
+ cs.pszMySQLConfigSection = NULL;
RETiRet;
}
BEGINmodInit()
CODESTARTmodInit
+INITLegCnfVars
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(errmsg, CORE_COMPONENT));
/* register our config handlers */
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionommysqlserverport", 0, eCmdHdlrInt, NULL, &iSrvPort, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionommysqlserverport", 0, eCmdHdlrInt, NULL, &cs.iSrvPort, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"ommysqlconfigfile",0,eCmdHdlrGetWord,NULL,&cs.pszMySQLConfigFile,STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"ommysqlconfigsection",0,eCmdHdlrGetWord,NULL,&cs.pszMySQLConfigSection,STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"ommysqlconfigfile",0,eCmdHdlrGetWord,NULL,&pszMySQLConfigFile,STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"ommysqlconfigsection",0,eCmdHdlrGetWord,NULL,&pszMySQLConfigSection,STD_LOADABLE_MODULE_ID));
ENDmodInit
/* vi:set ai:
diff --git a/plugins/omoracle/omoracle.c b/plugins/omoracle/omoracle.c
index a37533ee..736629a6 100644
--- a/plugins/omoracle/omoracle.c
+++ b/plugins/omoracle/omoracle.c
@@ -83,6 +83,7 @@
MODULE_TYPE_OUTPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("omoracle")
/** */
DEF_OMOD_STATIC_DATA
diff --git a/plugins/ompgsql/ompgsql.c b/plugins/ompgsql/ompgsql.c
index ea4b4b75..11f346f6 100644
--- a/plugins/ompgsql/ompgsql.c
+++ b/plugins/ompgsql/ompgsql.c
@@ -50,6 +50,7 @@
MODULE_TYPE_OUTPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("ompgsql")
/* internal structures
*/
@@ -65,6 +66,15 @@ typedef struct _instanceData {
ConnStatusType eLastPgSQLStatus; /* last status from postgres */
} instanceData;
+typedef struct configSettings_s {
+ EMPTY_STRUCT
+} configSettings_t;
+static configSettings_t __attribute__((unused)) cs;
+
+BEGINinitConfVars /* (re)set config variables to default values */
+CODESTARTinitConfVars
+ENDinitConfVars
+
static rsRetVal writePgSQL(uchar *psz, instanceData *pData);
@@ -357,6 +367,7 @@ ENDqueryEtryPt
BEGINmodInit()
CODESTARTmodInit
+INITLegCnfVars
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(errmsg, CORE_COMPONENT));
diff --git a/plugins/omprog/omprog.c b/plugins/omprog/omprog.c
index db461a00..1f6bb62f 100644
--- a/plugins/omprog/omprog.c
+++ b/plugins/omprog/omprog.c
@@ -45,6 +45,7 @@
MODULE_TYPE_OUTPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("omprog")
/* internal structures
*/
@@ -58,8 +59,17 @@ typedef struct _instanceData {
int bIsRunning; /* is binary currently running? 0-no, 1-yes */
} instanceData;
+typedef struct configSettings_s {
+ uchar *szBinary; /* name of binary to call */
+} configSettings_t;
+static configSettings_t cs;
+
+BEGINinitConfVars /* (re)set config variables to default values */
+CODESTARTinitConfVars
+ cs.szBinary = NULL; /* name of binary to call */
+ENDinitConfVars
+
/* config settings */
-static uchar *szBinary = NULL; /* name of binary to call */
BEGINcreateInstance
CODESTARTcreateInstance
@@ -300,7 +310,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
p += sizeof(":omprog:") - 1; /* eat indicator sequence (-1 because of '\0'!) */
CHKiRet(createInstance(&pData));
- CHKmalloc(pData->szBinary = (uchar*) strdup((char*)szBinary));
+ CHKmalloc(pData->szBinary = (uchar*) strdup((char*)cs.szBinary));
/* check if a non-standard template is to be applied */
if(*(p-1) == ';')
--p;
@@ -311,10 +321,8 @@ ENDparseSelectorAct
BEGINmodExit
CODESTARTmodExit
- if(szBinary != NULL) {
- free(szBinary);
- szBinary = NULL;
- }
+ free(cs.szBinary);
+ cs.szBinary = NULL;
CHKiRet(objRelease(errmsg, CORE_COMPONENT));
finalize_it:
ENDmodExit
@@ -332,22 +340,19 @@ ENDqueryEtryPt
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
DEFiRet;
-
- if(szBinary != NULL) {
- free(szBinary);
- szBinary = NULL;
- }
-
+ free(cs.szBinary);
+ cs.szBinary = NULL;
RETiRet;
}
BEGINmodInit()
CODESTARTmodInit
+INITLegCnfVars
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(errmsg, CORE_COMPONENT));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionomprogbinary", 0, eCmdHdlrGetWord, NULL, &szBinary, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionomprogbinary", 0, eCmdHdlrGetWord, NULL, &cs.szBinary, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
CODEmodInit_QueryRegCFSLineHdlr
ENDmodInit
diff --git a/plugins/omrelp/omrelp.c b/plugins/omrelp/omrelp.c
index 95c15f5d..39ffe7fb 100644
--- a/plugins/omrelp/omrelp.c
+++ b/plugins/omrelp/omrelp.c
@@ -46,6 +46,7 @@
MODULE_TYPE_OUTPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("omrelp")
/* internal structures
*/
@@ -64,6 +65,15 @@ typedef struct _instanceData {
relpClt_t *pRelpClt; /* relp client for this instance */
} instanceData;
+typedef struct configSettings_s {
+ EMPTY_STRUCT
+} configSettings_t;
+static configSettings_t __attribute__((unused)) cs;
+
+BEGINinitConfVars /* (re)set config variables to default values */
+CODESTARTinitConfVars
+ENDinitConfVars
+
/* get the syslog forward port from selector_t. The passed in
* struct must be one that is setup for forwarding.
* rgerhards, 2007-06-28
@@ -336,6 +346,7 @@ ENDqueryEtryPt
BEGINmodInit()
CODESTARTmodInit
+INITLegCnfVars
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
/* create our relp engine */
diff --git a/plugins/omruleset/omruleset.c b/plugins/omruleset/omruleset.c
index c439bd83..67aee97e 100644
--- a/plugins/omruleset/omruleset.c
+++ b/plugins/omruleset/omruleset.c
@@ -50,6 +50,9 @@
MODULE_TYPE_OUTPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("omruleset")
+
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal);
/* static data */
DEFobjCurrIf(ruleset);
@@ -60,8 +63,6 @@ DEFobjCurrIf(errmsg);
DEF_OMOD_STATIC_DATA
/* config variables */
-ruleset_t *pRuleset = NULL; /* ruleset to enqueue message to (NULL = Default, not recommended) */
-uchar *pszRulesetName = NULL;
typedef struct _instanceData {
@@ -69,6 +70,17 @@ typedef struct _instanceData {
uchar *pszRulesetName; /* primarily for debugging/display purposes */
} instanceData;
+typedef struct configSettings_s {
+ ruleset_t *pRuleset; /* ruleset to enqueue message to (NULL = Default, not recommended) */
+ uchar *pszRulesetName;
+} configSettings_t;
+static configSettings_t cs;
+
+BEGINinitConfVars /* (re)set config variables to default values */
+CODESTARTinitConfVars
+ resetConfigVariables(NULL, NULL);
+ENDinitConfVars
+
BEGINcreateInstance
CODESTARTcreateInstance
@@ -119,12 +131,12 @@ setRuleset(void __attribute__((unused)) *pVal, uchar *pszName)
rsRetVal localRet;
DEFiRet;
- localRet = ruleset.GetRuleset(&pRuleset, pszName);
+ localRet = ruleset.GetRuleset(ourConf, &cs.pRuleset, pszName);
if(localRet == RS_RET_NOT_FOUND) {
errmsg.LogError(0, RS_RET_RULESET_NOT_FOUND, "error: ruleset '%s' not found - ignored", pszName);
}
CHKiRet(localRet);
- pszRulesetName = pszName; /* save for later display purposes */
+ cs.pszRulesetName = pszName; /* save for later display purposes */
finalize_it:
if(iRet != RS_RET_OK) { /* cleanup needed? */
@@ -143,7 +155,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
}
- if(pRuleset == NULL) {
+ if(cs.pRuleset == NULL) {
errmsg.LogError(0, RS_RET_NO_RULESET, "error: no ruleset was specified, use "
"$ActionOmrulesetRulesetName directive first!");
ABORT_FINALIZE(RS_RET_NO_RULESET);
@@ -161,17 +173,17 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
* the format specified (if any) is always ignored.
*/
CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, iTplOpts, (uchar*) "RSYSLOG_FileFormat"));
- pData->pRuleset = pRuleset;
- pData->pszRulesetName = pszRulesetName;
- pRuleset = NULL; /* re-set, because there is a high risk of unwanted behavior if we leave it in! */
- pszRulesetName = NULL; /* note: we must not free, as we handed over this pointer to the instanceDat to the instanceDataa! */
+ pData->pRuleset = cs.pRuleset;
+ pData->pszRulesetName = cs.pszRulesetName;
+ cs.pRuleset = NULL; /* re-set, because there is a high risk of unwanted behavior if we leave it in! */
+ cs.pszRulesetName = NULL; /* note: we must not free, as we handed over this pointer to the instanceDat to the instanceDataa! */
CODE_STD_FINALIZERparseSelectorAct
ENDparseSelectorAct
BEGINmodExit
CODESTARTmodExit
- free(pszRulesetName);
+ free(cs.pszRulesetName);
objRelease(errmsg, CORE_COMPONENT);
objRelease(ruleset, CORE_COMPONENT);
ENDmodExit
@@ -189,7 +201,9 @@ ENDqueryEtryPt
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
DEFiRet;
- pRuleset = NULL;
+ cs.pRuleset = NULL;
+ free(cs.pszRulesetName);
+ cs.pszRulesetName = NULL;
RETiRet;
}
@@ -200,6 +214,7 @@ BEGINmodInit()
unsigned long opts;
int bMsgPassingSupported; /* does core support template passing as an array? */
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 */
diff --git a/plugins/omsnmp/omsnmp.c b/plugins/omsnmp/omsnmp.c
index 11870579..80230789 100644
--- a/plugins/omsnmp/omsnmp.c
+++ b/plugins/omsnmp/omsnmp.c
@@ -44,6 +44,7 @@
MODULE_TYPE_OUTPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("omsnmp")
/* internal structures
*/
@@ -58,27 +59,6 @@ static oid objid_sysdescr[] = { 1, 3, 6, 1, 2, 1, 1, 1, 0 };
static oid objid_snmptrap[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };
static oid objid_sysuptime[] = { 1, 3, 6, 1, 2, 1, 1, 3, 0 };
-static uchar* pszTransport = NULL; /* default transport */
-static uchar* pszTarget = NULL;
-/* note using an unsigned for a port number is not a good idea from an IPv6 point of view */
-static int iPort = 0;
-static int iSNMPVersion = 1; /* 0 Means SNMPv1, 1 Means SNMPv2c */
-static uchar* pszCommunity = NULL;
-static uchar* pszEnterpriseOID = NULL;
-static uchar* pszSnmpTrapOID = NULL;
-static uchar* pszSyslogMessageOID = NULL;
-static int iSpecificType = 0;
-static int iTrapType = SNMP_TRAP_ENTERPRISESPECIFIC;/*Default is SNMP_TRAP_ENTERPRISESPECIFIC */
-/*
- Possible Values
- SNMP_TRAP_COLDSTART (0)
- SNMP_TRAP_WARMSTART (1)
- SNMP_TRAP_LINKDOWN (2)
- SNMP_TRAP_LINKUP (3)
- SNMP_TRAP_AUTHFAIL (4)
- SNMP_TRAP_EGPNEIGHBORLOSS (5)
- SNMP_TRAP_ENTERPRISESPECIFIC (6)
-*/
typedef struct _instanceData {
uchar szTransport[OMSNMP_MAXTRANSPORLENGTH+1]; /* Transport - Can be udp, tcp, udp6, tcp6 and other types supported by NET-SNMP */
@@ -103,6 +83,46 @@ typedef struct _instanceData {
netsnmp_session *snmpsession; /* Holds to SNMP Session, NULL if not initialized */
} instanceData;
+typedef struct configSettings_s {
+ uchar* pszTransport; /* default transport */
+ uchar* pszTarget;
+ /* note using an unsigned for a port number is not a good idea from an IPv6 point of view */
+ int iPort;
+ int iSNMPVersion; /* 0 Means SNMPv1, 1 Means SNMPv2c */
+ uchar* pszCommunity;
+ uchar* pszEnterpriseOID;
+ uchar* pszSnmpTrapOID;
+ uchar* pszSyslogMessageOID;
+ int iSpecificType;
+ int iTrapType; /*Default is SNMP_TRAP_ENTERPRISESPECIFIC */
+ /*
+ Possible Values
+ SNMP_TRAP_COLDSTART (0)
+ SNMP_TRAP_WARMSTART (1)
+ SNMP_TRAP_LINKDOWN (2)
+ SNMP_TRAP_LINKUP (3)
+ SNMP_TRAP_AUTHFAIL (4)
+ SNMP_TRAP_EGPNEIGHBORLOSS (5)
+ SNMP_TRAP_ENTERPRISESPECIFIC (6)
+ */
+} configSettings_t;
+
+SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */
+
+BEGINinitConfVars /* (re)set config variables to default values */
+CODESTARTinitConfVars
+ cs.pszTransport = NULL;
+ cs.pszTarget = NULL;
+ cs.iPort = 0;
+ cs.iSNMPVersion = 1;
+ cs.pszCommunity = NULL;
+ cs.pszEnterpriseOID = NULL;
+ cs.pszSnmpTrapOID = NULL;
+ cs.pszSyslogMessageOID = NULL;
+ cs.iSpecificType = 0;
+ cs.iTrapType = SNMP_TRAP_ENTERPRISESPECIFIC;
+ENDinitConfVars
+
BEGINcreateInstance
CODESTARTcreateInstance
ENDcreateInstance
@@ -349,72 +369,72 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
FINALIZE;
/* Check Transport */
- if (pszTransport == NULL) {
+ if (cs.pszTransport == NULL) {
/*
* Default transport is UDP. Other values supported by NETSNMP are possible as well
*/
strncpy( (char*) pData->szTransport, "udp", sizeof("udp") );
} else {
/* Copy Transport */
- strncpy( (char*) pData->szTransport, (char*) pszTransport, strlen((char*) pszTransport) );
+ strncpy( (char*) pData->szTransport, (char*) cs.pszTransport, strlen((char*) cs.pszTransport) );
}
/* Check Target */
- if (pszTarget == NULL) {
+ if (cs.pszTarget == NULL) {
ABORT_FINALIZE( RS_RET_PARAM_ERROR );
} else {
/* Copy Target */
- CHKmalloc(pData->szTarget = (uchar*) strdup((char*)pszTarget));
+ CHKmalloc(pData->szTarget = (uchar*) strdup((char*)cs.pszTarget));
}
/* Copy Community */
- if (pszCommunity == NULL) /* Failsave */
+ if (cs.pszCommunity == NULL) /* Failsave */
strncpy( (char*) pData->szCommunity, "public", sizeof("public") );
else /* Copy Target */
- strncpy( (char*) pData->szCommunity, (char*) pszCommunity, strlen((char*) pszCommunity) );
+ strncpy( (char*) pData->szCommunity, (char*) cs.pszCommunity, strlen((char*) cs.pszCommunity) );
/* Copy Enterprise OID */
- if (pszEnterpriseOID == NULL) /* Failsave */
+ if (cs.pszEnterpriseOID == NULL) /* Failsave */
strncpy( (char*) pData->szEnterpriseOID, "1.3.6.1.4.1.3.1.1", sizeof("1.3.6.1.4.1.3.1.1") );
else /* Copy Target */
- strncpy( (char*) pData->szEnterpriseOID, (char*) pszEnterpriseOID, strlen((char*) pszEnterpriseOID) );
+ strncpy( (char*) pData->szEnterpriseOID, (char*) cs.pszEnterpriseOID, strlen((char*) cs.pszEnterpriseOID) );
/* Copy SnmpTrap OID */
- if (pszSnmpTrapOID == NULL) /* Failsave */
+ if (cs.pszSnmpTrapOID == NULL) /* Failsave */
strncpy( (char*) pData->szSnmpTrapOID, "1.3.6.1.4.1.19406.1.2.1", sizeof("1.3.6.1.4.1.19406.1.2.1") );
else /* Copy Target */
- strncpy( (char*) pData->szSnmpTrapOID, (char*) pszSnmpTrapOID, strlen((char*) pszSnmpTrapOID) );
+ strncpy( (char*) pData->szSnmpTrapOID, (char*) cs.pszSnmpTrapOID, strlen((char*) cs.pszSnmpTrapOID) );
/* Copy SyslogMessage OID */
- if (pszSyslogMessageOID == NULL) /* Failsave */
+ if (cs.pszSyslogMessageOID == NULL) /* Failsave */
strncpy( (char*) pData->szSyslogMessageOID, "1.3.6.1.4.1.19406.1.1.2.1", sizeof("1.3.6.1.4.1.19406.1.1.2.1") );
else /* Copy Target */
- strncpy( (char*) pData->szSyslogMessageOID, (char*) pszSyslogMessageOID, strlen((char*) pszSyslogMessageOID) );
+ strncpy( (char*) pData->szSyslogMessageOID, (char*) cs.pszSyslogMessageOID, strlen((char*) cs.pszSyslogMessageOID) );
/* Copy Port */
- if ( iPort == 0) /* If no Port is set we use the default Port 162 */
+ if ( cs.iPort == 0) /* If no Port is set we use the default Port 162 */
pData->iPort = 162;
else
- pData->iPort = iPort;
+ pData->iPort = cs.iPort;
/* Set SNMPVersion */
- if ( iSNMPVersion < 0 || iSNMPVersion > 1) /* Set default to 1 if out of range */
+ if ( cs.iSNMPVersion < 0 || cs.iSNMPVersion > 1) /* Set default to 1 if out of range */
pData->iSNMPVersion = 1;
else
- pData->iSNMPVersion = iSNMPVersion;
+ pData->iSNMPVersion = cs.iSNMPVersion;
/* Copy SpecificType */
- if ( iSpecificType == 0) /* If no iSpecificType is set, we use the default 0 */
+ if ( cs.iSpecificType == 0) /* If no iSpecificType is set, we use the default 0 */
pData->iSpecificType = 0;
else
- pData->iSpecificType = iSpecificType;
+ pData->iSpecificType = cs.iSpecificType;
/* Copy TrapType */
- if ( iTrapType < 0 && iTrapType >= 6) /* Only allow values from 0 to 6 !*/
+ if ( cs.iTrapType < 0 && cs.iTrapType >= 6) /* Only allow values from 0 to 6 !*/
pData->iTrapType = SNMP_TRAP_ENTERPRISESPECIFIC;
else
- pData->iTrapType = iTrapType;
+ pData->iTrapType = cs.iTrapType;
/* Create string for session peername! */
snprintf((char*)szTargetAndPort, sizeof(szTargetAndPort), "%s:%s:%d", pData->szTransport, pData->szTarget, pData->iPort);
@@ -453,48 +473,31 @@ ENDparseSelectorAct
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
DEFiRet;
-
- if (pszTarget != NULL)
- free(pszTarget);
- pszTarget = NULL;
-
- if (pszCommunity != NULL)
- free(pszCommunity);
- pszCommunity = NULL;
-
- if (pszEnterpriseOID != NULL)
- free(pszEnterpriseOID);
- pszEnterpriseOID = NULL;
-
- if (pszSnmpTrapOID != NULL)
- free(pszSnmpTrapOID);
- pszSnmpTrapOID = NULL;
-
- if (pszSyslogMessageOID != NULL)
- free(pszSyslogMessageOID);
- pszSyslogMessageOID = NULL;
-
- iPort = 0;
- iSNMPVersion = 1;
- iSpecificType = 0;
- iTrapType = SNMP_TRAP_ENTERPRISESPECIFIC;
-
+ free(cs.pszTarget);
+ cs.pszTarget = NULL;
+ free(cs.pszCommunity);
+ cs.pszCommunity = NULL;
+ free(cs.pszEnterpriseOID);
+ cs.pszEnterpriseOID = NULL;
+ free(cs.pszSnmpTrapOID);
+ cs.pszSnmpTrapOID = NULL;
+ free(cs.pszSyslogMessageOID);
+ cs.pszSyslogMessageOID = NULL;
+ cs.iPort = 0;
+ cs.iSNMPVersion = 1;
+ cs.iSpecificType = 0;
+ cs.iTrapType = SNMP_TRAP_ENTERPRISESPECIFIC;
RETiRet;
}
BEGINmodExit
CODESTARTmodExit
- if (pszTarget != NULL)
- free(pszTarget);
- if (pszCommunity != NULL)
- free(pszCommunity);
- if (pszEnterpriseOID != NULL)
- free(pszEnterpriseOID);
- if (pszSnmpTrapOID != NULL)
- free(pszSnmpTrapOID);
- if (pszSyslogMessageOID != NULL)
- free(pszSyslogMessageOID);
+ free(cs.pszTarget);
+ free(cs.pszCommunity);
+ free(cs.pszEnterpriseOID);
+ free(cs.pszSnmpTrapOID);
+ free(cs.pszSyslogMessageOID);
/* release what we no longer need */
objRelease(errmsg, CORE_COMPONENT);
@@ -509,20 +512,21 @@ ENDqueryEtryPt
BEGINmodInit()
CODESTARTmodInit
+SCOPINGmodInit
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(errmsg, CORE_COMPONENT));
- CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmptransport", 0, eCmdHdlrGetWord, NULL, &pszTransport, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmptarget", 0, eCmdHdlrGetWord, NULL, &pszTarget, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmptargetport", 0, eCmdHdlrInt, NULL, &iPort, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmpversion", 0, eCmdHdlrInt, NULL, &iSNMPVersion, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmpcommunity", 0, eCmdHdlrGetWord, NULL, &pszCommunity, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmpenterpriseoid", 0, eCmdHdlrGetWord, NULL, &pszEnterpriseOID, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmptrapoid", 0, eCmdHdlrGetWord, NULL, &pszSnmpTrapOID, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmpsyslogmessageoid", 0, eCmdHdlrGetWord, NULL, &pszSyslogMessageOID, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmpspecifictype", 0, eCmdHdlrInt, NULL, &iSpecificType, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmptraptype", 0, eCmdHdlrInt, NULL, &iTrapType, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmptransport", 0, eCmdHdlrGetWord, NULL, &cs.pszTransport, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmptarget", 0, eCmdHdlrGetWord, NULL, &cs.pszTarget, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmptargetport", 0, eCmdHdlrInt, NULL, &cs.iPort, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmpversion", 0, eCmdHdlrInt, NULL, &cs.iSNMPVersion, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmpcommunity", 0, eCmdHdlrGetWord, NULL, &cs.pszCommunity, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmpenterpriseoid", 0, eCmdHdlrGetWord, NULL, &cs.pszEnterpriseOID, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmptrapoid", 0, eCmdHdlrGetWord, NULL, &cs.pszSnmpTrapOID, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmpsyslogmessageoid", 0, eCmdHdlrGetWord, NULL, &cs.pszSyslogMessageOID, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmpspecifictype", 0, eCmdHdlrInt, NULL, &cs.iSpecificType, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmptraptype", 0, eCmdHdlrInt, NULL, &cs.iTrapType, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr( (uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
/*
diff --git a/plugins/omstdout/omstdout.c b/plugins/omstdout/omstdout.c
index cd689765..fb95e951 100644
--- a/plugins/omstdout/omstdout.c
+++ b/plugins/omstdout/omstdout.c
@@ -44,14 +44,15 @@
MODULE_TYPE_OUTPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("omstdout")
+
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal);
/* internal structures
*/
DEF_OMOD_STATIC_DATA
/* config variables */
-static int bUseArrayInterface = 0; /* shall action use array instead of string template interface? */
-static int bEnsureLFEnding = 1; /* shall action use array instead of string template interface? */
typedef struct _instanceData {
@@ -59,6 +60,17 @@ typedef struct _instanceData {
int bEnsureLFEnding; /* ensure that a linefeed is written at the end of EACH record (test aid for nettester) */
} instanceData;
+typedef struct configSettings_s {
+ int bUseArrayInterface; /* shall action use array instead of string template interface? */
+ int bEnsureLFEnding; /* shall action use array instead of string template interface? */
+} configSettings_t;
+static configSettings_t cs;
+
+BEGINinitConfVars /* (re)set config variables to default values */
+CODESTARTinitConfVars
+ resetConfigVariables(NULL, NULL);
+ENDinitConfVars
+
BEGINcreateInstance
CODESTARTcreateInstance
ENDcreateInstance
@@ -147,10 +159,10 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
/* check if a non-standard template is to be applied */
if(*(p-1) == ';')
--p;
- iTplOpts = (bUseArrayInterface == 0) ? 0 : OMSR_TPL_AS_ARRAY;
+ iTplOpts = (cs.bUseArrayInterface == 0) ? 0 : OMSR_TPL_AS_ARRAY;
CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, iTplOpts, (uchar*) "RSYSLOG_FileFormat"));
- pData->bUseArrayInterface = bUseArrayInterface;
- pData->bEnsureLFEnding = bEnsureLFEnding;
+ pData->bUseArrayInterface = cs.bUseArrayInterface;
+ pData->bEnsureLFEnding = cs.bEnsureLFEnding;
CODE_STD_FINALIZERparseSelectorAct
ENDparseSelectorAct
@@ -172,8 +184,8 @@ ENDqueryEtryPt
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
DEFiRet;
- bUseArrayInterface = 0;
- bEnsureLFEnding = 1;
+ cs.bUseArrayInterface = 0;
+ cs.bEnsureLFEnding = 1;
RETiRet;
}
@@ -184,6 +196,7 @@ BEGINmodInit()
unsigned long opts;
int bArrayPassingSupported; /* does core support template passing as an array? */
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 */
@@ -202,10 +215,10 @@ CODEmodInit_QueryRegCFSLineHdlr
if(bArrayPassingSupported) {
/* enable config comand only if core supports it */
CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionomstdoutarrayinterface", 0, eCmdHdlrBinary, NULL,
- &bUseArrayInterface, STD_LOADABLE_MODULE_ID));
+ &cs.bUseArrayInterface, STD_LOADABLE_MODULE_ID));
}
CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionomstdoutensurelfending", 0, eCmdHdlrBinary, NULL,
- &bEnsureLFEnding, STD_LOADABLE_MODULE_ID));
+ &cs.bEnsureLFEnding, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
diff --git a/plugins/omtemplate/Makefile.am b/plugins/omtemplate/Makefile.am
deleted file mode 100644
index e816c7c6..00000000
--- a/plugins/omtemplate/Makefile.am
+++ /dev/null
@@ -1,8 +0,0 @@
-pkglib_LTLIBRARIES = omtemplate.la
-
-omtemplate_la_SOURCES = omtemplate.c
-omtemplate_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS)
-omtemplate_la_LDFLAGS = -module -avoid-version
-omtemplate_la_LIBADD =
-
-EXTRA_DIST =
diff --git a/plugins/omtemplate/omtemplate.c b/plugins/omtemplate/omtemplate.c
deleted file mode 100644
index 1472ebeb..00000000
--- a/plugins/omtemplate/omtemplate.c
+++ /dev/null
@@ -1,221 +0,0 @@
-/* omtemplate.c
- * This is a template for an output module. It implements a very
- * simple single-threaded output, just as thought of by the output
- * plugin interface.
- *
- * NOTE: read comments in module-template.h for more specifics!
- *
- * File begun on 2009-03-16 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 <time.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
-DEFobjCurrIf(errmsg)
-
-typedef struct _instanceData {
- /* here you need to define all action-specific data. A record of type
- * instanceData will be handed over to each instance of the action. Keep
- * in mind that there may be several invocations of the same type of action
- * inside rsyslog.conf, and this is what keeps them apart. Do NOT use
- * static data for this!
- */
- unsigned iSrvPort; /* sample: server port */
-} instanceData;
-
-/* config variables
- * For the configuration interface, we need to keep track of some settings. This
- * is done in global variables. It works as follows: when configuration statements
- * are entered, the config file handler (or custom function) sets the global
- * variable here. When the action then actually is instantiated, this handler
- * copies over to instanceData whatever configuration settings (from the global
- * variables) apply. The global variables are NEVER used inside an action
- * instance (at least this is how it is supposed to work ;)
- */
-static int iSrvPort = 0; /* sample: server port */
-
-
-BEGINcreateInstance
-CODESTARTcreateInstance
-ENDcreateInstance
-
-
-BEGINisCompatibleWithFeature
-CODESTARTisCompatibleWithFeature
- /* use this to specify if select features are supported by this
- * plugin. If not, the framework will handle that. Currently, only
- * RepeatedMsgReduction ("last message repeated n times") is optional.
- */
- if(eFeat == sFEATURERepeatedMsgReduction)
- iRet = RS_RET_OK;
-ENDisCompatibleWithFeature
-
-
-BEGINfreeInstance
-CODESTARTfreeInstance
- /* this is a cleanup callback. All dynamically-allocated resources
- * in instance data must be cleaned up here. Prime examples are
- * malloc()ed memory, file & database handles and the like.
- */
-ENDfreeInstance
-
-
-BEGINdbgPrintInstInfo
-CODESTARTdbgPrintInstInfo
- /* permits to spit out some debug info */
-ENDdbgPrintInstInfo
-
-
-BEGINtryResume
-CODESTARTtryResume
- /* this is called when an action has been suspended and the
- * rsyslog core tries to resume it. The action must then
- * retry (if possible) and report RS_RET_OK if it succeeded
- * or RS_RET_SUSPENDED otherwise.
- * Note that no data can be written in this callback, as it is
- * not present. Prime examples of what can be retried are
- * reconnects to remote hosts, reconnects to database,
- * opening of files and the like.
- * If there is no retry-type of operation, the action may
- * return RS_RET_OK, so that it will get called on its doAction
- * entry point (where it receives data), retries there, and
- * immediately returns RS_RET_SUSPENDED if that does not work
- * out. This disables some optimizations in the core's retry logic,
- * but is a valid and expected behaviour. Note that it is also OK
- * for the retry entry point to return OK but the immediately following
- * doAction call to fail. In real life, for example, a buggy com line
- * may cause such behaviour.
- * Note that there is no guarantee that the core will very quickly
- * call doAction after the retry succeeded. Today, it does, but that may
- * not always be the case.
- */
-ENDtryResume
-
-BEGINdoAction
-CODESTARTdoAction
- /* this is where you receive the message and need to carry out the
- * action. Data is provided in ppString[i] where 0 <= i <= num of strings
- * requested.
- * Return RS_RET_OK if all goes well, RS_RET_SUSPENDED if the action can
- * currently not complete, or an error code or RS_RET_DISABLED. The later
- * two should only be returned if there is no hope that the action can be
- * restored unless an rsyslog restart (prime example is an invalid config).
- * Error code or RS_RET_DISABLED permanently disables the action, up to
- * the next restart.
- */
-ENDdoAction
-
-
-BEGINparseSelectorAct
-CODESTARTparseSelectorAct
-CODE_STD_STRING_REQUESTparseSelectorAct(1)
- /* first check if this config line is actually for us
- * This is a clumpsy interface. We receive the action-part of the selector line
- * and need to look at the first characters. If they match our signature
- * ":omtemplate:", then we need to instantiate an action. It is recommended that
- * newer actions just watch for the template and all other parameters are passed in
- * via $-config-lines, this will hopefully be compatbile with future config syntaxes.
- * If we do not detect our signature, we must return with RS_RET_CONFLINE_UNPROCESSED
- * and NOT do anything else.
- */
- if(strncmp((char*) p, ":omtemplate:", sizeof(":omtemplate:") - 1)) {
- ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
- }
-
- /* ok, if we reach this point, we have something for us */
- p += sizeof(":omtemplate:") - 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;
- /* if we have, call rsyslog runtime to get us template. Note that StdFmt below is
- * the standard name. Currently, we may need to patch tools/syslogd.c if we need
- * to add a new standard template.
- */
- CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_RQD_TPL_OPT_SQL, (uchar*) " StdFmt"));
-
- /* if we reach this point, all went well, and we can copy over to instanceData
- * those configuration elements that we need.
- */
- pData->iSrvPort = (unsigned) iSrvPort; /* set configured port */
-
-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;
- iSrvPort = 0; /* zero is the default port */
- RETiRet;
-}
-
-
-BEGINmodInit()
-CODESTARTmodInit
- *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
-CODEmodInit_QueryRegCFSLineHdlr
- CHKiRet(objUse(errmsg, CORE_COMPONENT));
- /* register our config handlers */
- /* confguration parameters MUST always be specified in lower case! */
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionomtemplteserverport", 0, eCmdHdlrInt, NULL, &iSrvPort, STD_LOADABLE_MODULE_ID));
- /* "resetconfigvariables" should be provided. Notat that it is a chained directive */
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
-ENDmodInit
-
-/* vi:set ai:
- */
diff --git a/plugins/omtesting/omtesting.c b/plugins/omtesting/omtesting.c
index 909ff29a..ff290c94 100644
--- a/plugins/omtesting/omtesting.c
+++ b/plugins/omtesting/omtesting.c
@@ -57,12 +57,12 @@
MODULE_TYPE_OUTPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("omtesting")
/* internal structures
*/
DEF_OMOD_STATIC_DATA
-static int bEchoStdout = 0; /* echo non-failed messages to stdout */
typedef struct _instanceData {
enum { MD_SLEEP, MD_FAIL, MD_RANDFAIL, MD_ALWAYS_SUSPEND }
@@ -76,6 +76,16 @@ typedef struct _instanceData {
int iCurrRetries;
} instanceData;
+typedef struct configSettings_s {
+ int bEchoStdout; /* echo non-failed messages to stdout */
+} configSettings_t;
+static configSettings_t cs;
+
+BEGINinitConfVars /* (re)set config variables to default values */
+CODESTARTinitConfVars
+ cs.bEchoStdout = 0;
+ENDinitConfVars
+
BEGINcreateInstance
CODESTARTcreateInstance
pData->iWaitSeconds = 1;
@@ -287,7 +297,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
dbgprintf("invalid mode '%s', doing 'sleep 1 0' - fix your config\n", szBuf);
}
- pData->bEchoStdout = bEchoStdout;
+ pData->bEchoStdout = cs.bEchoStdout;
CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
(uchar*)"RSYSLOG_TraditionalForwardFormat"));
@@ -308,10 +318,11 @@ ENDqueryEtryPt
BEGINmodInit()
CODESTARTmodInit
+INITLegCnfVars
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionomtestingechostdout", 0, eCmdHdlrBinary, NULL,
- &bEchoStdout, STD_LOADABLE_MODULE_ID));
+ &cs.bEchoStdout, STD_LOADABLE_MODULE_ID));
/* we seed the random-number generator in any case... */
srand(time(NULL));
ENDmodInit
diff --git a/plugins/omudpspoof/omudpspoof.c b/plugins/omudpspoof/omudpspoof.c
index 48d7a68e..506fca77 100644
--- a/plugins/omudpspoof/omudpspoof.c
+++ b/plugins/omudpspoof/omudpspoof.c
@@ -83,6 +83,7 @@
MODULE_TYPE_OUTPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("omudpspoof")
/* internal structures
*/
@@ -105,14 +106,27 @@ typedef struct _instanceData {
#define DFLT_SOURCE_PORT_START 32000
#define DFLT_SOURCE_PORT_END 42000
-/* config data */
-static uchar *pszTplName = NULL; /* name of the default template to use */
-static uchar *pszSourceNameTemplate = NULL; /* name of the template containing the spoofing address */
-static uchar *pszTargetHost = NULL;
-static uchar *pszTargetPort = NULL;
-static int iCompressionLevel = 0; /* zlib compressionlevel, the usual values */
-static int iSourcePortStart = DFLT_SOURCE_PORT_START;
-static int iSourcePortEnd = DFLT_SOURCE_PORT_END;
+typedef struct configSettings_s {
+ uchar *pszTplName; /* name of the default template to use */
+ uchar *pszSourceNameTemplate; /* name of the template containing the spoofing address */
+ uchar *pszTargetHost;
+ uchar *pszTargetPort;
+ int iCompressionLevel; /* zlib compressionlevel, the usual values */
+ int iSourcePortStart;
+ int iSourcePortEnd;
+} configSettings_t;
+static configSettings_t cs;
+
+BEGINinitConfVars /* (re)set config variables to default values */
+CODESTARTinitConfVars
+ cs.pszTplName = NULL;
+ cs.pszSourceNameTemplate = NULL;
+ cs.pszTargetHost = NULL;
+ cs.pszTargetPort = NULL;
+ cs.iCompressionLevel = 0;
+ cs.iSourcePortStart = DFLT_SOURCE_PORT_START;
+ cs.iSourcePortEnd = DFLT_SOURCE_PORT_END;
+ENDinitConfVars
/* add some variables needed for libnet */
@@ -394,28 +408,28 @@ CODE_STD_STRING_REQUESTparseSelectorAct(2)
p += sizeof(":omudpspoof:") - 1; /* eat indicator sequence (-1 because of '\0'!) */
CHKiRet(createInstance(&pData));
- sourceTpl = (pszSourceNameTemplate == NULL) ? UCHAR_CONSTANT("RSYSLOG_omudpspoofDfltSourceTpl")
- : pszSourceNameTemplate;
+ sourceTpl = (cs.pszSourceNameTemplate == NULL) ? UCHAR_CONSTANT("RSYSLOG_omudpspoofDfltSourceTpl")
+ : cs.pszSourceNameTemplate;
- if(pszTargetHost == NULL) {
+ if(cs.pszTargetHost == NULL) {
errmsg.LogError(0, NO_ERRCODE, "No $ActionOMUDPSpoofTargetHost given, can not continue with this action.");
ABORT_FINALIZE(RS_RET_HOST_NOT_SPECIFIED);
}
/* fill instance properties */
- CHKmalloc(pData->host = ustrdup(pszTargetHost));
- if(pszTargetPort == NULL)
+ CHKmalloc(pData->host = ustrdup(cs.pszTargetHost));
+ if(cs.pszTargetPort == NULL)
pData->port = NULL;
else
- CHKmalloc(pData->port = ustrdup(pszTargetPort));
- CHKiRet(OMSRsetEntry(*ppOMSR, 1, ustrdup(sourceTpl), OMSR_NO_RQD_TPL_OPTS));
- pData->compressionLevel = iCompressionLevel;
- pData->sourcePort = pData->sourcePortStart = iSourcePortStart;
- pData->sourcePortEnd = iSourcePortEnd;
+ CHKmalloc(pData->port = ustrdup(cs.pszTargetPort));
+ CHKiRet(OMSRsetEntry(*ppOMSR, 1, ustrdup(cs.pszSourceNameTemplate), OMSR_NO_RQD_TPL_OPTS));
+ pData->compressionLevel = cs.iCompressionLevel;
+ pData->sourcePort = pData->sourcePortStart = cs.iSourcePortStart;
+ pData->sourcePortEnd = cs.iSourcePortEnd;
/* process template */
CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
- (pszTplName == NULL) ? (uchar*)"RSYSLOG_TraditionalForwardFormat" : pszTplName));
+ (cs.pszTplName == NULL) ? (uchar*)"RSYSLOG_TraditionalForwardFormat" : cs.pszTplName));
CODE_STD_FINALIZERparseSelectorAct
ENDparseSelectorAct
@@ -427,12 +441,12 @@ ENDparseSelectorAct
static void
freeConfigVars(void)
{
- free(pszTplName);
- pszTplName = NULL;
- free(pszTargetHost);
- pszTargetHost = NULL;
- free(pszTargetPort);
- pszTargetPort = NULL;
+ free(cs.pszTplName);
+ cs.pszTplName = NULL;
+ free(cs.pszTargetHost);
+ cs.pszTargetHost = NULL;
+ free(cs.pszTargetPort);
+ cs.pszTargetPort = NULL;
}
@@ -461,15 +475,16 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
{
freeConfigVars();
/* we now must reset all non-string values */
- iCompressionLevel = 0;
- iSourcePortStart = DFLT_SOURCE_PORT_START;
- iSourcePortEnd = DFLT_SOURCE_PORT_END;
+ cs.iCompressionLevel = 0;
+ cs.iSourcePortStart = DFLT_SOURCE_PORT_START;
+ cs.iSourcePortEnd = DFLT_SOURCE_PORT_END;
return RS_RET_OK;
}
BEGINmodInit()
CODESTARTmodInit
+INITLegCnfVars
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(glbl, CORE_COMPONENT));
@@ -489,13 +504,13 @@ CODEmodInit_QueryRegCFSLineHdlr
ABORT_FINALIZE(RS_RET_ERR_LIBNET_INIT);
}
- CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspoofdefaulttemplate", 0, eCmdHdlrGetWord, NULL, &pszTplName, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspoofsourcenametemplate", 0, eCmdHdlrGetWord, NULL, &pszSourceNameTemplate, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspooftargethost", 0, eCmdHdlrGetWord, NULL, &pszTargetHost, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspooftargetport", 0, eCmdHdlrGetWord, NULL, &pszTargetPort, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspoofsourceportstart", 0, eCmdHdlrInt, NULL, &iSourcePortStart, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspoofsourceportend", 0, eCmdHdlrInt, NULL, &iSourcePortEnd, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpcompressionlevel", 0, eCmdHdlrInt, NULL, &iCompressionLevel, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspoofdefaulttemplate", 0, eCmdHdlrGetWord, NULL, &cs.pszTplName, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspoofsourcenametemplate", 0, eCmdHdlrGetWord, NULL, &cs.pszSourceNameTemplate, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspooftargethost", 0, eCmdHdlrGetWord, NULL, &cs.pszTargetHost, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspooftargetport", 0, eCmdHdlrGetWord, NULL, &cs.pszTargetPort, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspoofsourceportstart", 0, eCmdHdlrInt, NULL, &cs.iSourcePortStart, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspoofsourceportend", 0, eCmdHdlrInt, NULL, &cs.iSourcePortEnd, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpcompressionlevel", 0, eCmdHdlrInt, NULL, &cs.iCompressionLevel, NULL));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
diff --git a/plugins/omuxsock/omuxsock.c b/plugins/omuxsock/omuxsock.c
index b29276f9..cf27c93c 100644
--- a/plugins/omuxsock/omuxsock.c
+++ b/plugins/omuxsock/omuxsock.c
@@ -46,6 +46,7 @@
MODULE_TYPE_OUTPUT
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("omuxsock")
/* internal structures
*/
@@ -64,8 +65,18 @@ typedef struct _instanceData {
} instanceData;
/* config data */
-static uchar *tplName = NULL; /* name of the default template to use */
-static uchar *sockName = NULL; /* name of the default template to use */
+typedef struct configSettings_s {
+ uchar *tplName; /* name of the default template to use */
+ uchar *sockName; /* name of the default template to use */
+} configSettings_t;
+static configSettings_t cs;
+
+BEGINinitConfVars /* (re)set config variables to default values */
+CODESTARTinitConfVars
+ cs.tplName = NULL;
+ cs.sockName = NULL;
+ENDinitConfVars
+
static rsRetVal doTryResume(instanceData *pData);
@@ -239,16 +250,16 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
/* check if a non-standard template is to be applied */
if(*(p-1) == ';')
--p;
- CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, 0, tplName == NULL ? UCHAR_CONSTANT("RSYSLOG_TraditionalForwardFormat")
- : tplName ));
+ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, 0, cs.tplName == NULL ? UCHAR_CONSTANT("RSYSLOG_TraditionalForwardFormat")
+ : cs.tplName ));
- if(sockName == NULL) {
+ if(cs.sockName == NULL) {
errmsg.LogError(0, RS_RET_NO_SOCK_CONFIGURED, "No output socket configured for omuxsock\n");
ABORT_FINALIZE(RS_RET_NO_SOCK_CONFIGURED);
}
- pData->sockName = sockName;
- sockName = NULL; /* pData is now owner and will fee it */
+ pData->sockName = cs.sockName;
+ cs.sockName = NULL; /* pData is now owner and will fee it */
CODE_STD_FINALIZERparseSelectorAct
ENDparseSelectorAct
@@ -260,10 +271,10 @@ ENDparseSelectorAct
static inline void
freeConfigVars(void)
{
- free(tplName);
- tplName = NULL;
- free(sockName);
- sockName = NULL;
+ free(cs.tplName);
+ cs.tplName = NULL;
+ free(cs.sockName);
+ cs.sockName = NULL;
}
@@ -295,13 +306,14 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
BEGINmodInit()
CODESTARTmodInit
+INITLegCnfVars
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(glbl, CORE_COMPONENT));
CHKiRet(objUse(errmsg, CORE_COMPONENT));
- CHKiRet(regCfSysLineHdlr((uchar *)"omuxsockdefaulttemplate", 0, eCmdHdlrGetWord, NULL, &tplName, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"omuxsocksocket", 0, eCmdHdlrGetWord, NULL, &sockName, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"omuxsockdefaulttemplate", 0, eCmdHdlrGetWord, NULL, &cs.tplName, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"omuxsocksocket", 0, eCmdHdlrGetWord, NULL, &cs.sockName, NULL));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
diff --git a/plugins/pmaixforwardedfrom/pmaixforwardedfrom.c b/plugins/pmaixforwardedfrom/pmaixforwardedfrom.c
index fe3e85fa..76198e9c 100644
--- a/plugins/pmaixforwardedfrom/pmaixforwardedfrom.c
+++ b/plugins/pmaixforwardedfrom/pmaixforwardedfrom.c
@@ -42,6 +42,7 @@
MODULE_TYPE_PARSER
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("pmaixforwardedfrom")
PARSER_NAME("rsyslog.aixforwardedfrom")
/* internal structures
diff --git a/plugins/pmcisconames/pmcisconames.c b/plugins/pmcisconames/pmcisconames.c
index 61688cbf..d8235752 100644
--- a/plugins/pmcisconames/pmcisconames.c
+++ b/plugins/pmcisconames/pmcisconames.c
@@ -42,6 +42,7 @@
MODULE_TYPE_PARSER
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("pmcisconames")
PARSER_NAME("rsyslog.cisconames")
/* internal structures
diff --git a/plugins/pmlastmsg/pmlastmsg.c b/plugins/pmlastmsg/pmlastmsg.c
index 259c5d41..a290c446 100644
--- a/plugins/pmlastmsg/pmlastmsg.c
+++ b/plugins/pmlastmsg/pmlastmsg.c
@@ -48,6 +48,7 @@
MODULE_TYPE_PARSER
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("pmlastmsg")
PARSER_NAME("rsyslog.lastline")
/* internal structures
diff --git a/plugins/pmrfc3164sd/pmrfc3164sd.c b/plugins/pmrfc3164sd/pmrfc3164sd.c
index 53204ece..de5805bc 100644
--- a/plugins/pmrfc3164sd/pmrfc3164sd.c
+++ b/plugins/pmrfc3164sd/pmrfc3164sd.c
@@ -46,6 +46,7 @@
MODULE_TYPE_PARSER
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("pmrfc3164sd")
PARSER_NAME("contrib.rfc3164sd")
/* internal structures
diff --git a/plugins/pmsnare/pmsnare.c b/plugins/pmsnare/pmsnare.c
index f3658d11..aca0271f 100644
--- a/plugins/pmsnare/pmsnare.c
+++ b/plugins/pmsnare/pmsnare.c
@@ -59,6 +59,7 @@
MODULE_TYPE_PARSER
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("pmsnare")
PARSER_NAME("rsyslog.snare")
/* internal structures
diff --git a/plugins/sm_cust_bindcdr/sm_cust_bindcdr.c b/plugins/sm_cust_bindcdr/sm_cust_bindcdr.c
index baad667e..acf1bfad 100644
--- a/plugins/sm_cust_bindcdr/sm_cust_bindcdr.c
+++ b/plugins/sm_cust_bindcdr/sm_cust_bindcdr.c
@@ -52,6 +52,7 @@
MODULE_TYPE_STRGEN
MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("sm_cust_bindcdr")
STRGEN_NAME("Custom_BindCDR,sql")
/* internal structures
diff --git a/rsyslog.service.in b/rsyslog.service.in
index c03c9004..23234cde 100644
--- a/rsyslog.service.in
+++ b/rsyslog.service.in
@@ -3,7 +3,7 @@ Description=System Logging Service
[Service]
ExecStartPre=/bin/systemctl stop systemd-kmsg-syslogd.service
-ExecStart=@sbindir@/rsyslogd -n -c5
+ExecStart=@sbindir@/rsyslogd -n -c6
Sockets=syslog.socket
StandardOutput=null
diff --git a/runtime/Makefile.am b/runtime/Makefile.am
index 09cb6b41..67e235a0 100644
--- a/runtime/Makefile.am
+++ b/runtime/Makefile.am
@@ -8,11 +8,14 @@ librsyslog_la_SOURCES = \
rsyslog.c \
rsyslog.h \
typedefs.h \
+ dnscache.c \
+ dnscache.h \
unicode-helper.h \
atomic.h \
batch.h \
syslogd-types.h \
module-template.h \
+ im-helper.h \
obj-types.h \
nsd.h \
glbl.h \
@@ -20,6 +23,8 @@ librsyslog_la_SOURCES = \
unlimited_select.h \
conf.c \
conf.h \
+ rsconf.c \
+ rsconf.h \
parser.h \
parser.c \
strgen.h \
@@ -44,18 +49,8 @@ librsyslog_la_SOURCES = \
obj.h \
modules.c \
modules.h \
- apc.c \
- apc.h \
statsobj.c \
statsobj.h \
- sync.c \
- sync.h \
- expr.c \
- expr.h \
- ctok.c \
- ctok.h \
- ctok_token.c \
- ctok_token.h \
stream.c \
stream.h \
var.c \
@@ -64,16 +59,6 @@ librsyslog_la_SOURCES = \
wtp.h \
wti.c \
wti.h \
- sysvar.c \
- sysvar.h \
- vm.c \
- vm.h \
- vmstk.c \
- vmstk.h \
- vmprg.c \
- vmprg.h \
- vmop.c \
- vmop.h \
queue.c \
queue.h \
ruleset.c \
@@ -110,12 +95,12 @@ librsyslog_la_SOURCES = \
# runtime or will no longer be needed. -- rgerhards, 2008-06-13
if WITH_MODDIRS
-librsyslog_la_CPPFLAGS = -DSD_EXPORT_SYMBOLS -D_PATH_MODDIR=\"$(pkglibdir)/:$(moddirs)\" $(PTHREADS_CFLAGS)
+librsyslog_la_CPPFLAGS = -DSD_EXPORT_SYMBOLS -D_PATH_MODDIR=\"$(pkglibdir)/:$(moddirs)\" $(PTHREADS_CFLAGS) $(LIBEE_CFLAGS) -I\$(top_srcdir)/tools
else
-librsyslog_la_CPPFLAGS = -DSD_EXPORT_SYMBOLS -D_PATH_MODDIR=\"$(pkglibdir)/\" -I$(top_srcdir) $(PTHREADS_CFLAGS)
+librsyslog_la_CPPFLAGS = -DSD_EXPORT_SYMBOLS -D_PATH_MODDIR=\"$(pkglibdir)/\" -I$(top_srcdir) $(PTHREADS_CFLAGS) $(LIBEE_CFLAGS) -I\$(top_srcdir)/tools -I\$(top_srcdir)/grammar
endif
#librsyslog_la_LDFLAGS = -module -avoid-version
-librsyslog_la_LIBADD = $(DL_LIBS) $(RT_LIBS)
+librsyslog_la_LIBADD = $(DL_LIBS) $(RT_LIBS) $(LIBEE_LIBS)
#
# regular expression support
diff --git a/runtime/apc.c b/runtime/apc.c
deleted file mode 100644
index 3c6b7ec4..00000000
--- a/runtime/apc.c
+++ /dev/null
@@ -1,402 +0,0 @@
-/* apc.c - asynchronous procedure call support
- *
- * An asynchronous procedure call (APC) is a procedure call (guess what) that is potentially run
- * asynchronously to its main thread. It can be scheduled to occur at a caller-provided time.
- * As long as the procedure has not been called, the APC entry may be modified by the caller
- * or deleted. It is the caller's purpose to make sure proper synchronization is in place.
- * The APC object only case about APC's own control structures (which *are* properly
- * guarded by synchronization primitives).
- *
- * Module begun 2009-06-15 by Rainer Gerhards
- *
- * Copyright 2009 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 <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <pthread.h>
-
-#include "rsyslog.h"
-#include "obj.h"
-#include "apc.h"
-#include "srUtils.h"
-#include "datetime.h"
-
-/* static data */
-DEFobjStaticHelpers
-DEFobjCurrIf(datetime)
-
-/* following is a used to implement a monotonically increasing id for the apcs. That
- * ID can be used to cancel an apc request. Note that the ID is generated with modulo
- * arithmetic, so at some point, it will wrap. Howerver, this happens at 2^32-1 at
- * earliest, so this is not considered a problem.
- */
-apc_id_t apcID = 0;
-
-/* private data structures */
-
-/* the apc list and its entries
- * This is a doubly-linked list as we need to be able to do inserts
- * and deletes right in the middle of the list. It is inspired by the
- * Unix callout mechanism.
- * Note that we support two generic caller-provided parameters as
- * experience shows that at most two are often used. This causes very
- * little overhead, but simplifies caller code in cases where exactly
- * two parameters are needed. We hope this is a useful optimizaton.
- * rgerhards, 2009-06-15
- */
-typedef struct apc_list_s {
- struct apc_list_s *pNext;
- struct apc_list_s *pPrev;
- apc_id_t id;
- apc_t *pApc; /* pointer to the APC object to be scheduled */
-} apc_list_t;
-
-apc_list_t *apcListRoot = NULL;
-apc_list_t *apcListTail = NULL;
-pthread_mutex_t listMutex; /* needs to be locked for all list operations */
-
-
-/* destructor for the apc object */
-BEGINobjDestruct(apc) /* be sure to specify the object type also in END and CODESTART macros! */
-CODESTARTobjDestruct(apc)
-ENDobjDestruct(apc)
-
-
-/* ------------------------------ APC list handling functions ------------------------------ */
-
-/* Function that handles changes to the list root. Most importantly, this function
- * needs to schedule a new timer. It is OK to call this function with an empty list.
- */
-static rsRetVal
-listRootChanged(void)
-{
- DEFiRet;
-
- if(apcListRoot == NULL)
- FINALIZE;
-
- // TODO: implement!
-
-finalize_it:
- RETiRet;
-}
-
-
-/* insert an apc entry into the APC list. The same entry MUST NOT already be present!
- */
-static rsRetVal
-insertApc(apc_t *pThis, apc_id_t *pID)
-{
- apc_list_t *pCurr;
- apc_list_t *pNew;
- DEFiRet;
-
- CHKmalloc(pNew = (apc_list_t*) calloc(1, sizeof(apc_list_t)));
- pNew->pApc = pThis;
- pNew->id = *pID = apcID++;
-dbgprintf("insert apc %p, id %ld\n", pThis, pNew->id);
-
- /* find right list location */
- if(apcListRoot == NULL) {
- /* no need to search, list is empty */
- apcListRoot = pNew;
- apcListTail = pNew;
- CHKiRet(listRootChanged());
- } else {
- for(pCurr = apcListRoot ; pCurr != NULL ; pCurr = pCurr->pNext) {
- if(pCurr->pApc->ttExec > pThis->ttExec)
- break;
- }
-
- if(pCurr == NULL) {
- /* insert at tail */
- pNew->pPrev = apcListTail;
- apcListTail->pNext = pNew;
- apcListTail = pNew;
- } else {
- if(pCurr == apcListRoot) {
- /* new first entry */
- pCurr->pPrev = pNew;
- pNew->pNext = pCurr;
- apcListRoot = pNew;
- CHKiRet(listRootChanged());
- } else {
- /* in the middle of the list */
- pCurr->pPrev = pNew;
- pNew->pNext = pCurr;
- }
- }
- }
-
-
-finalize_it:
- RETiRet;
-}
-
-
-/* Delete an apc entry from the APC list. It is OK if the entry is not found,
- * in this case we assume it already has been processed.
- */
-static rsRetVal
-deleteApc(apc_id_t id)
-{
- apc_list_t *pCurr;
- DEFiRet;
-
-dbgprintf("trying to delete apc %ld\n", id);
- for(pCurr = apcListRoot ; pCurr != NULL ; pCurr = pCurr->pNext) {
- if(pCurr->id == id) {
-RUNLOG_STR("apc id found, now deleting!\n");
- if(pCurr == apcListRoot) {
- apcListRoot = pCurr->pNext;
- CHKiRet(listRootChanged());
- } else {
- pCurr->pPrev->pNext = pCurr->pNext;
- }
- if(pCurr->pNext == NULL) {
- apcListTail = pCurr->pPrev;
- } else {
- pCurr->pNext->pPrev = pCurr->pPrev;
- }
- free(pCurr);
- pCurr = NULL;
- break;
- }
- }
-
-finalize_it:
- RETiRet;
-}
-
-
-/* unlist all elements up to the current timestamp. Return this as a seperate list
- * to the caller. Returns an empty (NULL ptr) list if there are no such elements.
- * The caller must handle that gracefully. The list is returned in the parameter.
- */
-static rsRetVal
-unlistCurrent(apc_list_t **ppList)
-{
- apc_list_t *pCurr;
- time_t tCurr;
- DEFiRet;
- assert(ppList != NULL);
-
- datetime.GetTime(&tCurr);
-
- if(apcListRoot == NULL || apcListRoot->pApc->ttExec > tCurr) {
- *ppList = NULL;
- FINALIZE;
- }
-
- *ppList = apcListRoot;
- /* now search up to which entry we need to execute */
- for(pCurr = apcListRoot ; pCurr != NULL && pCurr->pApc->ttExec <= tCurr ; pCurr = pCurr->pNext) {
- /*JUST SKIP TO LAST ELEMENT*/;
- }
-
- if(pCurr == NULL) {
- /* all elements can be unlisted */
- apcListRoot = NULL;
- apcListTail = NULL;
- } else {
- /* need to set a new root */
- pCurr->pPrev->pNext = NULL; /* terminate newly unlisted list */
- pCurr->pPrev = NULL; /* we are the new root */
- apcListRoot = pCurr;
- }
-
-finalize_it:
- RETiRet;
-}
-
-
-/* ------------------------------ END APC list handling functions ------------------------------ */
-
-
-/* execute all list elements that are currently scheduled for execution. We do this in two phases.
- * In the first phase, we look the list mutex and move everything from the head of the queue to
- * the current timestamp to a new to-be-executed list. Then we unlock the mutex and do the actual
- * exec (which may take some time).
- * Note that the caller is responsible for proper
- * caller-level synchronization. The caller may schedule another Apc, this module must
- * ensure that (and it does so by not locking the list mutex while we call the Apc).
- * Note: this function "consumes" the apc_t, so it is no longer existing after this
- * function returns.
- */
-// TODO make static and associated with our own pthread-based timer
-rsRetVal
-execScheduled(void)
-{
- apc_list_t *pExecList;
- apc_list_t *pCurr;
- apc_list_t *pNext;
- DEFiRet;
-
- d_pthread_mutex_lock(&listMutex);
- iRet = unlistCurrent(&pExecList);
- d_pthread_mutex_unlock(&listMutex);
- CHKiRet(iRet);
-
- if(pExecList != NULL) {
- DBGPRINTF("running apc scheduler - we have %s to execute\n",
- pExecList == NULL ? "nothing" : "something");
- }
-
- for(pCurr = pExecList ; pCurr != NULL ; pCurr = pNext) {
-dbgprintf("executing apc list entry %p, apc %p\n", pCurr, pCurr->pApc);
- pNext = pCurr->pNext;
- pCurr->pApc->pProc(pCurr->pApc->param1, pCurr->pApc->param2);
- apcDestruct(&pCurr->pApc);
- free(pCurr);
- }
-
-finalize_it:
- RETiRet;
-}
-
-
-/* Standard-Constructor
- */
-BEGINobjConstruct(apc) /* be sure to specify the object type also in END macro! */
-ENDobjConstruct(apc)
-
-
-/* ConstructionFinalizer
- * Note that we use a non-standard calling interface: pID returns the current APC
- * id. This is the only way to handle the situation without the need for extra
- * locking.
- * rgerhards, 2008-01-09
- */
-static rsRetVal
-apcConstructFinalize(apc_t *pThis, apc_id_t *pID)
-{
- DEFiRet;
- ISOBJ_TYPE_assert(pThis, apc);
- assert(pID != NULL);
- d_pthread_mutex_lock(&listMutex);
- insertApc(pThis, pID);
- d_pthread_mutex_unlock(&listMutex);
- RETiRet;
-}
-
-
-/* some set methods */
-static rsRetVal
-SetProcedure(apc_t *pThis, void (*pProc)(void*, void*))
-{
- ISOBJ_TYPE_assert(pThis, apc);
- pThis->pProc = pProc;
- return RS_RET_OK;
-}
-static rsRetVal
-SetParam1(apc_t *pThis, void *param1)
-{
- ISOBJ_TYPE_assert(pThis, apc);
- pThis->param1 = param1;
- return RS_RET_OK;
-}
-static rsRetVal
-SetParam2(apc_t *pThis, void *param2)
-{
- ISOBJ_TYPE_assert(pThis, apc);
- pThis->param1 = param2;
- return RS_RET_OK;
-}
-
-
-/* cancel an Apc request, ID is provided. It is OK if the ID can not be found, this may
- * happen if the Apc was executed in the mean time. So it is safe to call CancelApc() at
- * any time.
- */
-static rsRetVal
-CancelApc(apc_id_t id)
-{
- BEGINfunc
- d_pthread_mutex_lock(&listMutex);
- deleteApc(id);
- d_pthread_mutex_unlock(&listMutex);
- ENDfunc
- return RS_RET_OK;
-}
-
-
-/* debugprint for the apc object */
-BEGINobjDebugPrint(apc) /* be sure to specify the object type also in END and CODESTART macros! */
-CODESTARTobjDebugPrint(apc)
- dbgoprint((obj_t*) pThis, "APC module, currently no state info available\n");
-ENDobjDebugPrint(apc)
-
-
-/* queryInterface function
- */
-BEGINobjQueryInterface(apc)
-CODESTARTobjQueryInterface(apc)
- if(pIf->ifVersion != apcCURR_IF_VERSION) { /* check for current version, increment on each change */
- ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED);
- }
-
- /* ok, we have the right interface, so let's fill it
- * Please note that we may also do some backwards-compatibility
- * work here (if we can support an older interface version - that,
- * of course, also affects the "if" above).
- */
- pIf->Construct = apcConstruct;
- pIf->ConstructFinalize = apcConstructFinalize;
- pIf->Destruct = apcDestruct;
- pIf->DebugPrint = apcDebugPrint;
- pIf->CancelApc = CancelApc;
- pIf->SetProcedure = SetProcedure;
- pIf->SetParam1 = SetParam1;
- pIf->SetParam2 = SetParam2;
-finalize_it:
-ENDobjQueryInterface(apc)
-
-
-/* Exit the apc class.
- * rgerhards, 2009-04-06
- */
-BEGINObjClassExit(apc, OBJ_IS_CORE_MODULE) /* class, version */
- objRelease(datetime, CORE_COMPONENT);
- pthread_mutex_destroy(&listMutex);
-ENDObjClassExit(apc)
-
-
-/* Initialize the apc class. Must be called as the very first method
- * before anything else is called inside this class.
- * rgerhards, 2008-02-19
- */
-BEGINObjClassInit(apc, 1, OBJ_IS_CORE_MODULE) /* class, version */
- /* request objects we use */
- CHKiRet(objUse(datetime, CORE_COMPONENT));
-
- /* set our own handlers */
- OBJSetMethodHandler(objMethod_DEBUGPRINT, apcDebugPrint);
- OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, apcConstructFinalize);
-
- /* do other initializations */
- pthread_mutex_init(&listMutex, NULL);
-ENDObjClassInit(apc)
-
-/* vi:set ai:
- */
diff --git a/runtime/apc.h b/runtime/apc.h
deleted file mode 100644
index 7c679b97..00000000
--- a/runtime/apc.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/* The apc object.
- *
- * See apc.c for more information.
- *
- * Copyright 2009 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.
- */
-#ifndef INCLUDED_APC_H
-#define INCLUDED_APC_H
-
-/* the apc object */
-typedef struct apc_s {
- BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
- time_t ttExec; /* when to call procedure (so far seconds...) */
- void (*pProc)(void*, void*); /* which procedure to call */
- void *param1; /* user-supplied parameters */
- void *param2; /* user-supplied parameters */
-} apc_t;
-
-typedef unsigned long apc_id_t; /* monotonically incrementing apc ID */
-
-/* interfaces */
-BEGINinterface(apc) /* name must also be changed in ENDinterface macro! */
- INTERFACEObjDebugPrint(apc);
- rsRetVal (*Construct)(apc_t **ppThis);
- rsRetVal (*ConstructFinalize)(apc_t *pThis, apc_id_t *);
- rsRetVal (*Destruct)(apc_t **ppThis);
- rsRetVal (*SetProcedure)(apc_t *pThis, void (*pProc)(void*, void*));
- rsRetVal (*SetParam1)(apc_t *pThis, void *);
- rsRetVal (*SetParam2)(apc_t *pThis, void *);
- rsRetVal (*CancelApc)(apc_id_t);
-ENDinterface(apc)
-#define apcCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
-
-
-/* prototypes */
-PROTOTYPEObj(apc);
-
-#endif /* #ifndef INCLUDED_APC_H */
diff --git a/runtime/cfsysline.c b/runtime/cfsysline.c
index 4997e0fb..7814e86a 100644
--- a/runtime/cfsysline.c
+++ b/runtime/cfsysline.c
@@ -37,6 +37,7 @@
#include "cfsysline.h"
#include "obj.h"
+#include "conf.h"
#include "errmsg.h"
#include "srUtils.h"
#include "unicode-helper.h"
@@ -541,7 +542,8 @@ finalize_it:
* time (TODO). -- rgerhards, 2008-02-14
*/
static rsRetVal
-doSyslogName(uchar **pp, rsRetVal (*pSetHdlr)(void*, int), void *pVal, syslogName_t *pNameTable)
+doSyslogName(uchar **pp, rsRetVal (*pSetHdlr)(void*, int),
+ void *pVal, syslogName_t *pNameTable)
{
DEFiRet;
cstr_t *pStrB;
@@ -583,6 +585,15 @@ doFacility(uchar **pp, rsRetVal (*pSetHdlr)(void*, int), void *pVal)
}
+static rsRetVal
+doGoneAway(__attribute__((unused)) uchar **pp,
+ __attribute__((unused)) rsRetVal (*pSetHdlr)(void*, int),
+ __attribute__((unused)) void *pVal)
+{
+ errmsg.LogError(0, RS_RET_CMD_GONE_AWAY, "config directive is no longer supported -- ignored");
+ return RS_RET_CMD_GONE_AWAY;
+}
+
/* Implements the severity syntax.
* rgerhards, 2008-02-14
*/
@@ -712,6 +723,9 @@ static rsRetVal cslchCallHdlr(cslCmdHdlr_t *pThis, uchar **ppConfLine)
case eCmdHdlrGetWord:
pHdlr = doGetWord;
break;
+ case eCmdHdlrGoneAway:
+ pHdlr = doGoneAway;
+ break;
default:
iRet = RS_RET_NOT_IMPLEMENTED;
goto finalize_it;
@@ -804,8 +818,7 @@ finalize_it:
* caller does not need to take care of that. The caller must, however,
* free pCmdName if he allocated it dynamically! -- rgerhards, 2007-08-09
*/
-rsRetVal regCfSysLineHdlr(uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData,
- void *pOwnerCookie)
+rsRetVal regCfSysLineHdlr(uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData, void *pOwnerCookie)
{
DEFiRet;
cslCmd_t *pThis;
@@ -910,6 +923,7 @@ rsRetVal processCfSysLineCommand(uchar *pCmdName, uchar **p)
uchar *pHdlrP; /* the handler's private p (else we could only call one handler) */
int bWasOnceOK; /* was the result of an handler at least once RS_RET_OK? */
uchar *pOKp = NULL; /* returned conf line pointer when it was OK */
+ int bHadScopingErr = 0; /* set if a scoping error occured */
iRet = llFind(&llCmdList, (void *) pCmdName, (void*) &pCmd);
@@ -927,7 +941,7 @@ rsRetVal processCfSysLineCommand(uchar *pCmdName, uchar **p)
* reason is that handlers are independent. An error in one
* handler does not necessarily mean that another one will
* fail, too. Later, we might add a config variable to control
- * this behaviour (but I am not sure if that is rally
+ * this behaviour (but I am not sure if that is really
* necessary). -- rgerhards, 2007-07-31
*/
pHdlrP = *p;
@@ -945,6 +959,10 @@ rsRetVal processCfSysLineCommand(uchar *pCmdName, uchar **p)
if(iRetLL != RS_RET_END_OF_LINKEDLIST)
iRet = iRetLL;
+ if(bHadScopingErr) {
+ iRet = RS_RET_CONF_INVLD_SCOPE;
+ }
+
finalize_it:
RETiRet;
}
diff --git a/runtime/cfsysline.h b/runtime/cfsysline.h
index 53f35f6a..2768243f 100644
--- a/runtime/cfsysline.h
+++ b/runtime/cfsysline.h
@@ -24,28 +24,12 @@
#include "linkedlist.h"
-/* types of configuration handlers
- */
-typedef enum cslCmdHdlrType {
- eCmdHdlrInvalid = 0, /* invalid handler type - indicates a coding error */
- eCmdHdlrCustomHandler, /* custom handler, just call handler function */
- eCmdHdlrUID,
- eCmdHdlrGID,
- eCmdHdlrBinary,
- eCmdHdlrFileCreateMode,
- eCmdHdlrInt,
- eCmdHdlrSize,
- eCmdHdlrGetChar,
- eCmdHdlrFacility,
- eCmdHdlrSeverity,
- eCmdHdlrGetWord
-} ecslCmdHdrlType;
-
/* this is a single entry for a parse routine. It describes exactly
* one entry point/handler.
* The short name is cslch (Configfile SysLine CommandHandler)
*/
struct cslCmdHdlr_s { /* config file sysline parse entry */
+ ecslConfObjType __attribute__((deprecated)) eConfObjType; /* which config object is this for? */
ecslCmdHdrlType eType; /* which type of handler is this? */
rsRetVal (*cslCmdHdlr)(); /* function pointer to use with handler (params depending on eType) */
void *pData; /* user-supplied data pointer */
diff --git a/runtime/conf.c b/runtime/conf.c
index cbba1790..f5e064ff 100644
--- a/runtime/conf.c
+++ b/runtime/conf.c
@@ -48,7 +48,6 @@
#endif
#include "rsyslog.h"
-#include "../tools/syslogd.h" /* TODO: this must be removed! */
#include "dirty.h"
#include "parse.h"
#include "action.h"
@@ -62,11 +61,9 @@
#include "srUtils.h"
#include "errmsg.h"
#include "net.h"
-#include "expr.h"
-#include "ctok.h"
-#include "ctok_token.h"
#include "rule.h"
#include "ruleset.h"
+#include "rsconf.h"
#include "unicode-helper.h"
#ifdef OS_SOLARIS
@@ -74,22 +71,19 @@
#endif
/* forward definitions */
-static rsRetVal cfline(uchar *line, rule_t **pfCurr);
-static rsRetVal processConfFile(uchar *pConfFile);
+//static rsRetVal cfline(rsconf_t *conf, uchar *line, rule_t **pfCurr);
/* static data */
DEFobjStaticHelpers
-DEFobjCurrIf(expr)
-DEFobjCurrIf(ctok)
-DEFobjCurrIf(ctok_token)
DEFobjCurrIf(module)
DEFobjCurrIf(errmsg)
DEFobjCurrIf(net)
DEFobjCurrIf(rule)
DEFobjCurrIf(ruleset)
-static int iNbrActions = 0; /* number of currently defined actions */
+int bConfStrictScoping = 0; /* force strict scoping during config processing? */
+
/* The following module-global variables are used for building
* tag and host selector lines during startup and config reload.
@@ -98,153 +92,12 @@ static int iNbrActions = 0; /* number of currently defined actions */
* be run in a single thread anyways. So there can be no race conditions.
* rgerhards 2005-10-18
*/
-static EHostnameCmpMode eDfltHostnameCmpMode = HN_NO_COMP;
-static cstr_t *pDfltHostnameCmp = NULL;
-static cstr_t *pDfltProgNameCmp = NULL;
-
+EHostnameCmpMode eDfltHostnameCmpMode = HN_NO_COMP;
+cstr_t *pDfltHostnameCmp = NULL;
+cstr_t *pDfltProgNameCmp = NULL;
-/* process a directory and include all of its files into
- * the current config file. There is no specific order of inclusion,
- * files are included in the order they are read from the directory.
- * The caller must have make sure that the provided parameter is
- * indeed a directory.
- * rgerhards, 2007-08-01
- */
-static rsRetVal doIncludeDirectory(uchar *pDirName)
-{
- DEFiRet;
- int iEntriesDone = 0;
- DIR *pDir;
- union {
- struct dirent d;
- char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
- } u;
- struct dirent *res;
- size_t iDirNameLen;
- size_t iFileNameLen;
- uchar szFullFileName[MAXFNAME];
-
- ASSERT(pDirName != NULL);
-
- if((pDir = opendir((char*) pDirName)) == NULL) {
- errmsg.LogError(errno, RS_RET_FOPEN_FAILURE, "error opening include directory");
- ABORT_FINALIZE(RS_RET_FOPEN_FAILURE);
- }
-
- /* prepare file name buffer */
- iDirNameLen = strlen((char*) pDirName);
- memcpy(szFullFileName, pDirName, iDirNameLen);
-
- /* now read the directory */
- iEntriesDone = 0;
- while(readdir_r(pDir, &u.d, &res) == 0) {
- if(res == NULL)
- break; /* this also indicates end of directory */
-# ifdef DT_REG
- /* TODO: find an alternate way to checking for special files if this is
- * not defined. This is currently a known problem on HP UX, but the work-
- * around is simple: do not create special files in that directory. So
- * fixing this is actually not the most important thing on earth...
- * rgerhards, 2008-03-04
- */
- if(res->d_type != DT_REG)
- continue; /* we are not interested in special files */
-# endif
- if(res->d_name[0] == '.')
- continue; /* these files we are also not interested in */
- ++iEntriesDone;
- /* construct filename */
- iFileNameLen = strlen(res->d_name);
- if (iFileNameLen > NAME_MAX)
- iFileNameLen = NAME_MAX;
- memcpy(szFullFileName + iDirNameLen, res->d_name, iFileNameLen);
- *(szFullFileName + iDirNameLen + iFileNameLen) = '\0';
- dbgprintf("including file '%s'\n", szFullFileName);
- processConfFile(szFullFileName);
- /* we deliberately ignore the iRet of processConfFile() - this is because
- * failure to process one file does not mean all files will fail. By ignoring,
- * we retry with the next file, which is the best thing we can do. -- rgerhards, 2007-08-01
- */
- }
-
- if(iEntriesDone == 0) {
- /* I just make it a debug output, because I can think of a lot of cases where it
- * makes sense not to have any files. E.g. a system maintainer may place a $Include
- * into the config file just in case, when additional modules be installed. When none
- * are installed, the directory will be empty, which is fine. -- rgerhards 2007-08-01
- */
- dbgprintf("warning: the include directory contained no files - this may be ok.\n");
- }
-
-finalize_it:
- if(pDir != NULL)
- closedir(pDir);
- RETiRet;
-}
-
-
-/* process a $include config line. That type of line requires
- * inclusion of another file.
- * rgerhards, 2007-08-01
- */
-rsRetVal
-doIncludeLine(uchar **pp, __attribute__((unused)) void* pVal)
-{
- DEFiRet;
- char pattern[MAXFNAME];
- uchar *cfgFile;
- glob_t cfgFiles;
- int result;
- size_t i = 0;
- struct stat fileInfo;
-
- ASSERT(pp != NULL);
- ASSERT(*pp != NULL);
-
- if(getSubString(pp, (char*) pattern, sizeof(pattern) / sizeof(char), ' ') != 0) {
- errmsg.LogError(0, RS_RET_NOT_FOUND, "could not parse config file name");
- ABORT_FINALIZE(RS_RET_NOT_FOUND);
- }
-
- /* Use GLOB_MARK to append a trailing slash for directories.
- * Required by doIncludeDirectory().
- */
- result = glob(pattern, GLOB_MARK, NULL, &cfgFiles);
- if(result == GLOB_NOSPACE || result == GLOB_ABORTED) {
- 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);
- }
-
- for(i = 0; i < cfgFiles.gl_pathc; i++) {
- cfgFile = (uchar*) cfgFiles.gl_pathv[i];
-
- if(stat((char*) cfgFile, &fileInfo) != 0)
- continue; /* continue with the next file if we can't stat() the file */
-
- if(S_ISREG(fileInfo.st_mode)) { /* config file */
- dbgprintf("requested to include config file '%s'\n", cfgFile);
- iRet = processConfFile(cfgFile);
- } else if(S_ISDIR(fileInfo.st_mode)) { /* config directory */
- dbgprintf("requested to include directory '%s'\n", cfgFile);
- iRet = doIncludeDirectory(cfgFile);
- } else { /* TODO: shall we handle symlinks or not? */
- dbgprintf("warning: unable to process IncludeConfig directive '%s'\n", cfgFile);
- }
- }
-
- globfree(&cfgFiles);
-
-finalize_it:
- RETiRet;
-}
-
-
-/* process a $ModLoad config line.
- */
+/* process a $ModLoad config line. */
rsRetVal
doModLoad(uchar **pp, __attribute__((unused)) void* pVal)
{
@@ -274,7 +127,7 @@ doModLoad(uchar **pp, __attribute__((unused)) void* pVal)
else
pModName = szName;
- CHKiRet(module.Load(pModName));
+ CHKiRet(module.Load(pModName, 1));
finalize_it:
RETiRet;
@@ -318,7 +171,7 @@ doNameLine(uchar **pp, void* pVal)
switch(eDir) {
case DIR_TEMPLATE:
- tplAddLine(szName, &p);
+ tplAddLine(loadConf, szName, &p);
break;
case DIR_OUTCHANNEL:
ochAddLine(szName, &p);
@@ -384,125 +237,6 @@ finalize_it:
}
-
-
-/* process a configuration file
- * started with code from init() by rgerhards on 2007-07-31
- */
-static rsRetVal
-processConfFile(uchar *pConfFile)
-{
- int iLnNbr = 0;
- FILE *cf;
- rule_t *pCurrRule = NULL;
- uchar *p;
- uchar cbuf[CFGLNSIZ];
- uchar *cline;
- int i;
- rsRetVal localRet;
- int bHadAnError = 0;
- uchar *pszOrgLine = NULL;
- size_t lenLine;
- DEFiRet;
- ASSERT(pConfFile != NULL);
-
- if((cf = fopen((char*)pConfFile, "r")) == NULL) {
- ABORT_FINALIZE(RS_RET_FOPEN_FAILURE);
- }
-
- /* Now process the file.
- */
- cline = cbuf;
- while (fgets((char*)cline, sizeof(cbuf) - (cline - cbuf), cf) != NULL) {
- ++iLnNbr;
- /* drop LF - TODO: make it better, replace fgets(), but its clean as it is */
- lenLine = ustrlen(cline);
- if(cline[lenLine-1] == '\n') {
- cline[lenLine-1] = '\0';
- }
- free(pszOrgLine);
- pszOrgLine = ustrdup(cline); /* save if needed for errmsg, NULL ptr is OK */
- /* check for end-of-section, comments, strip off trailing
- * spaces and newline character.
- */
- p = cline;
- skipWhiteSpace(&p);
- if (*p == '\0' || *p == '#')
- continue;
-
- /* we now need to copy the characters to the begin of line. As this overlaps,
- * we can not use strcpy(). -- rgerhards, 2008-03-20
- * TODO: review the code at whole - this is highly suspect (but will go away
- * once we do the rest of RainerScript).
- */
- for( i = 0 ; p[i] != '\0' ; ++i) {
- cline[i] = p[i];
- }
- cline[i] = '\0';
-
- for (p = (uchar*) strchr((char*)cline, '\0'); isspace((int) *--p);)
- /*EMPTY*/;
- if (*p == '\\') {
- if ((p - cbuf) > CFGLNSIZ - 30) {
- /* Oops the buffer is full - what now? */
- cline = cbuf;
- } else {
- *p = 0;
- cline = p;
- continue;
- }
- } else
- cline = cbuf;
- *++p = '\0'; /* TODO: check this */
-
- /* we now have the complete line, and are positioned at the first non-whitespace
- * character. So let's process it
- */
- if((localRet = cfline(cbuf, &pCurrRule)) != RS_RET_OK) {
- /* we log a message, but otherwise ignore the error. After all, the next
- * line can be correct. -- rgerhards, 2007-08-02
- */
- uchar szErrLoc[MAXFNAME + 64];
- if(localRet != RS_RET_OK_WARN) {
- dbgprintf("config line NOT successfully processed\n");
- bHadAnError = 1;
- }
- snprintf((char*)szErrLoc, sizeof(szErrLoc) / sizeof(uchar),
- "%s, line %d", pConfFile, iLnNbr);
- errmsg.LogError(0, NO_ERRCODE, "the last %s occured in %s:\"%s\"",
- (localRet == RS_RET_OK_WARN) ? "warning" : "error",
- (char*)szErrLoc, (char*)pszOrgLine);
- }
- }
-
- /* we probably have one selector left to be added - so let's do that now */
- if(pCurrRule != NULL) {
- CHKiRet(ruleset.AddRule(rule.GetAssRuleset(pCurrRule), &pCurrRule));
- }
-
- /* close the configuration file */
- fclose(cf);
-
-finalize_it:
- if(iRet != RS_RET_OK) {
- char errStr[1024];
- if(pCurrRule != NULL)
- rule.Destruct(&pCurrRule);
-
- rs_strerror_r(errno, errStr, sizeof(errStr));
- dbgprintf("error %d processing config file '%s'; os error (if any): %s\n",
- iRet, pConfFile, errStr);
- }
-
- free(pszOrgLine);
-
- if(bHadAnError && (iRet == RS_RET_OK)) { /* a bit dirty, enhance in future releases */
- iRet = RS_RET_NONFATAL_CONFIG_ERR;
- }
- RETiRet;
-}
-
-
/* Helper to cfline() and its helpers. Parses a template name
* from an "action" line. Must be called with the Line pointer
* pointing to the first character after the semicolon.
@@ -602,7 +336,7 @@ cflineParseFileName(uchar* p, uchar *pFileName, omodStringRequest_t *pOMSR, int
* rgerhards 2005-09-15
*/
/* GPLv3 - stems back to sysklogd */
-static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register rule_t *pRule)
+rsRetVal cflineProcessTradPRIFilter(uchar **pline, register rule_t *pRule)
{
uchar *p;
register uchar *q;
@@ -619,7 +353,7 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register rule_t *pRule
ASSERT(*pline != NULL);
ISOBJ_TYPE_assert(pRule, rule);
- dbgprintf(" - traditional PRI filter\n");
+ dbgprintf(" - traditional PRI filter '%s'\n", *pline);
errno = 0; /* keep strerror_r() stuff out of logerror messages */
pRule->f_filter_type = FILTER_PRI;
@@ -632,7 +366,6 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register rule_t *pRule
/* scan through the list of selectors */
for (p = *pline; *p && *p != '\t' && *p != ' ';) {
-
/* find the end of this facility name list */
for (q = p; *q && *q != '\t' && *q++ != '.'; )
continue;
@@ -643,8 +376,10 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register rule_t *pRule
*bp = '\0';
/* skip cruft */
- while (strchr(",;", *q))
- q++;
+ if(*q) {
+ while (strchr(",;", *q))
+ q++;
+ }
/* decode priority name */
if ( *buf == '!' ) {
@@ -755,81 +490,12 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register rule_t *pRule
}
-/* Helper to cfline(). This function processes an "if" type of filter,
- * what essentially means it parses an expression. As usual,
- * It processes the line up to the beginning of the action part.
- * A pointer to that beginnig is passed back to the caller.
- * rgerhards 2008-01-19
- */
-static rsRetVal cflineProcessIfFilter(uchar **pline, register rule_t *f)
-{
- DEFiRet;
- ctok_t *tok;
- ctok_token_t *pToken;
-
- ASSERT(pline != NULL);
- ASSERT(*pline != NULL);
- ASSERT(f != NULL);
-
- dbgprintf(" - general expression-based filter\n");
- errno = 0; /* keep strerror_r() stuff out of logerror messages */
-
- f->f_filter_type = FILTER_EXPR;
-
- /* if we come to over here, pline starts with "if ". We just skip that part. */
- (*pline) += 3;
-
- /* we first need a tokenizer... */
- CHKiRet(ctok.Construct(&tok));
- CHKiRet(ctok.Setpp(tok, *pline));
- CHKiRet(ctok.ConstructFinalize(tok));
-
- /* now construct our expression */
- CHKiRet(expr.Construct(&f->f_filterData.f_expr));
- CHKiRet(expr.ConstructFinalize(f->f_filterData.f_expr));
-
- /* ready to go... */
- CHKiRet(expr.Parse(f->f_filterData.f_expr, tok));
-
- /* we now need to parse off the "then" - and note an error if it is
- * missing...
- */
- CHKiRet(ctok.GetToken(tok, &pToken));
- if(pToken->tok != ctok_THEN) {
- ctok_token.Destruct(&pToken);
- ABORT_FINALIZE(RS_RET_SYNTAX_ERROR);
- }
-
- ctok_token.Destruct(&pToken); /* no longer needed */
-
- /* we are done, so we now need to restore things */
- CHKiRet(ctok.Getpp(tok, pline));
- CHKiRet(ctok.Destruct(&tok));
-
- /* debug support - print vmprg after construction (uncomment to use) */
- /* vmprgDebugPrint(f->f_filterData.f_expr->pVmprg); */
-
- /* we now need to skip whitespace to the action part, else we confuse
- * the legacy rsyslog conf parser. -- rgerhards, 2008-02-25
- */
- while(isspace(**pline))
- ++(*pline);
-
-finalize_it:
- if(iRet == RS_RET_SYNTAX_ERROR) {
- errmsg.LogError(0, RS_RET_SYNTAX_ERROR, "syntax error in expression");
- }
-
- RETiRet;
-}
-
-
/* Helper to cfline(). This function takes the filter part of a property
* based filter and decodes it. It processes the line up to the beginning
* of the action part. A pointer to that beginnig is passed back to the caller.
* rgerhards 2005-09-15
*/
-static rsRetVal cflineProcessPropFilter(uchar **pline, register rule_t *f)
+rsRetVal cflineProcessPropFilter(uchar **pline, register rule_t *f)
{
rsParsObj *pPars;
cstr_t *pCSCompOp;
@@ -841,7 +507,7 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register rule_t *f)
ASSERT(*pline != NULL);
ASSERT(f != NULL);
- dbgprintf(" - property-based filter\n");
+ dbgprintf(" - property-based filter '%s'\n", *pline);
errno = 0; /* keep strerror_r() stuff out of logerror messages */
f->f_filter_type = FILTER_PROP;
@@ -865,6 +531,14 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register rule_t *f)
rsParsDestruct(pPars);
return(iRet);
}
+ if(f->f_filterData.prop.propID == PROP_CEE) {
+ /* in CEE case, we need to preserve the actual property name */
+ if((f->f_filterData.prop.propName =
+ es_newStrFromBuf((char*)cstrGetSzStrNoNULL(pCSPropName)+2, cstrLen(pCSPropName)-2)) == NULL) {
+ cstrDestruct(&pCSPropName);
+ return(RS_RET_ERR);
+ }
+ }
cstrDestruct(&pCSPropName);
/* read operation */
@@ -897,6 +571,8 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register rule_t *f)
f->f_filterData.prop.operation = FIOP_CONTAINS;
} else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (uchar*) "isequal", 7)) {
f->f_filterData.prop.operation = FIOP_ISEQUAL;
+ } else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (uchar*) "isempty", 7)) {
+ f->f_filterData.prop.operation = FIOP_ISEMPTY;
} else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (uchar*) "startswith", 10)) {
f->f_filterData.prop.operation = FIOP_STARTSWITH;
} else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (unsigned char*) "regex", 5)) {
@@ -909,12 +585,14 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register rule_t *f)
}
rsCStrDestruct(&pCSCompOp); /* no longer needed */
- /* read compare value */
- iRet = parsQuotedCStr(pPars, &f->f_filterData.prop.pCSCompValue);
- if(iRet != RS_RET_OK) {
- errmsg.LogError(0, iRet, "error %d compare value property - ignoring selector", iRet);
- rsParsDestruct(pPars);
- return(iRet);
+ if(f->f_filterData.prop.operation != FIOP_ISEMPTY) {
+ /* read compare value */
+ iRet = parsQuotedCStr(pPars, &f->f_filterData.prop.pCSCompValue);
+ if(iRet != RS_RET_OK) {
+ errmsg.LogError(0, iRet, "error %d compare value property - ignoring selector", iRet);
+ rsParsDestruct(pPars);
+ return(iRet);
+ }
}
/* skip to action part */
@@ -937,7 +615,7 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register rule_t *f)
* from the config file ("+/-hostname"). It stores it for further reference.
* rgerhards 2005-10-19
*/
-static rsRetVal cflineProcessHostSelector(uchar **pline)
+rsRetVal cflineProcessHostSelector(uchar **pline)
{
DEFiRet;
@@ -987,7 +665,7 @@ finalize_it:
* from the config file ("!tagname"). It stores it for further reference.
* rgerhards 2005-10-18
*/
-static rsRetVal cflineProcessTagSelector(uchar **pline)
+rsRetVal cflineProcessTagSelector(uchar **pline)
{
DEFiRet;
@@ -1025,75 +703,32 @@ finalize_it:
}
-/* read the filter part of a configuration line and store the filter
- * in the supplied rule_t
- * rgerhards, 2007-08-01
- */
-static rsRetVal cflineDoFilter(uchar **pp, rule_t *f)
-{
- DEFiRet;
-
- ASSERT(pp != NULL);
- ISOBJ_TYPE_assert(f, rule);
-
- /* check which filter we need to pull... */
- switch(**pp) {
- case ':':
- CHKiRet(cflineProcessPropFilter(pp, f));
- break;
- case 'i': /* "if" filter? */
- if(*(*pp+1) && (*(*pp+1) == 'f') && isspace(*(*pp+2))) {
- CHKiRet(cflineProcessIfFilter(pp, f));
- break;
- }
- /*FALLTHROUGH*/
- default:
- CHKiRet(cflineProcessTradPRIFilter(pp, f));
- break;
- }
-
- /* we now check if there are some global (BSD-style) filter conditions
- * and, if so, we copy them over. rgerhards, 2005-10-18
- */
- if(pDfltProgNameCmp != NULL) {
-RUNLOG_STR("dflt ProgNameCmp != NULL, setting opCSProgNameComp");
- CHKiRet(rsCStrConstructFromCStr(&(f->pCSProgNameComp), pDfltProgNameCmp));
- }
-
- if(eDfltHostnameCmpMode != HN_NO_COMP) {
- f->eHostnameCmpMode = eDfltHostnameCmpMode;
- CHKiRet(rsCStrConstructFromCStr(&(f->pCSHostnameComp), pDfltHostnameCmp));
- }
-
-finalize_it:
- RETiRet;
-}
-
-
/* process the action part of a selector line
* rgerhards, 2007-08-01
*/
-static rsRetVal cflineDoAction(uchar **p, action_t **ppAction)
+rsRetVal cflineDoAction(rsconf_t *conf, uchar **p, action_t **ppAction)
{
- DEFiRet;
modInfo_t *pMod;
+ cfgmodules_etry_t *node;
omodStringRequest_t *pOMSR;
+ int bHadWarning = 0;
action_t *pAction = NULL;
void *pModData;
- int bHadWarning = 0;
+ DEFiRet;
ASSERT(p != NULL);
ASSERT(ppAction != NULL);
/* loop through all modules and see if one picks up the line */
- pMod = module.GetNxtType(NULL, eMOD_OUT);
- /* Note: clang static analyzer reports that pMod mybe == NULL. However, this is
+ node = module.GetNxtCnfType(conf, NULL, eMOD_OUT);
+ /* Note: clang static analyzer reports that node maybe == NULL. However, this is
* not possible, because we have the built-in output modules which are always
* present. Anyhow, we guard this by an assert. -- rgerhards, 2010-12-16
*/
- assert(pMod != NULL);
- while(pMod != NULL) {
+ assert(node != NULL);
+ while(node != NULL) {
pOMSR = NULL;
+ pMod = node->pMod;
iRet = pMod->mod.om.parseSelectorAct(p, &pModData, &pOMSR);
dbgprintf("tried selector action for %s: %d\n", module.GetName(pMod), iRet);
if(iRet == RS_RET_OK_WARN) {
@@ -1101,16 +736,20 @@ static rsRetVal cflineDoAction(uchar **p, action_t **ppAction)
iRet = RS_RET_OK;
}
if(iRet == RS_RET_OK || iRet == RS_RET_SUSPENDED) {
- if((iRet = addAction(&pAction, pMod, pModData, pOMSR, (iRet == RS_RET_SUSPENDED)? 1 : 0)) == RS_RET_OK) {
+ /* advance our config parser state: we now only accept an $End as valid,
+ * no more action statments.
+ */
+ if((iRet = addAction(&pAction, pMod, pModData, pOMSR, NULL, NULL,
+ (iRet == RS_RET_SUSPENDED)? 1 : 0)) == RS_RET_OK) {
/* now check if the module is compatible with select features */
if(pMod->isCompatibleWithFeature(sFEATURERepeatedMsgReduction) == RS_RET_OK)
- pAction->f_ReduceRepeated = bReduceRepeatMsgs;
+ 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 */
- iNbrActions++; /* one more active action! */
+ conf->actions.nbrActions++; /* one more active action! */
}
break;
}
@@ -1124,7 +763,7 @@ static rsRetVal cflineDoAction(uchar **p, action_t **ppAction)
dbgprintf("error %d parsing config line\n", (int) iRet);
break;
}
- pMod = module.GetNxtType(pMod, eMOD_OUT);
+ node = module.GetNxtCnfType(conf, node, eMOD_OUT);
}
*ppAction = pAction;
@@ -1134,103 +773,15 @@ static rsRetVal cflineDoAction(uchar **p, action_t **ppAction)
}
-/* Process a configuration file line in traditional "filter selector" format
- * or one that builds upon this format. Note that ppRule may be a NULL pointer,
- * which is valid and happens if there is no previous line (right at the start
- * of the master config file!).
- */
-static rsRetVal
-cflineClassic(uchar *p, rule_t **ppRule)
-{
- DEFiRet;
- action_t *pAction;
- rsRetVal localRet;
- int bHadWarning = 0;
-
- /* lines starting with '&' have no new filters and just add
- * new actions to the currently processed selector.
- */
- if(*p == '&') {
- ++p; /* eat '&' */
- skipWhiteSpace(&p); /* on to command */
- } else {
- /* we are finished with the current selector (on previous line).
- * So we now need to check
- * if it has any actions associated and, if so, link it to the linked
- * list. If it has nothing associated with it, we can simply discard
- * it. In any case, we create a fresh selector for our new filter.
- * We have one special case during initialization: then, the current
- * selector is NULL, which means we do not need to care about it at
- * all. -- rgerhards, 2007-08-01
- */
- if(*ppRule != NULL) {
- CHKiRet(ruleset.AddRule(rule.GetAssRuleset(*ppRule), ppRule));
- }
- CHKiRet(rule.Construct(ppRule)); /* create "fresh" selector */
- CHKiRet(rule.SetAssRuleset(*ppRule, ruleset.GetCurrent())); /* create "fresh" selector */
- CHKiRet(rule.ConstructFinalize(*ppRule)); /* create "fresh" selector */
- CHKiRet(cflineDoFilter(&p, *ppRule)); /* pull filters */
- }
-
- localRet = cflineDoAction(&p, &pAction);
- if(localRet == RS_RET_OK_WARN) {
- bHadWarning = 1;
- } else {
- CHKiRet(localRet);
- }
- CHKiRet(llAppend(&(*ppRule)->llActList, NULL, (void*) pAction));
-
-finalize_it:
- if(iRet == RS_RET_OK && bHadWarning)
- iRet = RS_RET_OK_WARN;
- RETiRet;
-}
-
-
-/* process a configuration line
- * I re-did this functon because it was desperately time to do so
- * rgerhards, 2007-08-01
- */
-static rsRetVal
-cfline(uchar *line, rule_t **pfCurr)
-{
- DEFiRet;
-
- ASSERT(line != NULL);
-
- dbgprintf("cfline: '%s'\n", line);
-
- /* check type of line and call respective processing */
- switch(*line) {
- case '!':
- iRet = cflineProcessTagSelector(&line);
- break;
- case '+':
- case '-':
- iRet = cflineProcessHostSelector(&line);
- break;
- case '$':
- ++line; /* eat '$' */
- iRet = cfsysline(line);
- break;
- default:
- iRet = cflineClassic(line, pfCurr);
- break;
- }
-
- RETiRet;
-}
-
-
/* return the current number of active actions
* rgerhards, 2008-07-28
*/
static rsRetVal
-GetNbrActActions(int *piNbrActions)
+GetNbrActActions(rsconf_t *conf, int *piNbrActions)
{
DEFiRet;
assert(piNbrActions != NULL);
- *piNbrActions = iNbrActions;
+ *piNbrActions = conf->actions.nbrActions;
RETiRet;
}
@@ -1252,15 +803,23 @@ CODESTARTobjQueryInterface(conf)
pIf->doNameLine = doNameLine;
pIf->cfsysline = cfsysline;
pIf->doModLoad = doModLoad;
- pIf->doIncludeLine = doIncludeLine;
- pIf->cfline = cfline;
- pIf->processConfFile = processConfFile;
pIf->GetNbrActActions = GetNbrActActions;
finalize_it:
ENDobjQueryInterface(conf)
+/* Reset config variables to default values.
+ * rgerhards, 2010-07-23
+ */
+static rsRetVal
+resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
+{
+ bConfStrictScoping = 0;
+ return RS_RET_OK;
+}
+
+
/* exit our class
* rgerhards, 2008-03-11
*/
@@ -1276,9 +835,6 @@ CODESTARTObjClassExit(conf)
}
/* release objects we no longer need */
- objRelease(expr, CORE_COMPONENT);
- objRelease(ctok, CORE_COMPONENT);
- objRelease(ctok_token, CORE_COMPONENT);
objRelease(module, CORE_COMPONENT);
objRelease(errmsg, CORE_COMPONENT);
objRelease(net, LM_NET_FILENAME);
@@ -1293,14 +849,16 @@ ENDObjClassExit(conf)
*/
BEGINAbstractObjClassInit(conf, 1, OBJ_IS_CORE_MODULE) /* class, version - CHANGE class also in END MACRO! */
/* request objects we use */
- CHKiRet(objUse(expr, CORE_COMPONENT));
- CHKiRet(objUse(ctok, CORE_COMPONENT));
- CHKiRet(objUse(ctok_token, CORE_COMPONENT));
CHKiRet(objUse(module, CORE_COMPONENT));
CHKiRet(objUse(errmsg, CORE_COMPONENT));
CHKiRet(objUse(net, LM_NET_FILENAME)); /* TODO: make this dependcy go away! */
CHKiRet(objUse(rule, CORE_COMPONENT));
CHKiRet(objUse(ruleset, CORE_COMPONENT));
+
+ /* These commands will NOT be supported -- the new v6.3 config system provides
+ * far better methods. We will remove the related code soon. -- rgerhards, 2012-01-09
+ */
+ CHKiRet(regCfSysLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, NULL));
ENDObjClassInit(conf)
/* vi:set ai:
diff --git a/runtime/conf.h b/runtime/conf.h
index 93683975..018d9111 100644
--- a/runtime/conf.h
+++ b/runtime/conf.h
@@ -20,6 +20,7 @@
*/
#ifndef INCLUDED_CONF_H
#define INCLUDED_CONF_H
+#include "action.h"
/* definitions used for doNameLine to differentiate between different command types
* (with otherwise identical code). This is a left-over from the previous config
@@ -27,18 +28,27 @@
* somewhat strange (at least its name). -- rgerhards, 2007-08-01
*/
enum eDirective { DIR_TEMPLATE = 0, DIR_OUTCHANNEL = 1, DIR_ALLOWEDSENDER = 2};
+extern ecslConfObjType currConfObj;
+extern int bConfStrictScoping; /* force strict scoping during config processing? */
/* interfaces */
BEGINinterface(conf) /* name must also be changed in ENDinterface macro! */
rsRetVal (*doNameLine)(uchar **pp, void* pVal);
rsRetVal (*cfsysline)(uchar *p);
rsRetVal (*doModLoad)(uchar **pp, __attribute__((unused)) void* pVal);
- rsRetVal (*doIncludeLine)(uchar **pp, __attribute__((unused)) void* pVal);
- rsRetVal (*cfline)(uchar *line, rule_t **pfCurr);
- rsRetVal (*processConfFile)(uchar *pConfFile);
- rsRetVal (*GetNbrActActions)(int *);
+ rsRetVal (*GetNbrActActions)(rsconf_t *conf, int *);
+ /* version 4 -- 2010-07-23 rgerhards */
+ /* "just" added global variables
+ * FYI: we reconsider repacking as a non-object, as only the core currently
+ * accesses this module. The current object structure complicates things without
+ * any real benefit.
+ */
+ /* version 5 -- 2011-04-19 rgerhards */
+ /* complete revamp, we now use the rsconf object */
+ /* version 6 -- 2011-07-06 rgerhards */
+ /* again a complete revamp, using flex/bison based parser now */
ENDinterface(conf)
-#define confCURR_IF_VERSION 3 /* increment whenever you change the interface structure! */
+#define confCURR_IF_VERSION 6 /* increment whenever you change the interface structure! */
/* in Version 3, entry point "ReInitConf()" was removed, as we do not longer need
* to support restart-type HUP -- rgerhards, 2009-07-15
*/
@@ -52,5 +62,14 @@ PROTOTYPEObj(conf);
rsRetVal cflineParseTemplateName(uchar** pp, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts, uchar *dfltTplName);
rsRetVal cflineParseFileName(uchar* p, uchar *pFileName, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts, uchar *pszTpl);
+/* more dirt to cover the new config interface (will go away...) */
+rsRetVal cflineProcessTagSelector(uchar **pline);
+rsRetVal cflineProcessHostSelector(uchar **pline);
+rsRetVal cflineProcessTradPRIFilter(uchar **pline, rule_t *pRule);
+rsRetVal cflineProcessPropFilter(uchar **pline, rule_t *f);
+rsRetVal cflineDoAction(rsconf_t *conf, uchar **p, action_t **ppAction);
+extern EHostnameCmpMode eDfltHostnameCmpMode;
+extern cstr_t *pDfltHostnameCmp;
+extern cstr_t *pDfltProgNameCmp;
#endif /* #ifndef INCLUDED_CONF_H */
diff --git a/runtime/ctok.c b/runtime/ctok.c
deleted file mode 100644
index 1da4f4d6..00000000
--- a/runtime/ctok.c
+++ /dev/null
@@ -1,624 +0,0 @@
-/* ctok.c - helper class to tokenize an input stream - which surprisingly
- * currently does not work with streams but with string. But that will
- * probably change over time ;) This class was originally written to support
- * the expression module but may evolve when (if) the expression module is
- * expanded (or aggregated) by a full-fledged ctoken based config parser.
- * Obviously, this class is used together with config files and not any other
- * parse function.
- *
- * Module begun 2008-02-19 by Rainer Gerhards
- *
- * Copyright (C) 2008-2012 Adiscon GmbH.
- *
- * This file is part of the rsyslog runtime library.
- *
- * 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 <stdlib.h>
-#include <ctype.h>
-#include <strings.h>
-#include <assert.h>
-
-#include "rsyslog.h"
-#include "template.h"
-#include "ctok.h"
-
-/* static data */
-DEFobjStaticHelpers
-DEFobjCurrIf(ctok_token)
-DEFobjCurrIf(var)
-
-
-/* Standard-Constructor
- */
-BEGINobjConstruct(ctok) /* be sure to specify the object type also in END macro! */
-ENDobjConstruct(ctok)
-
-
-/* ConstructionFinalizer
- * rgerhards, 2008-01-09
- */
-rsRetVal ctokConstructFinalize(ctok_t __attribute__((unused)) *pThis)
-{
- DEFiRet;
- RETiRet;
-}
-
-
-/* destructor for the ctok object */
-BEGINobjDestruct(ctok) /* be sure to specify the object type also in END and CODESTART macros! */
-CODESTARTobjDestruct(ctok)
- /* ... then free resources */
-ENDobjDestruct(ctok)
-
-
-/* unget character from input stream. At most one character can be ungotten.
- * This funtion is only permitted to be called after at least one character
- * has been read from the stream. Right now, we handle the situation simply by