summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--ChangeLog694
-rw-r--r--Makefile.am29
-rw-r--r--action.c331
-rw-r--r--action.h17
-rwxr-xr-xautogen.sh32
-rw-r--r--configure.ac283
-rw-r--r--dirty.h25
-rw-r--r--doc/Makefile.am33
-rw-r--r--doc/dataflow.pngbin0 -> 24601 bytes
-rw-r--r--doc/debug.html144
-rw-r--r--doc/dev_oplugins.html178
-rw-r--r--doc/direct_queue0.pngbin0 -> 2048 bytes
-rw-r--r--doc/direct_queue1.pngbin0 -> 2979 bytes
-rw-r--r--doc/direct_queue2.pngbin0 -> 4117 bytes
-rw-r--r--doc/direct_queue3.pngbin0 -> 4430 bytes
-rw-r--r--doc/direct_queue_directq.pngbin0 -> 10075 bytes
-rw-r--r--doc/direct_queue_rsyslog.pngbin0 -> 10465 bytes
-rw-r--r--doc/direct_queue_rsyslog2.pngbin0 -> 12350 bytes
-rw-r--r--doc/droppriv.html60
-rw-r--r--doc/expression.html3
-rw-r--r--doc/features.html25
-rw-r--r--doc/how2help.html116
-rw-r--r--doc/im3195.html2
-rw-r--r--doc/imfile.html2
-rw-r--r--doc/imgssapi.html2
-rw-r--r--doc/imklog.html21
-rw-r--r--doc/imrelp.html2
-rw-r--r--doc/imtcp.html49
-rw-r--r--doc/imuxsock.html2
-rw-r--r--doc/log_rotation_fix_size.html10
-rw-r--r--doc/man_rsyslogd.html438
-rw-r--r--doc/manual.html29
-rw-r--r--doc/modules.html5
-rw-r--r--doc/multi_ruleset.html275
-rw-r--r--doc/netstream.html4
-rw-r--r--doc/omlibdbi.html2
-rw-r--r--doc/ommail.html3
-rw-r--r--doc/ommysql.html2
-rw-r--r--doc/omoracle.html200
-rw-r--r--doc/omrelp.html3
-rw-r--r--doc/omsnmp.html1
-rw-r--r--doc/property_replacer.html50
-rw-r--r--doc/queue_analogy_tv.pngbin0 -> 18730 bytes
-rw-r--r--doc/queues.html109
-rw-r--r--doc/queues_analogy.html259
-rw-r--r--doc/rsconf1_actionexeconlywhenpreviousissuspended.html2
-rw-r--r--doc/rsconf1_actionresumeinterval.html4
-rw-r--r--doc/rsconf1_allowedsender.html2
-rw-r--r--doc/rsconf1_controlcharacterescapeprefix.html2
-rw-r--r--doc/rsconf1_debugprintcfsyslinehandlerlist.html4
-rw-r--r--doc/rsconf1_debugprintmodulelist.html3
-rw-r--r--doc/rsconf1_debugprinttemplatelist.html4
-rw-r--r--doc/rsconf1_dircreatemode.html2
-rw-r--r--doc/rsconf1_dirgroup.html2
-rw-r--r--doc/rsconf1_dirowner.html2
-rw-r--r--doc/rsconf1_dropmsgswithmaliciousdnsptrrecords.html4
-rw-r--r--doc/rsconf1_droptrailinglfonreception.html2
-rw-r--r--doc/rsconf1_dynafilecachesize.html4
-rw-r--r--doc/rsconf1_escapecontrolcharactersonreceive.html2
-rw-r--r--doc/rsconf1_failonchownfailure.html4
-rw-r--r--doc/rsconf1_filecreatemode.html2
-rw-r--r--doc/rsconf1_filegroup.html2
-rw-r--r--doc/rsconf1_fileowner.html2
-rw-r--r--doc/rsconf1_generateconfiggraph.html121
-rw-r--r--doc/rsconf1_gssforwardservicename.html2
-rw-r--r--doc/rsconf1_gsslistenservicename.html2
-rw-r--r--doc/rsconf1_gssmode.html2
-rw-r--r--doc/rsconf1_includeconfig.html4
-rw-r--r--doc/rsconf1_mainmsgqueuesize.html2
-rw-r--r--doc/rsconf1_markmessageperiod.html6
-rw-r--r--doc/rsconf1_maxopenfiles.html35
-rw-r--r--doc/rsconf1_moddir.html4
-rw-r--r--doc/rsconf1_modload.html2
-rw-r--r--doc/rsconf1_repeatedmsgreduction.html4
-rw-r--r--doc/rsconf1_resetconfigvariables.html4
-rw-r--r--doc/rsconf1_umask.html4
-rw-r--r--doc/rscript_abnf.html2
-rw-r--r--doc/rsyslog-vers.dot82
-rw-r--r--doc/rsyslog-vers.pngbin0 -> 162637 bytes
-rw-r--r--doc/rsyslog_conf.html1248
-rw-r--r--doc/rsyslog_conf_actions.html339
-rw-r--r--doc/rsyslog_conf_examples.html209
-rw-r--r--doc/rsyslog_conf_filter.html284
-rw-r--r--doc/rsyslog_conf_global.html295
-rw-r--r--doc/rsyslog_conf_modules.html72
-rw-r--r--doc/rsyslog_conf_nomatch.html48
-rw-r--r--doc/rsyslog_conf_output.html81
-rw-r--r--doc/rsyslog_conf_templates.html150
-rw-r--r--doc/rsyslog_confgraph_complex.conf108
-rw-r--r--doc/rsyslog_confgraph_complex.pngbin0 -> 143204 bytes
-rw-r--r--doc/rsyslog_confgraph_std.conf79
-rw-r--r--doc/rsyslog_confgraph_std.pngbin0 -> 167756 bytes
-rw-r--r--doc/rsyslog_high_database_rate.html9
-rw-r--r--doc/rsyslog_mysql.html11
-rw-r--r--doc/rsyslog_ng_comparison.html10
-rw-r--r--doc/rsyslog_pgsql.html336
-rw-r--r--doc/rsyslog_php_syslog_ng.html16
-rw-r--r--doc/rsyslog_stunnel.html9
-rw-r--r--doc/rsyslog_tls.html10
-rw-r--r--doc/src/dataflow.diabin0 -> 2662 bytes
-rw-r--r--doc/src/direct_queue0.diabin0 -> 966 bytes
-rw-r--r--doc/src/direct_queue1.diabin0 -> 1058 bytes
-rw-r--r--doc/src/direct_queue2.diabin0 -> 1143 bytes
-rw-r--r--doc/src/direct_queue3.diabin0 -> 1214 bytes
-rw-r--r--doc/src/direct_queue_directq.diabin0 -> 1705 bytes
-rw-r--r--doc/src/direct_queue_rsyslog.diabin0 -> 2311 bytes
-rw-r--r--doc/src/direct_queue_rsyslog2.diabin0 -> 2638 bytes
-rw-r--r--doc/src/queue_analogy_tv.diabin0 -> 1749 bytes
-rw-r--r--doc/src/rsyslog_pgsql.odtbin0 -> 41755 bytes
-rw-r--r--doc/status.html25
-rw-r--r--doc/syslog_protocol.html9
-rw-r--r--doc/tls_cert_machine.html12
-rw-r--r--doc/troubleshoot.html18
-rw-r--r--doc/v4compatibility.html77
-rw-r--r--m4/ax_check_off64_t.m469
-rw-r--r--m4/shave.m499
-rw-r--r--outchannel.c22
-rw-r--r--parse.c34
-rw-r--r--plugins/cust1/Makefile.am6
-rw-r--r--plugins/im3195/im3195.c3
-rw-r--r--plugins/imdiag/imdiag.c386
-rw-r--r--plugins/imfile/imfile.c80
-rw-r--r--plugins/imgssapi/imgssapi.c5
-rw-r--r--plugins/imklog/bsd.c11
-rw-r--r--plugins/imklog/imklog.c48
-rw-r--r--plugins/imklog/imklog.h1
-rw-r--r--plugins/imklog/ksym.c4
-rw-r--r--plugins/imklog/ksym_mod.c42
-rw-r--r--plugins/imklog/ksyms.h4
-rw-r--r--plugins/imklog/linux.c25
-rw-r--r--plugins/immark/immark.c1
-rw-r--r--plugins/imrelp/imrelp.c26
-rw-r--r--plugins/imtcp/imtcp.c80
-rw-r--r--plugins/imudp/imudp.c260
-rw-r--r--plugins/imuxsock/imuxsock.c20
-rw-r--r--plugins/omgssapi/omgssapi.c14
-rw-r--r--plugins/omlibdbi/omlibdbi.c2
-rw-r--r--plugins/ommail/ommail.c2
-rw-r--r--plugins/ommysql/ommysql.c2
-rw-r--r--plugins/omoracle/Makefile.am8
-rw-r--r--plugins/omoracle/omoracle.c572
-rw-r--r--plugins/omoracle/omoracle.h31
-rw-r--r--plugins/omoracle/omoracle.te13
-rw-r--r--plugins/ompgsql/createDB.sql2
-rw-r--r--plugins/ompgsql/ompgsql.c2
-rw-r--r--plugins/omprog/Makefile.am8
-rw-r--r--plugins/omprog/omprog.c357
-rw-r--r--plugins/omrelp/omrelp.c16
-rw-r--r--plugins/omsnmp/omsnmp.c2
-rw-r--r--plugins/omstdout/Makefile.am8
-rw-r--r--plugins/omstdout/omstdout.c214
-rw-r--r--plugins/omtemplate/Makefile.am8
-rw-r--r--plugins/omtemplate/omtemplate.c220
-rw-r--r--runtime/Makefile.am29
-rw-r--r--runtime/apc.c405
-rw-r--r--runtime/apc.h56
-rw-r--r--runtime/atomic.h8
-rw-r--r--runtime/cfsysline.c32
-rw-r--r--runtime/conf.c187
-rw-r--r--runtime/conf.h6
-rw-r--r--runtime/ctok.c34
-rw-r--r--runtime/datetime.c473
-rw-r--r--runtime/datetime.h22
-rw-r--r--runtime/debug.c39
-rw-r--r--runtime/debug.h5
-rw-r--r--runtime/expr.c65
-rw-r--r--runtime/glbl.c88
-rw-r--r--runtime/glbl.h12
-rw-r--r--runtime/linkedlist.c2
-rw-r--r--runtime/module-template.h103
-rw-r--r--runtime/modules.c45
-rw-r--r--runtime/modules.h7
-rw-r--r--runtime/msg.c2428
-rw-r--r--runtime/msg.h196
-rw-r--r--runtime/net.c57
-rw-r--r--runtime/net.h4
-rw-r--r--runtime/netstrm.c16
-rw-r--r--runtime/netstrm.h9
-rw-r--r--runtime/nsd.h8
-rw-r--r--runtime/nsd_gtls.c63
-rw-r--r--runtime/nsd_ptcp.c29
-rw-r--r--runtime/obj-types.h20
-rw-r--r--runtime/obj.c151
-rw-r--r--runtime/obj.h8
-rw-r--r--runtime/objomsr.c18
-rw-r--r--runtime/objomsr.h6
-rw-r--r--runtime/parser.c332
-rw-r--r--runtime/parser.h30
-rw-r--r--runtime/prop.c247
-rw-r--r--runtime/prop.h58
-rw-r--r--runtime/queue.c687
-rw-r--r--runtime/queue.h63
-rw-r--r--runtime/rsyslog.c33
-rw-r--r--runtime/rsyslog.h140
-rw-r--r--runtime/rule.c450
-rw-r--r--runtime/rule.h77
-rw-r--r--runtime/ruleset.c449
-rw-r--r--runtime/ruleset.h60
-rw-r--r--runtime/srUtils.h16
-rw-r--r--runtime/srutils.c69
-rw-r--r--runtime/stream.c1097
-rw-r--r--runtime/stream.h137
-rw-r--r--runtime/stringbuf.c301
-rw-r--r--runtime/stringbuf.h149
-rw-r--r--runtime/strms_sess.c300
-rw-r--r--runtime/strms_sess.h75
-rw-r--r--runtime/strmsrv.c968
-rw-r--r--runtime/strmsrv.h112
-rw-r--r--runtime/syslogd-types.h34
-rw-r--r--runtime/sysvar.c4
-rw-r--r--runtime/unicode-helper.h69
-rw-r--r--runtime/vm.c260
-rw-r--r--runtime/vm.h5
-rw-r--r--runtime/vmop.c95
-rw-r--r--runtime/vmop.h40
-rw-r--r--runtime/vmprg.c30
-rw-r--r--runtime/vmprg.h4
-rw-r--r--runtime/vmstk.h4
-rw-r--r--runtime/wti.c81
-rw-r--r--runtime/wti.h2
-rw-r--r--runtime/wtp.c48
-rw-r--r--runtime/wtp.h2
-rw-r--r--runtime/zlibw.c125
-rw-r--r--runtime/zlibw.h46
-rw-r--r--shave-libtool.in109
-rw-r--r--shave.in102
-rw-r--r--tcpclt.c14
-rw-r--r--tcpclt.h6
-rw-r--r--tcps_sess.c180
-rw-r--r--tcps_sess.h24
-rw-r--r--tcpsrv.c312
-rw-r--r--tcpsrv.h53
-rw-r--r--template.c235
-rw-r--r--template.h14
-rw-r--r--tests/1.rstest34
-rw-r--r--tests/2.rstest4
-rw-r--r--tests/3.rstest21
-rw-r--r--tests/DiagTalker.java73
-rw-r--r--tests/Makefile.am225
-rwxr-xr-xtests/asynwr_deadlock.sh23
-rwxr-xr-xtests/asynwr_deadlock2.sh69
-rwxr-xr-xtests/asynwr_deadlock4.sh25
-rwxr-xr-xtests/asynwr_simple.sh18
-rwxr-xr-xtests/asynwr_small.sh26
-rwxr-xr-xtests/asynwr_timeout.sh21
-rwxr-xr-xtests/asynwr_tinybuf.sh19
-rwxr-xr-xtests/cfg.sh12
-rw-r--r--tests/chkseq.c143
-rwxr-xr-xtests/complex1.sh21
-rwxr-xr-xtests/diag.sh137
-rwxr-xr-xtests/diskqueue-fsync.sh16
-rwxr-xr-xtests/diskqueue.sh20
-rwxr-xr-xtests/dynfile_cachemiss.sh34
-rwxr-xr-xtests/dynfile_invalid2.sh34
-rwxr-xr-xtests/dynfile_invld_async.sh2
-rwxr-xr-xtests/dynfile_invld_sync.sh2
-rwxr-xr-xtests/execonlyonce.sh28
-rwxr-xr-xtests/fieldtest.sh13
-rw-r--r--tests/getline.c57
-rwxr-xr-xtests/gzipwr_large.sh20
-rwxr-xr-xtests/gzipwr_large_dynfile.sh36
-rwxr-xr-xtests/imfile-basic.sh14
-rwxr-xr-xtests/imtcp-multiport.sh42
-rw-r--r--tests/inputfilegen.c23
-rwxr-xr-xtests/inputname.sh20
-rwxr-xr-xtests/killrsyslog.sh13
-rwxr-xr-xtests/longrun.sh30
-rwxr-xr-xtests/manytcp.sh11
-rw-r--r--tests/nettester.c534
-rwxr-xr-xtests/omod-if-array.sh14
-rw-r--r--tests/ourtail.c46
-rwxr-xr-xtests/parsertest.sh33
-rwxr-xr-xtests/pipeaction.sh33
-rwxr-xr-xtests/proprepltest.sh7
-rwxr-xr-xtests/queue-persist-drvr.sh28
-rwxr-xr-xtests/queue-persist.sh11
-rwxr-xr-xtests/random.sh18
-rw-r--r--tests/randomgen.c130
-rw-r--r--tests/rscript.c10
-rw-r--r--tests/rt-init.c4
-rw-r--r--tests/runtime-dummy.c4
-rwxr-xr-xtests/sndrcv.sh9
-rwxr-xr-xtests/sndrcv_drvr.sh50
-rwxr-xr-xtests/sndrcv_gzip.sh7
-rw-r--r--tests/tcpflood.c476
-rw-r--r--tests/testsuites/1.field13
-rw-r--r--tests/testsuites/1.inputname_imtcp_125143
-rw-r--r--tests/testsuites/1.inputname_imtcp_125153
-rw-r--r--tests/testsuites/1.inputname_imtcp_125163
-rw-r--r--tests/testsuites/1.omod-if-array2
-rw-r--r--tests/testsuites/1.parse13
-rw-r--r--tests/testsuites/1.retry.conf2
-rw-r--r--tests/testsuites/2.parse13
-rw-r--r--tests/testsuites/3.parse13
-rw-r--r--tests/testsuites/Apr.ts31643
-rw-r--r--tests/testsuites/Aug.ts31643
-rw-r--r--tests/testsuites/Dec.ts31643
-rw-r--r--tests/testsuites/Feb.ts31643
-rw-r--r--tests/testsuites/Jan.ts31643
-rw-r--r--tests/testsuites/Jul.ts31643
-rw-r--r--tests/testsuites/Jun.ts31643
-rw-r--r--tests/testsuites/Mar.ts31643
-rw-r--r--tests/testsuites/May.ts31643
-rw-r--r--tests/testsuites/Nov.ts31643
-rw-r--r--tests/testsuites/Oct.ts31643
-rw-r--r--tests/testsuites/Sep.ts31643
-rw-r--r--tests/testsuites/asynwr_deadlock.conf14
-rw-r--r--tests/testsuites/asynwr_deadlock2.conf16
-rw-r--r--tests/testsuites/asynwr_deadlock4.conf16
-rw-r--r--tests/testsuites/asynwr_simple.conf15
-rw-r--r--tests/testsuites/asynwr_small.conf14
-rw-r--r--tests/testsuites/asynwr_timeout.conf15
-rw-r--r--tests/testsuites/asynwr_tinybuf.conf15
-rw-r--r--tests/testsuites/complex1.conf81
-rw-r--r--tests/testsuites/date1.parse13
-rw-r--r--tests/testsuites/date2.parse13
-rw-r--r--tests/testsuites/date3.parse13
-rw-r--r--tests/testsuites/date4.parse13
-rw-r--r--tests/testsuites/date5.parse13
-rw-r--r--tests/testsuites/diag-common.conf16
-rw-r--r--tests/testsuites/diag-common2.conf16
-rw-r--r--tests/testsuites/diskqueue-fsync.conf17
-rw-r--r--tests/testsuites/diskqueue.conf16
-rw-r--r--tests/testsuites/dynfile_cachemiss.conf14
-rw-r--r--tests/testsuites/dynfile_invalid2.conf14
-rw-r--r--tests/testsuites/execonlyonce.conf12
-rw-r--r--tests/testsuites/execonlyonce.data2
-rw-r--r--tests/testsuites/field1.conf8
-rw-r--r--tests/testsuites/gzipwr_large.conf15
-rw-r--r--tests/testsuites/gzipwr_large_dynfile.conf17
-rw-r--r--tests/testsuites/imfile-basic.conf12
-rw-r--r--tests/testsuites/imtcp-multiport.conf13
-rw-r--r--tests/testsuites/inputname_imtcp.conf19
-rw-r--r--tests/testsuites/manytcp.conf13
-rw-r--r--tests/testsuites/master.nolimittag11
-rw-r--r--tests/testsuites/master.rfctag11
-rw-r--r--tests/testsuites/master.subsecond8
-rw-r--r--tests/testsuites/master.ts333922
-rw-r--r--tests/testsuites/master.tsmysql2
-rw-r--r--tests/testsuites/master.tspgsql2
-rw-r--r--tests/testsuites/mon1digit.ts31643
-rw-r--r--tests/testsuites/mon2digit.ts31643
-rw-r--r--tests/testsuites/nolimittag.conf8
-rw-r--r--tests/testsuites/omod-if-array.conf13
-rw-r--r--tests/testsuites/oversizeTag-1.parse12
-rw-r--r--tests/testsuites/parse-3164-buggyday.conf8
-rw-r--r--tests/testsuites/parse-nodate.conf14
-rw-r--r--tests/testsuites/parse1.conf8
-rw-r--r--tests/testsuites/parse1udp.conf9
-rw-r--r--tests/testsuites/parse3.conf10
-rw-r--r--tests/testsuites/parse_invld_regex.conf10
-rw-r--r--tests/testsuites/pipeaction.conf16
-rw-r--r--tests/testsuites/queue-persist.conf21
-rw-r--r--tests/testsuites/random.conf13
-rw-r--r--tests/testsuites/reallife.parse315
-rw-r--r--tests/testsuites/rfc3164.parse14
-rw-r--r--tests/testsuites/rfc5424-1.parse13
-rw-r--r--tests/testsuites/rfc5424-2.parse14
-rw-r--r--tests/testsuites/rfc5424-3.parse14
-rw-r--r--tests/testsuites/rfc5424-4.parse14
-rw-r--r--tests/testsuites/rfctag.conf9
-rw-r--r--tests/testsuites/samples.parse-3164-buggyday6
-rw-r--r--tests/testsuites/samples.parse-nodate6
-rw-r--r--tests/testsuites/samples.parse_invld_regex16
-rw-r--r--tests/testsuites/samples.snare_ccoff_udp14
-rw-r--r--tests/testsuites/samples.snare_ccoff_udp226
-rw-r--r--tests/testsuites/snare.parse183
-rw-r--r--tests/testsuites/snare_ccoff_udp.conf21
-rw-r--r--tests/testsuites/snare_ccoff_udp2.conf17
-rw-r--r--tests/testsuites/sndrcv_gzip_rcvr.conf11
-rw-r--r--tests/testsuites/sndrcv_gzip_sender.conf8
-rw-r--r--tests/testsuites/sndrcv_rcvr.conf11
-rw-r--r--tests/testsuites/sndrcv_sender.conf8
-rw-r--r--tests/testsuites/sndrcv_tls_anon_rcvr.conf22
-rw-r--r--tests/testsuites/sndrcv_tls_anon_sender.conf19
-rw-r--r--tests/testsuites/subsecond.conf8
-rw-r--r--tests/testsuites/threadingmq.conf16
-rw-r--r--tests/testsuites/threadingmqaq.conf20
-rw-r--r--tests/testsuites/ts3164.conf8
-rw-r--r--tests/testsuites/ts3339.conf8
-rw-r--r--tests/testsuites/tsmysql.conf8
-rw-r--r--tests/testsuites/tspgsql.conf8
-rw-r--r--tests/testsuites/upcase-date.parse14
-rw-r--r--tests/testsuites/weird.parse137
-rw-r--r--tests/testsuites/wr_large.conf16
-rw-r--r--tests/testsuites/x.509/ca-key.pem15
-rw-r--r--tests/testsuites/x.509/ca.pem17
-rw-r--r--tests/testsuites/x.509/client-cert.pem16
-rw-r--r--tests/testsuites/x.509/client-key.pem15
-rw-r--r--tests/testsuites/x.509/machine-cert.pem18
-rw-r--r--tests/testsuites/x.509/machine-key.pem15
-rw-r--r--tests/testsuites/x.509/request.pem10
-rwxr-xr-xtests/threadingmq.sh16
-rwxr-xr-xtests/threadingmqaq.sh18
-rwxr-xr-xtests/timestamp.sh13
-rwxr-xr-xtests/validation-run.sh7
-rwxr-xr-xtests/wr_large.sh16
-rwxr-xr-xtests/wr_large_async.sh14
-rwxr-xr-xtests/wr_large_sync.sh14
-rw-r--r--threads.c5
-rw-r--r--tools/Makefile.am9
-rw-r--r--tools/msggen.c39
-rw-r--r--tools/omfile.c693
-rw-r--r--tools/omfile.h10
-rw-r--r--tools/omfwd.c92
-rw-r--r--tools/ompipe.c240
-rw-r--r--tools/ompipe.h31
-rw-r--r--tools/omshell.c2
-rw-r--r--tools/omusrmsg.c36
-rw-r--r--tools/pidfile.c2
-rw-r--r--tools/regexp.c1
-rw-r--r--tools/rsyslog.conf.510
-rw-r--r--tools/syslogd.c1668
-rw-r--r--tools/syslogd.h58
-rw-r--r--tools/zpipe.c262
416 files changed, 23261 insertions, 6213 deletions
diff --git a/.gitignore b/.gitignore
index ea044fbe..bc362e7f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,3 +35,5 @@ log
logfile
debug
core.*
+shave
+shave-libtool
diff --git a/ChangeLog b/ChangeLog
index b547f0ea..9e146785 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,695 @@
---------------------------------------------------------------------------
+Version 4.6.6 [v4-stable] (rgerhards), 2010-11-??
+- 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, limited testing under v4]
+- bugfix: invalid processing in QUEUE_FULL condition
+ If the the multi-submit interface was used and a QUEUE_FULL condition
+ occured, the failed message was properly destructed. However, the
+ rest of the input batch, if it existed, was not processed. So this
+ lead to potential loss of messages and a memory leak. The potential
+ loss of messages was IMHO minor, because they would have been dropped
+ in most cases due to the queue remaining full, but very few lucky ones
+ from the batch may have made it. Anyhow, this has now been changed so
+ that the rest of the batch is properly tried to be enqueued and, if
+ not possible, destructed.
+- bugfix: invalid storage type for config variables
+- bugfix: stream driver mode was not correctly set on tcp ouput on big
+ endian systems.
+ thanks varmojfekoj for the patch
+- 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: memory and file descriptor leak in stream processing
+ Leaks could occur under some circumstances if the file stream handler
+ errored out during the open call. Among others, this could cause very
+ big memory leaks if there were a problem with unreadable disk queue
+ files. In regard to the memory leak, this
+ closes: http://bugzilla.adiscon.com/show_bug.cgi?id=256
+- bugfix: imfile potentially duplicates lines
+ This can happen when 0 bytes are read from the input file, and some
+ writer appends data to the file BEFORE we check if a rollover happens.
+ The check for rollover uses the inode and size as a criterion. So far,
+ we checked for equality of sizes, which is not given in this scenario,
+ but that does not indicate a rollover. From the source code comments:
+ Note that when we check the size, we MUST NOT check for equality.
+ The reason is that the file may have been written right after we
+ did try to read (so the file size has increased). That is NOT in
+ indicator of a rollover (this is an actual bug scenario we
+ experienced). So we need to check if the new size is smaller than
+ what we already have seen!
+ Also, under some circumstances an invalid truncation was detected. This
+ code has now been removed, a file change (and thus resent) is only
+ detected if the inode number changes.
+- 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!)
+- 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: omlibdbi did not use password from rsyslog.con
+ closes: http://bugzilla.adiscon.com/show_bug.cgi?id=203
+- bugfix: TCP connection invalidly aborted when messages needed to be
+ discarded (due to QUEUE_FULL or similar problem)
+- bugfix: a slightly more informative error message when a TCP
+ connections is aborted
+- bugfix: timestamp was incorrectly calculated for timezones with minute
+ offset
+ closes: http://bugzilla.adiscon.com/show_bug.cgi?id=271
+- some improvements thanks to clang's static code analyzer
+ o overall cleanup (mostly unnecessary writes and otherwise unused stuff)
+ o bugfix: fixed a very remote problem in msg.c which could occur when
+ running under extremely low memory conditions
+---------------------------------------------------------------------------
+Version 4.6.5 [v4-stable] (rgerhards), 2010-11-24
+- 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
+---------------------------------------------------------------------------
+Version 4.6.4 [v4-stable] (rgerhards), 2010-08-05
+- bugfix: zero-sized (empty) messages were processed by imtcp
+ they are now dropped as they always should have been
+- bugfix: programname filter in ! configuration can not be reset
+ Thanks to Kiss Gabor for the patch.
+---------------------------------------------------------------------------
+Version 4.6.3 [v4-stable] (rgerhards), 2010-07-07
+- improvded testbench
+ - added test with truly random data received via syslog to test
+ robustness
+ - added new configure option that permits to disable and enable an
+ extended testbench
+- bugfix: segfault on HUP when "HUPIsRestart" was set to "on"
+ thanks varmojfekoj for the patch
+- bugfix: default for $OMFileFlushOnTXEnd was wrong ("off").
+ This, in default mode, caused buffered writing to be used, what
+ means that it looked like no output were written or partial
+ lines. Thanks to Michael Biebl for pointing out this bug.
+- bugfix: testbench failed when not executed in UTC+1 timezone
+ accidently, the time zone information was kept inside some
+ to-be-checked-for responses
+- temporary bugfix replaced by permanent one for
+ message-induced off-by-one error (potential segfault) (see 4.6.2)
+ The analysis has been completed and a better fix been crafted and
+ integrated.
+- some doc fixes; incorrect config samples could cause confusion
+ thanks to Anthony Edwards for pointing the problems out
+---------------------------------------------------------------------------
+Version 4.6.2 [v4-stable] (rgerhards), 2010-03-26
+- new feature: "." action type added to support writing files to relative
+ pathes (this is primarily meant as a debug aid)
+- new feature: $OMFileAsyncWriting directive added
+ it permits to specifiy if asynchronous writing should be done or not
+- bugfix(temporary): message-induced off-by-one error (potential segfault)
+ Some types of malformed messages could trigger an off-by-one error
+ (for example, \0 or \n as the last character, and generally control
+ character escaption is questionable). This is due to not strictly
+ following a the \0 or string counted string paradigm (during the last
+ optimization on the cstring class). As a temporary fix, we have
+ introduced a proper recalculation of the size. However, a final
+ patch is expected in the future. See bug tracker for further details
+ and when the final patch will be available:
+ http://bugzilla.adiscon.com/show_bug.cgi?id=184
+ Note that the current patch is considered sufficient to solve the
+ situation, but it requires a bit more runtime than desirable.
+- bugfix: potential segfault in dynafile cache
+ This bug was triggered by an open failure. The the cache was full and
+ a new entry needed to be placed inside it, a victim for eviction was
+ selected. That victim was freed, then the open of the new file tried. If
+ the open failed, the victim entry was still freed, and the function
+ exited. However, on next invocation and cache search, the victim entry
+ was used as if it were populated, most probably resulting in a segfault.
+- bugfix: race condition during directory creation
+ If multiple files try to create a directory at (almost) the same time,
+ some of them may fail. This is a data race and also exists with other
+ processes that may create the same directory. We do now check for this
+ condition and gracefully handle it.
+- bugfix: potential re-use of free()ed file stream object in omfile
+ when dynaCache is enabled, the cache is full, a new entry needs to
+ be allocated, thus the LRU discarded, then a new entry is opend and that
+ fails. In that case, it looks like the discarded stream may be reused
+ improperly (based on code analysis, test case and confirmation pending)
+- added new property replacer option "date-rfc3164-buggyday" primarily
+ to ease migration from syslog-ng. See property replacer doc for
+ details. [backport from 5.5.3 because urgently needed by some]
+- improved testbench
+- bugfix: invalid buffer write in (file) stream class
+ currently being accessed buffer could be overwritten with new data.
+ While this probably did not cause access violations, it could case loss
+ and/or duplication of some data (definitely a race with no deterministic
+ outcome)
+- bugfix: potential hang condition during filestream close
+ predicate was not properly checked when waiting for the background file
+ writer
+- bugfix: improper synchronization when "$OMFileFlushOnTXEnd on" was used
+ Internal data structures were not properly protected due to missing
+ mutex calls.
+- bugfix: potential data loss during file stream shutdown
+- bugfix: potential problems during file stream shutdown
+ The shutdown/close sequence was not clean, what potentially (but
+ unlikely) could lead to some issues. We have not been able to describe
+ any fatal cases, but there was some bug potential. Sequence has now
+ been straighted out.
+- bugfix: potential problem (loop, abort) when file write error occured
+ When a write error occured in stream.c, variable iWritten had the error
+ code but this was handled as if it were the actual number of bytes
+ written. That was used in pointer arithmetic later on, and thus could
+ lead to all sorts of problems. However, this could only happen if the
+ error was EINTR or the file in question was a tty. All other cases were
+ handled properly. Now, iWritten is reset to zero in such cases, resulting
+ in proper retries.
+- bugfix: $omfileFlushOnTXEnd was turned on when set to off and vice
+ versa due to an invalid check
+- bugfix: recent patch to fix small memory leak could cause invalid free.
+ This could only happen during config file parsing.
+- bugfix(minor): handling of extremely large strings in dbgprintf() fixed
+ Previously, it could lead to garbagge output and, in extreme cases, also
+ to segfaults. Note: this was a problem only when debug output was
+ actually enabled, so it caused no problem in production use.
+- bugfix(minor): BSD_SO_COMPAT query function had some global vars not
+ properly initialized. However, in practice the loader initializes them
+ with zero, the desired value, so there were no actual issue in almost
+ all cases.
+---------------------------------------------------------------------------
+Version 4.6.1 [v4-stable] (rgerhards), 2010-03-04
+- re-enabled old pipe output (using new module ompipe, built-in) after
+ some problems with pipes (and especially in regard to xconsole) were
+ discovered. Thanks to Michael Biebl for reporting the issues.
+- bugfix: potential problems with large file support could cause segfault
+ ... and other weird problems. This seemed to affect 32bit-platforms
+ only, but I can not totally outrule there were issues on other
+ platforms as well. The previous code could cause system data types
+ to be defined inconsistently, and that could lead to various
+ troubles. Special thanks go to the Mandriva team for identifying
+ an initial problem, help discussing it and ultimately a fix they
+ contributed.
+- bugfix: fixed problem that caused compilation on FreeBSD 9.0 to fail.
+ bugtracker: http://bugzilla.adiscon.com/show_bug.cgi?id=181
+ Thanks to Christiano for reporting.
+- bugfix: potential segfault in omfile when a dynafile open failed
+ In that case, a partial cache entry was written, and some internal
+ pointers (iCurrElt) not correctly updated. In the next iteration, that
+ could lead to a segfault, especially if iCurrElt then points to the
+ then-partial record. Not very likely, but could happen in practice.
+- bugfix (theoretical): potential segfault in omfile under low memory
+ condition. This is only a theoretical bug, because it would only
+ happen when strdup() fails to allocate memory - which is highly
+ unlikely and will probably lead to all other sorts of errors.
+- bugfix: comment char ('#') in literal terminated script parsing
+ and thus could not be used.
+ but tracker: http://bugzilla.adiscon.com/show_bug.cgi?id=119
+ [merged in from v3.22.2]
+---------------------------------------------------------------------------
+Version 4.6.0 [v4-stable] (rgerhards), 2010-02-24
+***************************************************************************
+* This is a new stable v4 version. It contains all fixes and enhancements *
+* made during the 4.5.x phase as well as those listed below. *
+* Note: this version is scheduled to conclude the v4 development process. *
+* Do not expect any more new developments in v4. The focus is now *
+* on v5 (what also means we have a single devel branch again). *
+* ("development" means new feature development, bug fixes are of *
+* course provided for v4-stable) *
+***************************************************************************
+- improved testbench to contain samples for totally malformed messages
+ which miss parts of the message content
+- bugfix: some malformed messages could lead to a missing LF inside files
+ or some other missing parts of the template content.
+- bugfix: if a message ended immediately with a hostname, the hostname
+ was mistakenly interpreted as TAG, and localhost be used as hostname
+- bugfix: message without MSG part could case a segfault
+ [backported from v5 commit 98d1ed504ec001728955a5bcd7916f64cd85f39f]
+ This actually was a "recent" regression, but I did not realize that it
+ was introduced by the performance optimization in v4-devel. Shame on
+ me for having two devel versions at the same time...
+---------------------------------------------------------------------------
+Version 4.5.8 [v4-beta] (rgerhards), 2010-02-10
+- enhanced doc for using PostgreSQL
+ Thanks to Marc Schiffbauer for the new/updated doc
+- bugfix: property replacer returned invalid parameters under some (unusual)
+ conditions. In extreme cases, this could lead to garbled logs and/or
+ a system failure.
+- bugfix: invalid length returned (often) when using regular expressions
+ inside the property replacer
+- bugfix: submatch regex in property replacer did not honor "return 0 on
+ no match" config case
+- bugfix: imuxsock incorrectly stated inputname "imudp"
+ Thanks to Ryan Lynch for reporting this.
+- (slightly) enhanced support for FreeBSD by setting _PATH_MODDIR to
+ the correct value on FreeBSD.
+ Thanks to Cristiano for the patch.
+- bugfix: -d did not enable display of debug messages
+ regression from introduction of "debug on demand" mode
+ Thanks to Michael Biebl for reporting this bug
+- bugfix: blanks inside file names did not terminate file name parsing.
+ This could reslult in the whole rest of a line (including comments)
+ to be treated as file name in "write to file" actions.
+ Thanks to Jack for reporting this issue.
+- bugfix: rsyslog hang when writing to a named pipe which nobody was
+ reading. Thanks to Michael Biebl for reporting this bug.
+- bugfix: memory leak when sending messages in zip-compressed format
+ Thanks to Naoya Nakazawa for analyzing this issue and providing a patch.
+- bugfix: potential segfaults during queue shutdown
+ (bugs require certain non-standard settings to appear)
+ Thanks to varmojfekoj for the patch
+---------------------------------------------------------------------------
+Version 4.5.7 [v4-beta] (rgerhards), 2009-11-18
+- added a so-called "On Demand Debug" mode, in which debug output can
+ be generated only after the process has started, but not right from
+ the beginning. This is assumed to be useful for hard-to-find bugs.
+ Also improved the doc on the debug system.
+- bugfix (kind of): check if TCP connection is still alive if using TLS
+ Thanks to Jonathan Bond-Caron for the patch.
+- bugfix: hostname accidently set to IP address for some message sources,
+ for example imudp. Thanks to Anton for reporting this bug.
+- bugfix [imported from 4.4.3]: $ActionExecOnlyOnceEveryInterval did
+ not work.
+---------------------------------------------------------------------------
+Version 4.5.6 [v4-beta] (rgerhards), 2009-11-05
+- bugfix: named pipes did no longer work (they always got an open error)
+ this was a regression from the omfile rewrite in 4.5.0
+- bugfix(minor): diag function returned wrong queue memeber count
+ for the main queue if an active DA queue existed. This had no relevance
+ to real deployments (assuming they are not running the debug/diagnostic
+ module...), but sometimes caused grief and false alerts in the
+ testbench.
+- included some important fixes from v4-stable:
+ * bugfix: invalid handling of zero-sized messages
+ * bugfix: zero-sized UDP messages are no longer processed
+ * bugfix: random data could be appended to message
+ * bugfix: reverse lookup reduction logic in imudp do DNS queries too often
+- bugfix(testbench): testcase did not properly wait for rsyslod shutdown
+ thus some unpredictable behavior and a false negative test result
+ could occur. [BACKPORTED from v5]
+- bugfix(testbench): sequence check was not always performed correctly,
+ that could result in tests reporting success when they actually failed
+---------------------------------------------------------------------------
+Version 4.5.5 [v4-beta] (rgerhards), 2009-10-21
+- added $InputTCPServerNotifyOnConnectionClose config directive
+ see doc for details
+- bugfix: debug string larger than 1K were improperly displayed. Max size
+ is now 32K
+- bugfix: invalid storage class selected for some size config parameters.
+ This resulted in wrong values. The most prominent victim was the
+ directory creation mode, which was set to zero in some cases. For
+ details, see related blog post:
+ http://blog.gerhards.net/2009/10/another-note-on-hard-to-find-bugs.html
+---------------------------------------------------------------------------
+Version 4.5.4 [v4-beta] (rgerhards), 2009-09-29
+- bugfix: potential segfault in stream writer on destruction
+ Most severely affected omfile. The problem was that some buffers were
+ freed before the asynchronous writer thread was shut down. So the
+ writer thread accessed invalid data, which may even already be
+ overwritten. Symptoms (with omfile) were segfaults, grabled data
+ and files with random names placed around the file system (most
+ prominently into the root directory). Special thanks to Aaron for
+ helping to track this down.
+- bugfix: potential race in object loader (obj.c) during use/release
+ of object interface
+- bugfixes: potential problems in out file zip writer. Problems could
+ lead to abort and/or memory leak. The module is now hardened in a very
+ conservative way, which is sub-optimal from a performance point of view.
+ This should be improved if it has proven reliable in practice.
+---------------------------------------------------------------------------
+Version 4.5.3 [v4-beta] (rgerhards), 2009-09-17
+- bugfix: repeated messages were incorrectly processed
+ this could lead to loss of the repeated message content. As a side-
+ effect, it could probably also be possible that some segfault occurs
+ (quite unlikely). The root cause was that some counters introduced
+ during the malloc optimizations were not properly duplicated in
+ MsgDup(). Note that repeated message processing is not enabled
+ by default.
+- bugfix: message sanitation had some issues:
+ - control character DEL was not properly escaped
+ - NUL and LF characters were not properly stripped if no control
+ character replacement was to be done
+ - NUL characters in the message body were silently dropped (this was
+ a regeression introduced by some of the recent optimizations)
+- bugfix: strings improperly reused, resulting in some message properties
+ be populated with strings from previous messages. This was caused by
+ an improper predicate check. [backported from v5]
+- fixed some minor portability issues
+- bugfix: reverse lookup reduction logic in imudp do DNS queries too often
+ [imported from 4.4.2]
+---------------------------------------------------------------------------
+Version 4.5.2 [v4-beta] (rgerhards), 2009-08-21
+- legacy syslog parser changed so that it now accepts date stamps in
+ wrong case. Some devices seem to create them and I do not see any harm
+ in supporting that.
+- added $InputTCPMaxListeners directive - permits to specify how many
+ TCP servers shall be possible (default is 20).
+- bugfix: memory leak with some input modules. Those inputs that
+ use parseAndSubmitMsg() leak two small memory blocks with every message.
+ Typically, those process only relatively few messages, so the issue
+ does most probably not have any effect in practice.
+- bugfix: if tcp listen port could not be created, no error message was
+ emitted
+- bugfix: potential segfault in output file writer (omfile)
+ In async write mode, we use modular arithmetic to index the output
+ buffer array. However, the counter variables accidently were signed,
+ thus resulting in negative indizes after integer overflow. That in turn
+ could lead to segfaults, but was depending on the memory layout of
+ the instance in question (which in turn depended on a number of
+ variables, like compile settings but also configuration). The counters
+ are now unsigned (as they always should have been) and so the dangling
+ mis-indexing does no longer happen. This bug potentially affected all
+ installations, even if only some may actually have seen a segfault.
+- bugfix: hostnames with dashes in them were incorrectly treated as
+ malformed, thus causing them to be treated as TAG (this was a regression
+ introduced from the "rfc3164 strict" change in 4.5.0).
+---------------------------------------------------------------------------
+Version 4.5.1 [DEVEL] (rgerhards), 2009-07-15
+- CONFIG CHANGE: $HUPisRestart default is now "off". We are doing this
+ to support removal of restart-type HUP in v5.
+- bugfix: fromhost-ip was sometimes truncated
+- bugfix: potential segfault when zip-compressed syslog records were
+ received (double free)
+- bugfix: properties inputname, fromhost, fromhost-ip, msg were lost when
+ working with disk queues
+- performance enhancement: much faster, up to twice as fast (depending
+ on configuration)
+- bugfix: abort condition when RecvFrom was not set and message reduction
+ was on. Happend e.g. with imuxsock.
+- added $klogConsoleLogLevel directive which permits to set a new
+ console log level while rsyslog is active
+- bugfix: message could be truncated after TAG, often when forwarding
+ This was a result of an internal processing error if maximum field
+ sizes had been specified in the property replacer.
+- added ability for the TCP output action to "rebind" its send socket after
+ sending n messages (actually, it re-opens the connection, the name is
+ used because this is a concept very similiar to $ActionUDPRebindInterval).
+ New config directive $ActionSendTCPRebindInterval added for the purpose.
+ By default, rebinding is disabled. This is considered useful for load
+ balancers.
+- testbench improvements
+---------------------------------------------------------------------------
+Version 4.5.0 [DEVEL] (rgerhards), 2009-07-02
+- activation order of inputs changed, they are now activated only after
+ privileges are dropped. Thanks to Michael Terry for the patch.
+- greatly improved performance
+- greatly reduced memory requirements of msg object
+ to around half of the previous demand. This means that more messages can
+ be stored in core! Due to fewer cache misses, this also means some
+ performance improvement.
+- improved config error messages: now contain a copy of the config line
+ that (most likely) caused the error
+- reduced max value for $DynaFileCacheSize to 1,000 (the former maximum
+ of 10,000 really made no sense, even 1,000 is very high, but we like
+ to keep the user in control ;)).
+- added capability to fsync() queue disk files for enhanced reliability
+ (also add's speed, because you do no longer need to run the whole file
+ system in sync mode)
+- more strict parsing of the hostname in rfc3164 mode, hopefully
+ removes false positives (but may cause some trouble with hostname
+ parsing). For details, see this bug tracker:
+ http://bugzilla.adiscon.com/show_bug.cgi?id=126
+- omfile rewrite to natively support zip files (includes large extension
+ of the stream class)
+- added configuration commands (see doc for explanations)
+ * $OMFileZipLevel
+ * $OMFileIOBufferSize
+ * $OMFileFlushOnTXEnd
+ * $MainMsgQueueSyncQueueFiles
+ * $ActionQueueSyncQueueFiles
+- done some memory accesses explicitely atomic
+- bugfix: subtle (and usually irrelevant) issue in timout processing
+ timeout could be one second too early if nanoseconds wrapped
+- set a more sensible timeout for shutdow, now 1.5 seconds to complete
+ processing (this also removes those cases where the shutdown message
+ was not written because the termination happened before it)
+- internal bugfix: object pointer was only reset to NULL when an object
+ was actually destructed. This most likely had no effect to existing code,
+ but it may also have caused trouble in remote cases. Similarly, the fix
+ may also cause trouble...
+- bugfix: missing initialization during timestamp creation
+ This could lead to timestamps written in the wrong format, but not to
+ an abort
+---------------------------------------------------------------------------
+Version 4.4.3 [v4-stable] (rgerhards), 2009-10-??
+- bugfix: several smaller bugs resolved after flexelint review
+ Thanks to varmojfekoj for the patch.
+- bugfix: $ActionExecOnlyOnceEveryInterval did not work.
+ This was a regression from the time() optimizations done in v4.
+ Bug tracker: http://bugzilla.adiscon.com/show_bug.cgi?id=143
+ Thanks to Klaus Tachtler for reporting this bug.
+- bugfix: potential segfault on queue shutdown
+ Thanks to varmojfekoj for the patch.
+- bugfix: potential hang condition on queue shutdown
+ [imported from v3-stable]
+- bugfix: segfault on startup when -q or -Q option was given
+ [imported from v3-stable]
+---------------------------------------------------------------------------
+Version 4.4.2 [v4-stable] (rgerhards), 2009-10-09
+- bugfix: invalid handling of zero-sized messages, could lead to mis-
+ addressing and potential memory corruption/segfault
+- bugfix: zero-sized UDP messages are no longer processed
+ until now, they were forwarded to processing, but this makes no sense
+ Also, it looks like the system seems to provide a zero return code
+ on a UDP recvfrom() from time to time for some internal reasons. These
+ "receives" are now silently ignored.
+- bugfix: random data could be appended to message, possibly causing
+ segfaults
+- bugfix: reverse lookup reduction logic in imudp do DNS queries too often
+ A comparison was done between the current and the former source address.
+ However, this was done on the full sockaddr_storage structure and not
+ on the host address only. This has now been changed for IPv4 and IPv6.
+ The end result of this bug could be a higher UDP message loss rate than
+ necessary (note that UDP message loss can not totally be avoided due
+ to the UDP spec)
+---------------------------------------------------------------------------
+Version 4.4.1 [v4-stable] (rgerhards), 2009-09-02
+- features requiring Java are automatically disabled if Java is not
+ present (thanks to Michael Biebl for his help!)
+- bugfix: invalid double-quoted PRI, among others in outgoing messages
+ This causes grief with all receivers.
+ Bug tracker: http://bugzilla.adiscon.com/show_bug.cgi?id=147
+- bugfix: Java testing tools were required, even if testbench was disabled
+ This resulted in build errors if no Java was present on the build system,
+ even though none of the selected option actually required Java.
+ (I forgot to backport a similar fix to newer releases).
+- bugfix (backport): omfwd segfault
+ Note that the orginal (higher version) patch states this happens only
+ when debugging mode is turned on. That statement is wrong: if debug
+ mode is turned off, the message is not being emitted, but the division
+ by zero in the actual parameters still happens.
+---------------------------------------------------------------------------
+Version 4.4.0 [v4-stable] (rgerhards), 2009-08-21
+- bugfix: stderr/stdout were not closed to be able to emit error messages,
+ but this caused ssh sessions to hang. Now we close them after the
+ initial initialization. See forum thread:
+ http://kb.monitorware.com/controlling-terminal-issues-t9875.html
+- bugfix: sending syslog messages with zip compression did not work
+---------------------------------------------------------------------------
+Version 4.3.2 [v4-beta] (rgerhards), 2009-06-24
+- removed long-obsoleted property UxTradMsg
+- added a generic network stream server (in addition to rather specific
+ syslog tcp server)
+- added ability for the UDP output action to rebind its send socket after
+ sending n messages. New config directive $ActionSendUDPRebindInterval
+ added for the purpose. By default, rebinding is disabled. This is
+ considered useful for load balancers.
+- bugfix: imdiag/imtcp had a race condition
+- improved testbench (now much better code design and reuse)
+- added config switch --enable-testbench=no to turn off testbench
+---------------------------------------------------------------------------
+Version 4.3.1 [DEVEL] (rgerhards), 2009-05-25
+- added capability to run multiple tcp listeners (on different ports)
+- performance enhancement: imtcp calls parser no longer on input thread
+ but rather inside on of the potentially many main msg queue worker
+ threads (an enhancement scheduled for all input plugins where this is
+ possible)
+- added $GenerateConfigGraph configuration command which can be used
+ to generate nice-looking (and very informative) rsyslog configuration
+ graphs.
+- added $ActionName configuration directive (currently only used for
+ graph generation, but may find other uses)
+- improved doc
+ * added (hopefully) easier to grasp queue explanation
+- improved testbench
+ * added tests for queue disk-only mode (checks disk queue logic)
+- bugfix: light and full delay watermarks had invalid values, badly
+ affecting performance for delayable inputs
+- build system improvements - thanks to Michael Biebl
+- added new testing module imdiag, which enables to talk to the
+ rsyslog core at runtime. The current implementation is only a
+ beginning, but can be expanded over time
+---------------------------------------------------------------------------
+Version 4.3.0 [DEVEL] (rgerhards), 2009-04-17
+- new feature: new output plugin omprog, which permits to start program
+ and feed it (via its stdin) with syslog messages. If the program
+ terminates, it is restarted.
+- improved internal handling of RainerScript functions, building the
+ necessary plumbing to support more functions with decent runtime
+ performance. This is also necessary towards the long-term goal
+ of loadable library modules.
+- added new RainerScript function "tolower"
+- improved testbench
+ * added tests for tcp-based reception
+ * added tcp-load test (1000 connections, 20,000 messages)
+- added $MaxOpenFiles configuration directive
+- bugfix: solved potential memory leak in msg processing, could manifest
+ itself in imtcp
+- bugfix: ompgsql did not detect problems in sql command execution
+ this could cause loss of messages. The handling was correct if the
+ connection broke, but not if there was a problem with statement
+ execution. The most probable case for such a case would be invalid
+ sql inside the template, and this is now much easier to diagnose.
+---------------------------------------------------------------------------
+Version 4.2.0 [v4-stable] (rgerhards), 2009-06-23
+- bugfix: light and full delay watermarks had invalid values, badly
+ affecting performance for delayable inputs
+- imported all patches from 3.22.1 as of today (see below)
+- bugfix: compile problems in im3195
+---------------------------------------------------------------------------
+Version 4.1.7 [BETA] (rgerhards), 2009-04-22
+- bugfix: $InputTCPMaxSessions config directive was accepted, but not
+ honored. This resulted in a fixed upper limit of 200 connections.
+- bugfix: the default for $DirCreateMode was 0644, and as such wrong.
+ It has now been changed to 0700. For some background, please see
+ http://lists.adiscon.net/pipermail/rsyslog/2009-April/001986.html
+- bugfix: ompgsql did not detect problems in sql command execution
+ this could cause loss of messages. The handling was correct if the
+ connection broke, but not if there was a problem with statement
+ execution. The most probable case for such a case would be invalid
+ sql inside the template, and this is now much easier to diagnose.
+---------------------------------------------------------------------------
+Version 4.1.6 [DEVEL] (rgerhards), 2009-04-07
+- added new "csv" property replacer options to enable simple creation
+ of CSV-formatted outputs (format from RFC4180 is used)
+- implemented function support in RainerScript. That means the engine
+ parses and compile functions, as well as executes a few build-in
+ ones. Dynamic loading and registration of functions is not yet
+ supported - but we now have a good foundation to do that later on.
+- implemented the strlen() RainerScript function
+- added a template output module
+- added -T rsyslogd command line option, enables to specify a directory
+ where to chroot() into on startup. This is NOT a security feature but
+ introduced to support testing. Thus, -T does not make sure chroot()
+ is used in a secure way. (may be removed later)
+- added omstdout module for testing purposes. Spits out all messages to
+ stdout - no config option, no other features
+- added a parser testing suite (still needs to be extended, but a good
+ start)
+- modified $ModLoad statement so that for modules whom's name starts with
+ a dot, no path is prepended (this enables relative-pathes and should
+ not break any valid current config)
+- fixed a bug that caused action retries not to work correctly
+ situation was only cleared by a restart
+- bugfix: closed dynafile was potentially never written until another
+ dynafile name was generated - potential loss of messages
+- improved omfile so that it properly suspends itself if there is an
+ i/o or file name generation error. This enables it to be used with
+ the full high availability features of rsyslog's engine
+- bugfix: fixed some segaults on Solaris, where vsprintf() does not
+ check for NULL pointers
+- improved performance of regexp-based filters
+ Thanks to Arnaud Cornet for providing the idea and initial patch.
+- added a new way how output plugins may be passed parameters. This is
+ more effcient for some outputs. They new can receive fields not only
+ as a single string but rather in an array where each string is seperated.
+- added (some) developer documentation for output plugin interface
+- bugfix: potential abort with DA queue after high watermark is reached
+ There exists a race condition that can lead to a segfault. Thanks
+ go to vbernetr, who performed the analysis and provided patch, which
+ I only tweaked a very little bit.
+- bugfix: imtcp did incorrectly parse hostname/tag
+ Thanks to Luis Fernando Muñoz Mejías for the patch.
+---------------------------------------------------------------------------
+Version 4.1.5 [DEVEL] (rgerhards), 2009-03-11
+- bugfix: parser did not correctly parse fields in UDP-received messages
+- added ERE support in filter conditions
+ new comparison operation "ereregex"
+- added new config directive $RepeatedMsgContainsOriginalMsg so that the
+ "last message repeated n times" messages, if generated, may
+ have an alternate format that contains the message that is being repeated
+---------------------------------------------------------------------------
+Version 4.1.4 [DEVEL] (rgerhards), 2009-01-29
+- bugfix: inconsistent use of mutex/atomic operations could cause segfault
+ details are too many, for full analysis see blog post at:
+ http://blog.gerhards.net/2009/01/rsyslog-data-race-analysis.html
+- bugfix: unitialized mutex was used in msg.c:getPRI
+ This was subtle, because getPRI is called as part of the debugging code
+ (always executed) in syslogd.c:logmsg.
+- bufgix: $PreserveFQDN was not properly handled for locally emitted
+ messages
+---------------------------------------------------------------------------
+Version 4.1.3 [DEVEL] (rgerhards), 2008-12-17
+- added $InputTCPServerAddtlFrameDelimiter config directive, which
+ enables to specify an additional, non-standard message delimiter
+ for processing plain tcp syslog. This is primarily a fix for the invalid
+ framing used in Juniper's NetScreen products. Credit to forum user
+ Arv for suggesting this solution.
+- added $InputTCPServerInputName property, which enables a name to be
+ specified that will be available during message processing in the
+ inputname property. This is considered useful for logic that treats
+ messages differently depending on which input received them.
+- added $PreserveFQDN config file directive
+ Enables to use FQDNs in sender names where the legacy default
+ would have stripped the domain part.
+ Thanks to BlinkMind, Inc. http://www.blinkmind.com for sponsoring this
+ development.
+- bugfix: imudp went into an endless loop under some circumstances
+ (but could also leave it under some other circumstances...)
+ Thanks to David Lang and speedfox for reporting this issue.
+---------------------------------------------------------------------------
+Version 4.1.2 [DEVEL] (rgerhards), 2008-12-04
+- bugfix: code did not compile without zlib
+- security bugfix: $AllowedSender was not honored, all senders were
+ permitted instead (see http://www.rsyslog.com/Article322.phtml)
+- security fix: imudp emitted a message when a non-permitted sender
+ tried to send a message to it. This behaviour is operator-configurable.
+ If enabled, a message was emitted each time. That way an attacker could
+ effectively fill the disk via this facility. The message is now
+ emitted only once in a minute (this currently is a hard-coded limit,
+ if someone comes up with a good reason to make it configurable, we
+ will probably do that).
+- doc bugfix: typo in v3 compatibility document directive syntax
+ thanks to Andrej for reporting
+- imported other changes from 3.21.8 and 3.20.1 (see there)
+---------------------------------------------------------------------------
+Version 4.1.1 [DEVEL] (rgerhards), 2008-11-26
+- added $PrivDropToGroup, $PrivDropToUser, $PrivDropToGroupID,
+ $PrivDropToUserID config directives to enable dropping privileges.
+ This is an effort to provide a security enhancement. For the limits of this
+ approach, see http://wiki.rsyslog.com/index.php/Security
+- re-enabled imklog to compile on FreeBSD (brought in from beta)
+---------------------------------------------------------------------------
+Version 4.1.0 [DEVEL] (rgerhards), 2008-11-18
+
+********************************* WARNING *********************************
+This version has a slightly different on-disk format for message entries.
+As a consequence, old queue files being read by this version may have
+an invalid output timestamp, which could result to some malfunction inside
+the output driver. It is recommended to drain queues with the previous
+version before switching to this one.
+********************************* WARNING *********************************
+
+- greatly enhanced performance when compared to v3.
+- added configuration directive "HUPisRestart" which enables to configure
+ HUP to be either a full restart or "just" a leightweight way to
+ close open files.
+- enhanced legacy syslog parser to detect year if part of the timestamp
+ the format is based on what Cisco devices seem to emit.
+- added a setting "$OptimizeForUniprocessor" to enable users to turn off
+ pthread_yield calls which are counter-productive on multiprocessor
+ machines (but have been shown to be useful on uniprocessors)
+- reordered imudp processing. Message parsing is now done as part of main
+ message queue worker processing (was part of the input thread)
+ This should also improve performance, as potentially more work is
+ done in parallel.
+- bugfix: compressed syslog messages could be slightly mis-uncompressed
+ if the last byte of the compressed record was a NUL
+- added $UDPServerTimeRequery option which enables to work with
+ less acurate timestamps in favor of performance. This enables querying
+ of the time only every n-th time if imudp is running in the tight
+ receive loop (aka receiving messsages at a high rate)
+- doc bugfix: queue doc had wrong parameter name for setting controlling
+ worker thread shutdown period
+- restructured rsyslog.conf documentation
+- bugfix: memory leak in ompgsql
+ Thanks to Ken for providing the patch
+---------------------------------------------------------------------------
Version 3.22.4 [v3-stable] (rgerhards), 2010-??-??
- bugfix: timestamp was incorrectly calculated for timezones with minute
offset
@@ -121,9 +812,12 @@ Version 3.21.6 [DEVEL] (rgerhards), 2008-10-22
- bugfix: solved a segfault condition
- bugfix: subsecond time properties generated by imfile, imklog and
internal messages could be slightly inconsistent
+- bugfix: (potentially big) memory leak on HUP if queues could not be
+ drained before timeout - thanks to David Lang for pointing this out
- added capability to support multiple module search pathes. Thank
to Marius Tomaschewski for providing the patch.
- bugfix: im3195 did no longer compile
+- improved "make distcheck" by ensuring everything relevant is recompiled
---------------------------------------------------------------------------
Version 3.21.5 [DEVEL] (rgerhards), 2008-09-30
- performance optimization: unnecessary time() calls during message
diff --git a/Makefile.am b/Makefile.am
index d783a30e..f5f9a6ed 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,7 +1,6 @@
sbin_PROGRAMS =
pkglib_LTLIBRARIES =
-
if ENABLE_INET
pkglib_LTLIBRARIES += lmtcpsrv.la lmtcpclt.la
#
@@ -84,10 +83,22 @@ if ENABLE_SNMP
SUBDIRS += plugins/omsnmp
endif
+if ENABLE_CUST1
+SUBDIRS += plugins/cust1
+endif
+
if ENABLE_IMTEMPLATE
SUBDIRS += plugins/imtemplate
endif
+if ENABLE_OMSTDOUT
+SUBDIRS += plugins/omstdout
+endif
+
+if ENABLE_OMTEMPLATE
+SUBDIRS += plugins/omtemplate
+endif
+
if ENABLE_IMFILE
SUBDIRS += plugins/imfile
endif
@@ -100,12 +111,28 @@ if ENABLE_MAIL
SUBDIRS += plugins/ommail
endif
+if ENABLE_OMPROG
+SUBDIRS += plugins/omprog
+endif
+
if ENABLE_RFC3195
SUBDIRS += plugins/im3195
endif
+if ENABLE_ORACLE
+SUBDIRS += plugins/omoracle
+endif
+
# tests are added as last element, because tests may need different
# modules that need to be generated first
SUBDIRS += tests
+
+# make sure "make distcheck" tries to build all modules. This means that
+# a developer must always have an environment where every supporting library
+# is available. If that is not the case, the respective configure option may
+# temporarily be removed below. The intent behind forcing everthing to compile
+# 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-imfile --enable-snmp --enable-pgsql --enable-libdbi --enable-mysql --enable-omtemplate --enable-imtemplate --enable-relp --enable-rsyslogd --enable-mail --enable-klog --enable-diagtools --enable-gnutls --enable-omstdout --enable-omprog --enable-imdiag --enable-shave --enable-extended-tests
ACLOCAL_AMFLAGS = -I m4
diff --git a/action.c b/action.c
index 5c5bdbe9..724dea4b 100644
--- a/action.c
+++ b/action.c
@@ -4,7 +4,7 @@
*
* File begun on 2007-08-06 by RGerhards (extracted from syslogd.c)
*
- * Copyright 2007 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007-2010 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -43,6 +43,7 @@
#include "srUtils.h"
#include "errmsg.h"
#include "datetime.h"
+#include "unicode-helper.h"
/* forward definitions */
rsRetVal actionCallDoAction(action_t *pAction, msg_t *pMsg);
@@ -58,7 +59,9 @@ static int iActExecEveryNthOccur = 0; /* execute action every n-th occurence (0,
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 uchar *pszActionName; /* short name for the action */
/* main message 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 */
@@ -70,6 +73,7 @@ static int iActionQueueNumWorkers = 1; /* number of worker threads for the mm
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 */
@@ -149,6 +153,7 @@ actionResetQueueParams(void)
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 */
@@ -162,8 +167,7 @@ actionResetQueueParams(void)
glbliActionResumeRetryCount = 0; /* I guess it is smart to reset this one, too */
- if(pszActionQFName != NULL)
- d_free(pszActionQFName);
+ d_free(pszActionQFName);
pszActionQFName = NULL; /* prefix for the main message queue file */
RETiRet;
@@ -175,11 +179,12 @@ actionResetQueueParams(void)
*/
rsRetVal actionDestruct(action_t *pThis)
{
+ int i;
DEFiRet;
ASSERT(pThis != NULL);
if(pThis->pQueue != NULL) {
- queueDestruct(&pThis->pQueue);
+ qqueueDestruct(&pThis->pQueue);
}
if(pThis->pMod != NULL)
@@ -190,8 +195,35 @@ rsRetVal actionDestruct(action_t *pThis)
SYNC_OBJ_TOOL_EXIT(pThis);
pthread_mutex_destroy(&pThis->mutActExec);
- if(pThis->ppTpl != NULL)
- d_free(pThis->ppTpl);
+ d_free(pThis->pszName);
+ d_free(pThis->ppTpl);
+
+ /* message ptr cleanup */
+ for(i = 0 ; i < pThis->iNumTpls ; ++i) {
+ if(pThis->ppMsgs[i] != NULL) {
+ switch(pThis->eParamPassing) {
+ case ACT_ARRAY_PASSING:
+#if 0 /* later! */
+ iArr = 0;
+ while(((char **)pThis->ppMsgs[i])[iArr] != NULL) {
+ d_free(((char **)pThis->ppMsgs[i])[iArr++]);
+ ((char **)pThis->ppMsgs[i])[iArr++] = NULL;
+ }
+ d_free(pThis->ppMsgs[i]);
+ pThis->ppMsgs[i] = NULL;
+#endif
+ break;
+ case ACT_STRING_PASSING:
+ d_free(pThis->ppMsgs[i]);
+ break;
+ default:
+ assert(0);
+ }
+ }
+ }
+ d_free(pThis->ppMsgs);
+ d_free(pThis->lenMsgs);
+
d_free(pThis);
RETiRet;
@@ -208,10 +240,7 @@ rsRetVal actionConstruct(action_t **ppThis)
ASSERT(ppThis != NULL);
- if((pThis = (action_t*) calloc(1, sizeof(action_t))) == NULL) {
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- }
-
+ CHKmalloc(pThis = (action_t*) calloc(1, sizeof(action_t)));
pThis->iResumeInterval = glbliActionResumeInterval;
pThis->iResumeRetryCount = glbliActionResumeRetryCount;
pThis->tLastOccur = time(NULL); /* done once per action on startup only */
@@ -254,7 +283,7 @@ actionConstructFinalize(action_t *pThis)
* to be run on multiple threads. So far, this is forbidden by the interface
* spec. -- rgerhards, 2008-01-30
*/
- CHKiRet(queueConstruct(&pThis->pQueue, ActionQueType, 1, iActionQueueSize, (rsRetVal (*)(void*,void*))actionCallDoAction));
+ CHKiRet(qqueueConstruct(&pThis->pQueue, ActionQueType, 1, iActionQueueSize, (rsRetVal (*)(void*,void*))actionCallDoAction));
obj.SetName((obj_t*) pThis->pQueue, pszQName);
/* ... set some properties ... */
@@ -267,24 +296,25 @@ actionConstructFinalize(action_t *pThis)
errmsg.LogError(0, NO_ERRCODE, "Invalid " #directive ", error %d. Ignored, running with default setting", iRet); \
}
- queueSetpUsr(pThis->pQueue, pThis);
- setQPROP(queueSetsizeOnDiskMax, "$ActionQueueMaxDiskSpace", iActionQueMaxDiskSpace);
- setQPROP(queueSetMaxFileSize, "$ActionQueueFileSize", iActionQueMaxFileSize);
- setQPROPstr(queueSetFilePrefix, "$ActionQueueFileName", pszActionQFName);
- setQPROP(queueSetiPersistUpdCnt, "$ActionQueueCheckpointInterval", iActionQPersistUpdCnt);
- setQPROP(queueSettoQShutdown, "$ActionQueueTimeoutShutdown", iActionQtoQShutdown );
- setQPROP(queueSettoActShutdown, "$ActionQueueTimeoutActionCompletion", iActionQtoActShutdown);
- setQPROP(queueSettoWrkShutdown, "$ActionQueueWorkerTimeoutThreadShutdown", iActionQtoWrkShutdown);
- setQPROP(queueSettoEnq, "$ActionQueueTimeoutEnqueue", iActionQtoEnq);
- setQPROP(queueSetiHighWtrMrk, "$ActionQueueHighWaterMark", iActionQHighWtrMark);
- setQPROP(queueSetiLowWtrMrk, "$ActionQueueLowWaterMark", iActionQLowWtrMark);
- setQPROP(queueSetiDiscardMrk, "$ActionQueueDiscardMark", iActionQDiscardMark);
- setQPROP(queueSetiDiscardSeverity, "$ActionQueueDiscardSeverity", iActionQDiscardSeverity);
- setQPROP(queueSetiMinMsgsPerWrkr, "$ActionQueueWorkerThreadMinimumMessages", iActionQWrkMinMsgs);
- setQPROP(queueSetbSaveOnShutdown, "$ActionQueueSaveOnShutdown", bActionQSaveOnShutdown);
- setQPROP(queueSetiDeqSlowdown, "$ActionQueueDequeueSlowdown", iActionQueueDeqSlowdown);
- setQPROP(queueSetiDeqtWinFromHr, "$ActionQueueDequeueTimeBegin", iActionQueueDeqtWinFromHr);
- setQPROP(queueSetiDeqtWinToHr, "$ActionQueueDequeueTimeEnd", iActionQueueDeqtWinToHr);
+ qqueueSetpUsr(pThis->pQueue, pThis);
+ setQPROP(qqueueSetsizeOnDiskMax, "$ActionQueueMaxDiskSpace", iActionQueMaxDiskSpace);
+ 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
@@ -293,8 +323,8 @@ actionConstructFinalize(action_t *pThis)
bActionQSaveOnShutdown, iActionQueMaxDiskSpace);
- CHKiRet(queueStart(pThis->pQueue));
- dbgprintf("Action %p: queue %p created\n", pThis, pThis->pQueue);
+ CHKiRet(qqueueStart(pThis->pQueue));
+ DBGPRINTF("Action %p: queue %p created\n", pThis, pThis->pQueue);
/* and now reset the queue params (see comment in its function header!) */
actionResetQueueParams();
@@ -352,7 +382,13 @@ static rsRetVal actionTryResume(action_t *pThis)
ASSERT(pThis != NULL);
- ttNow = getActNow(pThis); /* cache "now" */
+ /* for resume handling, we must always obtain a fresh timestamp. We used
+ * to use the action timestamp, but in this case we will never reach a
+ * point where a resumption is actually tried, because the action timestamp
+ * is always in the past. So we can not avoid doing a fresh time() call
+ * here. -- rgerhards, 2009-03-18
+ */
+ time(&ttNow); /* cache "now" */
/* first check if it is time for a re-try */
if(ttNow > pThis->ttResumeRtry) {
@@ -376,7 +412,7 @@ static rsRetVal actionTryResume(action_t *pThis)
if(iRet == RS_RET_OK)
actionResume(pThis);
- dbgprintf("actionTryResume: iRet: %d, next retry (if applicable): %u [now %u]\n",
+ DBGPRINTF("actionTryResume: iRet: %d, next retry (if applicable): %u [now %u]\n",
iRet, (unsigned) pThis->ttResumeRtry, (unsigned) ttNow);
RETiRet;
@@ -418,23 +454,13 @@ actionCallDoAction(action_t *pAction, msg_t *pMsg)
DEFiRet;
int iRetries;
int i;
+ int iArr;
int iSleepPeriod;
int bCallAction;
int iCancelStateSave;
- uchar **ppMsgs; /* array of message pointers for doAction */
ASSERT(pAction != NULL);
- /* create the array for doAction() message pointers */
- if((ppMsgs = calloc(pAction->iNumTpls, sizeof(uchar *))) == NULL) {
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- }
-
- /* here we must loop to process all requested strings */
- for(i = 0 ; i < pAction->iNumTpls ; ++i) {
- CHKiRet(tplToString(pAction->ppTpl[i], pMsg, &(ppMsgs[i])));
- }
- iRetries = 0;
/* We now must guard the output module against execution by multiple threads. The
* plugin interface specifies that output modules must not be thread-safe (except
* if they notify us they are - functionality not yet implemented...).
@@ -444,6 +470,21 @@ actionCallDoAction(action_t *pAction, msg_t *pMsg)
d_pthread_mutex_lock(&pAction->mutActExec);
pthread_cleanup_push(mutexCancelCleanup, &pAction->mutActExec);
pthread_setcancelstate(iCancelStateSave, NULL);
+
+ /* here we must loop to process all requested strings */
+ for(i = 0 ; i < pAction->iNumTpls ; ++i) {
+ switch(pAction->eParamPassing) {
+ case ACT_STRING_PASSING:
+ CHKiRet(tplToString(pAction->ppTpl[i], pMsg, &(pAction->ppMsgs[i]), &(pAction->lenMsgs[i])));
+ break;
+ case ACT_ARRAY_PASSING:
+ CHKiRet(tplToArray(pAction->ppTpl[i], pMsg, (uchar***) &(pAction->ppMsgs[i])));
+ break;
+ default:assert(0); /* software bug if this happens! */
+ }
+ }
+
+ iRetries = 0;
do {
/* on first invocation, this if should never be true. We just put it at the top
* of the loop so that processing (and code) is simplified. This code is actually
@@ -468,9 +509,9 @@ actionCallDoAction(action_t *pAction, msg_t *pMsg)
if(bCallAction) {
/* call configured action */
- iRet = pAction->pMod->mod.om.doAction(ppMsgs, pMsg->msgFlags, pAction->pModData);
+ iRet = pAction->pMod->mod.om.doAction(pAction->ppMsgs, pMsg->msgFlags, pAction->pModData);
if(iRet == RS_RET_SUSPENDED) {
- dbgprintf("Action requested to be suspended, done that.\n");
+ DBGPRINTF("Action requested to be suspended, done that.\n");
actionSuspend(pAction, getActNow(pAction));
}
}
@@ -478,26 +519,72 @@ actionCallDoAction(action_t *pAction, msg_t *pMsg)
} while(iRet == RS_RET_SUSPENDED && (pAction->iResumeRetryCount == -1 || iRetries < pAction->iResumeRetryCount)); /* do...while! */
if(iRet == RS_RET_DISABLE_ACTION) {
- dbgprintf("Action requested to be disabled, done that.\n");
+ DBGPRINTF("Action requested to be disabled, done that.\n");
pAction->bEnabled = 0; /* that's it... */
}
- pthread_cleanup_pop(1); /* unlock mutex */
-
finalize_it:
/* cleanup */
for(i = 0 ; i < pAction->iNumTpls ; ++i) {
- if(ppMsgs[i] != NULL) {
- d_free(ppMsgs[i]);
+ if(pAction->ppMsgs[i] != NULL) {
+ switch(pAction->eParamPassing) {
+ case ACT_ARRAY_PASSING:
+ iArr = 0;
+ while(((char **)pAction->ppMsgs[i])[iArr] != NULL) {
+ d_free(((char **)pAction->ppMsgs[i])[iArr++]);
+ ((char **)pAction->ppMsgs[i])[iArr++] = NULL;
+ }
+ d_free(pAction->ppMsgs[i]);
+ pAction->ppMsgs[i] = NULL;
+ break;
+ case ACT_STRING_PASSING:
+ break;
+ default:
+ assert(0);
+ }
}
}
- d_free(ppMsgs);
+
+ pthread_cleanup_pop(1); /* unlock mutex */
+
msgDestruct(&pMsg); /* we are now finished with the message */
+ RETiRet;
+}
+#pragma GCC diagnostic warning "-Wempty-body"
+
+
+/* call the HUP handler for a given action, if such a handler is defined. The
+ * action mutex is locked, because the HUP handler most probably needs to modify
+ * some internal state information.
+ * rgerhards, 2008-10-22
+ */
+#pragma GCC diagnostic ignored "-Wempty-body"
+rsRetVal
+actionCallHUPHdlr(action_t *pAction)
+{
+ DEFiRet;
+ int iCancelStateSave;
+
+ ASSERT(pAction != NULL);
+ DBGPRINTF("Action %p checks HUP hdlr: %p\n", pAction, pAction->pMod->doHUP);
+
+ if(pAction->pMod->doHUP == NULL) {
+ FINALIZE; /* no HUP handler, so we are done ;) */
+ }
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave);
+ d_pthread_mutex_lock(&pAction->mutActExec);
+ pthread_cleanup_push(mutexCancelCleanup, &pAction->mutActExec);
+ pthread_setcancelstate(iCancelStateSave, NULL);
+ CHKiRet(pAction->pMod->doHUP(pAction->pModData));
+ pthread_cleanup_pop(1); /* unlock mutex */
+
+finalize_it:
RETiRet;
}
#pragma GCC diagnostic warning "-Wempty-body"
+
/* set the action message queue mode
* TODO: probably move this into queue object, merge with MainMsgQueue!
* rgerhards, 2008-01-28
@@ -508,16 +595,16 @@ static rsRetVal setActionQueType(void __attribute__((unused)) *pVal, uchar *pszT
if (!strcasecmp((char *) pszType, "fixedarray")) {
ActionQueType = QUEUETYPE_FIXED_ARRAY;
- dbgprintf("action queue type set to FIXED_ARRAY\n");
+ DBGPRINTF("action queue type set to FIXED_ARRAY\n");
} else if (!strcasecmp((char *) pszType, "linkedlist")) {
ActionQueType = QUEUETYPE_LINKEDLIST;
- dbgprintf("action queue type set to LINKEDLIST\n");
+ DBGPRINTF("action queue type set to LINKEDLIST\n");
} else if (!strcasecmp((char *) pszType, "disk")) {
ActionQueType = QUEUETYPE_DISK;
- dbgprintf("action queue type set to DISK\n");
+ DBGPRINTF("action queue type set to DISK\n");
} else if (!strcasecmp((char *) pszType, "direct")) {
ActionQueType = QUEUETYPE_DIRECT;
- dbgprintf("action queue type set to DIRECT (no queueing at all)\n");
+ 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);
iRet = RS_RET_INVALID_PARAMS;
@@ -563,15 +650,15 @@ actionWriteToAction(action_t *pAction)
/* we need to care about multiple occurences */
if( pAction->iExecEveryNthOccurTO > 0
&& (getActNow(pAction) - pAction->tLastOccur) > pAction->iExecEveryNthOccurTO) {
- dbgprintf("n-th occurence handling timed out (%d sec), restarting from 0\n",
+ DBGPRINTF("n-th occurence handling timed out (%d sec), restarting from 0\n",
(int) (getActNow(pAction) - pAction->tLastOccur));
pAction->iNbrNoExec = 0;
pAction->tLastOccur = getActNow(pAction);
}
if(pAction->iNbrNoExec < pAction->iExecEveryNthOccur - 1) {
++pAction->iNbrNoExec;
- dbgprintf("action %p passed %d times to execution - less than neded - discarding\n",
- pAction, pAction->iNbrNoExec);
+ DBGPRINTF("action %p passed %d times to execution - less than neded - discarding\n",
+ pAction, pAction->iNbrNoExec);
FINALIZE;
} else {
pAction->iNbrNoExec = 0; /* we execute the action now, so the number of no execs is down to */
@@ -588,30 +675,34 @@ actionWriteToAction(action_t *pAction)
*/
if(pAction->f_prevcount > 1) {
msg_t *pMsg;
- uchar szRepMsg[64];
- snprintf((char*)szRepMsg, sizeof(szRepMsg), "last message repeated %d times",
- pAction->f_prevcount);
+ size_t lenRepMsg;
+ uchar szRepMsg[1024];
if((pMsg = MsgDup(pAction->f_pMsg)) == NULL) {
/* it failed - nothing we can do against it... */
- dbgprintf("Message duplication failed, dropping repeat message.\n");
+ DBGPRINTF("Message duplication failed, dropping repeat message.\n");
ABORT_FINALIZE(RS_RET_ERR);
}
- /* We now need to update the other message properties.
- * ... RAWMSG is a problem ... Please note that digital
+ if(pAction->bRepMsgHasMsg == 0) { /* old format repeat message? */
+ lenRepMsg = snprintf((char*)szRepMsg, sizeof(szRepMsg), " last message repeated %d times",
+ pAction->f_prevcount);
+ } else {
+ lenRepMsg = snprintf((char*)szRepMsg, sizeof(szRepMsg), " message repeated %d times: [%.800s]",
+ pAction->f_prevcount, getMSG(pAction->f_pMsg));
+ }
+
+ /* We now need to update the other message properties. Please note that digital
* signatures inside the message are also invalidated.
*/
- datetime.getCurrTime(&(pMsg->tRcvdAt));
+ datetime.getCurrTime(&(pMsg->tRcvdAt), &(pMsg->ttGenTime));
memcpy(&pMsg->tTIMESTAMP, &pMsg->tRcvdAt, sizeof(struct syslogTime));
- MsgSetMSG(pMsg, (char*)szRepMsg);
- MsgSetRawMsg(pMsg, (char*)szRepMsg);
-
+ MsgReplaceMSG(pMsg, szRepMsg, lenRepMsg);
pMsgSave = pAction->f_pMsg; /* save message pointer for later restoration */
pAction->f_pMsg = pMsg; /* use the new msg (pointer will be restored below) */
}
- dbgprintf("Called action, logging to %s\n", module.GetStateName(pAction->pMod));
+ DBGPRINTF("Called action, logging to %s\n", module.GetStateName(pAction->pMod));
/* now check if we need to drop the message because otherwise the action would be too
* frequently called. -- rgerhards, 2008-04-08
@@ -619,24 +710,24 @@ actionWriteToAction(action_t *pAction)
* a purely logical point of view. However, if safes us to check the system time in
* (those common) cases where ExecOnceInterval is not used. -- rgerhards, 2008-09-16
*/
- if(pAction->f_time != 0 && pAction->iSecsExecOnceInterval > 0 &&
+ if(pAction->iSecsExecOnceInterval > 0 &&
pAction->iSecsExecOnceInterval + pAction->tLastExec > getActNow(pAction)) {
/* in this case we need to discard the message - its not yet time to exec the action */
- dbgprintf("action not yet ready again to be executed, onceInterval %d, tCurr %d, tNext %d\n",
+ DBGPRINTF("action not yet ready again to be executed, onceInterval %d, tCurr %d, tNext %d\n",
(int) pAction->iSecsExecOnceInterval, (int) getActNow(pAction),
(int) (pAction->iSecsExecOnceInterval + pAction->tLastExec));
+ pAction->tLastExec = getActNow(pAction); /* re-init time flags */
FINALIZE;
}
- pAction->f_time = pAction->tLastExec = getActNow(pAction); /* re-init time flags */
- /* Note: tLastExec could be set in the if block above, but f_time causes us a hard time
- * so far, I do not see a solution to getting rid of it. -- rgerhards, 2008-09-16
- */
+ /* we use reception time, not dequeue time - this is considered more appropriate and also faster ;) -- rgerhards, 2008-09-17 */
+ pAction->tLastExec = getActNow(pAction); /* re-init time flags */
+ pAction->f_time = pAction->f_pMsg->ttGenTime;
/* When we reach this point, we have a valid, non-disabled action.
* So let's enqueue our message for execution. -- rgerhards, 2007-07-24
*/
- iRet = queueEnqObj(pAction->pQueue, pAction->f_pMsg->flowCtlType, (void*) MsgAddRef(pAction->f_pMsg));
+ iRet = qqueueEnqObj(pAction->pQueue, pAction->f_pMsg->flowCtlType, (void*) MsgAddRef(pAction->f_pMsg));
if(iRet == RS_RET_OK)
pAction->f_prevcount = 0; /* message processed, so we start a new cycle */
@@ -660,32 +751,13 @@ finalize_it:
}
-/* call the configured action. Does all necessary housekeeping.
- * rgerhards, 2007-08-01
- * FYI: currently, this function is only called from the queue
- * consumer. So we (conceptually) run detached from the input
- * threads (which also means we may run much later than when the
- * message was generated).
+/* helper to actonCallAction, mostly needed because of this damn
+ * pthread_cleanup_push() POSIX macro...
*/
-#pragma GCC diagnostic ignored "-Wempty-body"
-rsRetVal
-actionCallAction(action_t *pAction, msg_t *pMsg)
+static rsRetVal
+doActionCallAction(action_t *pAction, msg_t *pMsg)
{
DEFiRet;
- int iCancelStateSave;
-
- ISOBJ_TYPE_assert(pMsg, msg);
- ASSERT(pAction != NULL);
-
- /* Make sure nodbody else modifies/uses this action object. Right now, this
- * is important because of "message repeated n times" processing and potentially
- * multiple worker threads. -- rgerhards, 2007-12-11
- */
- pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave);
- LockObj(pAction);
- pthread_cleanup_push(mutexCancelCleanup, pAction->Sync_mut);
- pthread_setcancelstate(iCancelStateSave, NULL);
-
/* first, we need to check if this is a disabled
* entry. If so, we must not further process it.
* rgerhards 2005-09-26
@@ -710,12 +782,12 @@ actionCallAction(action_t *pAction, msg_t *pMsg)
/* suppress duplicate messages */
if ((pAction->f_ReduceRepeated == 1) && pAction->f_pMsg != NULL &&
(pMsg->msgFlags & MARK) == 0 && getMSGLen(pMsg) == getMSGLen(pAction->f_pMsg) &&
- !strcmp(getMSG(pMsg), getMSG(pAction->f_pMsg)) &&
+ !ustrcmp(getMSG(pMsg), getMSG(pAction->f_pMsg)) &&
!strcmp(getHOSTNAME(pMsg), getHOSTNAME(pAction->f_pMsg)) &&
- !strcmp(getPROCID(pMsg), getPROCID(pAction->f_pMsg)) &&
- !strcmp(getAPPNAME(pMsg), getAPPNAME(pAction->f_pMsg))) {
+ !strcmp(getPROCID(pMsg, LOCK_MUTEX), getPROCID(pAction->f_pMsg, LOCK_MUTEX)) &&
+ !strcmp(getAPPNAME(pMsg, LOCK_MUTEX), getAPPNAME(pAction->f_pMsg, LOCK_MUTEX))) {
pAction->f_prevcount++;
- dbgprintf("msg repeated %d times, %ld sec of %d.\n",
+ DBGPRINTF("msg repeated %d times, %ld sec of %d.\n",
pAction->f_prevcount, (long) getActNow(pAction) - pAction->f_time,
repeatinterval[pAction->f_repeatcount]);
/* use current message, so we have the new timestamp (means we need to discard previous one) */
@@ -746,10 +818,36 @@ actionCallAction(action_t *pAction, msg_t *pMsg)
}
finalize_it:
+ RETiRet;
+}
+
+/* call the configured action. Does all necessary housekeeping.
+ * rgerhards, 2007-08-01
+ * FYI: currently, this function is only called from the queue
+ * consumer. So we (conceptually) run detached from the input
+ * threads (which also means we may run much later than when the
+ * message was generated).
+ */
+#pragma GCC diagnostic ignored "-Wempty-body"
+rsRetVal
+actionCallAction(action_t *pAction, msg_t *pMsg)
+{
+ DEFiRet;
+ int iCancelStateSave;
+
+ ISOBJ_TYPE_assert(pMsg, msg);
+ ASSERT(pAction != NULL);
+
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave);
+ LockObj(pAction);
+ pthread_cleanup_push(mutexCancelCleanup, pAction->Sync_mut);
+ pthread_setcancelstate(iCancelStateSave, NULL);
+ iRet = doActionCallAction(pAction, pMsg);
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave);
UnlockObj(pAction);
pthread_cleanup_pop(0); /* remove mutex cleanup handler */
pthread_setcancelstate(iCancelStateSave, NULL);
+
RETiRet;
}
#pragma GCC diagnostic warning "-Wempty-body"
@@ -763,6 +861,7 @@ actionAddCfSysLineHdrl(void)
{
DEFiRet;
+ 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 *)"actionqueuemaxdiskspace", 0, eCmdHdlrSize, NULL, &iActionQueMaxDiskSpace, NULL));
@@ -771,6 +870,7 @@ actionAddCfSysLineHdrl(void)
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(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));
@@ -785,6 +885,7 @@ actionAddCfSysLineHdrl(void)
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 *)"repeatedmsgcontainsoriginalmsg", 0, eCmdHdlrBinary, NULL, &bActionRepMsgHasMsg, NULL));
finalize_it:
RETiRet;
@@ -809,15 +910,18 @@ 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 processed this config line.\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->bExecWhenPrevSusp = bActExecWhenPrevSusp;
pAction->iSecsExecOnceInterval = iActExecOnceInterval;
pAction->iExecEveryNthOccur = iActExecEveryNthOccur;
pAction->iExecEveryNthOccurTO = iActExecEveryNthOccurTO;
+ pAction->bRepMsgHasMsg = bActionRepMsgHasMsg;
iActExecEveryNthOccur = 0; /* auto-reset */
iActExecEveryNthOccurTO = 0; /* auto-reset */
@@ -830,9 +934,9 @@ addAction(action_t **ppAction, modInfo_t *pMod, void *pModData, omodStringReques
*/
if(pAction->iNumTpls > 0) {
/* we first need to create the template pointer array */
- if((pAction->ppTpl = calloc(pAction->iNumTpls, sizeof(struct template *))) == NULL) {
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- }
+ CHKmalloc(pAction->ppTpl = (struct template **)calloc(pAction->iNumTpls, sizeof(struct template *)));
+ CHKmalloc(pAction->ppMsgs = (uchar**) calloc(pAction->iNumTpls, sizeof(uchar *)));
+ CHKmalloc(pAction->lenMsgs = (size_t*) calloc(pAction->iNumTpls, sizeof(size_t)));
}
for(i = 0 ; i < pAction->iNumTpls ; ++i) {
@@ -858,7 +962,14 @@ addAction(action_t **ppAction, modInfo_t *pMod, void *pModData, omodStringReques
ABORT_FINALIZE(RS_RET_RQD_TPLOPT_MISSING);
}
- dbgprintf("template: '%s' assigned\n", pTplName);
+ /* set parameter-passing mode */
+ if(iTplOpts & OMSR_TPL_AS_ARRAY) {
+ pAction->eParamPassing = ACT_ARRAY_PASSING;
+ } else {
+ pAction->eParamPassing = ACT_STRING_PASSING;
+ }
+
+ DBGPRINTF("template: '%s' assigned\n", pTplName);
}
pAction->pMod = pMod;
@@ -867,7 +978,7 @@ addAction(action_t **ppAction, modInfo_t *pMod, void *pModData, omodStringReques
if(pMod->isCompatibleWithFeature(sFEATURERepeatedMsgReduction) == RS_RET_OK)
pAction->f_ReduceRepeated = bReduceRepeatMsgs;
else {
- dbgprintf("module is incompatible with RepeatedMsgReduction - turned off\n");
+ DBGPRINTF("module is incompatible with RepeatedMsgReduction - turned off\n");
pAction->f_ReduceRepeated = 0;
}
pAction->bEnabled = 1; /* action is enabled */
diff --git a/action.h b/action.h
index d26d15b6..579a1215 100644
--- a/action.h
+++ b/action.h
@@ -43,10 +43,10 @@ struct action_s {
time_t tActNow; /* the current time for an action execution. Initially set to -1 and
populated on an as-needed basis. This is a performance optimization. */
time_t tLastExec; /* time this action was last executed */
- int bExecWhenPrevSusp;/* execute only when previous action is suspended? */
+ bool bExecWhenPrevSusp;/* execute only when previous action is suspended? */
int iSecsExecOnceInterval; /* if non-zero, minimum seconds to wait until action is executed again */
short bEnabled; /* is the related action enabled (1) or disabled (0)? */
- short bSuspended; /* is the related action temporarily suspended? */
+ bool bSuspended; /* is the related action temporarily suspended? */
time_t ttResumeRtry; /* when is it time to retry the resume? */
int iResumeInterval;/* resume interval for this action */
int iResumeRetryCount;/* how often shall we retry a suspended action? (-1 --> eternal) */
@@ -57,19 +57,25 @@ struct action_s {
time_t tLastOccur; /* time last occurence was seen (for timing them out) */
struct modInfo_s *pMod;/* pointer to output module handling this selector */
void *pModData; /* pointer to module data - content is module-specific */
- int f_ReduceRepeated;/* reduce repeated lines 0 - no, 1 - yes */
+ bool bRepMsgHasMsg; /* "message repeated..." has msg fragment in it (0-no, 1-yes) */
+ short f_ReduceRepeated;/* reduce repeated lines 0 - no, 1 - yes */
int f_prevcount; /* repetition cnt of prevline */
int f_repeatcount; /* number of "repeated" msgs */
+ enum { ACT_STRING_PASSING = 0, ACT_ARRAY_PASSING = 1 }
+ eParamPassing; /* mode of parameter passing to action */
int iNumTpls; /* number of array entries for template element below */
struct template **ppTpl;/* array of template to use - strings must be passed to doAction
* in this order. */
- struct msg* f_pMsg; /* pointer to the message (this will replace the other vars with msg
+ msg_t *f_pMsg; /* pointer to the message (this will replace the other vars with msg
* content later). This is preserved after the message has been
* processed - it is also used to detect duplicates.
*/
- queue_t *pQueue; /* action queue */
+ qqueue_t *pQueue; /* action queue */
SYNC_OBJ_TOOL; /* required for mutex support */
pthread_mutex_t mutActExec; /* mutex to guard actual execution of doAction for single-threaded modules */
+ uchar *pszName; /* action name (for documentation) */
+ uchar **ppMsgs; /* pointer to action-calling parameters (kept in structure to save alloc() time!) */
+ size_t *lenMsgs; /* length of message in ppMsgs */
};
typedef struct action_s action_t;
@@ -85,6 +91,7 @@ rsRetVal actionSetGlobalResumeInterval(int iNewVal);
rsRetVal actionDoAction(action_t *pAction);
rsRetVal actionCallAction(action_t *pAction, msg_t *pMsg);
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);
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 00000000..daa87a2a
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+(test -f $srcdir/configure.ac) || {
+ echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
+ echo " top-level package directory"
+ exit 1
+}
+
+if test -z "$*"; then
+ echo "**Warning**: I am going to run \`configure' with no arguments."
+ echo "If you wish to pass any to it, please specify them on the"
+ echo \`$0\'" command line."
+ echo
+fi
+
+(cd $srcdir && autoreconf --verbose --force --install) || exit 1
+
+conf_flags="--enable-shave --cache-file=config.cache"
+
+if test x$NOCONFIGURE = x; then
+ echo Running $srcdir/configure $conf_flags "$@" ...
+ $srcdir/configure $conf_flags "$@" \
+ && echo Now type \`make\' to compile. || exit 1
+else
+ echo Skipping configure process.
+fi
+
+
diff --git a/configure.ac b/configure.ac
index 91c3cbfa..0e5544b1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.61)
-AC_INIT([rsyslog],[3.22.3],[rsyslog@lists.adiscon.com])
+AC_INIT([rsyslog],[4.6.5],[rsyslog@lists.adiscon.com])
AM_INIT_AUTOMAKE
AC_CONFIG_SRCDIR([ChangeLog])
AC_CONFIG_MACRO_DIR([m4])
@@ -10,6 +10,12 @@ AC_CONFIG_HEADERS([config.h])
AC_GNU_SOURCE
+# check for Java compiler
+AC_CHECK_PROG(HAVE_JAVAC, [javac], [yes])
+if test x"$HAVE_JAVAC" = x"yes"; then
+ AC_MSG_WARN([no javac found, disabling features depending on it])
+fi
+
# Checks for programs.
AC_PROG_CC
AM_PROG_CC_C_O
@@ -35,6 +41,13 @@ case "${host}" in
# do not DEFINE OS_BSD
os_type="bsd"
;;
+ *-*-solaris*)
+ os_type="solaris"
+ AC_DEFINE([OS_SOLARIS], [1], [Indicator for a Solaris OS])
+ AC_DEFINE([_POSIX_PTHREAD_SEMANTICS], [1], [Use POSIX pthread semantics])
+ SOL_LIBS="-lsocket -lnsl"
+ AC_SUBST(SOL_LIBS)
+ ;;
esac
AC_DEFINE_UNQUOTED([HOSTENV], "$host", [the host environment, can be queried via a system variable])
@@ -56,7 +69,7 @@ AC_SUBST(DL_LIBS)
AC_HEADER_RESOLV
AC_HEADER_STDC
AC_HEADER_SYS_WAIT
-AC_CHECK_HEADERS([arpa/inet.h libgen.h fcntl.h locale.h netdb.h netinet/in.h paths.h stddef.h stdlib.h string.h sys/file.h sys/ioctl.h sys/param.h sys/socket.h sys/time.h sys/stat.h syslog.h unistd.h utmp.h])
+AC_CHECK_HEADERS([arpa/inet.h libgen.h malloc.h fcntl.h locale.h netdb.h netinet/in.h paths.h stddef.h stdlib.h string.h sys/file.h sys/ioctl.h sys/param.h sys/socket.h sys/time.h sys/stat.h syslog.h unistd.h utmp.h utmpx.h sys/epoll.h sys/prctl.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
@@ -91,7 +104,7 @@ AC_TYPE_SIGNAL
AC_FUNC_STAT
AC_FUNC_STRERROR_R
AC_FUNC_VPRINTF
-AC_CHECK_FUNCS([flock basename alarm clock_gettime gethostbyname gethostname gettimeofday localtime_r memset mkdir regcomp select setid socket strcasecmp strchr strdup strerror strndup strnlen strrchr strstr strtol strtoul uname ttyname_r])
+AC_CHECK_FUNCS([flock basename alarm clock_gettime gethostbyname gethostname gettimeofday localtime_r memset mkdir regcomp select setid socket strcasecmp strchr strdup strerror strndup strnlen strrchr strstr strtol strtoul uname ttyname_r epoll_wait getline malloc_trim prctl fdatasync lseek64])
# Check for MAXHOSTNAMELEN
AC_MSG_CHECKING(for MAXHOSTNAMELEN)
@@ -137,19 +150,14 @@ AC_ARG_WITH(moddirs,
AM_CONDITIONAL(WITH_MODDIRS, test x$moddirs != x)
AC_SUBST(moddirs)
+
# Large file support
-AC_ARG_ENABLE(largefile,
- [AS_HELP_STRING([--enable-largefile],[Enable large file support @<:@default=yes@:>@])],
- [case "${enableval}" in
- yes) enable_largefile="yes" ;;
- no) enable_largefile="no" ;;
- *) AC_MSG_ERROR(bad value ${enableval} for --enable-largefile) ;;
- esac],
- [enable_largefile="yes"]
-)
-if test "$enable_largefile" = "no"; then
- AC_DEFINE(NOLARGEFILE, 1, [Defined when large file support is disabled.])
-fi
+# http://www.gnu.org/software/autoconf/manual/html_node/System-Services.html#index-AC_005fSYS_005fLARGEFILE-1028
+AC_SYS_LARGEFILE
+case "${enable_largefile}" in
+ no) ;;
+ *) enable_largefile="yes" ;;
+esac
# Regular expressions
AC_ARG_ENABLE(regexp,
@@ -166,6 +174,7 @@ if test "$enable_regexp" = "yes"; then
AC_DEFINE(FEATURE_REGEXP, 1, [Regular expressions support enabled.])
fi
+
# zlib compression
AC_ARG_ENABLE(zlib,
[AS_HELP_STRING([--enable-zlib],[Enable zlib compression support @<:@default=yes@:>@])],
@@ -176,6 +185,7 @@ AC_ARG_ENABLE(zlib,
esac],
[enable_zlib=yes]
)
+AM_CONDITIONAL(ENABLE_ZLIB, test x$enable_zlib = xyes)
if test "$enable_zlib" = "yes"; then
AC_CHECK_HEADER(zlib.h, [zlib_header="yes"], [zlib_header="no" enable_zlib="false"])
if test "$zlib_header" = "yes"; then
@@ -186,17 +196,18 @@ if test "$enable_zlib" = "yes"; then
fi
fi
+
#gssapi
AC_ARG_ENABLE(gssapi_krb5,
[AS_HELP_STRING([--enable-gssapi-krb5],[Enable GSSAPI Kerberos 5 support @<:@default=no@:>@])],
[case "${enableval}" in
- yes) want_gssapi_krb5="yes" ;;
- no) want_gssapi_krb5="no" ;;
+ yes) enable_gssapi_krb5="yes" ;;
+ no) enable_gssapi_krb5="no" ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-gssapi-krb5) ;;
esac],
- [want_gssapi_krb5=no]
+ [enable_gssapi_krb5=no]
)
-if test $want_gssapi_krb5 = yes; then
+if test $enable_gssapi_krb5 = yes; then
AC_CHECK_LIB(gssapi_krb5, gss_acquire_cred, [
AC_CHECK_HEADER(gssapi/gssapi.h, [
AC_DEFINE(USE_GSSAPI,,
@@ -206,7 +217,8 @@ if test $want_gssapi_krb5 = yes; then
])
])
fi
-AM_CONDITIONAL(ENABLE_GSSAPI, test x$want_gssapi_krb5 = xyes)
+AM_CONDITIONAL(ENABLE_GSSAPI, test x$enable_gssapi_krb5 = xyes)
+
# multithreading via pthreads
AC_ARG_ENABLE(pthreads,
@@ -233,7 +245,10 @@ if test "x$enable_pthreads" != "xno"; then
[
AC_DEFINE([USE_PTHREADS], [1], [Multithreading support enabled.])
PTHREADS_LIBS="-lpthread"
- PTHREADS_CFLAGS="-pthread"
+ case "${os_type}" in
+ solaris) PTHREADS_CFLAGS="-pthreads" ;;
+ *) PTHREADS_CFLAGS="-pthread" ;;
+ esac
AC_SUBST(PTHREADS_LIBS)
AC_SUBST(PTHREADS_CFLAGS)
],
@@ -245,6 +260,7 @@ if test "x$enable_pthreads" != "xno"; then
)
fi
+
# klog
AC_ARG_ENABLE(klog,
[AS_HELP_STRING([--enable-klog],[Integrated klog functionality @<:@default=yes@:>@])],
@@ -259,6 +275,7 @@ AM_CONDITIONAL(ENABLE_IMKLOG, test x$enable_klog = xyes)
AM_CONDITIONAL(ENABLE_IMKLOG_BSD, test x$os_type = xbsd)
AM_CONDITIONAL(ENABLE_IMKLOG_LINUX, test x$os_type = xlinux)
+
#
# SYSLOG_UNIXAF
#
@@ -279,6 +296,7 @@ AC_ARG_ENABLE([unix],
AC_DEFINE([SYSLOG_UNIXAF], [1], [Description])
])
+
# inet
AC_ARG_ENABLE(inet,
[AS_HELP_STRING([--enable-inet],[Enable networking support @<:@default=yes@:>@])],
@@ -294,6 +312,7 @@ if test "$enable_inet" = "yes"; then
AC_DEFINE(SYSLOG_INET, 1, [network support is integrated.])
fi
+
#
# The following define determines whether the package adheres to the
# file system standard.
@@ -315,6 +334,7 @@ AC_ARG_ENABLE([fsstnd],
AC_DEFINE([FSSTND], [1], [Description])
])
+
# debug
AC_ARG_ENABLE(debug,
[AS_HELP_STRING([--enable-debug],[Enable debug mode @<:@default=no@:>@])],
@@ -332,6 +352,7 @@ if test "$enable_debug" = "no"; then
AC_DEFINE(NDEBUG, 1, [Defined if debug mode is disabled.])
fi
+
# runtime instrumentation
AC_ARG_ENABLE(rtinst,
[AS_HELP_STRING([--enable-rtinst],[Enable runtime instrumentation mode @<:@default=no@:>@])],
@@ -346,6 +367,7 @@ if test "$enable_rtinst" = "yes"; then
AC_DEFINE(RTINST, 1, [Defined if runtime instrumentation mode is enabled.])
fi
+
# valgrind
AC_ARG_ENABLE(valgrind,
[AS_HELP_STRING([--enable-valgrind],[Enable valgrind support settings @<:@default=no@:>@])],
@@ -443,6 +465,40 @@ AC_SUBST(PGSQL_CFLAGS)
AC_SUBST(PGSQL_LIBS)
+# oracle (OCI) support
+AC_ARG_ENABLE(oracle,
+ [AS_HELP_STRING([--enable-oracle],[Enable native Oracle database support @<:@default=no@:>@])],
+ [case "${enableval}" in
+ yes) enable_oracle="yes" ;;
+ no) enable_oracle="no" ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-oracle) ;;
+ esac],
+ [enable_oracle=no]
+)
+if test "x$enable_oracle" = "xyes"; then
+ AC_CHECK_PROG(
+ [HAVE_ORACLE_CONFIG],
+ [oracle-instantclient-config],
+ [yes],,,
+ )
+ if test "x${HAVE_ORACLE_CONFIG}" != "xyes"; then
+ AC_MSG_FAILURE([oracle-instantclient-config not found in PATH])
+ fi
+ AC_CHECK_LIB(
+ [occi],
+ [OCIEnvCreate],
+ [ORACLE_CFLAGS="`oracle-instantclient-config --cflags`"
+ ORACLE_LIBS="`oracle-instantclient-config --libs`"
+ ],
+ [AC_MSG_FAILURE([Oracle (OCI) libraray is missing])],
+ [`oracle-instantclient-config --libs --cflags`]
+ )
+fi
+AM_CONDITIONAL(ENABLE_ORACLE, test x$enable_oracle = xyes)
+AC_SUBST(ORACLE_CFLAGS)
+AC_SUBST(ORACLE_LIBS)
+
+
# libdbi support
AC_ARG_ENABLE(libdbi,
[AS_HELP_STRING([--enable-libdbi],[Enable libdbi database support @<:@default=no@:>@])],
@@ -556,6 +612,23 @@ AC_ARG_ENABLE(rsyslogd,
AM_CONDITIONAL(ENABLE_RSYSLOGD, test x$enable_rsyslogd = xyes)
+# capability to enable an extended testbench. By default, this is off. The reason
+# for this switch is that some test simply take too long to execute them on a regular
+# basis. So we enable to skip them, while the majority of tests can still be used. The
+# idea is that at least "make distcheck" executes the extended testbench, and also
+# developers should explicitely enable it after important changes. -- rgerhards, 2010-04-12
+AC_ARG_ENABLE(extended_tests,
+ [AS_HELP_STRING([--enable-extended-tests],[extended testbench @<:@default=no@:>@])],
+ [case "${enableval}" in
+ yes) enable_rsyslogd="yes" ;;
+ no) enable_rsyslogd="no" ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-extended-tests) ;;
+ esac],
+ [enable_extended_tests=no]
+)
+AM_CONDITIONAL(ENABLE_EXTENDED_TESTS, test x$enable_extended_tests = xyes)
+
+
# Mail support (so far we do not need a library, but we need to turn this on and off)
AC_ARG_ENABLE(mail,
[AS_HELP_STRING([--enable-mail],[Enable mail support @<:@default=no@:>@])],
@@ -603,6 +676,7 @@ AM_CONDITIONAL(ENABLE_RELP, test x$enable_relp = xyes)
AC_SUBST(RELP_CFLAGS)
AC_SUBST(RELP_LIBS)
+
# RFC 3195 support
AC_ARG_ENABLE(rfc3195,
[AS_HELP_STRING([--enable-rfc3195],[Enable RFC3195 support @<:@default=no@:>@])],
@@ -621,9 +695,26 @@ AC_SUBST(LIBLOGGING_CFLAGS)
AC_SUBST(LIBLOGGING_LIBS)
-# 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.
+# enable/disable the testbench (e.g. because some important parts
+# are missing)
+AC_ARG_ENABLE(testbench,
+ [AS_HELP_STRING([--enable-testbench],[file input module enabled @<:@default=yes@:>@])],
+ [case "${enableval}" in
+ yes) enable_testbench="yes" ;;
+ no) enable_testbench="no" ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-testbench) ;;
+ esac],
+ [enable_testbench=yes]
+)
+if test "$enable_testbench" = "yes"; then
+ if test x$HAVE_JAVAC = x; then
+ enable_testbench='no'
+ fi
+fi
+AM_CONDITIONAL(ENABLE_TESTBENCH, test x$enable_testbench = xyes)
+
+
+# settings for the file input module
AC_ARG_ENABLE(imfile,
[AS_HELP_STRING([--enable-imfile],[file input module enabled @<:@default=no@:>@])],
[case "${enableval}" in
@@ -633,14 +724,49 @@ AC_ARG_ENABLE(imfile,
esac],
[enable_imfile=no]
)
-#
-# you may want to do some library checks here - see snmp, mysql, pgsql modules
-# for samples
-#
AM_CONDITIONAL(ENABLE_IMFILE, test x$enable_imfile = xyes)
-AM_CONDITIONAL(ENABLE_IMTEMPLATE, test x$enable_imtemplate = xyes)
-# end of copy template - be sure to serach for imtemplate to find everything!
+
+# settings for the omprog output module
+AC_ARG_ENABLE(omprog,
+ [AS_HELP_STRING([--enable-omprog],[Compiles omprog module @<:@default=no@:>@])],
+ [case "${enableval}" in
+ yes) enable_omprog="yes" ;;
+ no) enable_omprog="no" ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-omprog) ;;
+ esac],
+ [enable_omprog=no]
+)
+AM_CONDITIONAL(ENABLE_OMPROG, test x$enable_omprog = xyes)
+
+
+# settings for omstdout
+AC_ARG_ENABLE(omstdout,
+ [AS_HELP_STRING([--enable-omstdout],[Compiles stdout module @<:@default=no@:>@])],
+ [case "${enableval}" in
+ yes) enable_omstdout="yes" ;;
+ no) enable_omstdout="no" ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-omstdout) ;;
+ esac],
+ [enable_omstdout=no]
+)
+AM_CONDITIONAL(ENABLE_OMSTDOUT, test x$enable_omstdout = xyes)
+
+# This provides a vehicle to integrate custom modules, that are not
+# part of rsyslog, into the build process. It is named cust1, so that
+# additional such modules can easily be added.
+AC_ARG_ENABLE(cust1,
+ [AS_HELP_STRING([--enable-cust1],[Compiles stdout module @<:@default=no@:>@])],
+ [case "${enableval}" in
+ yes) enable_cust1="yes" ;;
+ no) enable_cust1="no" ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-cust1) ;;
+ esac],
+ [enable_cust1=no]
+)
+AM_CONDITIONAL(ENABLE_CUST1, test x$enable_cust1 = 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.
@@ -658,10 +784,35 @@ AC_ARG_ENABLE(imtemplate,
# for samples
#
AM_CONDITIONAL(ENABLE_IMTEMPLATE, test x$enable_imtemplate = xyes)
-# end of copy template - be sure to serach for imtemplate to find everything!
+# 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!
+
+
+SHAVE_INIT
AC_CONFIG_FILES([Makefile \
+ shave \
+ shave-libtool \
runtime/Makefile \
tools/Makefile \
doc/Makefile \
@@ -673,6 +824,9 @@ AC_CONFIG_FILES([Makefile \
plugins/immark/Makefile \
plugins/imklog/Makefile \
plugins/imtemplate/Makefile \
+ plugins/omtemplate/Makefile \
+ plugins/omprog/Makefile \
+ plugins/omstdout/Makefile \
plugins/imfile/Makefile \
plugins/imrelp/Makefile \
plugins/imdiag/Makefile \
@@ -684,33 +838,52 @@ AC_CONFIG_FILES([Makefile \
plugins/omlibdbi/Makefile \
plugins/ommail/Makefile \
plugins/omsnmp/Makefile \
+ plugins/omoracle/Makefile \
+ plugins/cust1/Makefile \
tests/Makefile])
AC_OUTPUT
echo "****************************************************"
echo "rsyslog will be compiled with the following settings:"
echo
-echo "Multithreading support enabled: $enable_pthreads"
-echo "Klog functionality enabled: $enable_klog ($os_type)"
-echo "Regular expressions support enabled: $enable_regexp"
-echo "Zlib compression support enabled: $enable_zlib"
-echo "MySql support enabled: $enable_mysql"
-echo "libdbi support enabled: $enable_libdbi"
-echo "PostgreSQL support enabled: $enable_pgsql"
-echo "SNMP support enabled: $enable_snmp"
-echo "Mail support enabled: $enable_mail"
-echo "RELP support enabled: $enable_relp"
-echo "imdiag enabled: $enable_imdiag"
-echo "file input module enabled: $enable_imfile"
-echo "input template module will be compiled: $enable_imtemplate"
-echo "Large file support enabled: $enable_largefile"
-echo "Networking support enabled: $enable_inet"
-echo "GnuTLS network stream driver enabled: $enable_gnutls"
-echo "Enable GSSAPI Kerberos 5 support: $want_gssapi_krb5"
-echo "Debug mode enabled: $enable_debug"
-echo "Runtime Instrumentation enabled: $enable_rtinst"
-echo "Diagnostic tools enabled: $enable_diagtools"
-echo "valgrind support settings enabled: $enable_valgrind"
-echo "rsyslog runtime will be built: $enable_rsyslogrt"
-echo "rsyslogd will be built: $enable_rsyslogd"
-
+echo " Multithreading support enabled: $enable_pthreads"
+echo " Large file support enabled: $enable_largefile"
+echo " Networking support enabled: $enable_inet"
+echo " Regular expressions support enabled: $enable_regexp"
+echo " Zlib compression support enabled: $enable_zlib"
+echo " rsyslog runtime will be built: $enable_rsyslogrt"
+echo " rsyslogd will be built: $enable_rsyslogd"
+echo " custom module 1 will be built: $enable_cust1"
+echo
+echo "---{ input plugins }---"
+echo " Klog functionality enabled: $enable_klog ($os_type)"
+echo " imdiag enabled: $enable_imdiag"
+echo " file input module enabled: $enable_imfile"
+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 " omstdout module will be compiled: $enable_omstdout"
+echo " output template module will be compiled: $enable_omtemplate"
+echo
+echo "---{ database support }---"
+echo " MySql support enabled: $enable_mysql"
+echo " libdbi support enabled: $enable_libdbi"
+echo " PostgreSQL support enabled: $enable_pgsql"
+echo " Oracle (OCI) support enabled: $enable_oracle"
+echo
+echo "---{ protocol support }---"
+echo " GnuTLS network stream driver enabled: $enable_gnutls"
+echo " GSSAPI Kerberos 5 support enabled: $enable_gssapi_krb5"
+echo " RELP support enabled: $enable_relp"
+echo " SNMP support enabled: $enable_snmp"
+echo
+echo "---{ debugging support }---"
+echo " Testbench enabled: $enable_testbench"
+echo " Extended Testbench enabled: $enable_extended_tests"
+echo " Debug mode enabled: $enable_debug"
+echo " Runtime Instrumentation enabled: $enable_rtinst"
+echo " Diagnostic tools enabled: $enable_diagtools"
+echo " Valgrind support settings enabled: $enable_valgrind"
+echo
diff --git a/dirty.h b/dirty.h
index 796df18e..0153cb69 100644
--- a/dirty.h
+++ b/dirty.h
@@ -27,24 +27,14 @@
#ifndef DIRTY_H_INCLUDED
#define DIRTY_H_INCLUDED 1
-/* Flags to logmsg().
- */
-#define NOFLAG 0x000 /* no flag is set (to be used when a flag must be specified and none is required) */
-#define INTERNAL_MSG 0x001 /* msg generated by logmsgInternal() --> special handling */
-/* NO LONGER USED: #define SYNC_FILE 0x002 / * do fsync on file after printing */
-#define IGNDATE 0x004 /* ignore, if given, date in message and use date of reception as msg date */
-#define MARK 0x008 /* this message is a mark */
-
-#define MSG_PARSE_HOSTNAME 1
-#define MSG_DONT_PARSE_HOSTNAME 0
-
+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 bParseHost, int flags, flowControl_t flowCtlTypeu, uchar *pszInputName);
-
-/* TODO: the following 2 need to go in conf obj interface... */
-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);
+rsRetVal parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int flags, flowControl_t flowCtlTypeu, prop_t *pInputName, struct syslogTime *stTime, time_t ttGenTime);
+int parseRFCSyslogMsg(msg_t *pMsg, int flags);
+int parseLegacySyslogMsg(msg_t *pMsg, int flags);
+rsRetVal diagGetMainMsgQSize(int *piSize); /* for imdiag */
+char* getFIOPName(unsigned iFIOP);
/* Intervals at which we flush out "message repeated" messages,
* in seconds after previous message is logged. After each flush,
@@ -61,6 +51,9 @@ extern int bReduceRepeatMsgs;
#define BACKOFF(f) { if (++(f)->f_repeatcount > MAXREPEAT) \
(f)->f_repeatcount = MAXREPEAT; \
}
+extern int bDropTrailingLF;
+extern uchar cCCEscapeChar;
+extern int bEscapeCCOnRcv;
#ifdef USE_NETZIP
/* config param: minimum message size to try compression. The smaller
* the message, the less likely is any compression gain. We check for
diff --git a/doc/Makefile.am b/doc/Makefile.am
index e9b6fe96..ca2ee71c 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -5,6 +5,7 @@ html_files = \
features.html \
generic_design.html \
expression.html \
+ droppriv.html \
history.html \
how2help.html \
install.html \
@@ -12,13 +13,13 @@ html_files = \
ipv6.html \
log_rotation_fix_size.html \
manual.html \
- man_rsyslogd.html \
modules.html \
property_replacer.html \
rsyslog_ng_comparison.html \
rsyslog_conf.html \
rsyslog-example.conf \
rsyslog_mysql.html \
+ rsyslog_pgsql.html \
rsyslog_packages.html \
rsyslog_high_database_rate.html \
rsyslog_php_syslog_ng.html \
@@ -32,6 +33,7 @@ html_files = \
dev_queue.html \
omsnmp.html \
ommysql.html \
+ omoracle.html \
omlibdbi.html \
imfile.html \
imtcp.html \
@@ -75,6 +77,7 @@ html_files = \
rsconf1_filecreatemode.html \
rsconf1_filegroup.html \
rsconf1_fileowner.html \
+ rsconf1_generateconfiggraph.html \
rsconf1_gssforwardservicename.html \
rsconf1_gsslistenservicename.html \
rsconf1_gssmode.html \
@@ -87,6 +90,7 @@ html_files = \
rsconf1_resetconfigvariables.html \
rsconf1_umask.html \
v3compatibility.html \
+ v4compatibility.html \
im3195.html \
netstream.html \
ns_gtls.html \
@@ -98,6 +102,31 @@ html_files = \
omrelp.html \
syslog_parsing.html \
troubleshoot.html \
+ rsyslog_conf_actions.html \
+ rsyslog_conf_examples.html \
+ rsyslog_conf_filter.html \
+ rsyslog_conf_global.html \
+ rsyslog_conf_modules.html \
+ rsyslog_conf_output.html \
+ rsyslog_conf_templates.html \
+ rsyslog_conf_nomatch.html \
+ queues_analogy.html \
+ multi_ruleset.html \
src/classes.dia
-EXTRA_DIST = $(html_files)
+grfx_files = \
+ rsyslog_confgraph_complex.png\
+ rsyslog_confgraph_std.png \
+ direct_queue0.png \
+ direct_queue1.png \
+ direct_queue2.png \
+ direct_queue3.png \
+ direct_queue_rsyslog.png \
+ direct_queue_rsyslog2.png \
+ direct_queue_directq.png \
+ dataflow.png \
+ queue_analogy_tv.png \
+ gssapi.png \
+ rsyslog-vers.png
+
+EXTRA_DIST = $(html_files) $(grfx_files)
diff --git a/doc/dataflow.png b/doc/dataflow.png
new file mode 100644
index 00000000..fd614d8c
--- /dev/null
+++ b/doc/dataflow.png
Binary files differ
diff --git a/doc/debug.html b/doc/debug.html
index de77f04a..46759986 100644
--- a/doc/debug.html
+++ b/doc/debug.html
@@ -1,41 +1,145 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html><head>
-<meta http-equiv="Content-Language" content="en"><title>Debug Support</title></head>
+<html>
+<head>
+<meta http-equiv="Content-Language" content="en">
+<title>Rsyslog Debug Support</title></head>
<body>
-<h1>Debug Support</h1>
+<h1>Rsyslog Debug Support</h1>
<p>
-Rsyslog provides a number of debug aids. Some of them are activated by
+Rsyslog provides a number of debug aides. Some of them are activated by
adding the --enable-rtinst ./configure option ("rtinst" means runtime
instrumentation). Turning debugging on obviously costs some performance
(in some cases considerable).
</p>
<p>This is document is just being created and thus terse.</p>
-<p style="font-weight: bold;">Signals supported</p>
-<p>SIGUSR1 - turns debug messages on and off (expect this signal
-to go away over time)</p>
-<p>SIGUSR2 - outputs debug information (including active threads
+<h2>Signals supported</h2>
+<p><b>SIGUSR1</b> - turns debug messages on and off. Note that for this
+signal to work, rsyslogd must be running with debugging enabled, either
+via the -d command line switch or the environment options specified below.
+It is <b>not</b> required that rsyslog was compiled with debugging enabled
+(but depending on the settings this may lead to better debug info).
+<p><b>SIGUSR2</b> - outputs debug information (including active threads
and a call stack) for the state when SIGUSR2 was received. This is a
-one-time output. Can be sent as often as the user likes.</p>
-<p style="font-weight: bold;">Environment Variables</p>
-<p>There are two environment variables that set several debug settings. The "RSYSLOG_DEBUGLOG" (sample: &nbsp;RSYSLOG_DEBUGLOG="/path/to/debuglog/")
+one-time output. Can be sent as often as the user likes.
+<p><b>Note:</b> this signal <b>may go away</b> in later releases and may
+be replaced by something else.</p>
+<h2>Environment Variables</h2>
+<p>There are two environment variables that set several debug settings:
+<ul>
+<li>The "RSYSLOG_DEBUGLOG" (sample: &nbsp;RSYSLOG_DEBUGLOG="/path/to/debuglog/")
writes (allmost)
all debug message to the specified log file in addition to stdout. Some
system messages (e.g. segfault or abort message) are not written to the
-file as we can not capture them. Runtime debug support is controlled by
-"RSYSLOG_DEBUG". It contains an option string with the following
-options possible (all are case insensitive):</p><ul><li><span style="font-weight: bold;">LogFuncFlow</span> - print out the logical flow of functions (entering and exiting them)</li><li><span style="font-weight: bold;">FileTrace</span> - specifies which files to trace LogFuncFlow. If <span style="font-weight: bold;">not</span>
+file as we can not capture them.
+<li>Runtime debug support is controlled by "RSYSLOG_DEBUG".
+<p>The "RSYSLOG_DEBUG" environment variable contains an option string with the following
+options possible (all are case insensitive):</p>
+<ul>
+<li><b>LogFuncFlow</b> - print out the logical flow of functions (entering and exiting them)</li>
+<li><b>FileTrace</b> - specifies which files to trace LogFuncFlow. If <b>not</b>
set (the default), a LogFuncFlow trace is provided for all files. Set
to limit it to the files specified. FileTrace may be specified multiple
times, one file each (e.g. export RSYSLOG_DEBUG="LogFuncFlow
-FileTrace=vm.c FileTrace=expr.c"</li><li><span style="font-weight: bold;">PrintFuncDB</span> - print the content of the debug function database whenever debug information is printed (e.g. abort case)!</li><li><span style="font-weight: bold;">PrintAllDebugInfoOnExit</span> - print all debug information immediately before rsyslogd exits (<span style="font-weight: bold; font-style: italic;">currently not implemented!</span>)</li><li><span style="font-weight: bold;">PrintMutexAction</span> - print mutex action as it happens. Useful for finding deadlocks and such.</li><li><span style="font-weight: bold;">NoLogTimeStamp</span> - do not prefix log lines with a timestamp (default is to do that).</li><li><span style="font-weight: bold;">NoStdOut</span> - do not emit debug messages to stdout. If RSYSLOG_DEBUGLOG is not set, this means no messages will be displayed at all.</li><li><span style="font-weight: bold;">help</span> - display a very short list of commands - hopefully a life saver if you can't access the documentation...</li></ul>
+FileTrace=vm.c FileTrace=expr.c"</li>
+<li><b>PrintFuncDB</b> - print the content of the debug function database whenever debug information is printed (e.g. abort case)!</li>
+<li><b>PrintAllDebugInfoOnExit</b> - print all debug information immediately before rsyslogd exits (<span style="font-weight: bold; font-style: italic;">currently not implemented!</span>)</li>
+<li><b>PrintMutexAction</b> - print mutex action as it happens. Useful for finding deadlocks and such.</li>
+<li><b>NoLogTimeStamp</b> - do not prefix log lines with a timestamp (default is to do that).</li>
+<li><b>NoStdOut</b> - do not emit debug messages to stdout. If RSYSLOG_DEBUGLOG is not set, this means no messages will be displayed at all.</li>
+<li><b>Debug</b> - if present, turns on the debug system and enables debug output
+<li><b>DebugOnDemand</b> - if present, turns on the debug system but does not enable
+debug output itself. You need to send SIGUSR1 to turn it on when desired.
+<li><b>help</b> - display a very short list of commands - hopefully a life saver if you can't access the documentation...</li>
+</ul>
+</ul>
+<h3>Why Environment Variables?</h3>
+<p>You may ask why we use environment variables for debug-system parameters and not
+the usual rsyslog.conf configuration commands. After all, environment variables force one
+to change distro-specific configuration files, whereas regular configuration directives
+would fit nicely into the one central rsyslog.conf.
+<p>The problem here is that many settings of the debug system must be initialized
+before the full rsyslog engine starts up. At that point, there is no such thing like
+rsyslog.conf or the objects needed to process it present in an running instance.
+And even if we would enable to change settings some time later, that would mean that
+we can not correctly monitor (and debug) the initial startup phase of rsyslogd. What
+makes matters worse is that during this startup phase (and never again later!) some
+of the base debug structure needs to be created, at least if the build is
+configured for that (many of these things only happen in --enable-rtinst mode). So
+if we do not initialize the debug system <b>before</b> actually startig up the
+rsyslog core, we get a number of data structures wrong.
+<p>For these reasons, we utilize environment variables to initialize and configure
+the debugging system. We understand this may be somewhat painful, but now you know
+there are at least some good reasons for doing so.
+<h2>Getting debug information from a running Instance</h2>
+<p>It is possible to obtain debugging information from a running instance, but this requires
+some setup. We assume that the instance runs in the background, so debug output to
+stdout is not desired. As such, all debug information needs to go into a log file.
+<p>To create this setup, you need to
<ul>
+<li>point the RSYSLOG_DEBUGLOG environment variable to a file that is accessible
+during the while runtime (we strongly suggest a file in the local file system!)
+<li>set RSYSLOG_DEBUG at least to "DebugOnDeman NoStdOut"
+<li>make sure these environment variables are set in the correct (distro-specifc)
+startup script if you do not run rsyslogd interactively
</ul>
+<p>These settings enable the capability to react to SIGUSR1. The signal will toggle
+debug status when received. So send it one to turn debug loggin on, and send it again
+to turn debug logging off again. The third time it will be turned on again ... and so on.
+<p>On a typical system, you can signal rsyslogd as follows:
+<pre>
+kill -USR1 `cat /var/run/rsyslogd.pid`
+</pre>
+Important: there are backticks around the "cat"-command. If you use the regular
+quote it won't work. The debug log will show whether debug logging has been turned
+on or off. There is no other indication of the status.
+<p>Note: running with DebugOnDemand by itself does in practice not have any performance
+toll. However, switching debug logging on has a severe performance toll. Also, debug
+logging synchronizes much of the code, removing a lot of concurrency and thus
+potential race conditions. As such, the very same running instance may behave
+very differently with debug logging turned on vs. off. The on-demand debug log
+functionality is considered to be very valuable to analyze hard-to-find bugs that
+only manifest after a long runtime. Turning debug logging on a failing instance
+may reveal the cause of the failure. However, depending on the failure, debug logging
+may not even be successfully be turned on. Also note that with this rsyslog version we cannot
+obtain any debug information on events that happened <i>before</i> debug logging was
+turned on.
+<p>If an instance hangs, it is possible to obtain some useful information about the current
+threads and their calling stack by sending SIGUSR2. However, the usefulness of that
+information is very much depending on rsyslog compile-time settings, must importantly
+the --enable-rtinst configure flag. Note that activating this option causes additional overhead
+and slows down rsyslgod considerable. So if you do that, you need to check if it is
+capable to handle the workload. Also, threading behavior is modified by the
+runtime instrumentation.
+<p>Sending SIGUSR2 writes new process state information to the log file each time
+it is sent. So it may be useful to do that from time to time. It probably is most
+useful if the process seems to hang, in which case it may (may!) be able to output
+some diagnostic information on the current processing state. In that case, turning
+on the mutex debugging options (see above) is probably useful.
+<h2>Interpreting the Logs</h2>
+<p>Debug logs are primarily meant for rsyslog developers. But they may still provide valuable
+information to users. Just be warned that logs sometimes contains informaton the looks like
+an error, but actually is none. We put a lot of extra information into the logs, and there
+are some cases where it is OK for an error to happen, we just wanted to record it inside
+the log. The code handles many cases automatically. So, in short, the log may not make sense to
+you, but it (hopefully) makes sense to a developer. Note that we developers often need
+many lines of the log file, it is relatively rare that a problem can be diagnosed by
+looking at just a couple of (hundered) log records.
+<h2>Security Risks</h2>
+<p>The debug log will reveal potentially sensible information, including user accounts and
+passwords, to anyone able to read the log file. As such, it is recommended to properly
+guard access to the log file. Also, an instance running with debug log enabled runs much
+slower than one without. An attacker may use this to place carry out a denial-of-service
+attack or try to hide some information from the log file. As such, it is suggested to
+enable DebugOnDemand mode only for a reason. Note that when no debug mode is enabled,
+SIGUSR1 and SIGUSR2 are completely ignored.
+<p>When running in any of the debug modes (including on demand mode), an interactive
+instance of rsyslogd can be aborted by pressing ctl-c.
+<p>
<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&nbsp;© 2008 by <a href="http://www.gerhards.net/rainer">Rainer
-Gerhards</a> and
+<a href="http://www.rsyslog.com/">rsyslog</a> project.<br>
+Copyright &copy; 2008, 2009 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
<a href="http://www.adiscon.com/">Adiscon</a>.
Released under the GNU GPL version 3 or higher.</font></p>
-</body></html> \ No newline at end of file
+</body>
+</html>
diff --git a/doc/dev_oplugins.html b/doc/dev_oplugins.html
new file mode 100644
index 00000000..cc2f7f38
--- /dev/null
+++ b/doc/dev_oplugins.html
@@ -0,0 +1,178 @@
+<html>
+<head>
+<title>writing rsyslog output plugins (developer's guide)</title>
+</head>
+<body>
+<h1>Writing Rsyslog Output Plugins</h1>
+<p>This page is the begin of some developer documentation for writing output
+plugins. Doing so is quite easy (and that was a design goal), but there currently
+is only sparse documentation on the process available. I was tempted NOT to
+write this guide here because I know I will most probably not be able to
+write a complete guide.
+<p>However, I finally concluded that it may be better to have same information
+and pointers than to have nothing.
+<h2>Getting Started and Samples</h2>
+<p>The best to get started with rsyslog plugin development is by looking at
+existing plugins. All that start with "om" are <b>o</b>utput <b>m</b>odules. That
+means they are primarily thought of being message sinks. In theory, however, output
+plugins may aggergate other functionality, too. Nobody has taken this route so far
+so if you would like to do that, it is highly suggested to post your plan on the
+rsyslog mailing list, first (so that we can offer advise).
+<p>The rsyslog distribution tarball contains two plugins that are extremely well
+targeted for getting started:
+<ul>
+<li>omtemplate
+<li>omstdout
+</ul>
+Plugin omtemplate was specifically created to provide a copy template for new output
+plugins. It is bare of real functionality but has ample comments. Even if you decide
+to start from another plugin (or even from scratch), be sure to read omtemplate source
+and comments first. The omstdout is primarily a testing aide, but offers support for
+the two different parameter-passing conventions plugins can use (plus the way to
+differentiate between the two). It also is not bare of functionaly, only mostly
+bare of it ;). But you can actually execute it and play with it.
+<p>In any case, you should also read the comments in ./runtime/module-template.h.
+Output plugins are build based on a large set of code-generating macros. These
+macros handle most of the plumbing needed by the interface. As long as no
+special callback to rsyslog is needed (it typically is not), an output plugin does
+not really need to be aware that it is executed by rsyslog. As a plug-in programmer,
+you can (in most cases) "code as usual". However, all macros and entry points need to be
+provided and thus reading the code comments in the files mentioned is highly suggested.
+<p>In short, the best idea is to start with a template. Let's assume you start by
+copying omtemplate. Then, the basic steps you need to do are:
+<ul>
+<li>cp ./plugins/omtemplate ./plugins/your-plugin
+<li>mv cd ./plugins/your-plugin
+<li>vi Makefile.am, adjust to your-plugin
+<li>mv omtemplate.c your-plugin.c
+<li>cd ../..
+<li>vi Makefile.am configure.ac
+<br>search for omtemplate, copy and modify (follow comments)
+</ul>
+<p>Basically, this is all you need to do ... Well, except, of course, coding
+your plugin ;). For testing, you need rsyslog's debugging support. Some useful
+information is given in "<a href="troubleshoot.html">troubleshooting rsyslog</a>
+from the doc set.
+<h2>Special Topics</h2>
+<h3>Threading</h3>
+<p>Rsyslog uses massive parallel processing and multithreading. However, a plugin's entry
+points are guaranteed to be never called concurrently <b>for the same action</b>.
+That means your plugin must be able to be called concurrently by two or more
+threads, but you can be sure that for the same instance no concurrent calls
+happen. This is guaranteed by the interface specification and the rsyslog core
+guards against multiple concurrent calls. An instance, in simple words, is one
+that shares a single instanceData structure.
+<p>So as long as you do not mess around with global data, you do not need
+to think about multithreading (and can apply a purely sequential programming
+methodology).
+<p>Please note that duringt the configuraton parsing stage of execution, access to
+global variables for the configuration system is safe. In that stage, the core will
+only call sequentially into the plugin.
+<h3>Getting Message Data</h3>
+<p>The doAction() entry point of your plugin is provided with messages to be processed.
+It will only be activated after filtering and all other conditions, so you do not need
+to apply any other conditional but can simply process the message.
+<p>Note that you do NOT receive the full internal representation of the message
+object. There are various (including historical) reasons for this and, among
+others, this is a design decision based on security.
+<p>Your plugin will only receive what the end user has configured in a $template
+statement. However, starting with 4.1.6, there are two ways of receiving the
+template content. The default mode, and in most cases sufficient and optimal,
+is to receive a single string with the expanded template. As I said, this is usually
+optimal, think about writing things to files, emailing content or forwarding it.
+<p>The important philosophy is that a plugin should <b>never</b> reformat any
+of such strings - that would either remove the user's ability to fully control
+message formats or it would lead to duplicating code that is already present in the
+core. If you need some formatting that is not yet present in the core, suggest it
+to the rsyslog project, best done by sending a patch ;), and we will try hard to
+get it into the core (so far, we could accept all such suggestions - no promise, though).
+<p>If a single string seems not suitable for your application, the plugin can also
+request access to the template components. The typical use case seems to be databases, where
+you would like to access properties via specific fields. With that mode, you receive a
+char ** array, where each array element points to one field from the template (from
+left to right). Fields start at arrray index 0 and a NULL pointer means you have
+reached the end of the array (the typical Unix "poor man's linked list in an array"
+design). Note, however, that each of the individual components is a string. It is
+not a date stamp, number or whatever, but a string. This is because rsyslog processes
+strings (from a high-level design look at it) and so this is the natural data type.
+Feel free to convert to whatever you need, but keep in mind that malformed packets
+may have lead to field contents you'd never expected...
+<p>If you like to use the array-based parameter passing method, think that it
+is only available in rsyslog 4.1.6 and above. If you can accept that your plugin
+will not be working with previous versions, you do not need to handle pre 4.1.6 cases.
+However, it would be "nice" if you shut down yourself in these cases - otherwise the
+older rsyslog core engine will pass you a string where you expect the array of pointers,
+what most probably results in a segfault. To check whether or not the core supports the
+functionality, you can use this code sequence:
+<pre>
+<code>
+BEGINmodInit()
+ rsRetVal localRet;
+ rsRetVal (*pomsrGetSupportedTplOpts)(unsigned long *pOpts);
+ unsigned long opts;
+ int bArrayPassingSupported; /* does core support template passing as an array? */
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ /* check if the rsyslog core supports parameter passing code */
+ bArrayPassingSupported = 0;
+ localRet = pHostQueryEtryPt((uchar*)"OMSRgetSupportedTplOpts", &pomsrGetSupportedTplOpts);
+ if(localRet == RS_RET_OK) {
+ /* found entry point, so let's see if core supports array passing */
+ CHKiRet((*pomsrGetSupportedTplOpts)(&opts));
+ if(opts & OMSR_TPL_AS_ARRAY)
+ bArrayPassingSupported = 1;
+ } else if(localRet != RS_RET_ENTRY_POINT_NOT_FOUND) {
+ ABORT_FINALIZE(localRet); /* Something else went wrong, what is not acceptable */
+ }
+ DBGPRINTF("omstdout: array-passing is %ssupported by rsyslog core.\n", bArrayPassingSupported ? "" : "not ");
+
+ if(!bArrayPassingSupported) {
+ DBGPRINTF("rsyslog core too old, shutting down this plug-in\n");
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+</code>
+</pre>
+<p>The code first checks if the core supports the OMSRgetSupportedTplOpts() API (which is
+also not present in all versions!) and, if so, queries the core if the OMSR_TPL_AS_ARRAY mode
+is supported. If either does not exits, the core is too old for this functionality. The sample
+snippet above then shuts down, but a plugin may instead just do things different. In
+omstdout, you can see how a plugin may deal with the situation.
+<p><b>In any case, it is recommended that at least a graceful shutdown is made and the
+array-passing capability not blindly be used.</b> In such cases, we can not guard the
+plugin from segfaulting and if the plugin (as currently always) is run within
+rsyslog's process space, that results in a segfault for rsyslog. So do not do this.
+<h3>Batching of Messages</h3>
+<p>With the current plugin interface, each message is passed via a separate call to the plugin.
+This is annoying and costs performance in some uses cases (primarily for database outputs).
+However, that's the way it (currently) is, no easy way around it. There are some ideas
+to implement batching capabilities inside the rsyslog core, but without that the only
+resort is to do it inside your plugin yourself. You are not prohibited from doing so.
+There are some consequences, though: most importantly, the rsyslog core is no longer
+intersted in messages that it passed to a plugin. As such, it will not try to make sure
+the message is not lost before it was ultimately processed (because rsyslog, due to
+doAction() returning successfully, thinks the message *was* ultimately processed).
+<p>When the rsyslog core receives batching capabilities, this will be implemented in
+a way that is fully compatible to the existing plugin interface. While we have not yet
+thought about the implementation, that will probably mean that some new interfaces
+or options be used to turn on batching capabilities.
+<h3>Licensing</h3>
+<p>From the rsyslog point of view, plugins constitute separate projects. As such,
+we think plugins are not required to be compatible with GPLv3. However, this is
+no legal advise. If you intend to release something under a non-GPLV3 compatible license
+it is probably best to consult with your lawyer.
+<p>Most importantly, and this is definite, the rsyslog team does not expect
+or require you to contribute your plugin to the rsyslog project (but of course
+we are happy if you do).
+<h2>Copyright</h2>
+<p>Copyright (c) 2009 <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a>
+and <a href="http://www.adiscon.com/en/">Adiscon</a>.</p>
+<p>Permission is granted to copy, distribute and/or modify this document under
+the terms of the GNU Free Documentation License, Version 1.2 or any later
+version published by the Free Software Foundation; with no Invariant Sections,
+no Front-Cover Texts, and no Back-Cover Texts. A copy of the license can be
+viewed at <a href="http://www.gnu.org/copyleft/fdl.html">
+http://www.gnu.org/copyleft/fdl.html</a>.</p>
+</body>
+</html>
diff --git a/doc/direct_queue0.png b/doc/direct_queue0.png
new file mode 100644
index 00000000..6d1b373f
--- /dev/null
+++ b/doc/direct_queue0.png
Binary files differ
diff --git a/doc/direct_queue1.png b/doc/direct_queue1.png
new file mode 100644
index 00000000..503f8151
--- /dev/null
+++ b/doc/direct_queue1.png
Binary files differ
diff --git a/doc/direct_queue2.png b/doc/direct_queue2.png
new file mode 100644
index 00000000..cbb99af8
--- /dev/null
+++ b/doc/direct_queue2.png
Binary files differ
diff --git a/doc/direct_queue3.png b/doc/direct_queue3.png
new file mode 100644
index 00000000..cc49299f
--- /dev/null
+++ b/doc/direct_queue3.png
Binary files differ
diff --git a/doc/direct_queue_directq.png b/doc/direct_queue_directq.png
new file mode 100644
index 00000000..c5d8769d
--- /dev/null
+++ b/doc/direct_queue_directq.png
Binary files differ
diff --git a/doc/direct_queue_rsyslog.png b/doc/direct_queue_rsyslog.png
new file mode 100644
index 00000000..6150222d
--- /dev/null
+++ b/doc/direct_queue_rsyslog.png
Binary files differ
diff --git a/doc/direct_queue_rsyslog2.png b/doc/direct_queue_rsyslog2.png
new file mode 100644
index 00000000..807b064d
--- /dev/null
+++ b/doc/direct_queue_rsyslog2.png
Binary files differ
diff --git a/doc/droppriv.html b/doc/droppriv.html
new file mode 100644
index 00000000..7293e872
--- /dev/null
+++ b/doc/droppriv.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><title>dropping privileges in rsyslog</title>
+</head>
+<body>
+<h1>Dropping privileges in rsyslog</h1>
+<p><b>Available since:&nbsp;&nbsp;&nbsp;</b> 4.1.1</p>
+<p><b>Description</b>:</p>
+<p>
+Rsyslogd provides the ability to drop privileges by
+impersonating as another user and/or group after startup.
+
+<p>Please note that due to POSIX standards, rsyslogd always needs to start
+up as root if there is a listener who must bind to a network port below 1024.
+For example, the UDP listener usually needs to listen to 514 and as such
+rsyslogd needs to start up as root.
+
+<p>If you do not need this functionality, you can start rsyslog directly as an ordinary
+user. That is probably the safest way of operations. However, if a startup as
+root is required, you can use the $PrivDropToGroup and $PrivDropToUser config
+directives to specify a group and/or user that rsyslogd should drop to after initialization.
+Once this happend, the daemon runs without high privileges (depending, of
+course, on the permissions of the user account you specified).
+<p>There is some additional information available in the
+<a href="http://wiki.rsyslog.com/index.php/Security#Dropping_Privileges">rsyslog wiki</a>.
+<p><b>Configuration Directives</b>:</p>
+<ul>
+<li><b>$PrivDropToUser</b><br>
+Name of the user rsyslog should run under after startup. Please note that
+this user is looked up in the system tables. If the lookup fails, privileges are
+NOT dropped. Thus it is advisable to use the less convenient $PrivDropToUserID directive.
+If the user id can be looked up, but can not be set, rsyslog aborts.
+<br>
+</li>
+<li><b>$PrivDropToUserID</b><br>
+Much the same as $PrivDropToUser, except that a numerical user id instead of a name
+is specified.Thus, privilege drop will always happen.
+rsyslogd aborts.
+<li><b>$PrivDropToGroup</b><br>
+Name of the group rsyslog should run under after startup. Please note that
+this user is looked up in the system tables. If the lookup fails, privileges are
+NOT dropped. Thus it is advisable to use the less convenient $PrivDropToGroupID directive.
+Note that all supplementary groups are removed from the process if $PrivDropToGroup is
+specified.
+If the group id can be looked up, but can not be set, rsyslog aborts.
+<br>
+</li>
+<li><b>$PrivDropToGroupID</b><br>
+Much the same as $PrivDropToGroup, except that a numerical group id instead of a name
+is specified. Thus, privilege drop will always happen.
+</ul>
+<p>[<a href="rsyslog_conf.html">rsyslog.conf overview</a>]
+[<a href="manual.html">manual index</a>] [<a href="http://www.rsyslog.com/">rsyslog site</a>]</p>
+<p><font size="2">This documentation is part of the <a href="http://www.rsyslog.com/">rsyslog</a>
+project.<br>
+Copyright &copy; 2008 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/expression.html b/doc/expression.html
index e7eb7842..9e37cb7a 100644
--- a/doc/expression.html
+++ b/doc/expression.html
@@ -2,6 +2,7 @@
<html><head>
<meta http-equiv="Content-Language" content="en"><title>Expressions</title></head>
<body>
+<a href="rsyslog_conf_filter.html">back</a>
<h1>Expressions</h1>
<p>Rsyslog supports expressions at a growing number of places. So
far, they are supported for filtering messages.</p><p>Expression support is provided by RainerScript. For now, please see the formal expression definition in <a href="rainerscript.html">RainerScript ABNF</a>. It is the "expr" node.</p><p>C-like comments (/* some comment */) are supported <span style="font-weight: bold;">inside</span> the expression, but not yet in the rest of the configuration file.</p><p>[<a href="rsyslog_conf.html">rsyslog.conf overview</a>]
@@ -13,4 +14,4 @@ Copyright © 2008 by <a href="http://www.gerhards.net/rainer">Rainer
Gerhards</a> and
<a href="http://www.adiscon.com/">Adiscon</a>.
Released under the GNU GPL version 3 or higher.</font></p>
-</body></html> \ No newline at end of file
+</body></html>
diff --git a/doc/features.html b/doc/features.html
index d221eb77..626ff65d 100644
--- a/doc/features.html
+++ b/doc/features.html
@@ -1,8 +1,8 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head><title>rsyslog features</title>
-
</head>
<body>
+<a href="rsyslog_conf.html">back</a>
<h1>RSyslog - Features</h1>
<p><b>This page lists both current features as well as
those being considered for future versions of rsyslog.</b> If you
@@ -95,13 +95,16 @@ via custom plugins</li>
<li> an easy-to-write to plugin interface</li>
<li> ability to send SNMP trap messages</li>
<li> ability to filter out messages based on sequence of arrival</li>
+<li>support for comma-seperated-values (CSV) output generation
+(via the "csv" property replace option). The
+CSV format supported is that from RFC 4180.</li>
<li>support for arbitrary complex boolean, string and
arithmetic expressions in message filters</li>
</ul>
<h2>World's first</h2>
Rsyslog has an interesting number of "world's firsts" - things that
were implemented for the first time ever in rsyslog. Some of them are still features not available elsewhere.<br><ul>
-<li>world's first implementation of IETF I-D syslog-protocol (February 2006, version 1.12.2 and above)</li><li>world's first implementation of dynamic syslog on-the-wire compression (December 2006, version 1.13.0 and above)</li><li>world's first open-source implementation of a disk-queueing syslogd (January 2008, version 3.11.0 and above)</li>
+<li>world's first implementation of IETF I-D syslog-protocol (February 2006, version 1.12.2 and above), now RFC5424</li><li>world's first implementation of dynamic syslog on-the-wire compression (December 2006, version 1.13.0 and above)</li><li>world's first open-source implementation of a disk-queueing syslogd (January 2008, version 3.11.0 and above)</li>
<li>world's first implementation of IETF I-D
syslog-transport-tls (May 2008, version 3.19.0 and above)</li>
</ul>
@@ -116,6 +119,13 @@ submit feature requests there (or via our forums). If we like them but
they look quite long-lived (aka "not soon to be implemented"), they
will possibly be migrated to this list here and at some time moved back
to the bugzilla tracker.</p>
+<p><b>Note that we also maintain a
+<a href="http://www.rsyslog.com/sponsor_feature">list of features that are looking for sponsors</a>.
+If you are interested in any of these features, or any other feature, you may consider sponsoring
+the implementation. This is also a great way to show your commitment to the open source
+community. Plus, it can be financially attractive: just think about how much less it may
+be to sponsor a feature instead of purchasing a commercial implementation. Also, the benefit
+of being recognised as a sponsor may even drive new customers to your business!</b>
<ul>
<li>port it to more *nix variants (eg AIX and HP UX) - this
needs volunteers with access to those machines and knowledge </li>
@@ -134,4 +144,15 @@ future of RFC 3195 in rsyslog</a>.</li>
<p>To see when each feature was added, see the
<a href="http://www.rsyslog.com/Topic4.phtml">rsyslog
change log</a> (online 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 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 2 or higher.</font></p>
+
</body></html>
+
diff --git a/doc/how2help.html b/doc/how2help.html
index 0caa5a3a..4f0bd57a 100644
--- a/doc/how2help.html
+++ b/doc/how2help.html
@@ -1,59 +1,57 @@
-<html>
-<head>
-<title>How you can Help</title>
-</head>
-<body>
-<h2>How you can Help</h2>
-<p><b>You like rsyslog and would like to lend us a helping hand?</b> This page
-tells you how easy it is to help a little bit. You can contribute to the project
-even with a single mouse click! If you could pick a single item from the
-wish list, that would be awfully helpful!</p>
-<p>This is our wish list:</p>
-<ul>
- <li>let others know how great rsyslog is<ul>
- <li>rate us at <a href="http://freshmeat.net/rate/52985/">freshmeat.net</a>
- and <a href="http://www.icewalkers.com/vote.php?ID=2420">icewalkers.com</a></li>
- <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>
- </ul>
- </li>
- <li>let us know about rsyslog - we are eager for feedback<ul>
- <li>tell us what you like and what you not like - so that we can include
- that into development</li>
- <li>tell us what you use rsyslog for - especially if you have high
- traffic volume or an otherwise &quot;uncommon&quot; deployment. We are looking for
- case studies and experience how rsyslog performs in unusual scenarios.</li>
- <li>allow us to post your thoughts and experiences as a &quot;user story&quot; on
- the web site (so far, none are there ;))</li>
- </ul>
- </li>
- <li>if you know how to create packages (rpm, deb, ...)<ul>
- <li>we would very much appreciate your help with package creation. We know
- that it is important to have good binary packages for a product to
- spread widely. Yet, we do not have the knowledge to do it all ourselves.
- <a href="mailto:rgerhards@adiscon.com">Drop Rainer a note </a>if you
- could help us out.</li>
- </ul>
- </li>
- <li>if you have configured a device for sending syslog data, and that device
- is not in our
- <a href="http://www.monitorware.com/en/syslog-enabled-products/">syslog
- configuration database</a>, you might want to tell us how to configure it.</li>
- <li>if you are a corporate user<ul>
- <li>you might consider <a href="http://www.adiscon.com">Adiscon</a>'s
- commercial <a href="http://www.monitorware.com/">MonitorWare products</a>
- for Windows, e.g. to deliver Windows Event Log data to rsyslogd (sales
- of the commercial products funds the open source development - and they
- also work very well).</li>
- <li>you might be interested in
- <a href="http://www.adiscon.com/Common/en/Products/techsup.php">
- purchasing professional support or add-on development</a> for rsyslog</li>
- </ul>
- </li>
-</ul>
-<p><b>We appreciate your help very much.</b> A big thank you for anything you
-might do!</p>
-
-</body>
-</html>
+<html>
+<head>
+<title>How you can Help</title>
+</head>
+<body>
+<h2>How you can Help</h2>
+<p><b>You like rsyslog and would like to lend us a helping hand?</b> This page
+tells you how easy it is to help a little bit. You can contribute to the project
+even with a single mouse click! If you could pick a single item from the
+wish list, that would be awfully helpful!</p>
+<p>This is our wish list:</p>
+<ul>
+ <li>let others know how great rsyslog is<ul>
+ <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>
+ </ul>
+ </li>
+ <li>let us know about rsyslog - we are eager for feedback<ul>
+ <li>tell us what you like and what you not like - so that we can include
+ that into development</li>
+ <li>tell us what you use rsyslog for - especially if you have high
+ traffic volume or an otherwise &quot;uncommon&quot; deployment. We are looking for
+ case studies and experience how rsyslog performs in unusual scenarios.</li>
+ <li>allow us to post your thoughts and experiences as a &quot;user story&quot; on
+ the web site (so far, none are there ;))</li>
+ </ul>
+ </li>
+ <li>if you know how to create packages (rpm, deb, ...)<ul>
+ <li>we would very much appreciate your help with package creation. We know
+ that it is important to have good binary packages for a product to
+ spread widely. Yet, we do not have the knowledge to do it all ourselves.
+ <a href="mailto:rgerhards@adiscon.com">Drop Rainer a note </a>if you
+ could help us out.</li>
+ </ul>
+ </li>
+ <li>if you have configured a device for sending syslog data, and that device
+ is not in our
+ <a href="http://www.monitorware.com/en/syslog-enabled-products/">syslog
+ configuration database</a>, you might want to tell us how to configure it.</li>
+ <li>if you are a corporate user<ul>
+ <li>you might consider <a href="http://www.adiscon.com">Adiscon</a>'s
+ commercial <a href="http://www.monitorware.com/">MonitorWare products</a>
+ for Windows, e.g. to deliver Windows Event Log data to rsyslogd (sales
+ of the commercial products funds the open source development - and they
+ also work very well).</li>
+ <li>you might be interested in
+ <a href="http://www.adiscon.com/Common/en/Products/techsup.php">
+ purchasing professional support or add-on development</a> for rsyslog</li>
+ </ul>
+ </li>
+</ul>
+<p><b>We appreciate your help very much.</b> A big thank you for anything you
+might do!</p>
+
+</body>
+</html
diff --git a/doc/im3195.html b/doc/im3195.html
index d6f2f2ed..aad9f3d1 100644
--- a/doc/im3195.html
+++ b/doc/im3195.html
@@ -4,6 +4,8 @@
</head>
<body>
+<a href="rsyslog_conf_modules.html">back</a>
+
<h1>RFC3195 Input Module</h1>
<p><b>Module Name:&nbsp;&nbsp;&nbsp; im3195</b></p>
<p><b>Author: </b>Rainer Gerhards
diff --git a/doc/imfile.html b/doc/imfile.html
index 5bdbce5c..af0413dd 100644
--- a/doc/imfile.html
+++ b/doc/imfile.html
@@ -2,6 +2,8 @@
<html><head>
<meta http-equiv="Content-Language" content="en"><title>Text File Input Monitor</title></head>
<body>
+<a href="rsyslog_conf_modules.html">back</a>
+
<h1>Text File Input Module</h1>
<p><b>Module Name:&nbsp;&nbsp;&nbsp; imfile</b></p>
<p><b>Author: </b>Rainer Gerhards
diff --git a/doc/imgssapi.html b/doc/imgssapi.html
index d644303e..ec183fe7 100644
--- a/doc/imgssapi.html
+++ b/doc/imgssapi.html
@@ -4,6 +4,8 @@
</head>
<body>
+<a href="rsyslog_conf_modules.html">back</a>
+
<h1>GSSAPI Syslog Input Module</h1>
<p><b>Module Name:&nbsp;&nbsp;&nbsp; imgssapi</b></p>
<p><b>Author: </b>varmojfekoj</p>
diff --git a/doc/imklog.html b/doc/imklog.html
index b5b21e84..5bfab5ce 100644
--- a/doc/imklog.html
+++ b/doc/imklog.html
@@ -4,6 +4,8 @@
</head>
<body>
+<a href="rsyslog_conf_modules.html">back</a>
+
<h1>Kernel Log Input Module</h1>
<p><b>Module Name:&nbsp;&nbsp;&nbsp; imklog</b></p>
<p><b>Author: </b>Rainer Gerhards
@@ -32,9 +34,9 @@ handled. The default is "off", in which case these messages are
ignored. Switch it to on to submit non-kernel messages to rsyslog
processing.<span style="font-weight: bold;"></span></li>
<li><span style="font-weight: bold;"></span>$DebugPrintKernelSymbols
-(imklog) [on/<b>off</b>]<br>
+[on/<b>off</b>]<br>
Linux only, ignored on other platforms (but may be specified)</li>
-<li>$klogSymbolLookup (imklog) [on/<b>off</b>] --
+<li>$klogSymbolLookup [on/<b>off</b>] --
disables imklog kernel symbol translation (former klogd -x option). NOTE that
this option is counter-productive on recent kernels (>= 2.6) because the
kernel already does the symbol translation and this option breaks the information.<br>
@@ -42,10 +44,19 @@ kernel already does the symbol translation and this option breaks the informatio
it except if you have a very good reason. If you have one, let us know
because otherwise new versions will no longer support it.<br>
Linux only, ignored on other platforms (but may be specified)</li>
-<li>$klogUseSyscallInterface (imklog)&nbsp; [on/<b>off</b>]
+<li><b>$klogConsoleLogLevel</b> [<i>number</i>]
+(former klogd -c option) -- sets the console log level. If specified, only messages with
+up to the specified level are printed to the console. The default is -1, which means that
+the current settings are not modified. To get this behavior, do not specify
+$klogConsoleLogLevel in the configuration file. Note that this is a global parameter. Each time
+it is changed, the previous definition is re-set. The one activate will be that one that is
+active when imklog actually starts processing. In short words: do not specify this
+directive more than once!
+<br><b>Linux only</b>, ignored on other platforms (but may be specified)</li>
+<li><b>$klogUseSyscallInterface</b> [on/<b>off</b>]
-- former klogd -s option<br>
Linux only, ignored on other platforms (but may be specified)</li>
-<li>$klogSymbolsTwice (imklog) [on/<b>off</b>] --
+<li>$klogSymbolsTwice [on/<b>off</b>] --
former klogd -2 option<br>
Linux only, ignored on other platforms (but may be specified)<br style="font-weight: bold;">
</li>
@@ -67,7 +78,7 @@ is needed to start pulling kernel messages.<br>
<p><font size="2">This documentation is part of the
<a href="http://www.rsyslog.com/">rsyslog</a>
project.<br>
-Copyright © 2008 by <a href="http://www.gerhards.net/rainer">Rainer
+Copyright &copy; 2008-2009 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/imrelp.html b/doc/imrelp.html
index cfc6da38..2cf9c1f7 100644
--- a/doc/imrelp.html
+++ b/doc/imrelp.html
@@ -4,6 +4,8 @@
</head>
<body>
+<a href="rsyslog_conf_modules.html">back</a>
+
<h1>RELP Input Module</h1>
<p><b>Module Name:&nbsp;&nbsp;&nbsp; imrelp</b></p>
<p><b>Author: Rainer Gerhards</b></p>
diff --git a/doc/imtcp.html b/doc/imtcp.html
index ecc72748..0ccdecc7 100644
--- a/doc/imtcp.html
+++ b/doc/imtcp.html
@@ -2,6 +2,8 @@
<html><head>
<meta http-equiv="Content-Language" content="en"><title>TCP Syslog Input Module</title></head>
<body>
+<a href="rsyslog_conf_modules.html">back</a>
+
<h1>TCP Syslog Input Module</h1>
<p><b>Module Name:&nbsp;&nbsp;&nbsp; imtcp</b></p>
<p><b>Author: </b>Rainer Gerhards
@@ -12,18 +14,52 @@ Encryption can be provided by using <a href="rsyslog_stunnel.html">stunnel</a>
(an alternative is the use
the&nbsp;<a href="imgssapi.html">imgssapi</a>
modul).</p>
-<p>In the future, multiple receivers may be configured by
+<p>Multiple receivers may be configured by
specifying
-$InputTCPServerRun multiple times. This is not currently supported.
+$InputTCPServerRun multiple times. This is available since version 4.3.1, earlier
+versions do NOT support it.
</p>
<p><b>Configuration Directives</b>:</p>
<ul>
+<li>$InputTCPServerAddtlFrameDelimiter &lt;Delimiter&gt;<br>
+This directive permits to specify an additional frame delimiter for plain tcp syslog.
+The industry-standard specifies using the LF character as frame delimiter. Some vendors,
+notable Juniper in their NetScreen products, use an invalid frame delimiter, in Juniper's
+case the NUL character. This directive permits to specify the ASCII value of the delimiter
+in question. Please note that this does not guarantee that all wrong implementations can
+be cured with this directive. It is not even a sure fix with all versions of NetScreen,
+as I suggest the NUL character is the effect of a (common) coding error and thus will
+probably go away at some time in the future. But for the time being, the value 0 can
+probably be used to make rsyslog handle NetScreen's invalid syslog/tcp framing.
+For additional information, see this
+<a href="http://kb.monitorware.com/problem-with-netscreen-log-t1652.html">forum thread</a>.
+<br><b>If this doesn't work for you, please do not blame the rsyslog team. Instead file
+a bug report with Juniper!</b>
+<br>Note that a similar, but worse, issue exists with Cisco's IOS implementation. They do
+not use any framing at all. This is confirmed from Cisco's side, but there seems to be
+very limited interest in fixing this issue. This directive <b>can not</b> fix the Cisco bug.
+That would require much more code changes, which I was unable to do so far. Full details
+can be found at the <a href="http://www.rsyslog.com/Article321.phtml">Cisco tcp syslog anomaly</a>
+page.
+<li>$InputTCPServerNotifyOnConnectionClose [on/<b>off</b>] (available since 4.5.5)<br>
+instructs imtcp to emit a message if the remote peer closes a connection.<br>
+<b>Important:</b> This directive is global to all listeners and must be given right
+after loading imtcp, otherwise it may have no effect.</li>
<li>$InputTCPServerRun &lt;port&gt;<br>
Starts a TCP server on selected port</li>
-<li><ul><li>$InputTCPMaxSessions &lt;number&gt;</li></ul>
-Sets the maximum number of sessions supported</li><li>$InputTCPServerStreamDriverMode &lt;number&gt;<br>
-Sets the driver mode for the currently selected <a href="netstream.html">network stream driver</a>. &lt;number&gt; is driver specifc.</li><li>$InputTCPServerStreamDriverAuthMode &lt;mode-string&gt;<br>
-Sets the authentication mode for the currently selected <a href="netstream.html">network stream driver</a>. &lt;mode-string&gt; is driver specifc.</li><li>$InputTCPServerStreamDriverPermittedPeer &lt;id-string&gt;<br>
+<li>$InputTCPMaxListeners &lt;number&gt;<br>
+Sets the maximum number of listeners (server ports) supported. Default is 20. This must be set before the first $InputTCPServerRun directive.</li>
+<li>$InputTCPMaxSessions &lt;number&gt;<br>
+Sets the maximum number of sessions supported. Default is 200. This must be set before the first $InputTCPServerRun directive</li>
+<li>$InputTCPServerStreamDriverMode &lt;number&gt;<br>
+Sets the driver mode for the currently selected <a href="netstream.html">network stream driver</a>. &lt;number&gt; is driver specifc.</li>
+<li>$InputTCPServerInputName &lt;name&gt;<br>
+Sets a name for the inputname property. If no name is set "imtcp" is used by default. Setting a
+name is not strictly necessary, but can be useful to apply filtering based on which input
+the message was received from.
+<li>$InputTCPServerStreamDriverAuthMode &lt;mode-string&gt;<br>
+Sets the authentication mode for the currently selected <a href="netstream.html">network stream driver</a>. &lt;mode-string&gt; is driver specifc.</li>
+<li>$InputTCPServerStreamDriverPermittedPeer &lt;id-string&gt;<br>
Sets permitted peer IDs. Only these peers are able to connect to the
listener. &lt;id-string&gt; semantics depend on the currently selected
AuthMode and&nbsp; <a href="netstream.html">network stream driver</a>. PermittedPeers may not be set in anonymous modes.</li>
@@ -31,7 +67,6 @@ AuthMode and&nbsp; <a href="netstream.html">network stream driver</a>. Permitted
<b>Caveats/Known Bugs:</b>
<ul>
<li>module always binds to all interfaces</li>
-<li>only a single listener can be bound</li>
<li>can not be loaded together with <a href="imgssapi.html">imgssapi</a>
(which includes the functionality of imtcp)</li>
</ul>
diff --git a/doc/imuxsock.html b/doc/imuxsock.html
index 77491992..472470a0 100644
--- a/doc/imuxsock.html
+++ b/doc/imuxsock.html
@@ -4,6 +4,8 @@
<title>Unix Socket Input</title>
</head>
<body>
+<a href="rsyslog_conf_modules.html">back</a>
+
<h1>Unix Socket Input</h1>
<p><b>Module Name:&nbsp;&nbsp;&nbsp; imuxsock</b></p>
<p><b>Author: </b>Rainer Gerhards
diff --git a/doc/log_rotation_fix_size.html b/doc/log_rotation_fix_size.html
index 0b9a3b2e..190b24cb 100644
--- a/doc/log_rotation_fix_size.html
+++ b/doc/log_rotation_fix_size.html
@@ -3,6 +3,8 @@
<meta name="KEYWORDS" content="log rotation, howto, guide, fixed-size log">
</head>
<body>
+<a href="rsyslog_conf_output.html">back</a>
+
<h1>Log rotation with rsyslog</h1>
<P><small><i>Written by
Michael Meckelein</i></small></P>
@@ -54,6 +56,14 @@ file and fill it up with new logs. So the latest logs are always in log_roatatio
<p>With this approach two files for logging are used, each with a maximum size of 50 MB. So
we can say we have successfully configured a log rotation which satisfies our requirement.
We keep the logs at a fixed-size level of100 MB.</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 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 2 or higher.</font></p>
</body>
</html>
diff --git a/doc/man_rsyslogd.html b/doc/man_rsyslogd.html
deleted file mode 100644
index d18fd88a..00000000
--- a/doc/man_rsyslogd.html
+++ /dev/null
@@ -1,438 +0,0 @@
-<BODY><PRE>
-RSYSLOGD(8) Linux System Administration RSYSLOGD(8)
-
-
-
-<B>NAME</B>
- rsyslogd - reliable and extended syslogd
-
-<B>SYNOPSIS</B>
- <B>rsyslogd </B>[ <B>-4 </B>] [ <B>-6 </B>] [ <B>-A </B>] [ <B>-a </B><I>socket </I>] [ <B>-d </B>] [ <B>-e </B>]
- [ <B>-f </B><I>config file </I>] [ <B>-h </B>] [ <B>-i </B><I>pid file </I>] [ <B>-l </B><I>hostlist </I>]
- [ <B>-m </B><I>interval </I>] [ <B>-n </B>] [ <B>-o </B>] [ <B>-p </B><I>socket </I>]
- [ <B>-r </B><I>[port] </I>] [ <B>-s </B><I>domainlist </I>] [ <B>-t </B><I>port,max-nbr-of-sessions </I>]
- [ <B>-v </B>] [ <B>-w </B>] [ <B>-x </B>]
-
-
-<B>DESCRIPTION</B>
- <B>Rsyslogd </B>is a system utility providing support for message logging.
- Support of both internet and unix domain sockets enables this utility
- to support both local and remote logging (via UDP and TCP).
-
- <B>Rsyslogd</B>(8) is derived from the sysklogd package which in turn is
- derived from the stock BSD sources.
-
- <B>Rsyslogd </B>provides a kind of logging that many modern programs use.
- Every logged message contains at least a time and a hostname field,
- normally a program name field, too, but that depends on how trusty the
- logging program is. The rsyslog package supports free definition of
- output formats via templates. It also supports precise timestamps and
- writing directly to MySQL databases. If the database option is used,
- tools like phpLogCon can be used to view the log data.
-
- While the <B>rsyslogd </B>sources have been heavily modified a couple of notes
- are in order. First of all there has been a systematic attempt to
- insure that rsyslogd follows its default, standard BSD behavior. Of
- course, some configuration file changes are necessary in order to sup-
- port the template system. However, rsyslogd should be able to use a
- standard syslog.conf and act like the original syslogd. However, an
- original syslogd will not work correctly with a rsyslog-enhanced con-
- figuration file. At best, it will generate funny looking file names.
- The second important concept to note is that this version of rsyslogd
- interacts transparently with the version of syslog found in the stan-
- dard libraries. If a binary linked to the standard shared libraries
- fails to function correctly we would like an example of the anomalous
- behavior.
-
- The main configuration file <I>/etc/rsyslog.conf </I>or an alternative file,
- given with the <B>-f </B>option, is read at startup. Any lines that begin
- with the hash mark (‘‘#’’) and empty lines are ignored. If an error
- occurs during parsing the error element is ignored. It is tried to
- parse the rest of the line.
-
- For details and configuration examples, see the <B>rsyslog.conf (5) </B>man
- page.
-
-
-
-<B>OPTIONS</B>
- <B>-A </B>When sending UDP messages, there are potentially multiple paths
- to the target destination. By default, <B>rsyslogd </B>only sends to
- the first target it can successfully send to. If -A is given,
- messages are sent to all targets. This may improve reliability,
- but may also cause message duplication. This option should
- enabled only if it is fully understood.
-
- <B>-4 </B>Causes <B>rsyslogd </B>to listen to IPv4 addresses only. If neither -4
- nor -6 is given, <B>rsyslogd </B>listens to all configured addresses of
- the system.
-
- <B>-6 </B>Causes <B>rsyslogd </B>to listen to IPv6 addresses only. If neither -4
- nor -6 is given, <B>rsyslogd </B>listens to all configured addresses of
- the system.
-
- <B>-a </B><I>socket</I>
- Using this argument you can specify additional sockets from that
- <B>rsyslogd </B>has to listen to. This is needed if you’re going to
- let some daemon run within a chroot() environment. You can use
- up to 19 additional sockets. If your environment needs even
- more, you have to increase the symbol <B>MAXFUNIX </B>within the sys-
- logd.c source file. An example for a chroot() daemon is
- described by the people from OpenBSD at
- http://www.psionic.com/papers/dns.html.
-
- <B>-d </B>Turns on debug mode. Using this the daemon will not proceed a
- <B>fork</B>(2) to set itself in the background, but opposite to that
- stay in the foreground and write much debug information on the
- current tty. See the DEBUGGING section for more information.
-
- <B>-e </B>Set the default of $RepeatedMsgReduction config option to "off".
- Hine: "e" like "every message". For further information, see
- there.
-
- <B>-f </B><I>config file</I>
- Specify an alternative configuration file instead of <I>/etc/rsys-</I>
- <I>log.conf</I>, which is the default.
-
- <B>-h </B>By default rsyslogd will not forward messages it receives from
- remote hosts. Specifying this switch on the command line will
- cause the log daemon to forward any remote messages it receives
- to forwarding hosts which have been defined.
-
- <B>-i </B><I>pid file</I>
- Specify an alternative pid file instead of the default one.
- This option must be used if multiple instances of rsyslogd
- should run on a single machine.
-
- <B>-l </B><I>hostlist</I>
- Specify a hostname that should be logged only with its simple
- hostname and not the fqdn. Multiple hosts may be specified
- using the colon (‘‘:’’) separator.
-
- <B>-m </B><I>interval</I>
- The <B>rsyslogd </B>logs a mark timestamp regularly. The default
- <I>interval </I>between two <I>-- MARK -- </I>lines is 20 minutes. This can
- be changed with this option. Setting the <I>interval </I>to zero turns
- it off entirely.
-
- <B>-n </B>Avoid auto-backgrounding. This is needed especially if the
- <B>rsyslogd </B>is started and controlled by <B>init</B>(8).
-
- <B>-o </B>Omit reading the standard local log socket. This option is most
- useful for running multiple instances of rsyslogd on a single
- machine. When specified, no local log socket is opened at all.
-
- <B>-p </B><I>socket</I>
- You can specify an alternative unix domain socket instead of
- <I>/dev/log</I>.
-
- <B>-r </B><I>["port"]</I>
- Activates the syslog/udp listener service. The listener will
- listen to the specified port. If no port is specified, 0 is
- used as port number, which in turn will lead to a lookup of the
- system default syslog port. If there is no system default, 514
- is used. Please note that the port must immediately follow the
- -r option. Thus "-r514" is valid while "-r 514" is invalid (note
- the space).
-
- <B>-s </B><I>domainlist</I>
- Specify a domainname that should be stripped off before logging.
- Multiple domains may be specified using the colon (‘‘:’’) sepa-
- rator. Please be advised that no sub-domains may be specified
- but only entire domains. For example if <B>-s north.de </B>is speci-
- fied and the host logging resolves to satu.infodrom.north.de no
- domain would be cut, you will have to specify two domains like:
- <B>-s north.de:infodrom.north.de</B>.
-
- <B>-t </B><I>port,max-nbr-of-sessions</I>
- Activates the syslog/tcp listener service. The listener will
- listen to the specified port. If max-nbr-of-sessions is speci-
- fied, that becomes the maximum number of concurrent tcp ses-
- sions. If not specified, the default is 200. Please note that
- syslog/tcp is not standardized, but the implementation in rsys-
- logd follows common practice and is compatible with e.g. Cisco
- PIX, syslog-ng and MonitorWare (Windows). Please note that the
- port must immediately follow the -t option. Thus "-t514" is
- valid while "-t 514" is invalid (note the space).
-
- <B>-v </B>Print version and exit.
-
- <B>-w </B>Supress warnings issued when messages are received from non-
- authorized machines (those, that are in no AllowedSender list).
-
- <B>-x </B>Disable DNS for remote messages.
-
-
-<B>SIGNALS</B>
- <B>Rsyslogd </B>reacts to a set of signals. You may easily send a signal to
- <B>rsyslogd </B>using the following:
-
- kill -SIGNAL ‘cat /var/run/rsyslogd.pid‘
-
-
- <B>SIGHUP </B>This lets <B>rsyslogd </B>perform a re-initialization. All open files
- are closed, the configuration file (default is <I>/etc/rsys-</I>
- <I>log.conf</I>) will be reread and the <B>rsyslog</B>(3) facility is started
- again.
-
- <B>SIGTERM</B>
- <B>Rsyslogd </B>will die.
-
- <B>SIGINT</B>, <B>SIGQUIT</B>
- If debugging is enabled these are ignored, otherwise <B>rsyslogd</B>
- will die.
-
- <B>SIGUSR1</B>
- Switch debugging on/off. This option can only be used if <B>rsys-</B>
- <B>logd </B>is started with the <B>-d </B>debug option.
-
- <B>SIGCHLD</B>
- Wait for childs if some were born, because of wall’ing messages.
-
-
-<B>SUPPORT FOR REMOTE LOGGING</B>
- <B>Rsyslogd </B>provides network support to the syslogd facility. Network
- support means that messages can be forwarded from one node running
- rsyslogd to another node running rsyslogd (or a compatible syslog
- implementation) where they will be actually logged to a disk file.
-
- To enable this you have to specify either the <B>-r </B>or <B>-t </B>option on the
- command line. The default behavior is that <B>rsyslogd </B>won’t listen to
- the network. You can also combine these two options if you want rsys-
- logd to listen to both TCP and UDP messages.
-
- The strategy is to have rsyslogd listen on a unix domain socket for
- locally generated log messages. This behavior will allow rsyslogd to
- inter-operate with the syslog found in the standard C library. At the
- same time rsyslogd listens on the standard syslog port for messages
- forwarded from other hosts. To have this work correctly the <B>ser-</B>
- <B>vices</B>(5) files (typically found in <I>/etc</I>) must have the following entry:
-
- syslog 514/udp
-
- If this entry is missing <B>rsyslogd </B>will use the well known port of 514
- (so in most cases, it’s not really needed).
-
- To cause messages to be forwarded to another host replace the normal
- file line in the <I>rsyslog.conf </I>file with the name of the host to which
- the messages is to be sent prepended with an @ (for UDP delivery) or
- the sequence @@ (for TCP delivery). The host name can also be followed
- by a colon and a port number, in which case the message is sent to the
- specified port on the remote host.
-
- For example, to forward <B>ALL </B>messages to a remote host use the
- following <I>rsyslog.conf </I>entry:
-
- # Sample rsyslogd configuration file to
- # messages to a remote host forward all.
- *.* @hostname
- More samples can be found in sample.conf.
-
- If the remote hostname cannot be resolved at startup, because
- the name-server might not be accessible (it may be started after
- rsyslogd) you don’t have to worry. <B>Rsyslogd </B>will retry to
- resolve the name ten times and then complain. Another possibil-
- ity to avoid this is to place the hostname in <I>/etc/hosts</I>.
-
- With normal <B>syslogd</B>s you would get syslog-loops if you send out
- messages that were received from a remote host to the same host
- (or more complicated to a third host that sends it back to the
- first one, and so on).
-
- To avoid this no messages that were received from a remote host
- are sent out to another (or the same) remote host. You can dis-
- able this feature by the <B>-h </B>option.
-
- If the remote host is located in the same domain as the host,
- <B>rsyslogd </B>is running on, only the simple hostname will be logged
- instead of the whole fqdn.
-
- In a local network you may provide a central log server to have
- all the important information kept on one machine. If the net-
- work consists of different domains you don’t have to complain
- about logging fully qualified names instead of simple hostnames.
- You may want to use the strip-domain feature <B>-s </B>of this server.
- You can tell <B>rsyslogd </B>to strip off several domains other than
- the one the server is located in and only log simple hostnames.
-
- Using the <B>-l </B>option there’s also a possibility to define single
- hosts as local machines. This, too, results in logging only
- their simple hostnames and not the fqdns.
-
-
-<B>OUTPUT TO DATABASES</B>
- <B>Rsyslogd </B>has support for writing data to MySQL database tables. The
- exact specifics are described in the <B>rsyslog.conf (5) </B>man page. Be sure
- to read it if you plan to use database logging.
-
- While it is often handy to have the data in a database, you must be
- aware of the implications. Most importantly, database logging takes far
- longer than logging to a text file. A system that can handle a large
- log volume when writing to text files can most likely not handle a sim-
- ilar large volume when writing to a database table.
-
-
-<B>OUTPUT TO NAMED PIPES (FIFOs)</B>
- <B>Rsyslogd </B>has support for logging output to named pipes (fifos). A fifo
- or named pipe can be used as a destination for log messages by prepend-
- ing a pipy symbol (‘‘|’’) to the name of the file. This is handy for
- debugging. Note that the fifo must be created with the mkfifo command
- before <B>rsyslogd </B>is started.
-
- The following configuration file routes debug messages from the
- kernel to a fifo:
-
- # Sample configuration to route kernel debugging
- # messages ONLY to /usr/adm/debug which is a
- # named pipe.
- kern.=debug |/usr/adm/debug
-
-
-<B>INSTALLATION CONCERNS</B>
- There is probably one important consideration when installing rsyslogd.
- It is dependent on proper formatting of messages by the syslog func-
- tion. The functioning of the syslog function in the shared libraries
- changed somewhere in the region of libc.so.4.[2-4].n. The specific
- change was to null-terminate the message before transmitting it to the
- <I>/dev/log </I>socket. Proper functioning of this version of rsyslogd is
- dependent on null-termination of the message.
-
- This problem will typically manifest itself if old statically linked
- binaries are being used on the system. Binaries using old versions of
- the syslog function will cause empty lines to be logged followed by the
- message with the first character in the message removed. Relinking
- these binaries to newer versions of the shared libraries will correct
- this problem.
-
- The <B>rsyslogd</B>(8) can be run from <B>init</B>(8) or started as part of the rc.*
- sequence. If it is started from init the option <I>-n </I>must be set, other-
- wise you’ll get tons of syslog daemons started. This is because
- <B>init</B>(8) depends on the process ID.
-
-
-<B>SECURITY THREATS</B>
- There is the potential for the rsyslogd daemon to be used as a conduit
- for a denial of service attack. A rogue program(mer) could very easily
- flood the rsyslogd daemon with syslog messages resulting in the log
- files consuming all the remaining space on the filesystem. Activating
- logging over the inet domain sockets will of course expose a system to
- risks outside of programs or individuals on the local machine.
-
- There are a number of methods of protecting a machine:
-
- 1. Implement kernel firewalling to limit which hosts or networks
- have access to the 514/UDP socket.
-
- 2. Logging can be directed to an isolated or non-root filesystem
- which, if filled, will not impair the machine.
-
- 3. The ext2 filesystem can be used which can be configured to limit
- a certain percentage of a filesystem to usage by root only.
- <B>NOTE </B>that this will require rsyslogd to be run as a non-root
- process. <B>ALSO NOTE </B>that this will prevent usage of remote log-
- ging since rsyslogd will be unable to bind to the 514/UDP
- socket.
-
- 4. Disabling inet domain sockets will limit risk to the local
- machine.
-
- 5. Use step 4 and if the problem persists and is not secondary to a
- rogue program/daemon get a 3.5 ft (approx. 1 meter) length of
- sucker rod* and have a chat with the user in question.
-
- Sucker rod def. — 3/4, 7/8 or 1in. hardened steel rod, male
- threaded on each end. Primary use in the oil industry in West-
- ern North Dakota and other locations to pump ’suck’ oil from oil
- wells. Secondary uses are for the construction of cattle feed
- lots and for dealing with the occasional recalcitrant or bel-
- ligerent individual.
-
- <B>Message replay and spoofing</B>
- If remote logging is enabled, messages can easily be spoofed and
- replayed. As the messages are transmitted in clear-text, an attacker
- might use the information obtained from the packets for malicious
- things. Also, an attacker might reply recorded messages or spoof a
- sender’s IP address, which could lead to a wrong perception of system
- activity. Be sure to think about syslog network security before
- enabling it.
-
-
-<B>DEBUGGING</B>
- When debugging is turned on using <B>-d </B>option then <B>rsyslogd </B>will be very
- verbose by writing much of what it does on stdout. Whenever the con-
- figuration file is reread and re-parsed you’ll see a tabular, corre-
- sponding to the internal data structure. This tabular consists of four
- fields:
-
- <I>number </I>This field contains a serial number starting by zero. This num-
- ber represents the position in the internal data structure (i.e.
- the array). If one number is left out then there might be an
- error in the corresponding line in <I>/etc/rsyslog.conf</I>.
-
- <I>pattern</I>
- This field is tricky and represents the internal structure
- exactly. Every column stands for a facility (refer to <B>sys-</B>
- <B>log</B>(3)). As you can see, there are still some facilities left
- free for former use, only the left most are used. Every field
- in a column represents the priorities (refer to <B>syslog</B>(3)).
-
- <I>action </I>This field describes the particular action that takes place
- whenever a message is received that matches the pattern. Refer
- to the <B>syslog.conf</B>(5) manpage for all possible actions.
-
- <I>arguments</I>
- This field shows additional arguments to the actions in the last
- field. For file-logging this is the filename for the logfile;
- for user-logging this is a list of users; for remote logging
- this is the hostname of the machine to log to; for console-log-
- ging this is the used console; for tty-logging this is the spec-
- ified tty; wall has no additional arguments.
-
-
- <B>templates</B>
- There will also be a second internal structure which lists all
- defined templates and there contents. This also enables you to
- see the internally-defined, hardcoded templates.
-
-<B>FILES</B>
- <I>/etc/rsyslog.conf</I>
- Configuration file for <B>rsyslogd</B>. See <B>rsyslog.conf</B>(5) for exact
- information.
- <I>/dev/log</I>
- The Unix domain socket to from where local syslog messages are
- read.
- <I>/var/run/rsyslogd.pid</I>
- The file containing the process id of <B>rsyslogd</B>.
-
-<B>BUGS</B>
- Please review the file BUGS for up-to-date information on known bugs
- and annoyances.
-
-<B>Further Information</B>
- Please visit <B>http://www.rsyslog.com/doc </B>for additional information,
- tutorials and a support forum.
-
-<B>SEE ALSO</B>
- <B>rsyslog.conf</B>(5), <B>logger</B>(1), <B>syslog</B>(2), <B>syslog</B>(3), <B>services</B>(5),
- <B>savelog</B>(8)
-
-
-<B>COLLABORATORS</B>
- <B>rsyslogd </B>is derived from sysklogd sources, which in turn was taken from
- the BSD sources. Special thanks to Greg Wettstein (greg@wind.enjel-
- lic.com) and Martin Schulze (joey@linux.de) for the fine sysklogd pack-
- age.
-
- Rainer Gerhards
- Adiscon GmbH
- Grossrinderfeld, Germany
- rgerhards@adiscon.com
-
- Michael Meckelein
- Adiscon GmbH
- mmeckelein@adiscon.com
-
-
-
-Version 1.16.1 (devel) 17 July 2007 RSYSLOGD(8)
-</PRE></BODY>
diff --git a/doc/manual.html b/doc/manual.html
index 424caf21..aff23c8b 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -16,7 +16,10 @@ relay chains while at the same time being very easy to setup for the
novice user. And as we know what enterprise users really need, there is
also <a href="http://www.rsyslog.com/professional-services">professional
rsyslog support</a> available directly from the source!</p>
-<p><b>This documentation is for version 3.22.2 (v3-stable branch) of rsyslog.</b>
+<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 4.6.4 (v4-stable branch) of rsyslog.</b>
Visit the <i> <a href="http://www.rsyslog.com/status">rsyslog status page</a></i></b> to obtain current
version information and project status.
</p><p><b>If you like rsyslog, you might
@@ -25,22 +28,24 @@ time - even a single mouse click helps. Learn <a href="how2help.html">how to hel
Due to popular demand, there is now a <a href="rsyslog_ng_comparison.html">side-by-side comparison
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 document!</a> It will work even
+<a href="v3compatibility.html">be sure to read the rsyslog v3 compatibility document</a>,
+and if you are upgrading from v3, read the
+<a href="v4compatibility.html">rsyslog v4 compatibility document</a>.
+<p>Rsyslog will work even
if you do not read the doc, but doing so will definitely improve your experience.</p>
-<p><span style="font-weight: bold;"></span><b>Follow
-the links below for the</b><br></p><ul>
-
+<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="property_replacer.html">property replacer, an important core component</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 commented <a href="sample.conf.html">sample rsyslog.conf</a> </li>
<li><a href="bugs.html">rsyslog bug list</a></li>
<li><a href="rsyslog_packages.html"> rsyslog packages</a></li>
<li><a href="generic_design.html">backgrounder on
-generic syslog application design</a><!-- not good as it currently is ;) <li><a href="contributors.html">contributor &quot;Hall of Fame&quot;</a>--></li>
+generic syslog application design</a>
<li><a href="modules.html">description of rsyslog modules</a></li>
+<li><a href="http://cookbook.rsyslog.com">the rsyslog "cookbook"</a> - a set of configurations ready to use</li>
</ul>
<p><b>We have some in-depth papers on</b></p>
<ul>
@@ -48,8 +53,10 @@ generic syslog application design</a><!-- not good as it currently is ;) <li><a
<li><a href="build_from_repo.html">obtaining rsyslog from the source repository</a></li>
<li><a href="ipv6.html">rsyslog and IPv6</a> (which is fully supported)</li>
<li><a href="rsyslog_secure_tls.html">native TLS encryption for syslog</a></li>
+<li><a href="multi_ruleset.html">using multiple rule sets in rsyslog</a></li>
<li><a href="rsyslog_stunnel.html">ssl-encrypting syslog with stunnel</a></li>
<li><a href="rsyslog_mysql.html">writing syslog messages to MySQL (and other databases as well)</a></li>
+<li><a href="rsyslog_pgsql.html">writing syslog messages to PostgreSQL (and other databases as well)</a></li>
<li><a href="rsyslog_high_database_rate.html">writing massive amounts of syslog messages to a database</a></li>
<li><a href="rsyslog_reliable_forwarding.html">reliable forwarding to a remote server</a></li>
<li><a href="rsyslog_php_syslog_ng.html">using
@@ -60,7 +67,11 @@ the syslog priority (severity and facility) to the log file</a></li>
syslog sender over NAT</a> (online only)</li>
<li><a href="gssapi.html">an overview and howto of rsyslog gssapi support</a></li>
<li><a href="debug.html">debug support in rsyslog</a></li>
-<li><a href="dev_queue.html">the rsyslog message queue object (developer's view)</a></li>
+<li>Developer Documentation
+ <ul>
+ <li><a href="dev_oplugins.html">writing rsyslog output plugins</a></li>
+ <li><a href="dev_queue.html">the rsyslog message queue object (developer's view)</a></li>
+ </ul></li>
</ul>
<p>Our <a href="history.html">rsyslog history</a>
page is for you if you would like to learn a little more
diff --git a/doc/modules.html b/doc/modules.html
index 92887508..4eae6db3 100644
--- a/doc/modules.html
+++ b/doc/modules.html
@@ -4,9 +4,8 @@
</head>
<body>
<h1>About rsyslog Modules</h1>
- <P><small><i>Written by
- <a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer
- Gerhards</a> (2007-07-28)</i></small></P>
+<P><small><i>Written by
+<a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer Gerhards</a> (2007-07-28)</i></small></P>
<p><font color="#FF0000"><b>This document is incomplete. The module interface is
also quite incomplete and under development. Do not currently use it!</b></font>
You may want to visit <a href="http://rgerhards.blogspot.com/">Rainer's blog</a>
diff --git a/doc/multi_ruleset.html b/doc/multi_ruleset.html
new file mode 100644
index 00000000..8d8c614f
--- /dev/null
+++ b/doc/multi_ruleset.html
@@ -0,0 +1,275 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head>
+<title>Multiple Rulesets in rsyslog</title></head>
+<body>
+<h1>Multiple Rulesets in rsyslog</h1>
+<p>Starting with version 4.5.0 and 5.1.1, <a href="http://www.rsyslog.com">rsyslog</a> supports
+multiple rulesets within a single configuration.
+This is especially useful for routing the recpetion of remote messages to a set of specific rules.
+Note that the input module must support binding to non-standard rulesets, so the functionality
+may not be available with all inputs.
+<p>In this document, I am using <a href="imtcp.html">imtcp</a>, an input module
+that supports binding to non-standard rulesets since rsyslog started to support them.
+<h2>What is a Ruleset?</h2>
+If you have worked with (r)syslog.conf, you know that it is made up of what I call rules (others
+tend to call them selectors, a sysklogd term). Each rule consist of a filter and one or more
+actions to be carried out when the filter evaluates to true. A filter may be as simple as a
+traditional
+syslog priority based filter (like &quot;*.*&quot; or &quot;mail.info&quot; or a as complex as a
+script-like expression. Details on that are covered in the config file documentation. After the
+filter come action specifiers, and an action is something that does something to a message, e.g.
+write it to a file or forward it to a remote logging server.
+
+<p>A traditional configuration file is made up of one or more of these rules. When a new
+message arrives, its processing starts with the first rule (in order of appearance in
+rsyslog.conf) and continues for each rule until either all rules have been processed or
+a so-called &quote;discard&quot; action happens, in which case processing stops and the
+message is thrown away (what also happens after the last rule has been processed).
+
+<p>The <b>multi-ruleset</b> support now permits to specify more than one such rule sequence.
+You can think of a traditional config file just as a single default rule set, which is
+automatically bound to each of the inputs. This is even what actually happens. When
+rsyslog.conf is processed, the config file parser looks for the directive
+
+<pre>$RuleSet &lt;name&gt;
+</pre>
+
+<p>Where name is any name the user likes (but must not start with &quot;RSYSLOG_&quot;, which
+is the name space reserved for rsyslog use). If it finds this directive, it begins a new
+rule set (if the name was not yet know) or switches to an already-existing one (if the name
+was known). All rules defined between this $RuleSet directive and the next one are appended
+to the named ruleset. Note that the reserved name "RSYSLOG_DefaultRuleset" is used to
+specify rsyslogd's default ruleset. You can use that name whereever you can use a ruleset name,
+including when binding an input to it.
+
+<p>Inside a ruleset, messages are processed as described above: they start with the first rule
+and rules are processed in the order of appearance of the configuration file until either
+there are no more rules or the discard action is executed. Note that with multiple rulesets
+no longer <b>all</b> rsyslog.conf rules are executed but <b>only</b> those that are
+contained within the specific ruleset.
+
+<p>Inputs must explicitely bind to rulesets. If they don't do, the default ruleset is bound.
+
+<p>This brings up the next question:
+
+<h2>What does &quot;To bind to a Ruleset&quot; mean?</h2>
+<p>This term is used in the same sense as &quot;to bind an IP address to an interface&quot;:
+it means that a specific input, or part of an input (like a tcp listener) will use a specific
+ruleset to &quot;pass its messages to&quot;. So when a new message arrives, it will be processed
+via the bound ruleset. Rule from all other rulesets are irrelevant and will never be processed.
+<p>This makes multiple rulesets very handy to process local and remote message via
+seperate means: bind the respective receivers to different rule sets, and you do not need
+to seperate the messages by any other method.
+
+<p>Binding to rulesets is input-specifc. For imtcp, this is done via the
+
+<pre>$InputTCPServerBindRuleset &lt;name&gt;
+</pre>
+
+directive. Note that &quot;name&quote; must be the name of a ruleset that is already defined
+at the time the bind directive is given. There are many ways to make sure this happens, but
+I personally think that it is best to define all rule sets at the top of rsyslog.conf and
+define the inputs at the bottom. This kind of reverses the traditional recommended ordering, but
+seems to be a really useful and straightforward way of doing things.
+<h2>Can I use a different Ruleset as the default?</h2>
+<p>This is possible by using the
+
+<pre>$DefaultRuleset &lt;name&gt;
+</pre>
+
+Directive. Please note, however, that this directive is actually global: that is, it does not
+modify the ruleset to which the next input is bound but rather provides a system-wide
+default rule set for those inputs that did not explicitly bind to one. As such, the directive
+can not be used as a work-around to bind inputs to non-default rulesets that do not support
+ruleset binding.
+<h2>Examples</h2>
+<h3>Split local and remote logging</h3>
+<p>Let's say you have a pretty standard system that logs its local messages to the usual
+bunch of files that are specified in the default rsyslog.conf. As an example, your rsyslog.conf
+might look like this:
+
+<pre>
+# ... module loading ...
+# The authpriv file has restricted access.
+authpriv.* /var/log/secure
+# Log all the mail messages in one place.
+mail.* /var/log/maillog
+# Log cron stuff
+cron.* /var/log/cron
+# Everybody gets emergency messages
+*.emerg *
+... more ...
+</pre>
+
+<p>Now, you want to add receive messages from a remote system and log these to
+a special file, but you do not want to have these messages written to the files
+specified above. The traditional approach is to add a rule in front of all others that
+filters on the message, processes it and then discards it:
+
+<pre>
+# ... module loading ...
+# process remote messages
+:fromhost-ip, isequal, "192.0.2.1" /var/log/remotefile
+& ~
+# only messages not from 192.0.21 make it past this point
+
+# The authpriv file has restricted access.
+authpriv.* /var/log/secure
+# Log all the mail messages in one place.
+mail.* /var/log/maillog
+# Log cron stuff
+cron.* /var/log/cron
+# Everybody gets emergency messages
+*.emerg *
+... more ...
+</pre>
+
+<p>Note the tilde character, which is the discard action!. Also note that we assume that
+192.0.2.1 is the sole remote sender (to keep it simple).
+
+<p>With multiple rulesets, we can simply define a dedicated ruleset for the remote reception
+case and bind it to the receiver. This may be written as follows:
+
+<pre>
+# ... module loading ...
+# process remote messages
+# define new ruleset and add rules to it:
+$RuleSet remote
+*.* /var/log/remotefile
+# only messages not from 192.0.21 make it past this point
+
+# bind ruleset to tcp listener
+$InputTCPServerBindRuleset remote
+# and activate it:
+$InputTCPServerRun 10514
+
+# switch back to the default ruleset:
+$RuleSet RSYSLOG_DefaultRuleset
+# The authpriv file has restricted access.
+authpriv.* /var/log/secure
+# Log all the mail messages in one place.
+mail.* /var/log/maillog
+# Log cron stuff
+cron.* /var/log/cron
+# Everybody gets emergency messages
+*.emerg *
+... more ...
+</pre>
+
+<p>Here, we need to switch back to the default ruleset after we have defined our custom
+one. This is why I recommend a different ordering, which I find more intuitive. The sample
+below has it, and it leads to the same results:
+
+<pre>
+# ... module loading ...
+# at first, this is a copy of the unmodified rsyslog.conf
+# The authpriv file has restricted access.
+authpriv.* /var/log/secure
+# Log all the mail messages in one place.
+mail.* /var/log/maillog
+# Log cron stuff
+cron.* /var/log/cron
+# Everybody gets emergency messages
+*.emerg *
+... more ...
+# end of the "regular" rsyslog.conf. Now come the new definitions:
+
+# process remote messages
+# define new ruleset and add rules to it:
+$RuleSet remote
+*.* /var/log/remotefile
+
+# bind ruleset to tcp listener
+$InputTCPServerBindRuleset remote
+# and activate it:
+$InputTCPServerRun 10514
+</pre>
+
+<p>Here, we do not switch back to the default ruleset, because this is not needed as it is
+completely defined when we begin the &quot;remote&quot; ruleset.
+
+<p>Now look at the examples and compare them to the single-ruleset solution. You will notice
+that we do <b>not</b> need a real filter in the multi-ruleset case: we can simply use
+&quot;*.*&quot; as all messages now means all messages that are being processed by this
+rule set and all of them come in via the TCP receiver! This is what makes using multiple
+rulesets so much easier.
+
+<h3>Split local and remote logging for three different ports</h3>
+<p>This example is almost like the first one, but it extends it a little bit. While it is
+very similar, I hope it is different enough to provide a useful example why you may want
+to have more than two rulesets.
+
+<p>Again, we would like to use the &quot;regular&quot; log files for local logging, only. But
+this time we set up three syslog/tcp listeners, each one listening to a different
+port (in this example 10514, 10515, and 10516). Logs received from these receivers shall go into
+different files. Also, logs received from 10516 (and only from that port!) with
+&quot;mail.*&quot; priority, shall be written into a specif file and <b>not</b> be
+written to 10516's general log file.
+
+<p>This is the config:
+
+<pre>
+# ... module loading ...
+# at first, this is a copy of the unmodified rsyslog.conf
+# The authpriv file has restricted access.
+authpriv.* /var/log/secure
+# Log all the mail messages in one place.
+mail.* /var/log/maillog
+# Log cron stuff
+cron.* /var/log/cron
+# Everybody gets emergency messages
+*.emerg *
+... more ...
+# end of the "regular" rsyslog.conf. Now come the new definitions:
+
+# process remote messages
+
+#define rulesets first
+$RuleSet remote10514
+*.* /var/log/remote10514
+
+$RuleSet remote10515
+*.* /var/log/remote10515
+
+$RuleSet remote10516
+mail.* /var/log/mail10516
+& ~
+# note that the discard-action will prevent this messag from
+# being written to the remote10516 file - as usual...
+*.* /var/log/remote10516
+
+# and now define listners bound to the relevant ruleset
+$InputTCPServerBindRuleset remote10514
+$InputTCPServerRun 10514
+
+$InputTCPServerBindRuleset remote10515
+$InputTCPServerRun 10515
+
+$InputTCPServerBindRuleset remote10516
+$InputTCPServerRun 10516
+</pre>
+
+<p>Note that the &quot;mail.*&quot; rule inside the &quot;remote10516&quote; ruleset does
+not affect processing inside any other rule set, including the default rule set.
+
+
+<h2>Performance</h2>
+<p>No rule processing can be faster than not processing a rule at all. As such, it is useful
+for a high performance system to identify disjunct actions and try to split these off to
+different rule sets. In the example section, we had a case where three different tcp listeners
+need to write to three different files. This is a perfect example of where multiple rule sets
+are easier to use and offer more performance. The performance is better simply because there
+is no need to check the reception service - instead messages are automatically pushed to the
+right rule set and can be processed by very simple rules (maybe even with
+&quot;*.*&quot;-filters, the fastest ones available).
+
+<p>In the long term, multiple rule sets will probably lay the foundation for even better
+optimizations. So it is not a bad idea to get aquainted with them.
+
+<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; 2009 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/netstream.html b/doc/netstream.html
index e7d54c12..cbfa12ae 100644
--- a/doc/netstream.html
+++ b/doc/netstream.html
@@ -3,6 +3,8 @@
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h1>Network Stream Drivers</h1><p>Network stream drivers are a layer
between various parts of rsyslogd (e.g. the imtcp module) and the
transport layer. They provide sequenced delivery, authentication and
@@ -18,4 +20,4 @@ Copyright © 2008 by <a href="http://www.gerhards.net/rainer">Rainer
Gerhards</a> and
<a href="http://www.adiscon.com/">Adiscon</a>.
Released under the GNU GPL version 3 or higher.</font></p>
-</body></html> \ No newline at end of file
+</body></html>
diff --git a/doc/omlibdbi.html b/doc/omlibdbi.html
index 8ff74371..ec1d01b6 100644
--- a/doc/omlibdbi.html
+++ b/doc/omlibdbi.html
@@ -4,6 +4,8 @@
</head>
<body>
+<a href="rsyslog_conf_modules.html">back</a>
+
<h1>Generic Database Output Module (omlibdbi)</h1>
<p><b>Module Name:&nbsp;&nbsp;&nbsp; omlibdbi</b></p>
<p><b>Author: </b>Rainer Gerhards
diff --git a/doc/ommail.html b/doc/ommail.html
index c18cf3f8..0841dc9f 100644
--- a/doc/ommail.html
+++ b/doc/ommail.html
@@ -1,6 +1,6 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head><title>mail output module - sending syslog messages via mail</title>
-
+<a href="features.html">back</a>
</head>
<body>
<h1>Mail Output Module (ommail)</h1>
@@ -142,4 +142,5 @@ Copyright &copy; 2008 by <a href="http://www.gerhards.net/rainer">Rainer
Gerhards</a> and
<a href="http://www.adiscon.com/">Adiscon</a>.
Released under the GNU GPL version 3 or higher.</font></p>
+
</body></html>
diff --git a/doc/ommysql.html b/doc/ommysql.html
index e81ce532..9b35b402 100644
--- a/doc/ommysql.html
+++ b/doc/ommysql.html
@@ -5,6 +5,8 @@
</head>
<body>
+<a href="rsyslog_conf_modules.html">back</a>
+
<h1>MySQL Database Output Module</h1>
<p><b>Module Name:&nbsp;&nbsp;&nbsp; ommysql</b></p>
<p><b>Author: </b>Michael Meckelein (Initial Author) / Rainer Gerhards
diff --git a/doc/omoracle.html b/doc/omoracle.html
new file mode 100644
index 00000000..cfcf277f
--- /dev/null
+++ b/doc/omoracle.html
@@ -0,0 +1,200 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head>
+<meta http-equiv="Content-Language" content="en">
+<title>Oracle Database Output Module</title>
+</head>
+
+<body>
+<a href="rsyslog_conf_modules.html">rsyslog module reference</a>
+
+<h1>Oracle Database Output Module</h1>
+<p><b>Module Name:&nbsp;&nbsp;&nbsp; omoracle</b></p>
+<p><b>Author: </b>Luis Fernando Mu&ntilde;oz Mej&iacute;as &lt;Luis.Fernando.Munoz.Mejias@cern.ch&gt;</p>
+<p><b>Available since: </b>: 4.3.0
+<p><b>Status: </b>: contributed module, not maitained by rsyslog core authors
+<p><b>Description</b>:</p>
+<p>This module provides native support for logging to Oracle databases. It offers
+superior performance over the more generic <a href="omlibdbi.html">omlibdbi</a> module.
+It also includes a number of enhancements, most importantly prepared statements and
+batching, what provides a big performance improvements.
+</p>
+<p>Note that this module is maintained by its original author. If you need assistance with it,
+it is suggested to post questions to the
+<a href="http://lists.adiscon.net/mailman/listinfo/rsyslog">rsyslog mailing list</a>.
+<p>From the header comments of this module:
+<p><pre>
+
+ This is an output module feeding directly to an Oracle
+ database. It uses Oracle Call Interface, a propietary module
+ provided by Oracle.
+
+ Selector lines to be used are of this form:
+
+ :omoracle:;TemplateName
+
+ The module gets its configuration via rsyslog $... directives,
+ namely:
+
+ $OmoracleDBUser: user name to log in on the database.
+
+ $OmoracleDBPassword: password to log in on the database.
+
+ $OmoracleDB: connection string (an Oracle easy connect or a db
+ name as specified by tnsnames.ora)
+
+ $OmoracleBatchSize: Number of elements to send to the DB on each
+ transaction.
+
+ $OmoracleStatement: Statement to be prepared and executed in
+ batches. Please note that Oracle's prepared statements have their
+ placeholders as ':identifier', and this module uses the colon to
+ guess how many placeholders there will be.
+
+ All these directives are mandatory. The dbstring can be an Oracle
+ easystring or a DB name, as present in the tnsnames.ora file.
+
+ The form of the template is just a list of strings you want
+ inserted to the DB, for instance:
+
+ $template TestStmt,"%hostname%%msg%"
+
+ Will provide the arguments to a statement like
+
+ $OmoracleStatement \
+ insert into foo(hostname,message)values(:host,:message)
+
+ Also note that identifiers to placeholders are arbitrarry. You
+ need to define the properties on the template in the correct order
+ you want them passed to the statement!
+</pre>
+<p>Some additional documentation contributed by Ronny Egner:
+<pre>
+REQUIREMENTS:
+--------------
+
+- Oracle Instantclient 10g (NOT 11g) Base + Devel
+ (if you´re on 64-bit linux you should choose the 64-bit libs!)
+- JDK 1.6 (not neccessary for oracle plugin but "make" didd not finsished successfully without it)
+
+- "oracle-instantclient-config" script
+ (seems to shipped with instantclient 10g Release 1 but i was unable to find it for 10g Release 2 so here it is)
+
+
+====================== /usr/local/bin/oracle-instantclient-config =====================
+#!/bin/sh
+#
+# Oracle InstantClient SDK config file
+# Jean-Christophe Duberga - Bordeaux 2 University
+#
+
+# just adapt it to your environment
+incdirs="-I/usr/include/oracle/10.2.0.4/client64"
+libdirs="-L/usr/lib/oracle/10.2.0.4/client64/lib"
+
+usage="\
+Usage: oracle-instantclient-config [--prefix[=DIR]] [--exec-prefix[=DIR]] [--version] [--cflags] [--libs] [--static-libs]"
+
+if test $# -eq 0; then
+ echo "${usage}" 1>&2
+ exit 1
+fi
+
+while test $# -gt 0; do
+ case "$1" in
+ -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) optarg= ;;
+ esac
+
+ case $1 in
+ --prefix=*)
+ prefix=$optarg
+ if test $exec_prefix_set = no ; then
+ exec_prefix=$optarg
+ fi
+ ;;
+ --prefix)
+ echo $prefix
+ ;;
+ --exec-prefix=*)
+ exec_prefix=$optarg
+ exec_prefix_set=yes
+ ;;
+ --exec-prefix)
+ echo ${exec_prefix}
+ ;;
+ --version)
+ echo ${version}
+ ;;
+ --cflags)
+ echo ${incdirs}
+ ;;
+ --libs)
+ echo $libdirs -lclntsh -lnnz10 -locci -lociei -locijdbc10
+ ;;
+ --static-libs)
+ echo "No static libs" 1>&2
+ exit 1
+ ;;
+ *)
+ echo "${usage}" 1>&2
+ exit 1
+ ;;
+ esac
+ shift
+done
+
+=============== END ==============
+
+
+
+
+COMPILING RSYSLOGD
+-------------------
+
+
+./configure --enable-oracle
+
+
+
+
+RUNNING
+-------
+
+- make sure rsyslogd is able to locate the oracle libs (either via LD_LIBRARY_PATH or /etc/ld.so.conf)
+- set TNS_ADMIN to point to your tnsnames.ora
+- create a tnsnames.ora and test you are able to connect to the database
+
+- create user in oracle as shown in the following example:
+ create user syslog identified by syslog default tablespace users quota unlimited on users;
+ grant create session to syslog;
+ create role syslog_role;
+ grant syslog_role to syslog;
+ grant create table to syslog_role;
+ grant create sequence to syslog_role;
+
+- create tables as needed
+
+- configure rsyslog as shown in the following example
+ $ModLoad omoracle
+
+ $OmoracleDBUser syslog
+ $OmoracleDBPassword syslog
+ $OmoracleDB syslog
+ $OmoracleBatchSize 1
+ $OmoracleBatchItemSize 4096
+
+ $OmoracleStatementTemplate OmoracleStatement
+ $template OmoracleStatement,"insert into foo(hostname,message) values (:host,:message)"
+ $template TestStmt,"%hostname%%msg%"
+ *.* :omoracle:;TestStmt
+ (you guess it: username = password = database = "syslog".... see $rsyslogd_source/plugins/omoracle/omoracle.c for me info)
+</pre>
+<p>[<a href="rsyslog_conf.html">rsyslog.conf overview</a>]
+[<a href="manual.html">manual index</a>] [<a href="http://www.rsyslog.com/">rsyslog site</a>]</p>
+<p><font size="2">This documentation is part of the
+<a href="http://www.rsyslog.com/">rsyslog</a>
+project.<br>
+Copyright &copy; 2008, 2009 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>.
+Released under the GNU GPL version 3 or higher.</font></p>
+</body></html>
diff --git a/doc/omrelp.html b/doc/omrelp.html
index d5437a70..22e6845f 100644
--- a/doc/omrelp.html
+++ b/doc/omrelp.html
@@ -4,6 +4,8 @@
</head>
<body>
+<a href="rsyslog_conf_modules.html">back to rsyslog module documentation</a>
+
<h1>RELP Output Module (omrelp)</h1>
<p><b>Module Name:&nbsp;&nbsp;&nbsp; omrelp</b></p>
<p><b>Author: </b>Rainer Gerhards
@@ -42,6 +44,7 @@ special "RSYSLOG_ForwardFormat" (case sensitive!) template is used.<br>
# port 2514
*.* :omrelp:centralserv:2514;RSYSLOG_ForwardFormat
</textarea>
+Note: to use IPv6 addresses, encode them in [::1] format.
<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
diff --git a/doc/omsnmp.html b/doc/omsnmp.html
index 31aaef24..b38a594f 100644
--- a/doc/omsnmp.html
+++ b/doc/omsnmp.html
@@ -4,6 +4,7 @@
<title>SNMP Output Module</title></head>
<body>
+<a href="rsyslog_conf_modules.html">back</a>
<h1>SNMP Output Module</h1>
<p><b>Module Name:&nbsp;&nbsp;&nbsp; omsnmp</b></p>
diff --git a/doc/property_replacer.html b/doc/property_replacer.html
index c2a0c0d2..4d242a34 100644
--- a/doc/property_replacer.html
+++ b/doc/property_replacer.html
@@ -1,6 +1,7 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head><title>The Rsyslogd Property Replacer</title></head>
<body>
+<a href="rsyslog_conf_templates.html">back</a>
<h1>The Property Replacer</h1>
<p><b>The property replacer is a core component in
rsyslogd's output system.</b> A syslog message has a number of
@@ -29,10 +30,6 @@ Currently supported are:</p>
socket. Should be useful for debugging.</td>
</tr>
<tr>
-<td><b>uxtradmsg</b></td>
-<td>will disappear soon - do NOT use!</td>
-</tr>
-<tr>
<td><b>hostname</b></td>
<td>hostname from the message</td>
</tr>
@@ -228,7 +225,7 @@ sequence with a regular expression is: "%msg:R:.*Sev:. \(.*\)
\[.*--end%"</p>
<p>It is possible to specify some parametes after the "R". These are
comma-separated. They are:
-<p>R,&lt;regexp-type&gt;,&lt;submatch&gt;,&lt;nomatch&gt;,&lt;match-number&gt;
+<p>R,&lt;regexp-type&gt;,&lt;submatch&gt;,&lt;<a href="rsyslog_conf_nomatch.html">nomatch</a>&gt;,&lt;match-number&gt;
<p>regexp-type is either "BRE" for Posix basic regular expressions or
"ERE" for extended ones. The string must be given in upper case. The
default is "BRE" to be consistent with earlier versions of rsyslog that
@@ -240,13 +237,8 @@ that the first match is number 0, the second 1 and so on. Up to 10 matches
(up to number 9) are supported. Please note that it would be more
natural to have the match-number in front of submatch, but this would break
backward-compatibility. So the match-number must be specified after "nomatch".
-<p>nomatch is either "DFLT", "BLANK", ZERO or "FIELD" (all upper case!). It tells
-what to use if no match is found. With "DFLT", the strig "**NO MATCH**" is
-used. This was the only supported value up to rsyslog 3.19.5. With "BLANK"
-a blank text is used (""). With "ZERO", "0" is used.
-Finally, "FIELD" uses the full property text
-instead of the expression. Some folks have requested that, so it seems
-to be useful.
+<p><a href="rsyslog_conf_nomatch.html">nomatch</a> specifies what should
+be used in case no match is found.
<p>The following is a sample of an ERE expression that takes the first
submatch from the message string and replaces the expression with
the full field if no match is found:
@@ -318,6 +310,18 @@ case-insensitive. Currently, the following options are defined:
<td>convert property text to uppercase only</td>
</tr>
<tr>
+<td valign="top"><b>csv</b></td>
+<td>formats the resulting field (after all modifications) in CSV format
+as specified in <a href="http://www.ietf.org/rfc/rfc4180.txt">RFC 4180</a>.
+Rsyslog will always use double quotes. Note that in order to have full CSV-formatted
+text, you need to define a proper template. An example is this one:
+<br>$template csvline,"%syslogtag:::csv%,%msg:::csv%"
+<br>Most importantly, you need to provide the commas between the fields
+inside the template.
+<br><i>This feature was introduced in rsyslog 4.1.6.</i>
+</td>
+</tr>
+<tr>
<td><b>drop-last-lf</b></td>
<td>The last LF in the message (if any), is dropped.
Especially useful for PIX.</td>
@@ -331,6 +335,19 @@ Especially useful for PIX.</td>
<td>format as RFC 3164 date</td>
</tr>
<tr>
+<tr>
+<td valign="top"><b>date-rfc3164-buggyday</b></td>
+<td>similar to date-rfc3164, but emulates a common coding error: RFC 3164 demands
+that a space is written for single-digit days. With this option, a zero is
+written instead. This format seems to be used by syslog-ng and the
+date-rfc3164-buggyday option can be used in migration scenarios where otherwise
+lots of scripts would need to be adjusted. It is recommended <i>not</i> to use this
+option when forwarding to remote hosts - they may treat the date as invalid
+(especially when parsing strictly according to RFC 3164).</td>
+<br><i>This feature was introduced in rsyslog 4.6.2 and v4 versions above and
+5.5.3 and all versions above.</i>
+</tr>
+<tr>
<td><b>date-rfc3339</b></td>
<td>format as RFC 3339 date</td>
</tr>
@@ -410,4 +427,13 @@ to record severity and facility of a message)</li>
<li><a href="rsyslog_conf.html">Configuration file
syntax</a>, this is where you actually use the property replacer.</li>
</ul>
+<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, 2009 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 2 or higher.</font></p>
+
</body></html>
diff --git a/doc/queue_analogy_tv.png b/doc/queue_analogy_tv.png
new file mode 100644
index 00000000..fedcb558
--- /dev/null
+++ b/doc/queue_analogy_tv.png
Binary files differ
diff --git a/doc/queues.html b/doc/queues.html
index a2074d36..45ce1bd1 100644
--- a/doc/queues.html
+++ b/doc/queues.html
@@ -1,14 +1,21 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
-<meta http-equiv="Content-Language" content="de">
<title>Understanding rsyslog queues</title></head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
<h1>Understanding rsyslog Queues</h1>
<p>Rsyslog uses queues whenever two activities need to be loosely coupled. With a
queue, one part of the system "produces" something while another part "consumes"
this something. The "something" is most often syslog messages, but queues may
also be used for other purposes.</p>
+<p>This document provides a good insight into technical details, operation modes
+and implications. In addition to it, an
+<a href="queues_analogy.html">rsyslog queue concepts overview</a> document
+exists which tries to explain queues with the help of some analogies. This may
+probably be a better place to start reading about queues. I assume that once you
+have understood that document, the material here will be much easier to grasp
+and look much more natural.
<p>The most prominent example is the main message queue. Whenever rsyslog
receives a message (e.g. locally, via UDP, TCP or in whatever else way), it
places these messages into the main message queue. Later, it is dequeued by the
@@ -16,6 +23,36 @@ rule processor, which then evaluates which actions are to be carried out. In
front of each action, there is also a queue, which potentially de-couples the
filter processing from the actual action (e.g. writing to file, database or
forwarding to another host).</p>
+<h1>Where are Queues Used?</h1>
+<p>Currently, queues are used for the main message queue and for the
+actions.</p>
+<p>There is a single main message queue inside rsyslog. Each input module
+delivers messages to it. The main message queue worker filters messages based on
+rules specified in rsyslog.conf and dispatches them to the individual action
+queues. Once a message is in an action queue, it is deleted from the main
+message queue.</p>
+<p>There are multiple action queues, one for each configured action. By default,
+these queues operate in direct (non-queueing) mode. Action queues are fully
+configurable and thus can be changed to whatever is best for the given use case.</p>
+<p>Future versions of rsyslog will most probably utilize queues at other places,
+too.</p>
+<p> Wherever "<i>&lt;object&gt;</i>"&nbsp; is used in the config file
+statements, substitute "<i>&lt;object&gt;</i>" with either "MainMsg" or "Action". The
+former will set main message queue
+parameters, the later parameters for the next action that will be
+created. Action queue parameters can not be modified once the action has been
+specified. For example, to tell the main message queue to save its content on
+shutdown, use <i>$MainMsgQueueSaveOnShutdown on</i>".</p>
+<p>If the same parameter is specified multiple times before a queue is created,
+the last one specified takes precedence. The main message queue is created after
+parsing the config file and all of its potential includes. An action queue is
+created each time an action selector is specified. Action queue parameters are
+reset to default after an action queue has been created (to provide a clean
+environment for the next action).</p>
+<p>Not all queues necessarily support the full set of queue configuration
+parameters, because not all are applicable. For example, in current output
+module design, actions do not support multi-threading. Consequently, the number
+of worker threads is fixed to one for action queues and can not be changed.</p>
<h1>Queue Modes</h1>
<p>Rsyslog supports different queue modes, some with submodes. Each of them has
specific advantages and disadvantages. Selecting the right queue mode is quite
@@ -78,7 +115,11 @@ isolation. This is currently selected by specifying different <i>$WorkDirectory<
config directives before the queue creation statement.</p>
<p>To create a disk queue, use the "<i>$&lt;object&gt;QueueType Disk</i>" config
directive. Checkpoint intervals can be specified via "<i>$&lt;object&gt;QueueCheckpointInterval</i>",
-with 0 meaning no checkpoints. </p>
+with 0 meaning no checkpoints. Note that disk-based queues can be made very reliable
+by issuing a (f)sync after each write operation. Starting with version 4.3.2, this can
+be requested via "<i>&lt;object&gt;QueueSyncQueueFiles on/off</i> with the
+default being off. Activating this option has a performance penalty, so it should
+not be turned on without reason.</p>
<h2>In-Memory Queues</h2>
<p>In-memory queue mode is what most people have on their mind when they think
about computing queues. Here, the enqueued data elements are held in memory.
@@ -113,8 +154,7 @@ only memory if in use. A FixedArray queue may have a too large static memory
footprint in such cases.</p>
<p><b>In general, it is advised to use LinkedList mode if in doubt</b>. The
processing overhead compared to FixedArray is low and may be
-<span style="font-size: 12pt; line-height: 115%; font-family: 'Times New Roman',serif;" lang="EN-US">
-outweigh </span>by the reduction in memory use. Paging in most-often-unused
+outweigh by the reduction in memory use. Paging in most-often-unused
pointer array pages can be much slower than dynamically allocating them.</p>
<p>To create an in-memory queue, use the "<i>$&lt;object&gt;QueueType LinkedList</i>"
or&nbsp; "<i>$&lt;object&gt;QueueType FixedArray</i>" config directive.</p>
@@ -219,11 +259,12 @@ parall. Thus, the upper limit ca be set via "<i>$&lt;object&gt;QueueWorkerThread
If it, for example, is set to four, no more than four workers will ever be
started, no matter how many elements are enqueued. </p>
<p>Worker threads that have been started are kept running until an inactivity
-timeout happens. The timeout can be set via "<i>$&lt;object&gt;QueueWorkerTimeoutShutdown</i>"
+timeout happens. The timeout can be set via "<i>$&lt;object&gt;QueueWorkerTimeoutThreadShutdown</i>"
and is specified in milliseconds. If you do not like to keep the workers
running, simply set it to 0, which means immediate timeout and thus immediate
shutdown. But consider that creating threads involves some overhead, and this is
-why we keep them running.</p>
+why we keep them running. If you would like to never shutdown any worker
+threads, specify -1 for this parameter.</p>
<h2>Discarding Messages</h2>
<p>If the queue reaches the so called "discard watermark" (a number of queued
elements), less important messages can automatically be discarded. This is in an
@@ -258,19 +299,15 @@ unavoidable and you prefer to discard less important messages first.</p>
disk space, it is finally full. If so, rsyslogd throttles the data element
submitter. If that, for example, is a reliable input (TCP, local log socket),
that will slow down the message originator which is a good
-<span style="font-size: 12pt; line-height: 115%; font-family: 'Times New Roman',serif;" lang="EN-US">
-resolution </span>for this scenario.</p>
-<p>During
-<span style="font-size: 12pt; line-height: 115%; font-family: 'Times New Roman',serif;" lang="EN-US">
-throtteling</span>, a disk-assisted queue continues to write to disk and
+resolution for this scenario.</p>
+<p>During throtteling, a disk-assisted queue continues to write to disk and
messages are also discarded based on severity as well as regular dequeuing and
processing continues. So chances are good the situation will be resolved by
simply throttling. Note, though, that throtteling is highly undesirable for
unreliable sources, like UDP message reception. So it is not a good thing to run
into throtteling mode at all.</p>
<p>We can not hold processing
-<span style="font-size: 12pt; line-height: 115%; font-family: 'Times New Roman',serif;" lang="EN-US">
-infinitely</span>, not even when throtteling. For example, throtteling the local
+infinitely, not even when throtteling. For example, throtteling the local
log socket too long would cause the system at whole come to a standstill. To
prevent this, rsyslogd times out after a configured period ("<i>$&lt;object&gt;QueueTimeoutEnqueue</i>",
specified in milliseconds) if no space becomes available. As a last resort, it
@@ -301,8 +338,7 @@ There are two configuration directives, both should be used together or
results are unpredictable:" <i>$&lt;object&gt;QueueDequeueTimeBegin &lt;hour&gt;</i>" and&nbsp;"<i>$&lt;object&gt;QueueDequeueTimeEnd &lt;hour&gt;</i>". The hour parameter must be specified in 24-hour format (so 10pm is 22). A use case for this parameter can be found in the <a href="http://wiki.rsyslog.com/index.php/OffPeakHours">rsyslog wiki</a>. </p>
<h2>Terminating Queues</h2>
<p>Terminating a process sounds easy, but can be complex.
-<span style="font-size: 12pt; line-height: 115%; font-family: 'Times New Roman',serif;" lang="EN-US">
-Terminating </span>a running queue is in fact the most complex operation a queue
+Terminating a running queue is in fact the most complex operation a queue
object can perform. You don't see that from a user's point of view, but its
quite hard work for the developer to do everything in the right order.</p>
<p>The complexity arises when the queue has still data enqueued when it
@@ -323,38 +359,13 @@ it terminates. This includes data elements there were begun being processed by
workers that needed to be cancelled due to too-long processing. For a large
queue, this operation may be lengthy. No timeout applies to a required shutdown
save.</p>
-<h1>Where are Queues Used?</h1>
-<p>&nbsp;Currently, queues are used for the main message queue and for the
-actions.</p>
-<p>There is a single main message queue inside rsyslog. Each input module
-delivers messages to it. The main message queue worker filters messages based on
-rules specified in rsyslog.conf and dispatches them to the individual action
-queues. Once a message is in an action queue, it is deleted from the main
-message queue.</p>
-<p>There are multiple action queues, one for each configured action. By default,
-these queues operate in direct (non-queueing) mode. Action queues are fully
-configurable and thus can be changed to whatever is best for the given use case.</p>
-<p>Future versions of rsyslog will most probably utilize queues at other places,
-too.</p>
-<p>
-<span style="font-size: 12pt; line-height: 115%; font-family: 'Times New Roman',serif;" lang="EN-US">
-Wherever </span>"<i>&lt;object&gt;</i>"&nbsp; was used above in the config file
-statements, substitute "<i>&lt;object&gt;</i>" with either "MainMsg" or "Action". The
-former will set main message queue
-<span style="font-size: 12pt; line-height: 115%; font-family: 'Times New Roman',serif;" lang="EN-US">
-parameters</span>, the later parameters for the next action that will be
-created. Action queue parameters can not be modified once the action has been
-specified. For example, to tell the main message queue to save its content on
-shutdown, use <i>$MainMsgQueueSaveOnShutdown on</i>".</p>
-<p>If the same parameter is specified multiple times before a queue is created,
-the last one specified takes precedence. The main message queue is created after
-parsing the config file and all of its potential includes. An action queue is
-created each time an action selector is specified. Action queue parameters are
-reset to default after an action queue has been created (to provide a clean
-environment for the next action).</p>
-<p>Not all queues necessarily support the full set of queue configuration
-parameters, because not all are applicable. For example, in current output
-module design, actions do not support multi-threading. Consequently, the number
-of worker threads is fixed to one for action queues and can not be changed.</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, 2009 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 3 or higher.</font></p>
-</body></html> \ No newline at end of file
+</body></html>
diff --git a/doc/queues_analogy.html b/doc/queues_analogy.html
new file mode 100644
index 00000000..d7533ad5
--- /dev/null
+++ b/doc/queues_analogy.html
@@ -0,0 +1,259 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head>
+<title>turning lanes and rsyslog queues - an analogy</title></head>
+<body>
+<a href="rsyslog_conf_global.html">back</a>
+
+<h1>Turning Lanes and Rsyslog Queues - an Analogy</h1>
+<p>If there is a single object absolutely vital to understanding the way
+rsyslog works, this object is queues. Queues offer a variety of services,
+including support for multithreading. While there is elaborate in-depth
+documentation on the ins and outs of <a href="queues.html">rsyslog queues</a>,
+some of the concepts are hard to grasp even for experienced people. I think this
+is because rsyslog uses a very high layer of abstraction which includes things
+that look quite unnatural, like queues that do <b>not</b> actually queue...
+<p>With this document, I take a different approach: I will not describe every specific
+detail of queue operation but hope to be able to provide the core idea of how
+queues are used in rsyslog by using an analogy. I will compare the rsyslog data flow
+with real-life traffic flowing at an intersection.
+<p>But first let's set the stage for the rsyslog part. The graphic below describes
+the data flow inside rsyslog:
+<p align="center"><img src="dataflow.png" alt="rsyslog data flow">
+<p>Note that there is a <a href="http://www.rsyslog.com/Article350.phtml">video tutorial</a>
+available on the data flow. It is not perfect, but may aid in understanding this picture.
+<p>For our needs, the important fact to know is that messages enter rsyslog on &quot;the
+left side&quot; (for example, via UDP), are being preprocessed, put into the
+so-called main queue, taken off that queue, be filtered and be placed into one or
+several action queues (depending on filter results). They leave rsyslog on &quot;the
+right side&quot; where output modules (like the file or database writer) consume them.
+<p>So there are always <b>two</b> stages where a message (conceptually) is queued - first
+in the main queue and later on in <i>n</i> action specific queues (with <i>n</i> being the number of
+actions that the message in question needs to be processed by, what is being decided
+by the &quot;Filter Engine&quot;). As such, a message will be in at least two queues
+during its lifetime (with the exception of messages being discarded by the queue itself,
+but for the purpose of this document, we will ignore that possibility).
+<p>Also, it is vitally
+important to understand that <b>each</b> action has a queue sitting in front of it.
+If you have dug into the details of rsyslog configuration, you have probably seen that
+a queue mode can be set for each action. And the default queue mode is the so-called
+&quot;direct mode&quot;, in which &quot;the queue does not actually enqueue data&quot;.
+That sounds silly, but is not. It is an important abstraction that helps keep the code clean.
+<p>To understand this, we first need to look at who is the active component. In our data flow,
+the active part always sits to the left of the object. For example, the &quot;Preprocessor&quot;
+is being called by the inputs and calls itself into the main message queue. That is, the queue
+receiver is called, it is passive. One might think that the &quot;Parser &amp; Filter Engine&quot;
+is an active component that actively pulls messages from the queue. This is wrong! Actually,
+it is the queue that has a pool of worker threads, and these workers pull data from the queue
+and then call the passively waiting Parser and Filter Engine with those messages. So the
+main message queue is the active part, the Parser and Filter Engine is passive.
+<p>Let's now try an analogy analogy for this part: Think about a TV show. The show is produced
+in some TV studio, from there sent (actively) to a radio tower. The radio tower passively
+receives from the studio and then actively sends out a signal, which is passively received
+by your TV set. In our simplified view, we have the following picture:
+<p align="center"><img src="queue_analogy_tv.png" alt="rsyslog queues and TV analogy">
+<p>The lower part of the picture lists the equivalent rsyslog entities, in an abstracted way.
+Every queue has a producer (in the above sample the input) and a consumer (in the above sample the Parser
+and Filter Engine). Their active and passive functions are equivalent to the TV entities
+that are listed on top of the rsyslog entity. For example, a rsyslog consumer can never
+actively initiate reception of a message in the same way a TV set cannot actively
+&quot;initiate&quot; a TV show - both can only &quot;handle&quot; (display or process)
+what is sent to them.
+<p>Now let's look at the action queues: here, the active part, the producer, is the
+Parser and Filter Engine. The passive part is the Action Processor. The later does any
+processing that is necessary to call the output plugin, in particular it processes the template
+to create the plugin calling parameters (either a string or vector of arguments). From the
+action queue's point of view, Action Processor and Output form a single entity. Again, the
+TV set analogy holds. The Output <b>does not</b> actively ask the queue for data, but
+rather passively waits until the queue itself pushes some data to it.
+
+<p>Armed with this knowledge, we can now look at the way action queue modes work. My analogy here
+is a junction, as shown below (note that the colors in the pictures below are <b>not</b> related to
+the colors in the pictures above!):
+<p align="center"><img src="direct_queue0.png">
+<p>This is a very simple real-life traffic case: one road joins another. We look at
+traffic on the straight road, here shown by blue and green arrows. Traffic in the
+opposing direction is shown in blue. Traffic flows without
+any delays as long as nobody takes turns. To be more precise, if the opposing traffic takes
+a (right) turn, traffic still continues to flow without delay. However, if a car in the red traffic
+flow intends to do a (left, then) turn, the situation changes:
+<p align="center"><img src="direct_queue1.png">
+<p>The turning car is represented by the green arrow. It cannot turn unless there is a gap
+in the &quot;blue traffic stream&quot;. And as this car blocks the roadway, the remaining
+traffic (now shown in red, which should indicate the block condition),
+must wait until the &quot;green&quot; car has made its turn. So
+a queue will build up on that lane, waiting for the turn to be completed.
+Note that in the examples below I do not care that much about the properties of the
+opposing traffic. That is, because its structure is not really important for what I intend to
+show. Think about the blue arrow as being a traffic stream that most of the time blocks
+left-turners, but from time to time has a gap that is sufficiently large for a left-turn
+to complete.
+<p>Our road network designers know that this may be unfortunate, and for more important roads
+and junctions, they came up with the concept of turning lanes:
+<p align="center"><img src="direct_queue2.png">
+<p>Now, the car taking the turn can wait in a special area, the turning lane. As such,
+the &quot;straight&quot; traffic is no longer blocked and can flow in parallel to the
+turning lane (indicated by a now-green-again arrow).
+
+<p>However, the turning lane offers only finite space. So if too many cars intend to
+take a left turn, and there is no gap in the &quot;blue&quot; traffic, we end up with
+this well-known situation:
+<p align="center"><img src="direct_queue3.png">
+<p>The turning lane is now filled up, resulting in a tailback of cars intending to
+left turn on the main driving lane. The end result is that &quot;straight&quot; traffic
+is again being blocked, just as in our initial problem case without the turning lane.
+In essence, the turning lane has provided some relief, but only for a limited amount of
+cars. Street system designers now try to weight cost vs. benefit and create (costly)
+turning lanes that are sufficiently large to prevent traffic jams in most, but not all
+cases.
+<p><b>Now let's dig a bit into the mathematical properties of turning lanes.</b> We assume that
+cars all have the same length. So, units of cars, the length is alsways one (which is nice,
+as we don't need to care about that factor any longer ;)). A turning lane has finite capacity of
+<i>n</i> cars. As long as the number of cars wanting to take a turn is less than or eqal
+to <i>n</i>, &quot;straigth traffic&quot; is not blocked (or the other way round, traffic
+is blocked if at least <i>n + 1</i> cars want to take a turn!). We can now find an optimal
+value for <i>n</i>: it is a function of the probability that a car wants to turn
+and the cost of the turning lane
+(as well as the probability there is a gap in the &quot;blue&quot; traffic, but we ignore this
+in our simple sample).
+If we start from some finite upper bound of <i>n</i>, we can decrease
+<i>n</i> to a point where it reaches zero. But let's first look at <i>n = 1</i>, in which case exactly
+one car can wait on the turning lane. More than one car, and the rest of the traffic is blocked.
+Our everyday logic indicates that this is actually the lowest boundary for <i>n</i>.
+<p>In an abstract view, however, <i>n</i> can be zero and that works nicely. There still can be
+<i>n</i> cars at any given time on the turning lane, it just happens that this means there can
+be no car at all on it. And, as usual, if we have at least <i>n + 1</i> cars wanting to turn,
+the main traffic flow is blocked. True, but <i>n + 1 = 0 + 1 = 1</i> so as soon as there is any
+car wanting to take a turn, the main traffic flow is blocked (remember, in all cases, I assume
+no sufficiently large gaps in the opposing traffic).
+<p>This is the situation our everyday perception calls &quot;road without turning lane&quot;.
+In my math model, it is a &quot;road with turning lane of size 0&quot;. The subtle difference
+is important: my math model guarantees that, in an abstract sense, there always is a turning
+lane, it may just be too short. But it exists, even though we don't see it. And now I can
+claim that even in my small home village, all roads have turning lanes, which is rather
+impressive, isn't it? ;)
+<p><b>And now we finally have arrived at rsyslog's queues!</b> Rsyslog action queues exists for
+all actions just like all roads in my village have turning lanes! And as in this real-life sample,
+it may be hard to see the action queues for that reason. In rsyslog, the &quot;direct&quot; queue
+mode is the equivalent to the 0-sized turning lane. And actions queues are the equivalent to turning
+lanes in general, with our real-life <i>n</i> being the maximum queue size.
+The main traffic line (which sometimes is blocked) is the equivalent to the
+main message queue. And the periods without gaps in the opposing traffic are equivalent to
+execution time of an action. In a rough sketch, the rsyslog main and action queues look like in the
+following picture.
+<p align="center"><img src="direct_queue_rsyslog.png">
+<p>We need to read this picture from right to left (otherwise I would need to redo all
+the graphics ;)). In action 3, you see a 0-sized turning lane, aka an action queue in &quot;direct&quot;
+mode. All other queues are run in non-direct modes, but with different sizes greater than 0.
+<p>Let us first use our car analogy:
+Assume we are in a car on the main lane that wants to take turn into the &quot;action 4&quot;
+road. We pass action 1, where a number of cars wait in the turning lane and we pass
+action 2, which has a slightly smaller, but still not filled up turning lane. So we pass that
+without delay, too. Then we come to &quot;action 3&quot;, which has no turning lane. Unfortunately,
+the car in front of us wants to turn left into that road, so it blocks the main lane. So, this time
+we need to wait. An observer standing on the sidewalk may see that while we need to wait, there are
+still some cars in the &quot;action 4&quot; turning lane. As such, even though no new cars can
+arrive on the main lane, cars still turn into the &quot;action 4&quot; lane. In other words,
+an observer standing in &quot;action 4&quot; road is unable to see that traffic on the main lane
+is blocked.
+<p>Now on to rsyslog: Other than in the real-world traffic example, messages in rsyslog
+can - at more or less the
+same time - &quot;take turns&quot; into several roads at once. This is done by duplicating the message
+if the road has a non-zero-sized &quot;turning lane&quot; - or in rsyslog terms a queue that is
+running in any non-direct mode. If so, a deep copy of the message object is made, that placed into
+the action queue and then the initial message proceeds on the &quot;main lane&quot;. The action
+queue then pushes the duplicates through action processing. This is also the reason why a
+discard action inside a non-direct queue does not seem to have an effect. Actually, it discards the
+copy that was just created, but the original message object continues to flow.
+<p>
+In action 1, we have some entries in the action queue, as we have in action 2 (where the queue is
+slightly shorter). As we have seen, new messages pass action one and two almost instantaneously.
+However, when a messages reaches action 3, its flow is blocked. Now, message processing must wait
+for the action to complete. Processing flow in a direct mode queue is something like a U-turn:
+
+<p align="center"><img src="direct_queue_directq.png" alt="message processing in an rsyslog action queue in direct mode">
+<p>The message starts to execute the action and once this is done, processing flow continues.
+In a real-life analogy, this may be the route of a delivery man who needs to drop a parcel
+in a side street before he continues driving on the main route. As a side-note, think of what happens
+with the rest of the delivery route, at least for today, if the delivery truck has a serious accident
+in the side street. The rest of the parcels won't be delivered today, will they? This is exactly how the
+discard action works. It drops the message object inside the action and thus the message will no
+longer be available for further delivery - but as I said, only if the discard is done in a
+direct mode queue (I am stressing this example because it often causes a lot of confusion).
+<p>Back to the overall scenario. We have seen that messages need to wait for action 3 to
+complete. Does this necessarily mean that at the same time no messages can be processed
+in action 4? Well, it depends. As in the real-life scenario, action 4 will continue to
+receive traffic as long as its action queue (&quot;turn lane&quot;) is not drained. In
+our drawing, it is not. So action 4 will be executed while messages still wait for action 3
+to be completed.
+<p>Now look at the overall picture from a slightly different angle:
+<p align="center"><img src="direct_queue_rsyslog2.png" alt="message processing in an rsyslog action queue in direct mode">
+<p>The number of all connected green and red arrows is four - one each for action 1, 2 and 3
+(this one is dotted as action 4 was a special case) and one for the &quot;main lane&quot; as
+well as action 3 (this one contains the sole red arrow). <b>This number is the lower bound for
+the number of threads in rsyslog's output system (&quot;right-hand part&quot; of the main message
+queue)!</b> Each of the connected arrows is a continuous thread and each &quot;turn lane&quot; is
+a place where processing is forked onto a new thread. Also, note that in action 3 the processing
+is carried out on the main thread, but not in the non-direct queue modes.
+<p>I have said this is &quot;the lower bound for the number of threads...&quot;. This is with
+good reason: the main queue may have more than one worker thread (individual action queues
+currently do not support this, but could do in the future - there are good reasons for that, too
+but exploring why would finally take us away from what we intend to see). Note that you
+configure an upper bound for the number of main message queue worker threads. The actual number
+varies depending on a lot of operational variables, most importantly the number of messages
+inside the queue. The number <i>t_m</i> of actually running threads is within the integer-interval
+[0,confLimit] (with confLimit being the operator configured limit, which defaults to 5).
+Output plugins may have more than one thread created by themselves. It is quite unusual for an
+output plugin to create such threads and so I assume we do not have any of these.
+Then, the overall number of threads in rsyslog's filtering and output system is
+<i>t_total = t_m + number of actions in non-direct modes</i>. Add the number of
+inputs configured to that and you have the total number of threads running in rsyslog at
+a given time (assuming again that inputs utilize only one thread per plugin, a not-so-safe
+assumption).
+<p>A quick side-note: I gave the lower bound for <i>t_m</i> as zero, which is somewhat in contrast
+to what I wrote at the begin of the last paragraph. Zero is actually correct, because rsyslog
+stops all worker threads when there is no work to do. This is also true for the action queues.
+So the ultimate lower bound for a rsyslog output system without any work to carry out actually is zero.
+But this bound will never be reached when there is continuous flow of activity. And, if you are
+curios: if the number of workers is zero, the worker wakeup process is actually handled within the
+threading context of the &quot;left-hand-side&quot; (or producer) of the queue. After being
+started, the worker begins to play the active queue component again. All of this, of course,
+can be overridden with configuration directives.
+<p>When looking at the threading model, one can simply add n lanes to the main lane but otherwise
+retain the traffic analogy. This is a very good description of the actual process (think what this
+means to the &quot;turning lanes&quot;; hint: there still is only one per action!).
+<p><b>Let's try to do a warp-up:</b> I have hopefully been able to show that in rsyslog, an action
+queue &quot;sits in front of&quot; each output plugin. Messages are received and flow, from input
+to output, over various stages and two level of queues to the outputs. Actions queues are always
+present, but may not easily be visible when in direct mode (where no actual queuing takes place).
+The "road junction with turning lane" analogy well describes the way - and intent - of the various
+queue levels in rsyslog.
+<p>On the output side, the queue is the active component, <b>not</b> the consumer. As such, the consumer
+cannot ask the queue for anything (like n number of messages) but rather is activated by the queue
+itself. As such, a queue somewhat resembles a &quot;living thing&quot; whereas the outputs are
+just tools that this &quot;living thing&quot; uses.
+<p><b>Note that I left out a couple of subtleties</b>, especially when it comes
+to error handling and terminating
+a queue (you hopefully have now at least a rough idea why I say &quot;terminating <b>a queue</b>&quot;
+and not &quot;terminating an action&quot; - <i>who is the &quot;living thing&quot;?</i>). An action returns
+a status to the queue, but it is the queue that ultimately decides which messages can finally be
+considered processed and which not. Please note that the queue may even cancel an output right in
+the middle of its action. This happens, if configured, if an output needs more than a configured
+maximum processing time and is a guard condition to prevent slow outputs from deferring a rsyslog
+restart for too long. Especially in this case re-queuing and cleanup is not trivial. Also, note that
+I did not discuss disk-assisted queue modes. The basic rules apply, but there are some additional
+constraints, especially in regard to the threading model. Transitioning between actual
+disk-assisted mode and pure-in-memory-mode (which is done automatically when needed) is also far from
+trivial and a real joy for an implementer to work on ;).
+<p>If you have not done so before, it may be worth reading the
+<a href="queues.html">rsyslog queue user's guide,</a> which most importantly lists all
+the knobs you can turn to tweak queue operation.
+<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; 2009 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/rsconf1_actionexeconlywhenpreviousissuspended.html b/doc/rsconf1_actionexeconlywhenpreviousissuspended.html
index d5cf8b14..1626b4ca 100644
--- a/doc/rsconf1_actionexeconlywhenpreviousissuspended.html
+++ b/doc/rsconf1_actionexeconlywhenpreviousissuspended.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$ActionExecOnlyWhenPreviousIsSuspended</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> off</p>
diff --git a/doc/rsconf1_actionresumeinterval.html b/doc/rsconf1_actionresumeinterval.html
index a854a212..c0365470 100644
--- a/doc/rsconf1_actionresumeinterval.html
+++ b/doc/rsconf1_actionresumeinterval.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$ActionResumeInterval</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> 30</p>
@@ -27,4 +29,4 @@ Copyright &copy; 2007 by <a href="http://www.gerhards.net/rainer">Rainer Gerhard
<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
version 2 or higher.</font></p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/doc/rsconf1_allowedsender.html b/doc/rsconf1_allowedsender.html
index 4a980b89..ac39e268 100644
--- a/doc/rsconf1_allowedsender.html
+++ b/doc/rsconf1_allowedsender.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$AllowedSender</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> all allowed</p>
diff --git a/doc/rsconf1_controlcharacterescapeprefix.html b/doc/rsconf1_controlcharacterescapeprefix.html
index 6dab1e2e..45cd9230 100644
--- a/doc/rsconf1_controlcharacterescapeprefix.html
+++ b/doc/rsconf1_controlcharacterescapeprefix.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$ControlCharacterEscapePrefix</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> \</p>
diff --git a/doc/rsconf1_debugprintcfsyslinehandlerlist.html b/doc/rsconf1_debugprintcfsyslinehandlerlist.html
index 1aad7552..e158de43 100644
--- a/doc/rsconf1_debugprintcfsyslinehandlerlist.html
+++ b/doc/rsconf1_debugprintcfsyslinehandlerlist.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$DebugPrintCFSyslineHandlerList</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> on</p>
@@ -19,4 +21,4 @@ Copyright &copy; 2007 by <a href="http://www.gerhards.net/rainer">Rainer Gerhard
<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
version 2 or higher.</font></p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/doc/rsconf1_debugprintmodulelist.html b/doc/rsconf1_debugprintmodulelist.html
index 4d8e9bff..f25663fb 100644
--- a/doc/rsconf1_debugprintmodulelist.html
+++ b/doc/rsconf1_debugprintmodulelist.html
@@ -3,6 +3,7 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
<h2>$DebugPrintModuleList</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> on</p>
@@ -19,4 +20,4 @@ Copyright &copy; 2007 by <a href="http://www.gerhards.net/rainer">Rainer Gerhard
<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
version 2 or higher.</font></p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/doc/rsconf1_debugprinttemplatelist.html b/doc/rsconf1_debugprinttemplatelist.html
index 243530e1..b5f1f28f 100644
--- a/doc/rsconf1_debugprinttemplatelist.html
+++ b/doc/rsconf1_debugprinttemplatelist.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$DebugPrintTemplateList</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> on</p>
@@ -19,4 +21,4 @@ Copyright &copy; 2007 by <a href="http://www.gerhards.net/rainer">Rainer Gerhard
<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
version 2 or higher.</font></p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/doc/rsconf1_dircreatemode.html b/doc/rsconf1_dircreatemode.html
index 057f53e5..b22b6c59 100644
--- a/doc/rsconf1_dircreatemode.html
+++ b/doc/rsconf1_dircreatemode.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$DirCreateMode</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> 0700</p>
diff --git a/doc/rsconf1_dirgroup.html b/doc/rsconf1_dirgroup.html
index 4575a7e8..4bc8692f 100644
--- a/doc/rsconf1_dirgroup.html
+++ b/doc/rsconf1_dirgroup.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$DirGroup</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> </p>
diff --git a/doc/rsconf1_dirowner.html b/doc/rsconf1_dirowner.html
index 34291856..f779c008 100644
--- a/doc/rsconf1_dirowner.html
+++ b/doc/rsconf1_dirowner.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$DirOwner</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> </p>
diff --git a/doc/rsconf1_dropmsgswithmaliciousdnsptrrecords.html b/doc/rsconf1_dropmsgswithmaliciousdnsptrrecords.html
index e0a53ae6..95027a70 100644
--- a/doc/rsconf1_dropmsgswithmaliciousdnsptrrecords.html
+++ b/doc/rsconf1_dropmsgswithmaliciousdnsptrrecords.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$DropMsgsWithMaliciousDnsPTRRecords</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> off</p>
@@ -19,4 +21,4 @@ Copyright &copy; 2007 by <a href="http://www.gerhards.net/rainer">Rainer Gerhard
<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
version 2 or higher.</font></p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/doc/rsconf1_droptrailinglfonreception.html b/doc/rsconf1_droptrailinglfonreception.html
index 1e3aa8af..fb59b871 100644
--- a/doc/rsconf1_droptrailinglfonreception.html
+++ b/doc/rsconf1_droptrailinglfonreception.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$DropTrailingLFOnReception</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> on</p>
diff --git a/doc/rsconf1_dynafilecachesize.html b/doc/rsconf1_dynafilecachesize.html
index 3813f981..cacbf6e5 100644
--- a/doc/rsconf1_dynafilecachesize.html
+++ b/doc/rsconf1_dynafilecachesize.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$DynaFileCacheSize</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> 10</p>
@@ -20,4 +22,4 @@ Copyright &copy; 2007 by <a href="http://www.gerhards.net/rainer">Rainer Gerhard
<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
version 2 or higher.</font></p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/doc/rsconf1_escapecontrolcharactersonreceive.html b/doc/rsconf1_escapecontrolcharactersonreceive.html
index 26917736..178f9a6f 100644
--- a/doc/rsconf1_escapecontrolcharactersonreceive.html
+++ b/doc/rsconf1_escapecontrolcharactersonreceive.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$EscapeControlCharactersOnReceive</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> on</p>
diff --git a/doc/rsconf1_failonchownfailure.html b/doc/rsconf1_failonchownfailure.html
index 0e646e36..d8bbab82 100644
--- a/doc/rsconf1_failonchownfailure.html
+++ b/doc/rsconf1_failonchownfailure.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$FailOnChownFailure</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> on</p>
@@ -19,4 +21,4 @@ Copyright &copy; 2007 by <a href="http://www.gerhards.net/rainer">Rainer Gerhard
<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
version 2 or higher.</font></p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/doc/rsconf1_filecreatemode.html b/doc/rsconf1_filecreatemode.html
index c8440864..10b0317b 100644
--- a/doc/rsconf1_filecreatemode.html
+++ b/doc/rsconf1_filecreatemode.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$FileCreateMode</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> 0644</p>
diff --git a/doc/rsconf1_filegroup.html b/doc/rsconf1_filegroup.html
index 92e4813b..935f074a 100644
--- a/doc/rsconf1_filegroup.html
+++ b/doc/rsconf1_filegroup.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$FileGroup</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> </p>
diff --git a/doc/rsconf1_fileowner.html b/doc/rsconf1_fileowner.html
index f8d5ebf3..62125c8d 100644
--- a/doc/rsconf1_fileowner.html
+++ b/doc/rsconf1_fileowner.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$FileOwner</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> </p>
diff --git a/doc/rsconf1_generateconfiggraph.html b/doc/rsconf1_generateconfiggraph.html
new file mode 100644
index 00000000..0b18463a
--- /dev/null
+++ b/doc/rsconf1_generateconfiggraph.html
@@ -0,0 +1,121 @@
+<html>
+<head>
+<title>rsyslog.conf file</title>
+</head>
+<body>
+<a href="rsyslog_conf_global.html">back</a>
+
+<h2>$GenerateConfigGraph</h2>
+<p><b>Type:</b> global configuration directive</p>
+<p><b>Default:</b> </p>
+<p><b>Available Since:</b> 4.3.1</p>
+<p><b>Description:</b></p>
+<p>This directive permits to create (hopefully) good-looking visualizations of rsyslogd's
+configuration. It does not affect rsyslog operation. If the directive is specified multiple
+times, all but the last are ignored. If it is specified, a graph is created. This happens
+both during a regular startup as well a config check run. It is recommended to include
+this directive only for documentation purposes and remove it from a production
+configuraton.
+<p>The graph is not drawn by rsyslog itself. Instead, it uses the great open source tool
+<a href="http://www.graphviz.org">Graphviz</a> to do the actual drawing. This has at least
+two advantages:
+<ul>
+<li>the graph drawing support code in rsyslog is extremly slim and without overhead
+<li>the user may change or further annotate the generated file, thus potentially
+improving his documentation
+</ul>
+The drawback, of course, is that you need to run Graphviz once you have generated
+the control file with rsyslog. Fortunately, the process to do so is rather easy:
+<ol>
+<li>add &quot;$GenerateConfigGraph /path/to/file.dot&quot; to rsyslog.conf (from now on, I
+will call the file just file.dot). Optionally, add &quot;$ActionName&quot; statement
+<b>in front of</b> those actions that you like to use friendly names with. If you do
+this, keep the names short.
+<li>run rsyslog at least once (either in regular or configuration check mode)
+<li>remember to remove the $GenerateConfigGraph directive when you no longer need it (or
+comment it out)
+<li>change your working directory to where you place the dot file
+<li>if you would like to edit the rsyslog-generated file, now is the time to do so
+<li>do &quot;dot -Tpng file.dot &gt; file.png&quot;
+<li>remember that you can use &quot;convert -resize 50% file.png resized.png&quot; if
+dot's output is too large (likely) or too small. Resizing can be especially useful if
+you intend to get a rough overview over your configuration.
+</ol>
+After completing these steps, you should have a nice graph of your configuration. Details
+are missing, but that is exactly the point. At the start of the graph is always (at least
+in this version, could be improved) a node called &quot;inputs&quot; in a tripple hexagon
+shape. This represents all inputs active in the system (assuming you have defined some,
+what the current version does not check). Next comes the main queue. It is given in a
+hexagon shape. That shape indicates that a queue is peresent and used to de-couple
+the inbound from the outbound part of the graph. In technical terms, here is a
+threading boundary. Action with &quot;real&quot; queues (other than in direct mode)
+also utilize this shape. For actions, notice that a &quot;hexagon action&quot; creates
+a deep copy of the message. As such, a &quot;discard hexagon action&quot; actually does
+nothing, because it duplicates the message and then discards <b>the duplicate</b>.
+At the end of the diagram, you always see a &quot;discard&quot; action. This indicates
+that rsyslog discards messages which have been run through all available rules.
+<p>Edges are labeled with information about when they are taken. For filters, the type of
+filter, but not any specifics, are given. It is also indicated if no filter is
+applied in the configuration file (by using a &quot;*.*&quot; selector). Edges without
+labels are unconditionally taken. The actions themselfs are labeled with the name of
+the output module that handles them. If provided, the name given via
+&quot;ActionName&quot; is used instead. No further details are provided.
+<p>If there is anything in red, this should draw your attention. In this case, rsyslogd
+has detected something that does not look quite right. A typical example is a discard
+action which is followed by some other actions in an action unit. Even though something
+may be red, it can be valid - rsyslogd's graph generator does not yet check each and
+every speciality, so the configuration may just cover a very uncommon case.
+<p>Now let's look at some examples. The graph below was generated on a fairly standard
+Fedora rsyslog.conf file. It had only the usually commented-out last forwarding action
+activated:
+<p align="center">
+<img src="rsyslog_confgraph_std.png" alt="rsyslog configuration graph for a default fedora rsyslog.conf">
+<p>This is the typical structure for a simple rsyslog configuration. There are a couple of
+actions, each guarded by a filter. Messages run from top to bottom and control branches
+whenever a filter evaluates to true. As there is no discard action, all messages will
+run through all filters and discarded in the system default discard action right after
+all configured actions.
+</p>
+<p>A more complex example can be seen in the next graph. This is a configuration I
+created for testing the graph-creation features, so it contains a little bit of
+everything. However, real-world configurations can look quite complex, too (and I
+wouldn't say this one is very complex):
+<p align="center">
+<img src="rsyslog_confgraph_complex.png">
+</p>
+<p>Here, we have a user-defined discard action. You can immediately see this because
+processing branches after the first &quot;builtin-file&quot; action. Those messages
+where the filter evaluates to true for will never run through the left-hand action
+branch. However, there is also a configuration error present: there are two more
+actions (now shown red) after the discard action. As the message is discarded, these will
+never be executed. Note that the discard branch contains no further filters. This is
+because these actions are all part of the same action unit, which is guarded only by
+an entry filter. The same is present a bit further down at the node labeled
+&quot;write_system_log_2&quot;. This note has one more special feature, that is label
+was set via &quot;ActionName&quot;, thus is does not have standard form (the same
+happened to the node named &quot;Forward&quot; right at the top of the diagram.
+Inside this diagram, the &quot;Forward&quot; node is executed asynchonously on its own
+queue. All others are executed synchronously.
+<p>Configuration graphs are useful for documenting a setup, but are also a great
+<a href="troubleshoot.html">troubleshooting</a> resource. It is important to
+remember that <b>these graphs are generated
+from rsyslogd's in-memory action processing structures</b>. You can not get closer
+to understanding on how rsyslog interpreted its configuration files.
+So if the graph does not look
+what you intended to do, there is probably something worng in rsyslog.conf.
+<p>If something is not working as expected, but you do not spot the error immediately,
+I recommend to generate a graph and zoom it so that you see all of it in one great picture.
+You may not be able to read anything, but the structure should look good to you and
+so you can zoom into those areas that draw your attention.
+<p><b>Sample:</b></p>
+<p><code><b>$DirOwner /path/to/graphfile-file.dot</b></code></p>
+
+<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; 2009 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 2 or higher.</font></p>
+</body>
+</html>
diff --git a/doc/rsconf1_gssforwardservicename.html b/doc/rsconf1_gssforwardservicename.html
index 9d39dc2a..45d9ba98 100644
--- a/doc/rsconf1_gssforwardservicename.html
+++ b/doc/rsconf1_gssforwardservicename.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$GssForwardServiceName</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> host</p>
diff --git a/doc/rsconf1_gsslistenservicename.html b/doc/rsconf1_gsslistenservicename.html
index cd03dc58..5fdf3edc 100644
--- a/doc/rsconf1_gsslistenservicename.html
+++ b/doc/rsconf1_gsslistenservicename.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$GssListenServiceName</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> host</p>
diff --git a/doc/rsconf1_gssmode.html b/doc/rsconf1_gssmode.html
index 71c50696..2b1d5656 100644
--- a/doc/rsconf1_gssmode.html
+++ b/doc/rsconf1_gssmode.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$GssMode</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> encryption</p>
diff --git a/doc/rsconf1_includeconfig.html b/doc/rsconf1_includeconfig.html
index 24462f77..132cee6f 100644
--- a/doc/rsconf1_includeconfig.html
+++ b/doc/rsconf1_includeconfig.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$IncludeConfig</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> </p>
@@ -43,4 +45,4 @@ Copyright &copy; 2007 by <a href="http://www.gerhards.net/rainer">Rainer Gerhard
<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
version 2 or higher.</font></p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/doc/rsconf1_mainmsgqueuesize.html b/doc/rsconf1_mainmsgqueuesize.html
index acf88e94..ffed1c09 100644
--- a/doc/rsconf1_mainmsgqueuesize.html
+++ b/doc/rsconf1_mainmsgqueuesize.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$MainMsgQueueSize</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> 10000</p>
diff --git a/doc/rsconf1_markmessageperiod.html b/doc/rsconf1_markmessageperiod.html
index 9b6590cd..a6486ba1 100644
--- a/doc/rsconf1_markmessageperiod.html
+++ b/doc/rsconf1_markmessageperiod.html
@@ -3,9 +3,11 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$MarkMessagePeriod</h2>
<p><b>Type:</b> specific to immark input module</p>
-<p><b>Default:</b> 1800 (20 minutes)</p>
+<p><b>Default:</b> 1200 (20 minutes)</p>
<p><b>Description:</b></p>
<p>This specifies when mark messages are to be written to output modules. The
time specified is in seconds. Specifying 0 is possible and disables mark
@@ -27,4 +29,4 @@ Copyright &copy; 2007 by <a href="http://www.gerhards.net/rainer">Rainer Gerhard
<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
version 2 or higher.</font></p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/doc/rsconf1_maxopenfiles.html b/doc/rsconf1_maxopenfiles.html
new file mode 100644
index 00000000..b6c9cc0e
--- /dev/null
+++ b/doc/rsconf1_maxopenfiles.html
@@ -0,0 +1,35 @@
+<html>
+<head>
+<title>$MaxOpenFiles - rsyslog.conf file</title>
+</head>
+<body>
+<a href="rsyslog_conf_global.html">[rsyslog configuration directive overview]</a>
+
+<h2>$MaxOpenFiles</h2>
+<p><b>Available Since:</b> 4.3.0</p>
+<p><b>Type:</b> global configuration directive</p>
+<p><b>Default:</b> <i>operating system default</i></p>
+<p><b>Description:</b></p>
+<p>Set the maximum number of files that the rsyslog process can have open at any given
+time. Note that this includes open tcp sockets, so this setting is the upper limit for
+the number of open TCP connections as well. If you expect a large nubmer of concurrent
+connections, it is suggested that the number is set to the max number connected plus 1000.
+Please note that each dynafile also requires up to 100 open file handles.
+<p>The setting is similar to running "ulimit -n number-of-files".
+<p>Please note that depending on permissions and operating system configuration, the
+setrlimit() request issued by rsyslog may fail, in which case the previous limit is kept
+in effect. Rsyslog will emit a warning message in this case.
+<p><b>Sample:</b></p>
+<p><code><b>$MaxOpenFiles 2000</b></code></p>
+<p><b>Bugs:</b></p>
+<p>For some reason, this settings seems not to work on all platforms. If you experience
+problems, please let us know so that we can (hopefully) narrow down the issue.
+<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; 2009 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/rsconf1_moddir.html b/doc/rsconf1_moddir.html
index ced07dc9..889de05d 100644
--- a/doc/rsconf1_moddir.html
+++ b/doc/rsconf1_moddir.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$ModDir</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> system default for user libraries, e.g.
@@ -24,4 +26,4 @@ Copyright &copy; 2007 by <a href="http://www.gerhards.net/rainer">Rainer Gerhard
<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
version 2 or higher.</font></p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/doc/rsconf1_modload.html b/doc/rsconf1_modload.html
index a2b8087a..ce457ea5 100644
--- a/doc/rsconf1_modload.html
+++ b/doc/rsconf1_modload.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$ModLoad</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> </p>
diff --git a/doc/rsconf1_repeatedmsgreduction.html b/doc/rsconf1_repeatedmsgreduction.html
index 20e56f89..248e8343 100644
--- a/doc/rsconf1_repeatedmsgreduction.html
+++ b/doc/rsconf1_repeatedmsgreduction.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$RepeatedMsgReduction</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> depending on -e</p>
@@ -20,4 +22,4 @@ Copyright &copy; 2007 by <a href="http://www.gerhards.net/rainer">Rainer Gerhard
<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
version 2 or higher.</font></p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/doc/rsconf1_resetconfigvariables.html b/doc/rsconf1_resetconfigvariables.html
index 9794d158..46cf0bdf 100644
--- a/doc/rsconf1_resetconfigvariables.html
+++ b/doc/rsconf1_resetconfigvariables.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$ResetConfigVariables</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> </p>
@@ -19,4 +21,4 @@ Copyright &copy; 2007 by <a href="http://www.gerhards.net/rainer">Rainer Gerhard
<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
version 2 or higher.</font></p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/doc/rsconf1_umask.html b/doc/rsconf1_umask.html
index ee47dbad..8e41e672 100644
--- a/doc/rsconf1_umask.html
+++ b/doc/rsconf1_umask.html
@@ -3,6 +3,8 @@
<title>rsyslog.conf file</title>
</head>
<body>
+<a href="rsyslog_conf_global.html">back</a>
+
<h2>$UMASK</h2>
<p><b>Type:</b> global configuration directive</p>
<p><b>Default:</b> </p>
@@ -21,4 +23,4 @@ Copyright &copy; 2007 by <a href="http://www.gerhards.net/rainer">Rainer Gerhard
<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
version 2 or higher.</font></p>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/doc/rscript_abnf.html b/doc/rscript_abnf.html
index 278fb59c..d60edb5c 100644
--- a/doc/rscript_abnf.html
+++ b/doc/rscript_abnf.html
@@ -30,6 +30,8 @@ table... values('&amp;$facility&amp;','&amp;$severity&amp;...?]<br>&nbsp; endact
table... values('&amp;$facility&amp;','&amp;$severity&amp;...?]<br>&nbsp;&nbsp;)<br><br>... or ...</p><p>define action writeMySQL(<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
table... values('&amp;$facility&amp;','&amp;$severity&amp;...?]<br>&nbsp; &nbsp;)</p><p>if $message contains "error" then action writeMySQL(action.dbname='differentDB')</p><p></p><p>[<a href="rsyslog_conf.html">rsyslog.conf overview</a>]
+<h2>Implementation</h2>
+RainerScript will be implemented via a hand-crafted LL(1) parser. I was tempted to use yacc, but it turned out the resulting code was not thread-safe and as such did not fit within the context of rsyslog. Also, limited error handling is not a real problem for us: if there is a problem in parsing the configuration file, we stop processing. Guessing what was meant and trying to recover would IMHO not be good choices for something like a syslogd.
[<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>
diff --git a/doc/rsyslog-vers.dot b/doc/rsyslog-vers.dot
new file mode 100644
index 00000000..a5563f94
--- /dev/null
+++ b/doc/rsyslog-vers.dot
@@ -0,0 +1,82 @@
+// This file is part of rsyslog.
+//
+// rsyslog "family tree" compressed version
+//
+// see http://www.graphviz.org for how to obtain the graphviz processor
+// which is used to build the actual graph.
+//
+// generate the graph with
+// $ dot rsyslog-vers.dot -Tpng >rsyslog-vers.png
+
+digraph G {
+ label="\n\nrsyslog \"family tree\"\nhttp://www.rsyslog.com";
+ fontsize=20;
+
+ v1stable [label="v1-stable", shape=box, style=dotted];
+ v2stable [label="v2-stable", shape=box, style=filled];
+ v3stable [label="v3-stable", shape=box, style=filled];
+ beta [label="beta", shape=box, style=filled];
+ devel [label="devel", shape=box, style=filled];
+ "1.0.5" [style=dotted];
+ perf [style=dashed];
+ "imudp-select" [style=dashed label="imudp-\nselect"];
+ msgnolock [style=dashed];
+ "file-errHdlr" [style=dashed];
+ solaris [style=dashed];
+ tests [style=dashed];
+ "0.x.y" [group=master];
+ "1.10.0" [group=master];
+ "1.21.2" [group=master];
+ "3.10.0" [group=master];
+ "3.15.1" [group=master];
+ "3.17.0" [group=master];
+ "3.19.x" [group=master];
+ "3.21.x" [group=master];
+ "4.1.0" [group=master];
+ "4.1.4" [group=master];
+ "4.1.5" [group=master];
+ "4.1.6" [group=master];
+
+ sysklogd -> "0.x.y" [color=red];
+ "0.x.y" -> "1.0.0";
+ "0.x.y" -> "1.10.0" [color=red];
+ "1.0.0" -> "1.0.5" [style=dashed];
+ "1.10.0" -> "1.21.2" [color=red style=dashed];
+ "1.21.2" -> "2.0.0" [color=blue];
+ "2.0.0" -> "2.0.5" [style=dashed, color=blue];
+ "1.21.2" -> "3.10.0" [color=red];
+ "3.10.0" -> "3.15.1" [color=red style=dashed];
+ "3.15.1" -> "tests";
+ "3.15.1" -> "3.17.0" [color=red style=dashed];
+ "3.15.1" -> "3.16.x";
+ "3.16.x" -> "3.18.x" [color=blue, style=dashed];
+ "3.17.0" -> "3.18.x";
+ "3.17.0" -> "3.19.x" [color=red, style=dashed];
+ "3.19.x" -> "3.20.x";
+ "3.19.x" -> "3.21.x" [color=red];
+ "3.18.x" -> debian_lenny;
+ "3.18.x" -> "3.20.x" [color=blue, style=dashed];
+ "3.21.x" -> "3.21.11";
+ "3.21.x" -> "4.1.0" [color=red];
+ "3.21.x" -> "perf";
+ "perf" -> "4.1.0";
+ "4.1.0" -> "4.1.4" [color=red, style=dashed];
+ "4.1.4" -> "file-errHdlr";
+ "4.1.4" -> "4.1.5" [color=red];
+ "4.1.5" -> "4.1.6" [color=red];
+ "3.21.x" -> msgnolock
+ "3.21.x" -> "imudp-select";
+ "4.1.5" -> solaris;
+ "file-errHdlr" -> "4.1.6";
+ "tests" -> "4.1.6";
+
+ "1.0.5" -> v1stable [dir=none, style=dotted];
+ "2.0.5" -> v2stable [dir=none, style=dotted];
+ "3.20.x" -> v3stable [dir=none, style=dotted];
+ "3.21.11" -> beta [dir=none, style=dotted];
+ "4.1.6" -> devel [dir=none, style=dotted];
+
+ {rank=same; "4.1.5" "solaris"}
+ {rank=same; "3.18.x" "debian_lenny"}
+ {rank=same; "1.0.5" "2.0.5" "3.20.x" "3.21.11" "4.1.6"}
+}
diff --git a/doc/rsyslog-vers.png b/doc/rsyslog-vers.png
new file mode 100644
index 00000000..e8ec8b81
--- /dev/null
+++ b/doc/rsyslog-vers.png
Binary files differ
diff --git a/doc/rsyslog_conf.html b/doc/rsyslog_conf.html
index ac695656..6990c6bd 100644
--- a/doc/rsyslog_conf.html
+++ b/doc/rsyslog_conf.html
@@ -20,262 +20,13 @@ 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>Modules</h2>
-<p>Rsyslog has a modular design. This enables functionality to be
-dynamically loaded from modules, which may also be written by any
-third party. Rsyslog itself offers all non-core functionality as
-modules. Consequently, there is a growing
-number of modules. Here is the entry point to their documentation and
-what they do (list is currently not complete)</p>
-<p>Please note that each module provides configuration
-directives, which are NOT necessarily being listed below. Also
-remember, that a modules configuration directive (and functionality) is
-only available if it has been loaded (using $ModLoad).</p>
-<p>It is relatively easy to write a rsyslog module. <b>If none of the provided
-modules solve your need, you may consider writing one or have one written
-for you by
-<a href="http://www.rsyslog.com/professional-services">Adiscon's professional services for rsyslog</a>
-</b>(this often is a very cost-effective and efficient way of getting what you need).
-
-<h3>Input Modules</h3>
-<p>Input modules are used to gather messages from various sources. They interface
-to message generators.
-<ul>
-<li><a href="imfile.html">imfile</a>
--&nbsp; input module for text files</li>
-<li><a href="imrelp.html">imrelp</a> - RELP
-input module</li>
-<li>imudp - udp syslog message input</li>
-<li><a href="imtcp.html">imtcp</a> - input
-plugin for plain tcp syslog</li>
-<li><a href="imgssapi.html">imgssapi</a> -
-input plugin for plain tcp and GSS-enabled syslog</li>
-<li>immark - support for mark messages</li>
-<li><a href="imklog.html">imklog</a> - kernel logging</li>
-<li><a href="imuxsock.html">imuxsock</a> -
-unix sockets, including the system log socket</li>
-<li><a href="im3195.html">im3195</a> -
-accepts syslog messages via RFC 3195</li>
-</ul>
-
-<h3>Output Modules</h3>
-<p>Output modules process messages. With them, message formats can be transformed
-and messages be transmitted to various different targets.
-<ul>
-<li><a href="omsnmp.html">omsnmp</a> - SNMP
-trap output module</li>
-<li><a href="omrelp.html">omrelp</a> - RELP
-output module</li>
-<li>omgssapi - output module for GSS-enabled syslog</li>
-<li><a href="ommysql.html">ommysql</a> - output module for MySQL</li>
-<li>ompgsql - output module for PostgreSQL</li>
-<li><a href="omlibdbi.html">omlibdbi</a> -
-generic database output module (Firebird/Interbase, MS SQL, Sybase,
-SQLLite, Ingres, Oracle, mSQL)</li>
-<li><a href="ommail.html">ommail</a> -
-permits rsyslog to alert folks by mail if something important happens</li>
-</ul>
-
+<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.<br>
-<h2>Global Directives</h2>
-<p>All global directives need to be specified on a line by their
-own and must start with a dollar-sign. Here is a list in alphabetical
-order. Follow links for a description.</p>
-<p>Not all directives have an in-depth description right now.
-Default values for them are in bold. A more in-depth description will
-appear as implementation progresses. Directives may change during that
-process, we will NOT try hard to maintain backwards compatibility
-(after all, v3 is still very early in development and quite
-unstable...). So you have been warned ;)</p>
-<p><b>Be sure to read information about <a href="queues.html">queues in rsyslog</a></b> -
-many parameter settings modify queue parameters. If in doubt, use the
-default, it is usually well-chosen and applicable in most cases.</p>
-<ul>
-<li><a href="rsconf1_actionexeconlywhenpreviousissuspended.html">$ActionExecOnlyWhenPreviousIsSuspended</a></li>
-<li>$ActionExecOnlyOnceEveryInterval &lt;seconds&gt; -
-execute action only if the last execute is at last
-&lt;seconds&gt; seconds in the past (more info in <a href="ommail.html">ommail</a>,
-but may be used with any action)</li>
-<li><i><b>$ActionExecOnlyEveryNthTime</b> &lt;number&gt;</i> - If configured, the next action will
-only be executed every n-th time. For example, if configured to 3, the first two messages
-that go into the action will be dropped, the 3rd will actually cause the action to execute,
-the 4th and 5th will be dropped, the 6th executed under the action, ... and so on. Note:
-this setting is automatically re-set when the actual action is defined.</li>
-<li><i><b>$ActionExecOnlyEveryNthTimeTimeout</b> &lt;number-of-seconds&gt;</i> - has a meaning only if
-$ActionExecOnlyEveryNthTime is also configured for the same action. If so, the timeout
-setting specifies after which period the counting of "previous actions" expires and
-a new action count is begun. Specify 0 (the default) to disable timeouts.
-<br>
-<i>Why is this option needed?</i> Consider this case: a message comes in at, eg., 10am. That's
-count 1. Then, nothing happens for the next 10 hours. At 8pm, the next
-one occurs. That's count 2. Another 5 hours later, the next message
-occurs, bringing the total count to 3. Thus, this message now triggers
-the rule.
-<br>
-The question is if this is desired behavior? Or should the rule only be
-triggered if the messages occur within an e.g. 20 minute window? If the
-later is the case, you need a
-<br>
-$ActionExecOnlyEveryNthTimeTimeout 1200
-<br>
-This directive will timeout previous messages seen if they are older
-than 20 minutes. In the example above, the count would now be always 1
-and consequently no rule would ever be triggered.
-
-<li>$ActionFileDefaultTemplate [templateName] - sets a new default template for file actions</li>
-<li>$ActionFileEnableSync [on/<span style="font-weight: bold;">off</span>] - enables file
-syncing capability of omfile</li>
-<li>$ActionForwardDefaultTemplate [templateName] - sets a new
-default template for UDP and plain TCP forwarding action</li>
-<li>$ActionGSSForwardDefaultTemplate [templateName] - sets a
-new default template for GSS-API forwarding action</li>
-<li>$ActionQueueCheckpointInterval &lt;number&gt;</li>
-<li>$ActionQueueDequeueSlowdown &lt;number&gt; [number
-is timeout in <i> micro</i>seconds (1000000us is 1sec!),
-default 0 (no delay). Simple rate-limiting!]</li>
-<li>$ActionQueueDiscardMark &lt;number&gt; [default
-9750]</li>
-<li>$ActionQueueDiscardSeverity &lt;number&gt;
-[*numerical* severity! default 4 (warning)]</li>
-<li>$ActionQueueFileName &lt;name&gt;</li>
-<li>$ActionQueueHighWaterMark &lt;number&gt; [default
-8000]</li>
-<li>$ActionQueueImmediateShutdown [on/<b>off</b>]</li>
-<li>$ActionQueueSize &lt;number&gt;</li>
-<li>$ActionQueueLowWaterMark &lt;number&gt; [default
-2000]</li>
-<li>$ActionQueueMaxFileSize &lt;size_nbr&gt;, default 1m</li>
-<li>$ActionQueueTimeoutActionCompletion &lt;number&gt;
-[number is timeout in ms (1000ms is 1sec!), default 1000, 0 means
-immediate!]</li>
-<li>$ActionQueueTimeoutEnqueue &lt;number&gt; [number
-is timeout in ms (1000ms is 1sec!), default 2000, 0 means indefinite]</li>
-<li>$ActionQueueTimeoutShutdown &lt;number&gt; [number
-is timeout in ms (1000ms is 1sec!), default 0 (indefinite)]</li>
-<li>$ActionQueueWorkerTimeoutThreadShutdown
-&lt;number&gt; [number is timeout in ms (1000ms is 1sec!),
-default 60000 (1 minute)]</li>
-<li>$ActionQueueType [FixedArray/LinkedList/<b>Direct</b>/Disk]</li>
-<li>$ActionQueueSaveOnShutdown&nbsp; [on/<b>off</b>]
-</li>
-<li>$ActionQueueWorkerThreads &lt;number&gt;, num worker threads, default 1, recommended 1</li>
-<li>$ActionQueueWorkerThreadMinumumMessages &lt;number&gt;, default 100</li>
-<li><a href="rsconf1_actionresumeinterval.html">$ActionResumeInterval</a></li>
-<li>$ActionResumeRetryCount &lt;number&gt; [default 0, -1 means eternal]</li>
-<li>$ActionSendResendLastMsgOnReconn &lt;[on/<b>off</b>]&gt; specifies if the last message is to be resend when a connecition broken and has been reconnedcted. May increase reliability, but comes at the risk of message duplication.
-<li>$ActionSendStreamDriver &lt;driver basename&gt; just like $DefaultNetstreamDriver, but for the specific action.
-</li><li>$ActionSendStreamDriverMode &lt;mode&gt;, default 0, mode to use with the stream driver
-(driver-specific)</li><li>$ActionSendStreamDriverAuthMode &lt;mode&gt;,&nbsp; authentication mode to use with the stream driver. Note that this directive requires TLS
-netstream drivers. For all others, it will be ignored.
-(driver-specific)</li><li>$ActionSendStreamDriverPermittedPeer &lt;ID&gt;,&nbsp; accepted fingerprint (SHA1) or name of remote peer. Note that this directive requires TLS
-netstream drivers. For all others, it will be ignored.
-(driver-specific) -<span style="font-weight: bold;"> directive may go away</span>!</li>
-<li><a href="rsconf1_allowedsender.html">$AllowedSender</a></li>
-<li><a href="rsconf1_controlcharacterescapeprefix.html">$ControlCharacterEscapePrefix</a></li>
-<li><a href="rsconf1_debugprintcfsyslinehandlerlist.html">$DebugPrintCFSyslineHandlerList</a></li>
-
-<li><a href="rsconf1_debugprintmodulelist.html">$DebugPrintModuleList</a></li>
-<li><a href="rsconf1_debugprinttemplatelist.html">$DebugPrintTemplateList</a></li>
-<li>$DefaultNetstreamDriver &lt;drivername&gt;, the default <a href="netstream.html">network stream driver</a> to use. Defaults to&nbsp;ptcp.$DefaultNetstreamDriverCAFile &lt;/path/to/cafile.pem&gt;</li>
-<li>$DefaultNetstreamDriverCertFile &lt;/path/to/certfile.pem&gt;</li>
-<li>$DefaultNetstreamDriverKeyFile &lt;/path/to/keyfile.pem&gt;</li>
-<li><a href="rsconf1_dircreatemode.html">$DirCreateMode</a></li>
-<li><a href="rsconf1_dirgroup.html">$DirGroup</a></li>
-<li><a href="rsconf1_dirowner.html">$DirOwner</a></li>
-<li><a href="rsconf1_dropmsgswithmaliciousdnsptrrecords.html">$DropMsgsWithMaliciousDnsPTRRecords</a></li>
-<li><a href="rsconf1_droptrailinglfonreception.html">$DropTrailingLFOnReception</a></li>
-<li><a href="rsconf1_dynafilecachesize.html">$DynaFileCacheSize</a></li>
-<li><a href="rsconf1_escapecontrolcharactersonreceive.html">$EscapeControlCharactersOnReceive</a></li>
-<li>$ErrorMessagesToStderr [<b>on</b>|off] - direct rsyslogd error message to stderr (in addition to other targets)</li>
-<li><a href="rsconf1_failonchownfailure.html">$FailOnChownFailure</a></li>
-<li><a href="rsconf1_filecreatemode.html">$FileCreateMode</a></li>
-<li><a href="rsconf1_filegroup.html">$FileGroup</a></li>
-<li><a href="rsconf1_fileowner.html">$FileOwner</a></li>
-<li><a href="rsconf1_gssforwardservicename.html">$GssForwardServiceName</a></li>
-<li><a href="rsconf1_gsslistenservicename.html">$GssListenServiceName</a></li>
-<li><a href="rsconf1_gssmode.html">$GssMode</a></li>
-<li><a href="rsconf1_includeconfig.html">$IncludeConfig</a></li><li>MainMsgQueueCheckpointInterval &lt;number&gt;</li>
-<li>$MainMsgQueueDequeueSlowdown &lt;number&gt; [number
-is timeout in <i> micro</i>seconds (1000000us is 1sec!),
-default 0 (no delay). Simple rate-limiting!]</li>
-<li>$MainMsgQueueDiscardMark &lt;number&gt; [default
-9750]</li>
-<li>$MainMsgQueueDiscardSeverity &lt;severity&gt;
-[either a textual or numerical severity! default 4 (warning)]</li>
-<li>$MainMsgQueueFileName &lt;name&gt;</li>
-<li>$MainMsgQueueHighWaterMark &lt;number&gt; [default
-8000]</li>
-<li>$MainMsgQueueImmediateShutdown [on/<b>off</b>]</li>
-<li><a href="rsconf1_mainmsgqueuesize.html">$MainMsgQueueSize</a></li>
-<li>$MainMsgQueueLowWaterMark &lt;number&gt; [default
-2000]</li>
-<li>$MainMsgQueueMaxFileSize &lt;size_nbr&gt;, default
-1m</li>
-<li>$MainMsgQueueTimeoutActionCompletion
-&lt;number&gt; [number is timeout in ms (1000ms is 1sec!),
-default
-1000, 0 means immediate!]</li>
-<li>$MainMsgQueueTimeoutEnqueue &lt;number&gt; [number
-is timeout in ms (1000ms is 1sec!), default 2000, 0 means indefinite]</li>
-<li>$MainMsgQueueTimeoutShutdown &lt;number&gt; [number
-is timeout in ms (1000ms is 1sec!), default 0 (indefinite)]</li>
-<li>$MainMsgQueueWorkerTimeoutThreadShutdown
-&lt;number&gt; [number is timeout in ms (1000ms is 1sec!),
-default 60000 (1 minute)]</li>
-<li>$MainMsgQueueType [<b>FixedArray</b>/LinkedList/Direct/Disk]</li>
-<li>$MainMsgQueueSaveOnShutdown&nbsp; [on/<b>off</b>]
-</li>
-<li>$MainMsgQueueWorkerThreads &lt;number&gt;, num
-worker threads, default 1, recommended 1</li>
-<li>$MainMsgQueueWorkerThreadMinumumMessages &lt;number&gt;, default 100</li>
-<li><a href="rsconf1_markmessageperiod.html">$MarkMessagePeriod</a> (immark)</li>
-<li><b><i>$MaxMessageSize</i></b> &lt;size_nbr&gt;, default 2k - allows to specify maximum supported message size
-(both for sending and receiving). The default
-should be sufficient for almost all cases. Do not set this below 1k, as it would cause
-interoperability problems with other syslog implementations.<br>
-Change the setting to e.g. 32768 if you would like to
-support large message sizes for IHE (32k is the current maximum
-needed for IHE). I was initially tempted to set the default to 32k,
-but there is a some memory footprint with the current
-implementation in rsyslog.
-<br>If you intend to receive Windows Event Log data (e.g. via
-<a href="http://www.eventreporter.com/">EventReporter</a>), you might want to
-increase this number to an even higher value, as event
-log messages can be very lengthy ("$MaxMessageSize 64k" is not a bad idea).
-Note: testing showed that 4k seems to be
-the typical maximum for <b>UDP</b> based syslog. This is an IP stack
-restriction. Not always ... but very often. If you go beyond
-that value, be sure to test that rsyslogd actually does what
-you think it should do ;) It is highly suggested to use a TCP based transport
-instead of UDP (plain TCP syslog, RELP). This resolves the UDP stack size restrictions.
-<br>Note that 2k, the current default, is the smallest size that must be
-supported in order to be compliant to the upcoming new syslog RFC series.
-</li>
-<li><a href="rsconf1_moddir.html">$ModDir</a></li>
-<li><a href="rsconf1_modload.html">$ModLoad</a></li>
-<li><a href="rsconf1_repeatedmsgreduction.html">$RepeatedMsgReduction</a></li>
-<li><a href="rsconf1_resetconfigvariables.html">$ResetConfigVariables</a></li>
-<li>$WorkDirectory &lt;name&gt; (directory for spool
-and other work files)</li>
-<li>$UDPServerAddress &lt;IP&gt; (imudp) -- local IP
-address (or name) the UDP listens should bind to</li>
-<li>$UDPServerRun &lt;port&gt; (imudp) -- former
--r&lt;port&gt; option, default 514, start UDP server on this
-port, "*" means all addresses</li>
-<li><a href="rsconf1_umask.html">$UMASK</a></li>
-</ul>
-<p><b>Where &lt;size_nbr&gt; is specified above,</b>
-modifiers can be used after the number part. For example, 1k means
-1024. Supported are k(ilo), m(ega), g(iga), t(era), p(eta) and e(xa).
-Lower case letters refer to the traditional binary defintion (e.g. 1m
-equals 1,048,576) whereas upper case letters refer to their new
-1000-based definition (e.g 1M equals 1,000,000).</p>
-<p>Numbers may include '.' and ',' for readability. So you can
-for example specify either "1000" or "1,000" with the same result.
-Please note that rsyslogd simply ignores the punctuation. Form it's
-point of view, "1,,0.0.,.,0" also has the value 1000. </p>
+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
@@ -294,977 +45,15 @@ priorities belonging to the specified action.<br>
<br>
Lines starting with a hash mark ("#'') and empty lines are ignored.
</p>
-<h2>Templates</h2>
-<p>Templates are a key feature of rsyslog. They allow to specify
-any
-format a user might want. They are also used for dynamic file name
-generation. Every output in rsyslog uses templates - this holds true
-for files, user messages and so on.
-Please note that there is an
-<a href="http://www.rsyslog.com/Article354.phtml">online tutorial on rsyslog templates</a>
-available on the web. We recommend viewing it.
-The database writer expects its
-template to be a proper SQL statement - so this is highly customizable
-too. You might ask how does all of this work when no templates at all
-are specified. Good question ;) The answer is simple, though. Templates
-compatible with the stock syslogd formats are hardcoded into rsyslogd.
-So if no template is specified, we use one of these hardcoded
-templates. Search for "template_" in syslogd.c and you will find the
-hardcoded ones.</p>
-<p>A template consists of a template directive, a name, the
-actual template text and optional options. A sample is:</p>
-<blockquote><code>$template MyTemplateName,"\7Text
-%property% some more text\n",&lt;options&gt;</code></blockquote>
-<p>The "$template" is the template directive. It tells rsyslog
-that this line contains a template. "MyTemplateName" is the template
-name. All
-other config lines refer to this name. The text within quotes is the
-actual template text. The backslash is an escape character, much as it
-is in C. It does all these "cool" things. For example, \7 rings the
-bell (this is an ASCII value), \n is a new line. C programmers and perl
-coders have the advantage of knowing this, but the set in rsyslog is a
-bit restricted currently.
-</p>
-<p>All text in the template is used literally, except for things
-within percent signs. These are properties and allow you access to the
-contents of the syslog message. Properties are accessed via the
-property replacer (nice name, huh) and it can do cool things, too. For
-example, it can pick a substring or do date-specific formatting. More
-on this is below, on some lines of the property replacer.<br>
-<br>
-The &lt;options&gt; part is optional. It carries options
-influencing the template as whole. See details below. Be sure NOT to
-mistake template options with property options - the later ones are
-processed by the property replacer and apply to a SINGLE property, only
-(and not the whole template).<br>
-<br>
-Template options are case-insensitive. Currently defined are: </p>
-<p><b>sql</b> - format the string suitable for a SQL
-statement in MySQL format. This will replace single quotes ("'") and
-the backslash character by their backslash-escaped counterpart ("\'"
-and "\\") inside each field. Please note that in MySQL configuration,
-the <code class="literal">NO_BACKSLASH_ESCAPES</code>
-mode must be turned off for this format to work (this is the default).</p>
-<p><b>stdsql</b> - format the string suitable for a
-SQL statement that is to be sent to a standards-compliant sql server.
-This will replace single quotes ("'") by two single quotes ("''")
-inside each field. You must use stdsql together with MySQL if in MySQL
-configuration the
-<code class="literal">NO_BACKSLASH_ESCAPES</code> is
-turned on.</p>
-<p>Either the <b>sql</b> or <b>stdsql</b>&nbsp;
-option <b>must</b> be specified when a template is used
-for writing to a database, otherwise injection might occur. Please note
-that due to the unfortunate fact that several vendors have violated the
-sql standard and introduced their own escape methods, it is impossible
-to have a single option doing all the work.&nbsp; So you yourself
-must make sure you are using the right format. <b>If you choose
-the wrong one, you are still vulnerable to sql injection.</b><br>
-<br>
-Please note that the database writer *checks* that the sql option is
-present in the template. If it is not present, the write database
-action is disabled. This is to guard you against accidental forgetting
-it and then becoming vulnerable to SQL injection. The sql option can
-also be useful with files - especially if you want to import them into
-a database on another machine for performance reasons. However, do NOT
-use it if you do not have a real need for it - among others, it takes
-some toll on the processing time. Not much, but on a really busy system
-you might notice it ;)</p>
-<p>The default template for the write to database action has the
-sql option set. As we currently support only MySQL and the sql option
-matches the default MySQL configuration, this is a good choice.
-However, if you have turned on
-<code class="literal">NO_BACKSLASH_ESCAPES</code> in
-your MySQL config, you need to supply a template with the stdsql
-option. Otherwise you will become vulnerable to SQL injection. <br>
-<br>
-To escape:<br>
-% = \%<br>
-\ = \\ --&gt; '\' is used to escape (as in C)<br>
-$template TraditionalFormat,%timegenerated% %HOSTNAME%
-%syslogtag%%msg%\n"<br>
-<br>
-Properties can be accessed by the <a href="property_replacer.html">property
-replacer</a> (see there for details).</p>
-<p><b>Please note that templates can also by
-used to generate selector lines with dynamic file names.</b> For
-example, if you would like to split syslog messages from different
-hosts to different files (one per host), you can define the following
-template:</p>
-<blockquote><code>$template
-DynFile,"/var/log/system-%HOSTNAME%.log"</code></blockquote>
-<p>This template can then be used when defining an output
-selector line. It will result in something like
-"/var/log/system-localhost.log"</p>
-<p>Template
-names beginning with "RSYSLOG_" are reserved for rsyslog use. Do NOT
-use them if, otherwise you may receive a conflict in the future (and
-quite unpredictable behaviour). There is a small set of pre-defined
-templates that you can use without the need to define it:</p>
-<ul>
-<li><span style="font-weight: bold;">RSYSLOG_TraditionalFileFormat</span>
-- the "old style" default log file format with low-precision timestamps</li>
-<li><span style="font-weight: bold;">RSYSLOG_FileFormat</span>
-- a modern-style logfile format similar to TraditionalFileFormat, buth
-with high-precision timestamps and timezone information</li>
-<li><span style="font-weight: bold;">RSYSLOG_TraditionalForwardFormat</span>
-- the traditional forwarding format with low-precision timestamps. Most
-useful if you send&nbsp;messages to other syslogd's or rsyslogd
-below
-version 3.12.5.</li>
-<li><span style="font-weight: bold;">RSYSLOG_ForwardFormat</span>
-- a new high-precision forwarding format very similar to the
-traditional one, but with high-precision timestamps and timezone
-information. Recommended to be used when sending messages to rsyslog
-3.12.5 or above.</li>
-<li><span style="font-weight: bold;">RSYSLOG_SyslogProtocol23Format</span>
-- the format specified in IETF's internet-draft
-ietf-syslog-protocol-23, which is assumed to be come the new syslog
-standard RFC. This format includes several improvements. The rsyslog
-message parser understands this format, so you can use it together with
-all relatively recent versions of rsyslog. Other syslogd's may get
-hopelessly confused if receiving that format, so check before you use
-it. Note that the format is unlikely to change when the final RFC comes
-out, but this may happen.</li>
-<li><span style="font-weight: bold;">RSYSLOG_DebugFormat</span>
-- a special format used for troubleshooting property problems. This format
-is meant to be written to a log file. Do <b>not</b> use for production or remote
-forwarding.</li>
-</ul>
-<h2>Output Channels</h2>
-<p>Output Channels are a new concept first introduced in rsyslog
-0.9.0. <b>As of this writing, it is most likely that they will
-be replaced by something different in the future.</b> So if you
-use them, be prepared to change you configuration file syntax when you
-upgrade to a later release.<br>
-<br>
-The idea behind output channel definitions is that it shall provide an
-umbrella for any type of output that the user might want. In essence,<br>
-this is the "file" part of selector lines (and this is why we are not
-sure output channel syntax will stay after the next review). There is a<br>
-difference, though: selector channels both have filter conditions
-(currently facility and severity) as well as the output destination.
-Output channels define the output definition, only. As of this build,
-they can only be used to write to files - not pipes, ttys or whatever
-else. If we stick with output channels, this will change over time.</p>
-<p>In concept, an output channel includes everything needed to
-know about an output actions. In practice, the current implementation
-only carries<br>
-a filename, a maximum file size and a command to be issued when this
-file size is reached. More things might be present in future version,
-which might also change the syntax of the directive.</p>
-<p>Output channels are defined via an $outchannel directive. It's
-syntax is as follows:<br>
-<br>
-$outchannel name,file-name,max-size,action-on-max-size<br>
-<br>
-name is the name of the output channel (not the file), file-name is the
-file name to be written to, max-size the maximum allowed size and
-action-on-max-size a command to be issued when the max size is reached.
-This command always has exactly one parameter. The binary is that part
-of action-on-max-size before the first space, its parameter is
-everything behind that space.<br>
-<br>
-Please note that max-size is queried BEFORE writing the log message to
-the file. So be sure to set this limit reasonably low so that any
-message might fit. For the current release, setting it 1k lower than
-you expected is helpful. The max-size must always be specified in bytes
-- there are no special symbols (like 1k, 1m,...) at this point of
-development.<br>
-<br>
-Keep in mind that $outchannel just defines a channel with "name". It
-does not activate it. To do so, you must use a selector line (see
-below). That selector line includes the channel name plus an $ sign in
-front of it. A sample might be:<br>
-<br>
-*.* $mychannel<br>
-<br>
-In its current form, output channels primarily provide the ability to
-size-limit an output file. To do so, specify a maximum size. When this
-size is reached, rsyslogd will execute the action-on-max-size command
-and then reopen the file and retry. The command should be something
-like a <a href="log_rotation_fix_size.html">log rotation
-script</a> or a similar thing.</p>
-<p>If there is no action-on-max-size command or the command did
-not resolve the situation, the file is closed and never reopened by
-rsyslogd (except, of course, by huping it). This logic was integrated
-when we first experienced severe issues with files larger 2gb, which
-could lead to rsyslogd dumping core. In such cases, it is more
-appropriate to stop writing to a single file. Meanwhile, rsyslogd has
-been fixed to support files larger 2gb, but obviously only on file
-systems and operating system versions that do so. So it can still make
-sense to enforce a 2gb file size limit.</p>
-<h2>Filter Conditions</h2>
-<p>Rsyslog offers four different types "filter conditions":</p>
-<ul>
-<li>BSD-style blocks</li>
-<li>"traditional" severity and facility based selectors</li>
-<li>property-based filters</li>
-<li>expression-based filters</li>
-</ul>
-<h3>Blocks</h3>
-<p>Rsyslogd supports BSD-style blocks inside rsyslog.conf. Each
-block of lines is separated from the previous block by a program or
-hostname specification. A block will only log messages corresponding to
-the most recent program and hostname specifications given. Thus, a
-block which selects &#8216;ppp&#8217; as the program, directly followed by a block
-that selects messages from the hostname &#8216;dialhost&#8217;, then the second
-block will only log messages from the ppp program on dialhost.
-</p>
-<p>A program specification is a line beginning with &#8216;!prog&#8217; and
-the following blocks will be associated with calls to syslog from that
-specific program. A program specification for &#8216;foo&#8217; will also match any
-message logged by the kernel with the prefix &#8216;foo: &#8217;. Alternatively, a
-program specification &#8216;-foo&#8217; causes the following blocks to be applied
-to messages from any program but the one specified. A hostname
-specification of the form &#8216;+hostname&#8217; and the following blocks will be
-applied to messages received from the specified hostname.
-Alternatively, a hostname specification &#8216;-hostname&#8217; causes the
-following blocks to be applied to messages from any host but the one
-specified. If the hostname is given as &#8216;@&#8217;, the local hostname will be
-used. (NOT YET IMPLEMENTED) A program or hostname specification may be
-reset by giving the program or hostname as &#8216;*&#8217;.</p>
-<p>Please note that the "#!prog", "#+hostname" and "#-hostname"
-syntax available in BSD syslogd is not supported by rsyslogd. By
-default, no hostname or program is set.</p>
-<h3>Selectors</h3>
-<p><b>Selectors are the traditional way of filtering syslog
-messages.</b> They have been kept in rsyslog with their original
-syntax, because it is well-known, highly effective and also needed for
-compatibility with stock syslogd configuration files. If you just need
-to filter based on priority and facility, you should do this with
-selector lines. They are <b>not</b> second-class citizens
-in rsyslog and offer the best performance for this job.</p>
-<p>The selector field itself again consists of two parts, a
-facility and a priority, separated by a period (".''). Both parts are
-case insensitive and can also be specified as decimal numbers, but
-don't do that, you have been warned. Both facilities and priorities are
-described in rsyslog(3). The names mentioned below correspond to the
-similar LOG_-values in /usr/include/rsyslog.h.<br>
-<br>
-The facility is one of the following keywords: auth, authpriv, cron,
-daemon, kern, lpr, mail, mark, news, security (same as auth), syslog,
-user, uucp and local0 through local7. The keyword security should not
-be used anymore and mark is only for internal use and therefore should
-not be used in applications. Anyway, you may want to specify and
-redirect these messages here. The facility specifies the subsystem that
-produced the message, i.e. all mail programs log with the mail facility
-(LOG_MAIL) if they log using syslog.<br>
-<br>
-The priority is one of the following keywords, in ascending order:
-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. The
-priority defines the severity of the message.<br>
-<br>
-The behavior of the original BSD syslogd is that all messages of the
-specified priority and higher are logged according to the given action.
-Rsyslogd behaves the same, but has some extensions.<br>
-<br>
-In addition to the above mentioned names the rsyslogd(8) understands
-the following extensions: An asterisk ("*'') stands for all facilities
-or all priorities, depending on where it is used (before or after the
-period). The keyword none stands for no priority of the given facility.<br>
-<br>
-You can specify multiple facilities with the same priority pattern in
-one statement using the comma (",'') operator. You may specify as much
-facilities as you want. Remember that only the facility part from such
-a statement is taken, a priority part would be skipped.</p>
-<p>Multiple selectors may be specified for a single action using
-the semicolon (";'') separator. Remember that each selector in the
-selector field is capable to overwrite the preceding ones. Using this
-behavior you can exclude some priorities from the pattern.</p>
-<p>Rsyslogd has a syntax extension to the original BSD source,
-that makes its use more intuitively. You may precede every priority
-with an equation sign ("='') to specify only this single priority and
-not any of the above. You may also (both is valid, too) precede the
-priority with an exclamation mark ("!'') to ignore all that
-priorities, either exact this one or this and any higher priority. If
-you use both extensions than the exclamation mark must occur before the
-equation sign, just use it intuitively.</p>
-<h3>Property-Based Filters</h3>
-<p>Property-based filters are unique to rsyslogd. They allow to
-filter on any property, like HOSTNAME, syslogtag and msg. A list of all
-currently-supported properties can be found in the <a href="property_replacer.html">property replacer documentation</a>
-(but keep in mind that only the properties, not the replacer is
-supported). With this filter, each properties can be checked against a
-specified value, using a specified compare operation.</p>
-<p>A property-based filter must start with a colon in column 0.
-This tells rsyslogd that it is the new filter type. The colon must be
-followed by the property name, a comma, the name of the compare
-operation to carry out, another comma and then the value to compare
-against. This value must be quoted. There can be spaces and tabs
-between the commas. Property names and compare operations are
-case-sensitive, so "msg" works, while "MSG" is an invalid property
-name. In brief, the syntax is as follows:</p>
-<p><code><b>:property, [!]compare-operation, "value"</b></code></p>
-<p>The following <b>compare-operations</b> are
-currently supported:</p>
-<table id="table1" border="1" width="100%">
-<tbody>
-<tr>
-<td>contains</td>
-<td>Checks if the string provided in value is contained in
-the property. There must be an exact match, wildcards are not supported.</td>
-</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
-difference to contains is that contains searches for the value anywhere
-inside the property value, whereas all characters must be identical for
-isequal. As such, isequal is most useful for fields like syslogtag or
-FROMHOST, where you probably know the exact contents.</td>
-</tr>
-<tr>
-<td>startswith</td>
-<td>Checks if the value is found exactly at the beginning
-of the property value. For example, if you search for "val" with
-<p><code><b>:msg, startswith, "val"</b></code></p>
-<p>it will be a match if msg contains "values are in this
-message" but it won't match if the msg contains "There are values in
-this message" (in the later case, contains would match). Please note
-that "startswith" is by far faster than regular expressions. So even
-once they are implemented, it can make very much sense
-(performance-wise) to use "startswith".</p>
-</td>
-</tr>
-<tr>
-<td>regex</td>
-<td>Compares the property against the provided POSIX
-regular
-expression.</td>
-</tr>
-</tbody>
-</table>
-<p>You can use the bang-character (!) immediately in front of a
-compare-operation, the outcome of this operation is negated. For
-example, if msg contains "This is an informative message", the
-following sample would not match:</p>
-<p><code><b>:msg, contains, "error"</b></code></p>
-<p>but this one matches:</p>
-<p><code><b>:msg, !contains, "error"</b></code></p>
-<p>Using negation can be useful if you would like to do some
-generic processing but exclude some specific events. You can use the
-discard action in conjunction with that. A sample would be:</p>
-<p><code><b>*.*
-/var/log/allmsgs-including-informational.log<br>
-:msg, contains, "informational"&nbsp; <font color="#ff0000" size="4">~</font>
-<br>
-*.* /var/log/allmsgs-but-informational.log</b></code></p>
-<p>Do not overlook the red tilde in line 2! In this sample, all
-messages are written to the file allmsgs-including-informational.log.
-Then, all messages containing the string "informational" are discarded.
-That means the config file lines below the "discard line" (number 2 in
-our sample) will not be applied to this message. Then, all remaining
-lines will also be written to the file allmsgs-but-informational.log.</p>
-<p><b>Value</b> is a quoted string. It supports some
-escape sequences:</p>
-<p>\" - the quote character (e.g. "String with \"Quotes\"")<br>
-\\ - the backslash character (e.g. "C:\\tmp")</p>
-<p>Escape sequences always start with a backslash. Additional
-escape sequences might be added in the future. Backslash characters <b>must</b>
-be escaped. Any other sequence then those outlined above is invalid and
-may lead to unpredictable results.</p>
-<p>Probably, "msg" is the most prominent use case of property
-based filters. It is the actual message text. If you would like to
-filter based on some message content (e.g. the presence of a specific
-code), this can be done easily by:</p>
-<p><code><b>:msg, contains, "ID-4711"</b></code></p>
-<p>This filter will match when the message contains the string
-"ID-4711". Please note that the comparison is case-sensitive, so it
-would not match if "id-4711" would be contained in the message.</p>
-<p><code><b>:msg, regex, "fatal .* error"</b></code></p>
-<p>This filter uses a POSIX regular expression. It matches when
-the
-string contains the words "fatal" and "error" with anything in between
-(e.g. "fatal net error" and "fatal lib error" but not "fatal error" as
-two spaces are required by the regular expression!).</p>
-<p>Getting property-based filters right can sometimes be
-challenging. In order to help you do it with as minimal effort as
-possible, rsyslogd spits out debug information for all property-based
-filters during their evaluation. To enable this, run rsyslogd in
-foreground and specify the "-d" option.</p>
-<p>Boolean operations inside property based filters (like
-'message contains "ID17" or message contains "ID18"') are currently not
-supported (except for "not" as outlined above). Please note that while
-it is possible to query facility and severity via property-based
-filters, it is far more advisable to use classic selectors (see above)
-for those cases.</p>
-<h3>Expression-Based Filters</h3>
-Expression based filters allow
-filtering on arbitrary complex expressions, which can include boolean,
-arithmetic and string operations. Expression filters will evolve into a
-full configuration scripting language. Unfortunately, their syntax will
-slightly change during that process. So if you use them now, you need
-to be prepared to change your configuration files some time later.
-However, we try to implement the scripting facility as soon as possible
-(also in respect to stage work needed). So the window of exposure is
-probably not too long.<br>
-<br>
-Expression based filters are indicated by the keyword "if" in column 1
-of a new line. They have this format:<br>
-<br>
-if expr then action-part-of-selector-line<br>
-<br>
-"If" and "then" are fixed keywords that mus be present. "expr" is a
-(potentially quite complex) expression. So the <a href="expression.h">expression documentation</a> for
-details. "action-part-of-selector-line" is an action, just as you know
-it (e.g. "/var/log/logfile" to write to that file).<br>
-<br>
-A few quick samples:<br>
-<br>
-<code>
-*.* /var/log/file1 # the traditional way<br>
-if $msg contains 'error' /var/log/errlog # the expression-based way<br>
-</code>
-<br>
-Right now, you need to specify numerical values if you would like to
-check for facilities and severity. These can be found in <a href="http://www.ietf.org/rfc/rfc3164.txt">RFC 3164</a>.
-If you don't like that, you can of course also use the textual property
-- just be sure to use the right one. As expression support is enhanced,
-this will change. For example, if you would like to filter on message
-that have facility local0, start with "DEVNAME" and have either
-"error1" or "error0" in their message content, you could use the
-following filter:<br>
-<br>
-<code>
-if $syslogfacility-text == 'local0' and $msg
-startswith 'DEVNAME' and ($msg contains 'error1' or $msg contains
-'error0') then /var/log/somelog<br>
-</code>
-<br>
-Please note that the above <span style="font-weight: bold;">must
-all be on one line</span>! And if you would like to store all
-messages except those that contain "error1" or "error0", you just need
-to add a "not":<br>
-<br>
-<code>
-if $syslogfacility-text == 'local0' and $msg
-startswith 'DEVNAME' and <span style="font-weight: bold;">not</span>
-($msg contains 'error1' or $msg contains
-'error0') then /var/log/somelog<br>
-</code>
-<br>
-If you would like to do case-insensitive comparisons, use
-"contains_i" instead of "contains" and "startswith_i" instead of
-"startswith".<br>
-<br>
-Note that regular expressions are currently NOT
-supported in expression-based filters. These will be added later when
-function support is added to the expression engine (the reason is that
-regular expressions will be a separate loadable module, which requires
-some more prequisites before it can be implemented).<br>
-<h2>ACTIONS</h2>
-<p>The action field of a rule describes what to do with the
-message. In general, message content is written to a kind of "logfile".
-But also other actions might be done, like writing to a database table
-or forwarding to another host.<br>
-<br>
-Templates can be used with all actions. If used, the specified template
-is used to generate the message content (instead of the default
-template). To specify a template, write a semicolon after the action
-value immediately followed by the template name.<br>
-<br>
-Beware: templates MUST be defined BEFORE they are used. It is OK to
-define some templates, then use them in selector lines, define more
-templates and use use them in the following selector lines. But it is
-NOT permitted to use a template in a selector line that is above its
-definition. If you do this, the action will be ignored.</p>
-<p><b>You can have multiple actions for a single selector </b>&nbsp;(or
-more precisely a single filter of such a selector line). Each action
-must be on its own line and the line must start with an ampersand
-('&amp;') character and have no filters. An example would be</p>
-<p><code><b>*.=crit rger<br>
-&amp; root<br>
-&amp; /var/log/critmsgs</b></code></p>
-<p>These three lines send critical messages to the user rger and
-root and also store them in /var/log/critmsgs. <b>Using multiple
-actions per selector is</b> convenient and also <b>offers
-a performance benefit</b>. As the filter needs to be evaluated
-only once, there is less computation required to process the directive
-compared to the otherwise-equal config directives below:</p>
-<p><code><b>*.=crit rger<br>
-*.=crit root<br>
-*.=crit /var/log/critmsgs</b></code></p>
-<p>&nbsp;</p>
-<h3>Regular File</h3>
-<p>Typically messages are logged to real files. The file has to
-be specified with full pathname, beginning with a slash "/''.<br>
-<br>
-You may prefix each entry with the minus "-'' sign to omit syncing the
-file after every logging. Note that you might lose information if the
-system crashes right behind a write attempt. Nevertheless this might
-give you back some performance, especially if you run programs that use
-logging in a very verbose manner.</p>
-<p>If your system is connected to a reliable UPS and you receive
-lots of log data (e.g. firewall logs), it might be a very good idea to
-turn of
-syncing by specifying the "-" in front of the file name. </p>
-<p><b>The filename can be either static </b>(always
-the same) or <b>dynamic</b> (different based on message
-received). The later is useful if you would automatically split
-messages into different files based on some message criteria. For
-example, dynamic file name selectors allow you to split messages into
-different files based on the host that sent them. With dynamic file
-names, everything is automatic and you do not need any filters. </p>
-<p>It works via the template system. First, you define a template
-for the file name. An example can be seen above in the description of
-template. We will use the "DynFile" template defined there. Dynamic
-filenames are indicated by specifying a questions mark "?" instead of a
-slash, followed by the template name. Thus, the selector line for our
-dynamic file name would look as follows:</p>
-<blockquote>
-<code>*.* ?DynFile</code>
-</blockquote>
-<p>That's all you need to do. Rsyslog will now automatically
-generate file names for you and store the right messages into the right
-files. Please note that the minus sign also works with dynamic file
-name selectors. Thus, to avoid syncing, you may use</p>
-<blockquote>
-<code>*.* -?DynFile</code></blockquote>
-<p>And of course you can use templates to specify the output
-format:</p>
-<blockquote>
-<code>*.* ?DynFile;MyTemplate</code></blockquote>
-<p><b>A word of caution:</b> rsyslog creates files as
-needed. So if a new host is using your syslog server, rsyslog will
-automatically create a new file for it.</p>
-<p><b>Creating directories is also supported</b>. For
-example you can use the hostname as directory and the program name as
-file name:</p>
-<blockquote>
-<code>$template DynFile,"/var/log/%HOSTNAME%/%programname%.log"</code></blockquote>
-<h3>Named Pipes</h3>
-<p>This version of rsyslogd(8) has support for logging output to
-named pipes (fifos). A fifo or named pipe can be used as a destination
-for log messages by prepending a pipe symbol ("|'') to the name of the
-file. This is handy for debugging. Note that the fifo must be created
-with the mkfifo(1) command before rsyslogd(8) is started.</p>
-<h3>Terminal and Console</h3>
-<p>If the file you specified is a tty, special tty-handling is
-done, same with /dev/console.</p>
-<h3>Remote Machine</h3>
-<p>Rsyslogd provides full remote logging, i.e. is able to send
-messages to a remote host running rsyslogd(8) and to receive messages
-from remote hosts. Using this feature you're able to control all syslog
-messages on one host, if all other machines will log remotely to that.
-This tears down<br>
-administration needs.<br>
-<br>
-<b>Please note that this version of rsyslogd by default does NOT
-forward messages it has received from the network to another host.
-Specify the "-h" option to enable this.</b></p>
-<p>To forward messages to another host, prepend the hostname with
-the at sign ("@"). A single at sign means that messages will
-be forwarded via UDP protocol (the standard for syslog). If you prepend
-two at signs ("@@"), the messages will be transmitted via TCP. Please
-note that plain TCP based syslog is not officially standardized, but
-most major syslogds support it (e.g. syslog-ng or WinSyslog). The
-forwarding action indicator (at-sign) can be followed by one or more
-options. If they are given, they must be immediately (without a space)
-following the final at sign and be enclosed in parenthesis. The
-individual options must be separated by commas. The following options
-are right now defined:</p>
-<table id="table2" border="1" width="100%">
-<tbody>
-<tr>
-<td>
-<p align="center"><b>z&lt;number&gt;</b></p>
-</td>
-<td>Enable zlib-compression for the message. The
-&lt;number&gt; is the compression level. It can be 1 (lowest
-gain, lowest CPU overhead) to 9 (maximum compression, highest CPU
-overhead). The level can also be 0, which means "no compression". If
-given, the "z" option is ignored. So this does not make an awful lot of
-sense. There is hardly a difference between level 1 and 9 for typical
-syslog messages. You can expect a compression gain between 0% and 30%
-for typical messages. Very chatty messages may compress up to 50%, but
-this is seldom seen with typically traffic. Please note that rsyslogd
-checks the compression gain. Messages with 60 bytes or less will never
-be compressed. This is because compression gain is pretty unlikely and
-we prefer to save CPU cycles. Messages over that size are always
-compressed. However, it is checked if there is a gain in compression
-and only if there is, the compressed message is transmitted. Otherwise,
-the uncompressed messages is transmitted. This saves the receiver CPU
-cycles for decompression. It also prevents small message to actually
-become larger in compressed form.
-<p><b>Please note that when a TCP transport is used,
-compression will also turn on syslog-transport-tls framing. See the "o"
-option for important information on the implications.</b></p>
-<p>Compressed messages are automatically detected and
-decompressed by the receiver. There is nothing that needs to be
-configured on the receiver side.</p>
-</td>
-</tr>
-<tr>
-<td>
-<p align="center"><b>o</b></p>
-</td>
-<td><b>This option is experimental. Use at your own
-risk and only if you know why you need it! If in doubt, do NOT turn it
-on.</b>
-<p>This option is only valid for plain TCP based
-transports. It selects a different framing based on IETF internet draft
-syslog-transport-tls-06. This framing offers some benefits over
-traditional LF-based framing. However, the standardization effort is
-not yet complete. There may be changes in upcoming versions of this
-standard. Rsyslog will be kept in line with the standard. There is some
-chance that upcoming changes will be incompatible to the current
-specification. In this case, all systems using -transport-tls framing
-must be upgraded. There will be no effort made to retain compatibility
-between different versions of rsyslog. The primary reason for that is
-that it seems technically impossible to provide compatibility between
-some of those changes. So you should take this note very serious. It is
-not something we do not *like* to do (and may change our mind if enough
-people beg...), it is something we most probably *can not* do for
-technical reasons (aka: you can beg as much as you like, it won't
-change anything...).</p>
-<p>The most important implication is that compressed syslog
-messages via TCP must be considered with care. Unfortunately, it is
-technically impossible to transfer compressed records over traditional
-syslog plain tcp transports, so you are left with two evil choices...</p>
-</td>
-</tr>
-</tbody>
-</table>
-<p><br>
-The hostname may be followed by a colon and the destination port.</p>
-<p>The following is an example selector line with forwarding:</p>
-<p>*.*&nbsp;&nbsp;&nbsp; @@(o,z9)192.168.0.1:1470</p>
-<p>In this example, messages are forwarded via plain TCP with
-experimental framing and maximum compression to the host 192.168.0.1 at
-port 1470.</p>
-<p>*.* @192.168.0.1</p>
-<p>In the example above, messages are forwarded via UDP to the
-machine 192.168.0.1, the destination port defaults to 514. Messages
-will not be compressed.</p>
-<p>Note that IPv6 addresses contain colons. So if an IPv6 address is specified
-in the hostname part, rsyslogd could not detect where the IP address ends
-and where the port starts. There is a syntax extension to support this:
-put squary brackets around the address (e.g. "[2001::1]"). Square
-brackets also work with real host names and IPv4 addresses, too.
-<p>A valid sample to send messages to the IPv6 host 2001::1 at port 515
-is as follows:
-<p>*.* @[2001::1]:515
-<p>This works with TCP, too.
-<p><b>Note to sysklogd users:</b> sysklogd does <b>not</b>
-support RFC 3164 format, which is the default forwarding template in
-rsyslog. As such, you will experience duplicate hostnames if rsyslog is
-the sender and sysklogd is the receiver. The fix is simple: you need to
-use a different template. Use that one:</p>
-<p class="MsoPlainText">$template
-sysklogd,"&lt;%PRI%&gt;%TIMESTAMP% %syslogtag%%msg%\""<br>
-*.* @192.168.0.1;sysklogd</p>
-<h3>List of Users</h3>
-<p>Usually critical messages are also directed to "root'' on
-that machine. You can specify a list of users that shall get the
-message by simply writing the login. You may specify more than one user
-by separating them with commas (",''). If they're logged in they get
-the message. Don't think a mail would be sent, that might be too late.</p>
-<h3>Everyone logged on</h3>
-<p>Emergency messages often go to all users currently online to
-notify them that something strange is happening with the system. To
-specify this wall(1)-feature use an asterisk ("*'').</p>
-<h3>Call Plugin</h3>
-<p>This is a generic way to call an output plugin. The plugin
-must support this functionality. Actual parameters depend on the
-module, so see the module's doc on what to supply. The general syntax
-is as follows:</p>
-<p>:modname:params;template</p>
-<p>Currently, the ommysql database output module supports this
-syntax (in addtion to the "&gt;" syntax it traditionally
-supported). For ommysql, the module name is "ommysql" and the params
-are the traditional ones. The ;template part is not module specific, it
-is generic rsyslog functionality available to all modules.</p>
-<p>As an example, the ommysql module may be called as follows:</p>
-<p>:ommysql:dbhost,dbname,dbuser,dbpassword;dbtemplate</p>
-<p>For details, please see the "Database Table" section of this
-documentation.</p>
-<p>Note: as of this writing, the ":modname:" part is hardcoded
-into the module. So the name to use is not necessarily the name the
-module's plugin file is called.</p>
-<h3>Database Table</h3>
-<p>This allows logging of the message to a database table.
-Currently, only MySQL databases are supported. However, other database
-drivers will most probably be developed as plugins. By default, a <a href="http://www.monitorware.com/">MonitorWare</a>-compatible
-schema is required for this to work. You can create that schema with
-the createDB.SQL file that came with the rsyslog package. You can also<br>
-use any other schema of your liking - you just need to define a proper
-template and assign this template to the action.<br>
-<br>
-The database writer is called by specifying a greater-then sign
-("&gt;") in front of the database connect information. Immediately
-after that<br>
-sign the database host name must be given, a comma, the database name,
-another comma, the database user, a comma and then the user's password.
-If a specific template is to be used, a semicolon followed by the
-template name can follow the connect information. This is as follows:<br>
-<br>
-&gt;dbhost,dbname,dbuser,dbpassword;dbtemplate</p>
-<p><b>Important: to use the database functionality, the
-MySQL output module must be loaded in the config file</b> BEFORE
-the first database table action is used. This is done by placing the</p>
-<p><code><b>$ModLoad ommysql</b></code></p>
-<p>directive some place above the first use of the database write
-(we recommend doing at the the beginning of the config file).</p>
-<h3>Discard</h3>
-<p>If the discard action is carried out, the received message is
-immediately discarded. No further processing of it occurs. Discard has
-primarily been added to filter out messages before carrying on any
-further processing. For obvious reasons, the results of "discard" are
-depending on where in the configuration file it is being used. Please
-note that once a message has been discarded there is no way to retrieve
-it in later configuration file lines.</p>
-<p>Discard can be highly effective if you want to filter out some
-annoying messages that otherwise would fill your log files. To do that,
-place the discard actions early in your log files. This often plays
-well with property-based filters, giving you great freedom in
-specifying what you do not want.</p>
-<p>Discard is just the single tilde character with no further
-parameters:</p>
-<p>~</p>
-<p>For example,</p>
-<p>*.*&nbsp;&nbsp; ~</p>
-<p>discards everything (ok, you can achive the same by not
-running rsyslogd at all...).</p>
-<h3>Output Channel</h3>
-<p>Binds an output channel definition (see there for details) to
-this action. Output channel actions must start with a $-sign, e.g. if
-you would like to bind your output channel definition "mychannel" to
-the action, use "$mychannel". Output channels support template
-definitions like all all other actions.</p>
-<h3>Shell Execute</h3>
-<p>This executes a program in a subshell. The program is passed
-the template-generated message as the only command line parameter.
-Rsyslog waits until the program terminates and only then continues to
-run.</p>
-<p>^program-to-execute;template</p>
-<p>The program-to-execute can be any valid executable. It
-receives the template string as a single parameter (argv[1]).</p>
-<p><b>WARNING:</b> The Shell Execute action was added
-to serve an urgent need. While it is considered reasonable save when
-used with some thinking, its implications must be considered. The
-current implementation uses a system() call to execute the command.
-This is not the best way to do it (and will hopefully changed in
-further releases). Also, proper escaping of special characters is done
-to prevent command injection. However, attackers always find smart ways
-to circumvent escaping, so we can not say if the escaping applied will
-really safe you from all hassles. Lastly, rsyslog will wait until the
-shell command terminates. Thus, a program error in it (e.g. an infinite
-loop) can actually disable rsyslog. Even without that, during the
-programs run-time no messages are processed by rsyslog. As the IP
-stacks buffers are quickly overflowed, this bears an increased risk of
-message loss. You must be aware of these implications. Even though they
-are severe, there are several cases where the "shell execute" action is
-very useful. This is the reason why we have included it in its current
-form. To mitigate its risks, always a) test your program thoroughly, b)
-make sure its runtime is as short as possible (if it requires a longer
-run-time, you might want to spawn your own sub-shell asynchronously),
-c) apply proper firewalling so that only known senders can send syslog
-messages to rsyslog. Point c) is especially important: if rsyslog is
-accepting message from any hosts, chances are much higher that an
-attacker might try to exploit the "shell execute" action.</p>
-<h2>TEMPLATE NAME</h2>
-<p>Every ACTION can be followed by a template name. If so, that
-template is used for message formatting. If no name is given, a
-hard-coded default template is used for the action. There can only be
-one template name for each given action. The default template is
-specific to each action. For a description of what a template is and
-what you can do with it, see "TEMPLATES" at the top of this document.</p>
-<h2>EXAMPLES</h2>
-<p>Below are example for templates and selector lines. I hope
+<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>
-<h3>TEMPLATES</h3>
-<p>Please note that the samples are split across multiple lines.
-A template MUST NOT actually be split across multiple lines.<br>
-<br>
-A template that resembles traditional syslogd file output:<br>
-$template TraditionalFormat,"%timegenerated% %HOSTNAME%<br>
-%syslogtag%%msg:::drop-last-lf%\n"<br>
-<br>
-A template that tells you a little more about the message:<br>
-$template
-precise,"%syslogpriority%,%syslogfacility%,%timegenerated%,%HOSTNAME%,<br>
-%syslogtag%,%msg%\n"<br>
-<br>
-A template for RFC 3164 format:<br>
-$template RFC3164fmt,"&lt;%PRI%&gt;%TIMESTAMP% %HOSTNAME%
-%syslogtag%%msg%"<br>
-<br>
-A template for the format traditonally used for user messages:<br>
-$template usermsg," XXXX%syslogtag%%msg%\n\r"<br>
-<br>
-And a template with the traditonal wall-message format:<br>
-$template wallmsg,"\r\n\7Message from syslogd@%HOSTNAME% at
-%timegenerated%<br>
-<br>
-A template that can be used for the database write (please note the SQL<br>
-template option)<br>
-$template MySQLInsert,"insert iut, message, receivedat values<br>
-('%iut%', '%msg:::UPPERCASE%', '%timegenerated:::date-mysql%')<br>
-into systemevents\r\n", SQL<br>
-<br>
-The following template emulates <a href="http://www.winsyslog.com/en/">WinSyslog</a>
-format (it's an <a href="http://www.adiscon.com/en/">Adiscon</a>
-format, you do not feel bad if you don't know it ;)). It's interesting
-to see how it takes different parts out of the date stamps. What
-happens is that the date stamp is split into the actual date and time
-and the these two are combined with just a comma in between them.<br>
-<br>
-$template WinSyslogFmt,"%HOSTNAME%,%timegenerated:1:10:date-rfc3339%,<br>
-%timegenerated:12:19:date-rfc3339%,%timegenerated:1:10:date-rfc3339%,<br>
-%timegenerated:12:19:date-rfc3339%,%syslogfacility%,%syslogpriority%,<br>
-%syslogtag%%msg%\n"</p>
-<h3>SELECTOR LINES</h3>
-<p># Store critical stuff in critical<br>
-#<br>
-*.=crit;kern.none /var/adm/critical<br>
-<br>
-This will store all messages with the priority crit in the file
-/var/adm/critical, except for any kernel message.<br>
-<br>
-<br>
-# Kernel messages are first, stored in the kernel<br>
-# file, critical messages and higher ones also go<br>
-# to another host and to the console. Messages to<br>
-# the host finlandia are forwarded in RFC 3164<br>
-# format (using the template defined above).<br>
-#<br>
-kern.* /var/adm/kernel<br>
-kern.crit @finlandia;RFC3164fmt<br>
-kern.crit /dev/console<br>
-kern.info;kern.!err /var/adm/kernel-info<br>
-<br>
-The first rule direct any message that has the kernel facility to the
-file /var/adm/kernel.<br>
-<br>
-The second statement directs all kernel messages of the priority crit
-and higher to the remote host finlandia. This is useful, because if the
-host crashes and the disks get irreparable errors you might not be able
-to read the stored messages. If they're on a remote host, too, you
-still can try to find out the reason for the crash.<br>
-<br>
-The third rule directs these messages to the actual console, so the
-person who works on the machine will get them, too.<br>
-<br>
-The fourth line tells rsyslogd to save all kernel messages that come
-with priorities from info up to warning in the file
-/var/adm/kernel-info. Everything from err and higher is excluded.<br>
-<br>
-<br>
-# The tcp wrapper loggs with mail.info, we display<br>
-# all the connections on tty12<br>
-#<br>
-mail.=info /dev/tty12<br>
-<br>
-This directs all messages that uses mail.info (in source LOG_MAIL |
-LOG_INFO) to /dev/tty12, the 12th console. For example the tcpwrapper
-tcpd(8) uses this as it's default.<br>
-<br>
-<br>
-# Store all mail concerning stuff in a file<br>
-#<br>
-mail.*;mail.!=info /var/adm/mail<br>
-<br>
-This pattern matches all messages that come with the mail facility,
-except for the info priority. These will be stored in the file
-/var/adm/mail.<br>
-<br>
-<br>
-# Log all mail.info and news.info messages to info<br>
-#<br>
-mail,news.=info /var/adm/info<br>
-<br>
-This will extract all messages that come either with mail.info or with
-news.info and store them in the file /var/adm/info.<br>
-<br>
-<br>
-# Log info and notice messages to messages file<br>
-#<br>
-*.=info;*.=notice;\<br>
-mail.none /var/log/messages<br>
-<br>
-This lets rsyslogd log all messages that come with either the info or
-the notice facility into the file /var/log/messages, except for all<br>
-messages that use the mail facility.<br>
-<br>
-<br>
-# Log info messages to messages file<br>
-#<br>
-*.=info;\<br>
-mail,news.none /var/log/messages<br>
-<br>
-This statement causes rsyslogd to log all messages that come with the
-info priority to the file /var/log/messages. But any message coming
-either with the mail or the news facility will not be stored.<br>
-<br>
-<br>
-# Emergency messages will be displayed using wall<br>
-#<br>
-*.=emerg *<br>
-<br>
-This rule tells rsyslogd to write all emergency messages to all
-currently logged in users. This is the wall action.<br>
-<br>
-<br>
-# Messages of the priority alert will be directed<br>
-# to the operator<br>
-#<br>
-*.alert root,rgerhards<br>
-<br>
-This rule directs all messages with a priority of alert or higher to
-the terminals of the operator, i.e. of the users "root'' and
-"rgerhards'' if they're logged in.<br>
-<br>
-<br>
-*.* @finlandia<br>
-<br>
-This rule would redirect all messages to a remote host called
-finlandia. This is useful especially in a cluster of machines where all
-syslog messages will be stored on only one machine.<br>
-<br>
-In the format shown above, UDP is used for transmitting the message.
-The destination port is set to the default auf 514. Rsyslog is also
-capable of using much more secure and reliable TCP sessions for message
-forwarding. Also, the destination port can be specified. To select TCP,
-simply add one additional @ in front of the host name (that is, @host
-is UPD, @@host is TCP). For example:<br>
-<br>
-<br>
-*.* @@finlandia<br>
-<br>
-To specify the destination port on the remote machine, use a colon
-followed by the port number after the machine name. The following
-forwards to port 1514 on finlandia:<br>
-<br>
-<br>
-*.* @@finlandia:1514<br>
-<br>
-This syntax works both with TCP and UDP based syslog. However, you will
-probably primarily need it for TCP, as there is no well-accepted port
-for this transport (it is non-standard). For UDP, you can usually stick
-with the default auf 514, but might want to modify it for security rea-<br>
-sons. If you would like to do that, it's quite easy:<br>
-<br>
-<br>
-*.* @finlandia:1514<br>
-<br>
-<br>
-<br>
-*.* &gt;dbhost,dbname,dbuser,dbpassword;dbtemplate<br>
-<br>
-This rule writes all message to the database "dbname" hosted on
-"dbhost". The login is done with user "dbuser" and password
-"dbpassword". The actual table that is updated is specified within the
-template (which contains the insert statement). The template is called
-"dbtemplate" in this case.</p>
-<p>:msg,contains,"error" @errorServer</p>
-<p>This rule forwards all messages that contain the word "error"
-in the msg part to the server "errorServer". Forwarding is via UDP.
-Please note the colon in fron</p>
-<h2>CONFIGURATION FILE SYNTAX DIFFERENCES</h2>
+<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
@@ -1279,4 +68,15 @@ 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>
-</body></html>
+
+<p>[<a href="rsyslog_conf.html">back to top</a>]
+[<a href="manual.html">manual index</a>]
+[<a href="http://www.rsyslog.com/">rsyslog site</a>]</p>
+<p><font size="2">This documentation is part of the
+<a href="http://www.rsyslog.com/">rsyslog</a> project.<br>
+Copyright &copy; 2008,2009 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 3 or higher.</font></p>
+</body>
+</html>
+>
diff --git a/doc/rsyslog_conf_actions.html b/doc/rsyslog_conf_actions.html
new file mode 100644
index 00000000..6020dd88
--- /dev/null
+++ b/doc/rsyslog_conf_actions.html
@@ -0,0 +1,339 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><title>Actions - rsyslog.conf</title></head>
+<body>
+<p>This is a part of the rsyslog.conf documentation.</p>
+<a href="rsyslog_conf.html">back</a>
+<h2>Actions</h2>
+<p>The action field of a rule describes what to do with the
+message. In general, message content is written to a kind of "logfile".
+But also other actions might be done, like writing to a database table
+or forwarding to another host.<br>
+<br>
+Templates can be used with all actions. If used, the specified template
+is used to generate the message content (instead of the default
+template). To specify a template, write a semicolon after the action
+value immediately followed by the template name.<br>
+<br>
+Beware: templates MUST be defined BEFORE they are used. It is OK to
+define some templates, then use them in selector lines, define more
+templates and use use them in the following selector lines. But it is
+NOT permitted to use a template in a selector line that is above its
+definition. If you do this, the action will be ignored.</p>
+<p><b>You can have multiple actions for a single selector </b>&nbsp;(or
+more precisely a single filter of such a selector line). Each action
+must be on its own line and the line must start with an ampersand
+('&amp;') character and have no filters. An example would be</p>
+<p><code><b>*.=crit rger<br>
+&amp; root<br>
+&amp; /var/log/critmsgs</b></code></p>
+<p>These three lines send critical messages to the user rger and
+root and also store them in /var/log/critmsgs. <b>Using multiple
+actions per selector is</b> convenient and also <b>offers
+a performance benefit</b>. As the filter needs to be evaluated
+only once, there is less computation required to process the directive
+compared to the otherwise-equal config directives below:</p>
+<p><code><b>*.=crit rger<br>
+*.=crit root<br>
+*.=crit /var/log/critmsgs</b></code></p>
+<p>&nbsp;</p>
+<h3>Regular File</h3>
+<p>Typically messages are logged to real files. The file usually is
+specified by full pathname, beginning with a slash "/".
+Starting with version 4.6.2 and 5.4.1 (previous v5 version do NOT support this)
+relative file names can also be specified. To do so, these must begin with a
+dot. For example, use "./file-in-current-dir.log" to specify a file in the
+current directory. Please note that rsyslogd usually changes its working
+directory to the root, so relative file names must be tested with care (they
+were introduced primarily as a debugging vehicle, but may have useful other applications
+as well).<br>
+<br>
+<br>
+You may prefix each entry with the minus "-'' sign to omit syncing the
+file after every logging. Note that you might lose information if the
+system crashes right behind a write attempt. Nevertheless this might
+give you back some performance, especially if you run programs that use
+logging in a very verbose manner.</p>
+<p>If your system is connected to a reliable UPS and you receive
+lots of log data (e.g. firewall logs), it might be a very good idea to
+turn of
+syncing by specifying the "-" in front of the file name. </p>
+<p><b>The filename can be either static </b>(always
+the same) or <b>dynamic</b> (different based on message
+received). The later is useful if you would automatically split
+messages into different files based on some message criteria. For
+example, dynamic file name selectors allow you to split messages into
+different files based on the host that sent them. With dynamic file
+names, everything is automatic and you do not need any filters. </p>
+<p>It works via the template system. First, you define a template
+for the file name. An example can be seen above in the description of
+template. We will use the "DynFile" template defined there. Dynamic
+filenames are indicated by specifying a questions mark "?" instead of a
+slash, followed by the template name. Thus, the selector line for our
+dynamic file name would look as follows:</p>
+<blockquote>
+<code>*.* ?DynFile</code>
+</blockquote>
+<p>That's all you need to do. Rsyslog will now automatically
+generate file names for you and store the right messages into the right
+files. Please note that the minus sign also works with dynamic file
+name selectors. Thus, to avoid syncing, you may use</p>
+<blockquote>
+<code>*.* -?DynFile</code></blockquote>
+<p>And of course you can use templates to specify the output
+format:</p>
+<blockquote>
+<code>*.* ?DynFile;MyTemplate</code></blockquote>
+<p><b>A word of caution:</b> rsyslog creates files as
+needed. So if a new host is using your syslog server, rsyslog will
+automatically create a new file for it.</p>
+<p><b>Creating directories is also supported</b>. For
+example you can use the hostname as directory and the program name as
+file name:</p>
+<blockquote>
+<code>$template DynFile,"/var/log/%HOSTNAME%/%programname%.log"</code></blockquote>
+<h3>Named Pipes</h3>
+<p>This version of rsyslogd(8) has support for logging output to
+named pipes (fifos). A fifo or named pipe can be used as a destination
+for log messages by prepending a pipe symbol ("|'') to the name of the
+file. This is handy for debugging. Note that the fifo must be created
+with the mkfifo(1) command before rsyslogd(8) is started.</p>
+<h3>Terminal and Console</h3>
+<p>If the file you specified is a tty, special tty-handling is
+done, same with /dev/console.</p>
+<h3>Remote Machine</h3>
+<p>Rsyslogd provides full remote logging, i.e. is able to send
+messages to a remote host running rsyslogd(8) and to receive messages
+from remote hosts. Using this feature you're able to control all syslog
+messages on one host, if all other machines will log remotely to that.
+This tears down administration needs.</p>
+<p>To forward messages to another host, prepend the hostname with
+the at sign ("@"). A single at sign means that messages will
+be forwarded via UDP protocol (the standard for syslog). If you prepend
+two at signs ("@@"), the messages will be transmitted via TCP. Please
+note that plain TCP based syslog is not officially standardized, but
+most major syslogds support it (e.g. syslog-ng or
+<a href="http://www.winsyslog.com/">WinSyslog</a>). The
+forwarding action indicator (at-sign) can be followed by one or more
+options. If they are given, they must be immediately (without a space)
+following the final at sign and be enclosed in parenthesis. The
+individual options must be separated by commas. The following options
+are right now defined:</p>
+<table id="table2" border="1" width="100%">
+<tbody>
+<tr>
+<td>
+<p align="center"><b>z&lt;number&gt;</b></p>
+</td>
+<td>Enable zlib-compression for the message. The
+&lt;number&gt; is the compression level. It can be 1 (lowest
+gain, lowest CPU overhead) to 9 (maximum compression, highest CPU
+overhead). The level can also be 0, which means "no compression". If
+given, the "z" option is ignored. So this does not make an awful lot of
+sense. There is hardly a difference between level 1 and 9 for typical
+syslog messages. You can expect a compression gain between 0% and 30%
+for typical messages. Very chatty messages may compress up to 50%, but
+this is seldom seen with typically traffic. Please note that rsyslogd
+checks the compression gain. Messages with 60 bytes or less will never
+be compressed. This is because compression gain is pretty unlikely and
+we prefer to save CPU cycles. Messages over that size are always
+compressed. However, it is checked if there is a gain in compression
+and only if there is, the compressed message is transmitted. Otherwise,
+the uncompressed messages is transmitted. This saves the receiver CPU
+cycles for decompression. It also prevents small message to actually
+become larger in compressed form.
+<p><b>Please note that when a TCP transport is used,
+compression will also turn on syslog-transport-tls framing. See the "o"
+option for important information on the implications.</b></p>
+<p>Compressed messages are automatically detected and
+decompressed by the receiver. There is nothing that needs to be
+configured on the receiver side.</p>
+</td>
+</tr>
+<tr>
+<td>
+<p align="center"><b>o</b></p>
+</td>
+<td><b>This option is experimental. Use at your own
+risk and only if you know why you need it! If in doubt, do NOT turn it
+on.</b>
+<p>This option is only valid for plain TCP based
+transports. It selects a different framing based on IETF internet draft
+syslog-transport-tls-06. This framing offers some benefits over
+traditional LF-based framing. However, the standardization effort is
+not yet complete. There may be changes in upcoming versions of this
+standard. Rsyslog will be kept in line with the standard. There is some
+chance that upcoming changes will be incompatible to the current
+specification. In this case, all systems using -transport-tls framing
+must be upgraded. There will be no effort made to retain compatibility
+between different versions of rsyslog. The primary reason for that is
+that it seems technically impossible to provide compatibility between
+some of those changes. So you should take this note very serious. It is
+not something we do not *like* to do (and may change our mind if enough
+people beg...), it is something we most probably *can not* do for
+technical reasons (aka: you can beg as much as you like, it won't
+change anything...).</p>
+<p>The most important implication is that compressed syslog
+messages via TCP must be considered with care. Unfortunately, it is
+technically impossible to transfer compressed records over traditional
+syslog plain tcp transports, so you are left with two evil choices...</p>
+</td>
+</tr>
+</tbody>
+</table>
+<p><br>
+The hostname may be followed by a colon and the destination port.</p>
+<p>The following is an example selector line with forwarding:</p>
+<p>*.*&nbsp;&nbsp;&nbsp; @@(o,z9)192.168.0.1:1470</p>
+<p>In this example, messages are forwarded via plain TCP with
+experimental framing and maximum compression to the host 192.168.0.1 at
+port 1470.</p>
+<p>*.* @192.168.0.1</p>
+<p>In the example above, messages are forwarded via UDP to the
+machine 192.168.0.1, the destination port defaults to 514. Messages
+will not be compressed.</p>
+<p>Note that IPv6 addresses contain colons. So if an IPv6 address is specified
+in the hostname part, rsyslogd could not detect where the IP address ends
+and where the port starts. There is a syntax extension to support this:
+put squary brackets around the address (e.g. "[2001::1]"). Square
+brackets also work with real host names and IPv4 addresses, too.
+<p>A valid sample to send messages to the IPv6 host 2001::1 at port 515
+is as follows:
+<p>*.* @[2001::1]:515
+<p>This works with TCP, too.
+<p><b>Note to sysklogd users:</b> sysklogd does <b>not</b>
+support RFC 3164 format, which is the default forwarding template in
+rsyslog. As such, you will experience duplicate hostnames if rsyslog is
+the sender and sysklogd is the receiver. The fix is simple: you need to
+use a different template. Use that one:</p>
+<p class="MsoPlainText">$template
+sysklogd,"&lt;%PRI%&gt;%TIMESTAMP% %syslogtag%%msg%\""<br>
+*.* @192.168.0.1;sysklogd</p>
+<h3>List of Users</h3>
+<p>Usually critical messages are also directed to "root'' on
+that machine. You can specify a list of users that shall get the
+message by simply writing the login. You may specify more than one user
+by separating them with commas (",''). If they're logged in they get
+the message. Don't think a mail would be sent, that might be too late.</p>
+<h3>Everyone logged on</h3>
+<p>Emergency messages often go to all users currently online to
+notify them that something strange is happening with the system. To
+specify this wall(1)-feature use an asterisk ("*'').</p>
+<h3>Call Plugin</h3>
+<p>This is a generic way to call an output plugin. The plugin
+must support this functionality. Actual parameters depend on the
+module, so see the module's doc on what to supply. The general syntax
+is as follows:</p>
+<p>:modname:params;template</p>
+<p>Currently, the ommysql database output module supports this
+syntax (in addtion to the "&gt;" syntax it traditionally
+supported). For ommysql, the module name is "ommysql" and the params
+are the traditional ones. The ;template part is not module specific, it
+is generic rsyslog functionality available to all modules.</p>
+<p>As an example, the ommysql module may be called as follows:</p>
+<p>:ommysql:dbhost,dbname,dbuser,dbpassword;dbtemplate</p>
+<p>For details, please see the "Database Table" section of this
+documentation.</p>
+<p>Note: as of this writing, the ":modname:" part is hardcoded
+into the module. So the name to use is not necessarily the name the
+module's plugin file is called.</p>
+<h3>Database Table</h3>
+<p>This allows logging of the message to a database table.
+Currently, only MySQL databases are supported. However, other database
+drivers will most probably be developed as plugins. By default, a <a href="http://www.monitorware.com/">MonitorWare</a>-compatible
+schema is required for this to work. You can create that schema with
+the createDB.SQL file that came with the rsyslog package. You can also<br>
+use any other schema of your liking - you just need to define a proper
+template and assign this template to the action.<br>
+<br>
+The database writer is called by specifying a greater-then sign
+("&gt;") in front of the database connect information. Immediately
+after that<br>
+sign the database host name must be given, a comma, the database name,
+another comma, the database user, a comma and then the user's password.
+If a specific template is to be used, a semicolon followed by the
+template name can follow the connect information. This is as follows:<br>
+<br>
+&gt;dbhost,dbname,dbuser,dbpassword;dbtemplate</p>
+<p><b>Important: to use the database functionality, the
+MySQL output module must be loaded in the config file</b> BEFORE
+the first database table action is used. This is done by placing the</p>
+<p><code><b>$ModLoad ommysql</b></code></p>
+<p>directive some place above the first use of the database write
+(we recommend doing at the the beginning of the config file).</p>
+<h3>Discard</h3>
+<p>If the discard action is carried out, the received message is
+immediately discarded. No further processing of it occurs. Discard has
+primarily been added to filter out messages before carrying on any
+further processing. For obvious reasons, the results of "discard" are
+depending on where in the configuration file it is being used. Please
+note that once a message has been discarded there is no way to retrieve
+it in later configuration file lines.</p>
+<p>Discard can be highly effective if you want to filter out some
+annoying messages that otherwise would fill your log files. To do that,
+place the discard actions early in your log files. This often plays
+well with property-based filters, giving you great freedom in
+specifying what you do not want.</p>
+<p>Discard is just the single tilde character with no further
+parameters:</p>
+<p>~</p>
+<p>For example,</p>
+<p>*.*&nbsp;&nbsp; ~</p>
+<p>discards everything (ok, you can achive the same by not
+running rsyslogd at all...).</p>
+<h3>Output Channel</h3>
+<p>Binds an output channel definition (see there for details) to
+this action. Output channel actions must start with a $-sign, e.g. if
+you would like to bind your output channel definition "mychannel" to
+the action, use "$mychannel". Output channels support template
+definitions like all all other actions.</p>
+<h3>Shell Execute</h3>
+<p>This executes a program in a subshell. The program is passed
+the template-generated message as the only command line parameter.
+Rsyslog waits until the program terminates and only then continues to
+run.</p>
+<p>^program-to-execute;template</p>
+<p>The program-to-execute can be any valid executable. It
+receives the template string as a single parameter (argv[1]).</p>
+<p><b>WARNING:</b> The Shell Execute action was added
+to serve an urgent need. While it is considered reasonable save when
+used with some thinking, its implications must be considered. The
+current implementation uses a system() call to execute the command.
+This is not the best way to do it (and will hopefully changed in
+further releases). Also, proper escaping of special characters is done
+to prevent command injection. However, attackers always find smart ways
+to circumvent escaping, so we can not say if the escaping applied will
+really safe you from all hassles. Lastly, rsyslog will wait until the
+shell command terminates. Thus, a program error in it (e.g. an infinite
+loop) can actually disable rsyslog. Even without that, during the
+programs run-time no messages are processed by rsyslog. As the IP
+stacks buffers are quickly overflowed, this bears an increased risk of
+message loss. You must be aware of these implications. Even though they
+are severe, there are several cases where the "shell execute" action is
+very useful. This is the reason why we have included it in its current
+form. To mitigate its risks, always a) test your program thoroughly, b)
+make sure its runtime is as short as possible (if it requires a longer
+run-time, you might want to spawn your own sub-shell asynchronously),
+c) apply proper firewalling so that only known senders can send syslog
+messages to rsyslog. Point c) is especially important: if rsyslog is
+accepting message from any hosts, chances are much higher that an
+attacker might try to exploit the "shell execute" action.</p>
+<h3>Template Name</h3>
+<p>Every ACTION can be followed by a template name. If so, that
+template is used for message formatting. If no name is given, a
+hard-coded default template is used for the action. There can only be
+one template name for each given action. The default template is
+specific to each action. For a description of what a template is and
+what you can do with it, see "TEMPLATES" at the top of this document.</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 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 2 or higher.</font></p>
+</body>
+</html>
+
diff --git a/doc/rsyslog_conf_examples.html b/doc/rsyslog_conf_examples.html
new file mode 100644
index 00000000..b46460e5
--- /dev/null
+++ b/doc/rsyslog_conf_examples.html
@@ -0,0 +1,209 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><title>Examples - rsyslog.conf</title></head>
+<body>
+<p>This is a part of the rsyslog.conf documentation.</p>
+<a href="rsyslog_conf.html">back</a>
+<h2>Examples</h2>
+<p>Below are example for templates and selector lines. I hope
+they are self-explanatory. If not, please see
+www.monitorware.com/rsyslog/ for advise.</p>
+<h3>TEMPLATES</h3>
+<p>Please note that the samples are split across multiple lines.
+A template MUST NOT actually be split across multiple lines.<br>
+<br>
+A template that resembles traditional syslogd file output:<br>
+$template TraditionalFormat,"%timegenerated% %HOSTNAME%<br>
+%syslogtag%%msg:::drop-last-lf%\n"<br>
+<br>
+A template that tells you a little more about the message:<br>
+$template
+precise,"%syslogpriority%,%syslogfacility%,%timegenerated%,%HOSTNAME%,<br>
+%syslogtag%,%msg%\n"<br>
+<br>
+A template for RFC 3164 format:<br>
+$template RFC3164fmt,"&lt;%PRI%&gt;%TIMESTAMP% %HOSTNAME%
+%syslogtag%%msg%"<br>
+<br>
+A template for the format traditonally used for user messages:<br>
+$template usermsg," XXXX%syslogtag%%msg%\n\r"<br>
+<br>
+And a template with the traditonal wall-message format:<br>
+$template wallmsg,"\r\n\7Message from syslogd@%HOSTNAME% at
+%timegenerated%<br>
+<br>
+A template that can be used for the database write (please note the SQL<br>
+template option)<br>
+$template MySQLInsert,"insert iut, message, receivedat values<br>
+('%iut%', '%msg:::UPPERCASE%', '%timegenerated:::date-mysql%')<br>
+into systemevents\r\n", SQL<br>
+<br>
+The following template emulates <a href="http://www.winsyslog.com/en/">WinSyslog</a>
+format (it's an <a href="http://www.adiscon.com/en/">Adiscon</a>
+format, you do not feel bad if you don't know it ;)). It's interesting
+to see how it takes different parts out of the date stamps. What
+happens is that the date stamp is split into the actual date and time
+and the these two are combined with just a comma in between them.<br>
+<br>
+$template WinSyslogFmt,"%HOSTNAME%,%timegenerated:1:10:date-rfc3339%,<br>
+%timegenerated:12:19:date-rfc3339%,%timegenerated:1:10:date-rfc3339%,<br>
+%timegenerated:12:19:date-rfc3339%,%syslogfacility%,%syslogpriority%,<br>
+%syslogtag%%msg%\n"</p>
+<h3>SELECTOR LINES</h3>
+<p># Store critical stuff in critical<br>
+#<br>
+*.=crit;kern.none /var/adm/critical<br>
+<br>
+This will store all messages with the priority crit in the file
+/var/adm/critical, except for any kernel message.<br>
+<br>
+<br>
+# Kernel messages are first, stored in the kernel<br>
+# file, critical messages and higher ones also go<br>
+# to another host and to the console. Messages to<br>
+# the host finlandia are forwarded in RFC 3164<br>
+# format (using the template defined above).<br>
+#<br>
+kern.* /var/adm/kernel<br>
+kern.crit @finlandia;RFC3164fmt<br>
+kern.crit /dev/console<br>
+kern.info;kern.!err /var/adm/kernel-info<br>
+<br>
+The first rule direct any message that has the kernel facility to the
+file /var/adm/kernel.<br>
+<br>
+The second statement directs all kernel messages of the priority crit
+and higher to the remote host finlandia. This is useful, because if the
+host crashes and the disks get irreparable errors you might not be able
+to read the stored messages. If they're on a remote host, too, you
+still can try to find out the reason for the crash.<br>
+<br>
+The third rule directs these messages to the actual console, so the
+person who works on the machine will get them, too.<br>
+<br>
+The fourth line tells rsyslogd to save all kernel messages that come
+with priorities from info up to warning in the file
+/var/adm/kernel-info. Everything from err and higher is excluded.<br>
+<br>
+<br>
+# The tcp wrapper loggs with mail.info, we display<br>
+# all the connections on tty12<br>
+#<br>
+mail.=info /dev/tty12<br>
+<br>
+This directs all messages that uses mail.info (in source LOG_MAIL |
+LOG_INFO) to /dev/tty12, the 12th console. For example the tcpwrapper
+tcpd(8) uses this as it's default.<br>
+<br>
+<br>
+# Store all mail concerning stuff in a file<br>
+#<br>
+mail.*;mail.!=info /var/adm/mail<br>
+<br>
+This pattern matches all messages that come with the mail facility,
+except for the info priority. These will be stored in the file
+/var/adm/mail.<br>
+<br>
+<br>
+# Log all mail.info and news.info messages to info<br>
+#<br>
+mail,news.=info /var/adm/info<br>
+<br>
+This will extract all messages that come either with mail.info or with
+news.info and store them in the file /var/adm/info.<br>
+<br>
+<br>
+# Log info and notice messages to messages file<br>
+#<br>
+*.=info;*.=notice;\<br>
+mail.none /var/log/messages<br>
+<br>
+This lets rsyslogd log all messages that come with either the info or
+the notice facility into the file /var/log/messages, except for all<br>
+messages that use the mail facility.<br>
+<br>
+<br>
+# Log info messages to messages file<br>
+#<br>
+*.=info;\<br>
+mail,news.none /var/log/messages<br>
+<br>
+This statement causes rsyslogd to log all messages that come with the
+info priority to the file /var/log/messages. But any message coming
+either with the mail or the news facility will not be stored.<br>
+<br>
+<br>
+# Emergency messages will be displayed using wall<br>
+#<br>
+*.=emerg *<br>
+<br>
+This rule tells rsyslogd to write all emergency messages to all
+currently logged in users. This is the wall action.<br>
+<br>
+<br>
+# Messages of the priority alert will be directed<br>
+# to the operator<br>
+#<br>
+*.alert root,rgerhards<br>
+<br>
+This rule directs all messages with a priority of alert or higher to
+the terminals of the operator, i.e. of the users "root'' and
+"rgerhards'' if they're logged in.<br>
+<br>
+<br>
+*.* @finlandia<br>
+<br>
+This rule would redirect all messages to a remote host called
+finlandia. This is useful especially in a cluster of machines where all
+syslog messages will be stored on only one machine.<br>
+<br>
+In the format shown above, UDP is used for transmitting the message.
+The destination port is set to the default auf 514. Rsyslog is also
+capable of using much more secure and reliable TCP sessions for message
+forwarding. Also, the destination port can be specified. To select TCP,
+simply add one additional @ in front of the host name (that is, @host
+is UPD, @@host is TCP). For example:<br>
+<br>
+<br>
+*.* @@finlandia<br>
+<br>
+To specify the destination port on the remote machine, use a colon
+followed by the port number after the machine name. The following
+forwards to port 1514 on finlandia:<br>
+<br>
+<br>
+*.* @@finlandia:1514<br>
+<br>
+This syntax works both with TCP and UDP based syslog. However, you will
+probably primarily need it for TCP, as there is no well-accepted port
+for this transport (it is non-standard). For UDP, you can usually stick
+with the default auf 514, but might want to modify it for security rea-<br>
+sons. If you would like to do that, it's quite easy:<br>
+<br>
+<br>
+*.* @finlandia:1514<br>
+<br>
+<br>
+<br>
+*.* &gt;dbhost,dbname,dbuser,dbpassword;dbtemplate<br>
+<br>
+This rule writes all message to the database "dbname" hosted on
+"dbhost". The login is done with user "dbuser" and password
+"dbpassword". The actual table that is updated is specified within the
+template (which contains the insert statement). The template is called
+"dbtemplate" in this case.</p>
+<p>:msg,contains,"error" @errorServer</p>
+<p>This rule forwards all messages that contain the word "error"
+in the msg part to the server "errorServer". Forwarding is via UDP.
+Please note the colon in fron</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 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 2 or higher.</font></p>
+</body>
+</html>
+
diff --git a/doc/rsyslog_conf_filter.html b/doc/rsyslog_conf_filter.html
new file mode 100644
index 00000000..34839616
--- /dev/null
+++ b/doc/rsyslog_conf_filter.html
@@ -0,0 +1,284 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><title>Filter Conditions - rsyslog.conf</title></head>
+<body>
+<p>This is a part of the rsyslog.conf documentation.</p>
+<a href="rsyslog_conf.html">back</a>
+<h2>Filter Conditions</h2>
+<p>Rsyslog offers four different types "filter conditions":</p>
+<ul>
+<li>BSD-style blocks</li>
+<li>"traditional" severity and facility based selectors</li>
+<li>property-based filters</li>
+<li>expression-based filters</li>
+</ul>
+<h3>Blocks</h3>
+<p>Rsyslogd supports BSD-style blocks inside rsyslog.conf. Each
+block of lines is separated from the previous block by a program or
+hostname specification. A block will only log messages corresponding to
+the most recent program and hostname specifications given. Thus, a
+block which selects &#8216;ppp&#8217; as the program, directly followed by a block
+that selects messages from the hostname &#8216;dialhost&#8217;, then the second
+block will only log messages from the ppp program on dialhost.
+</p>
+<p>A program specification is a line beginning with &#8216;!prog&#8217; and
+the following blocks will be associated with calls to syslog from that
+specific program. A program specification for &#8216;foo&#8217; will also match any
+message logged by the kernel with the prefix &#8216;foo: &#8217;. Alternatively, a
+program specification &#8216;-foo&#8217; causes the following blocks to be applied
+to messages from any program but the one specified. A hostname
+specification of the form &#8216;+hostname&#8217; and the following blocks will be
+applied to messages received from the specified hostname.
+Alternatively, a hostname specification &#8216;-hostname&#8217; causes the
+following blocks to be applied to messages from any host but the one
+specified. If the hostname is given as &#8216;@&#8217;, the local hostname will be
+used. (NOT YET IMPLEMENTED) A program or hostname specification may be
+reset by giving the program or hostname as &#8216;*&#8217;.</p>
+<p>Please note that the "#!prog", "#+hostname" and "#-hostname"
+syntax available in BSD syslogd is not supported by rsyslogd. By
+default, no hostname or program is set.</p>
+<h3>Selectors</h3>
+<p><b>Selectors are the traditional way of filtering syslog
+messages.</b> They have been kept in rsyslog with their original
+syntax, because it is well-known, highly effective and also needed for
+compatibility with stock syslogd configuration files. If you just need
+to filter based on priority and facility, you should do this with
+selector lines. They are <b>not</b> second-class citizens
+in rsyslog and offer the best performance for this job.</p>
+<p>The selector field itself again consists of two parts, a
+facility and a priority, separated by a period (".''). Both parts are
+case insensitive and can also be specified as decimal numbers, but
+don't do that, you have been warned. Both facilities and priorities are
+described in syslog(3). The names mentioned below correspond to the
+similar LOG_-values in /usr/include/syslog.h.<br>
+<br>
+The facility is one of the following keywords: auth, authpriv, cron,
+daemon, kern, lpr, mail, mark, news, security (same as auth), syslog,
+user, uucp and local0 through local7. The keyword security should not
+be used anymore and mark is only for internal use and therefore should
+not be used in applications. Anyway, you may want to specify and
+redirect these messages here. The facility specifies the subsystem that
+produced the message, i.e. all mail programs log with the mail facility
+(LOG_MAIL) if they log using syslog.<br>
+<br>
+The priority is one of the following keywords, in ascending order:
+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. The
+priority defines the severity of the message.<br>
+<br>
+The behavior of the original BSD syslogd is that all messages of the
+specified priority and higher are logged according to the given action.
+Rsyslogd behaves the same, but has some extensions.<br>
+<br>
+In addition to the above mentioned names the rsyslogd(8) understands
+the following extensions: An asterisk ("*'') stands for all facilities
+or all priorities, depending on where it is used (before or after the
+period). The keyword none stands for no priority of the given facility.<br>
+<br>
+You can specify multiple facilities with the same priority pattern in
+one statement using the comma (",'') operator. You may specify as much
+facilities as you want. Remember that only the facility part from such
+a statement is taken, a priority part would be skipped.</p>
+<p>Multiple selectors may be specified for a single action using
+the semicolon (";'') separator. Remember that each selector in the
+selector field is capable to overwrite the preceding ones. Using this
+behavior you can exclude some priorities from the pattern.</p>
+<p>Rsyslogd has a syntax extension to the original BSD source,
+that makes its use more intuitively. You may precede every priority
+with an equals sign ("='') to specify only this single priority and
+not any of the above. You may also (both is valid, too) precede the
+priority with an exclamation mark ("!'') to ignore all that
+priorities, either exact this one or this and any higher priority. If
+you use both extensions than the exclamation mark must occur before the
+equals sign, just use it intuitively.</p>
+<h3>Property-Based Filters</h3>
+<p>Property-based filters are unique to rsyslogd. They allow to
+filter on any property, like HOSTNAME, syslogtag and msg. A list of all
+currently-supported properties can be found in the <a href="property_replacer.html">property replacer documentation</a>
+(but keep in mind that only the properties, not the replacer is
+supported). With this filter, each properties can be checked against a
+specified value, using a specified compare operation.</p>
+<p>A property-based filter must start with a colon in column 0.
+This tells rsyslogd that it is the new filter type. The colon must be
+followed by the property name, a comma, the name of the compare
+operation to carry out, another comma and then the value to compare
+against. This value must be quoted. There can be spaces and tabs
+between the commas. Property names and compare operations are
+case-sensitive, so "msg" works, while "MSG" is an invalid property
+name. In brief, the syntax is as follows:</p>
+<p><code><b>:property, [!]compare-operation, "value"</b></code></p>
+<p>The following <b>compare-operations</b> are
+currently supported:</p>
+<table id="table1" border="1" width="100%">
+<tbody>
+<tr>
+<td>contains</td>
+<td>Checks if the string provided in value is contained in
+the property. There must be an exact match, wildcards are not supported.</td>
+</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
+difference to contains is that contains searches for the value anywhere
+inside the property value, whereas all characters must be identical for
+isequal. As such, isequal is most useful for fields like syslogtag or
+FROMHOST, where you probably know the exact contents.</td>
+</tr>
+<tr>
+<td>startswith</td>
+<td>Checks if the value is found exactly at the beginning
+of the property value. For example, if you search for "val" with
+<p><code><b>:msg, startswith, "val"</b></code></p>
+<p>it will be a match if msg contains "values are in this
+message" but it won't match if the msg contains "There are values in
+this message" (in the later case, contains would match). Please note
+that "startswith" is by far faster than regular expressions. So even
+once they are implemented, it can make very much sense
+(performance-wise) to use "startswith".</p>
+</td>
+</tr>
+<tr>
+<td>regex</td>
+<td>Compares the property against the provided POSIX
+BRE regular
+expression.</td>
+</tr>
+<tr>
+<td>ereregex</td>
+<td>Compares the property against the provided POSIX
+ERE regular
+expression.</td>
+</tr>
+</tbody>
+</table>
+<p>You can use the bang-character (!) immediately in front of a
+compare-operation, the outcome of this operation is negated. For
+example, if msg contains "This is an informative message", the
+following sample would not match:</p>
+<p><code><b>:msg, contains, "error"</b></code></p>
+<p>but this one matches:</p>
+<p><code><b>:msg, !contains, "error"</b></code></p>
+<p>Using negation can be useful if you would like to do some
+generic processing but exclude some specific events. You can use the
+discard action in conjunction with that. A sample would be:</p>
+<p><code><b>*.*
+/var/log/allmsgs-including-informational.log<br>
+:msg, contains, "informational"&nbsp; <font color="#ff0000" size="4">~</font>
+<br>
+*.* /var/log/allmsgs-but-informational.log</b></code></p>
+<p>Do not overlook the red tilde in line 2! In this sample, all
+messages are written to the file allmsgs-including-informational.log.
+Then, all messages containing the string "informational" are discarded.
+That means the config file lines below the "discard line" (number 2 in
+our sample) will not be applied to this message. Then, all remaining
+lines will also be written to the file allmsgs-but-informational.log.</p>
+<p><b>Value</b> is a quoted string. It supports some
+escape sequences:</p>
+<p>\" - the quote character (e.g. "String with \"Quotes\"")<br>
+\\ - the backslash character (e.g. "C:\\tmp")</p>
+<p>Escape sequences always start with a backslash. Additional
+escape sequences might be added in the future. Backslash characters <b>must</b>
+be escaped. Any other sequence then those outlined above is invalid and
+may lead to unpredictable results.</p>
+<p>Probably, "msg" is the most prominent use case of property
+based filters. It is the actual message text. If you would like to
+filter based on some message content (e.g. the presence of a specific
+code), this can be done easily by:</p>
+<p><code><b>:msg, contains, "ID-4711"</b></code></p>
+<p>This filter will match when the message contains the string
+"ID-4711". Please note that the comparison is case-sensitive, so it
+would not match if "id-4711" would be contained in the message.</p>
+<p><code><b>:msg, regex, "fatal .* error"</b></code></p>
+<p>This filter uses a POSIX regular expression. It matches when
+the
+string contains the words "fatal" and "error" with anything in between
+(e.g. "fatal net error" and "fatal lib error" but not "fatal error" as
+two spaces are required by the regular expression!).</p>
+<p>Getting property-based filters right can sometimes be
+challenging. In order to help you do it with as minimal effort as
+possible, rsyslogd spits out debug information for all property-based
+filters during their evaluation. To enable this, run rsyslogd in
+foreground and specify the "-d" option.</p>
+<p>Boolean operations inside property based filters (like
+'message contains "ID17" or message contains "ID18"') are currently not
+supported (except for "not" as outlined above). Please note that while
+it is possible to query facility and severity via property-based
+filters, it is far more advisable to use classic selectors (see above)
+for those cases.</p>
+<h3>Expression-Based Filters</h3>
+Expression based filters allow
+filtering on arbitrary complex expressions, which can include boolean,
+arithmetic and string operations. Expression filters will evolve into a
+full configuration scripting language. Unfortunately, their syntax will
+slightly change during that process. So if you use them now, you need
+to be prepared to change your configuration files some time later.
+However, we try to implement the scripting facility as soon as possible
+(also in respect to stage work needed). So the window of exposure is
+probably not too long.<br>
+<br>
+Expression based filters are indicated by the keyword "if" in column 1
+of a new line. They have this format:<br>
+<br>
+if expr then action-part-of-selector-line<br>
+<br>
+"If" and "then" are fixed keywords that mus be present. "expr" is a
+(potentially quite complex) expression. So the <a href="expression.html">expression documentation</a> for
+details. "action-part-of-selector-line" is an action, just as you know
+it (e.g. "/var/log/logfile" to write to that file).<br>
+<br>
+A few quick samples:<br>
+<br>
+<code>
+*.* /var/log/file1 # the traditional way<br>
+if $msg contains 'error' then /var/log/errlog # the expression-based way<br>
+</code>
+<br>
+Right now, you need to specify numerical values if you would like to
+check for facilities and severity. These can be found in <a href="http://www.ietf.org/rfc/rfc3164.txt">RFC 3164</a>.
+If you don't like that, you can of course also use the textual property
+- just be sure to use the right one. As expression support is enhanced,
+this will change. For example, if you would like to filter on message
+that have facility local0, start with "DEVNAME" and have either
+"error1" or "error0" in their message content, you could use the
+following filter:<br>
+<br>
+<code>
+if $syslogfacility-text == 'local0' and $msg
+startswith 'DEVNAME' and ($msg contains 'error1' or $msg contains
+'error0') then /var/log/somelog<br>
+</code>
+<br>
+Please note that the above <span style="font-weight: bold;">must
+all be on one line</span>! And if you would like to store all
+messages except those that contain "error1" or "error0", you just need
+to add a "not":<br>
+<br>
+<code>
+if $syslogfacility-text == 'local0' and $msg
+startswith 'DEVNAME' and <span style="font-weight: bold;">not</span>
+($msg contains 'error1' or $msg contains
+'error0') then /var/log/somelog<br>
+</code>
+<br>
+If you would like to do case-insensitive comparisons, use
+"contains_i" instead of "contains" and "startswith_i" instead of
+"startswith".<br>
+<br>
+Note that regular expressions are currently NOT
+supported in expression-based filters. These will be added later when
+function support is added to the expression engine (the reason is that
+regular expressions will be a separate loadable module, which requires
+some more prequisites before it can be implemented).<br>
+
+<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 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 2 or higher.</font></p>
+</body>
+</html>
+
diff --git a/doc/rsyslog_conf_global.html b/doc/rsyslog_conf_global.html
new file mode 100644
index 00000000..9e85ae1e
--- /dev/null
+++ b/doc/rsyslog_conf_global.html
@@ -0,0 +1,295 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><title>Configuration Directives - rsyslog.conf</title></head>
+<body>
+<p>This is a part of the rsyslog.conf documentation.</p>
+<a href="rsyslog_conf.html">back</a>
+<h2>Configuration Directives</h2>
+<p>All configuration directives need to be specified on a line by their
+own and must start with a dollar-sign. Note that those starting with
+the word "Action" modify the next action and should be specified
+in front of it.
+<p>Here is a list in alphabetical order. Follow links for a description.</p>
+<p>Not all directives have an in-depth description right now.
+Default values for them are in bold. A more in-depth description will
+appear as implementation progresses.
+</p>
+<p><b>Be sure to read information about <a href="queues.html">queues in rsyslog</a></b> -
+many parameter settings modify queue parameters. If in doubt, use the
+default, it is usually well-chosen and applicable in most cases.</p>
+<ul>
+<li><a href="rsconf1_actionexeconlywhenpreviousissuspended.html">$ActionExecOnlyWhenPreviousIsSuspended</a></li>
+<li>$ActionName &lt;a_single_word&gt; - used primarily for documentation, e.g. when
+generating a configuration graph. Available sice 4.3.1.
+<li>$ActionExecOnlyOnceEveryInterval &lt;seconds&gt; -
+execute action only if the last execute is at last
+&lt;seconds&gt; seconds in the past (more info in <a href="ommail.html">ommail</a>,
+but may be used with any action)</li>
+<li><i><b>$ActionExecOnlyEveryNthTime</b> &lt;number&gt;</i> - If configured, the next action will
+only be executed every n-th time. For example, if configured to 3, the first two messages
+that go into the action will be dropped, the 3rd will actually cause the action to execute,
+the 4th and 5th will be dropped, the 6th executed under the action, ... and so on. Note:
+this setting is automatically re-set when the actual action is defined.</li>
+<li><i><b>$ActionExecOnlyEveryNthTimeTimeout</b> &lt;number-of-seconds&gt;</i> - has a meaning only if
+$ActionExecOnlyEveryNthTime is also configured for the same action. If so, the timeout
+setting specifies after which period the counting of "previous actions" expires and
+a new action count is begun. Specify 0 (the default) to disable timeouts.
+<br>
+<i>Why is this option needed?</i> Consider this case: a message comes in at, eg., 10am. That's
+count 1. Then, nothing happens for the next 10 hours. At 8pm, the next
+one occurs. That's count 2. Another 5 hours later, the next message
+occurs, bringing the total count to 3. Thus, this message now triggers
+the rule.
+<br>
+The question is if this is desired behavior? Or should the rule only be
+triggered if the messages occur within an e.g. 20 minute window? If the
+later is the case, you need a
+<br>
+$ActionExecOnlyEveryNthTimeTimeout 1200
+<br>
+This directive will timeout previous messages seen if they are older
+than 20 minutes. In the example above, the count would now be always 1
+and consequently no rule would ever be triggered.
+
+<li>$ActionFileDefaultTemplate [templateName] - sets a new default template for file actions</li>
+<li>$ActionFileEnableSync [on/<span style="font-weight: bold;">off</span>] - enables file
+syncing capability of omfile</li>
+<li>$ActionForwardDefaultTemplate [templateName] - sets a new
+default template for UDP and plain TCP forwarding action</li>
+<li>$ActionGSSForwardDefaultTemplate [templateName] - sets a
+new default template for GSS-API forwarding action</li>
+<li>$ActionQueueCheckpointInterval &lt;number&gt;</li>
+<li>$ActionQueueDequeueSlowdown &lt;number&gt; [number
+is timeout in <i> micro</i>seconds (1000000us is 1sec!),
+default 0 (no delay). Simple rate-limiting!]</li>
+<li>$ActionQueueDiscardMark &lt;number&gt; [default
+9750]</li>
+<li>$ActionQueueDiscardSeverity &lt;number&gt;
+[*numerical* severity! default 4 (warning)]</li>
+<li>$ActionQueueFileName &lt;name&gt;</li>
+<li>$ActionQueueHighWaterMark &lt;number&gt; [default
+8000]</li>
+<li>$ActionQueueImmediateShutdown [on/<b>off</b>]</li>
+<li>$ActionQueueSize &lt;number&gt;</li>
+<li>$ActionQueueLowWaterMark &lt;number&gt; [default
+2000]</li>
+<li>$ActionQueueMaxFileSize &lt;size_nbr&gt;, default 1m</li>
+<li>$ActionQueueTimeoutActionCompletion &lt;number&gt;
+[number is timeout in ms (1000ms is 1sec!), default 1000, 0 means
+immediate!]</li>
+<li>$ActionQueueTimeoutEnqueue &lt;number&gt; [number
+is timeout in ms (1000ms is 1sec!), default 2000, 0 means indefinite]</li>
+<li>$ActionQueueTimeoutShutdown &lt;number&gt; [number
+is timeout in ms (1000ms is 1sec!), default 0 (indefinite)]</li>
+<li>$ActionQueueWorkerTimeoutThreadShutdown
+&lt;number&gt; [number is timeout in ms (1000ms is 1sec!),
+default 60000 (1 minute)]</li>
+<li>$ActionQueueType [FixedArray/LinkedList/<b>Direct</b>/Disk]</li>
+<li>$ActionQueueSaveOnShutdown&nbsp; [on/<b>off</b>]
+</li>
+<li>$ActionQueueWorkerThreads &lt;number&gt;, num worker threads, default 1, recommended 1</li>
+<li>$ActionQueueWorkerThreadMinumumMessages &lt;number&gt;, default 100</li>
+<li><a href="rsconf1_actionresumeinterval.html">$ActionResumeInterval</a></li>
+<li>$ActionResumeRetryCount &lt;number&gt; [default 0, -1 means eternal]</li>
+<li>$ActionSendResendLastMsgOnReconn &lt;[on/<b>off</b>]&gt; specifies if the last message is to be resend when a connecition broken and has been reconnedcted. May increase reliability, but comes at the risk of message duplication.
+<li>$ActionSendStreamDriver &lt;driver basename&gt; just like $DefaultNetstreamDriver, but for the specific action</li>
+<li>$ActionSendStreamDriverMode &lt;mode&gt;, default 0, mode to use with the stream driver (driver-specific)</li>
+<li>$ActionSendStreamDriverAuthMode &lt;mode&gt;,&nbsp; authentication mode to use with the stream driver. Note that this directive requires TLS
+netstream drivers. For all others, it will be ignored.
+(driver-specific)</li>
+<li>$ActionSendStreamDriverPermittedPeer &lt;ID&gt;,&nbsp; accepted fingerprint (SHA1) or name of remote peer. Note that this directive requires TLS
+netstream drivers. For all others, it will be ignored.
+(driver-specific) -<span style="font-weight: bold;"> directive may go away</span>!</li>
+<li><b>$ActionSendTCPRebindInterval</b> nbr</a>- [available since 4.5.1] - instructs the TCP send
+action to close and re-open the connection to the remote host every nbr of messages sent.
+Zero, the default, means that no such processing is done. This directive is useful for
+use with load-balancers. Note that there is some performance overhead associated with it,
+so it is advisable to not too often &quot;rebind&quot; the connection (what
+&quot;too often&quot; actually means depends on your configuration, a rule of thumb is
+that it should be not be much more often than once per second).</li>
+<li><b>$ActionSendUDPRebindInterval</b> nbr</a>- [available since 4.3.2] - instructs the UDP send
+action to rebind the send socket every nbr of messages sent. Zero, the default, means
+that no rebind is done. This directive is useful for use with load-balancers.</li>
+<li><a href="rsconf1_allowedsender.html">$AllowedSender</a></li>
+<li><a href="rsconf1_controlcharacterescapeprefix.html">$ControlCharacterEscapePrefix</a></li>
+<li><a href="rsconf1_debugprintcfsyslinehandlerlist.html">$DebugPrintCFSyslineHandlerList</a></li>
+
+<li><a href="rsconf1_debugprintmodulelist.html">$DebugPrintModuleList</a></li>
+<li><a href="rsconf1_debugprinttemplatelist.html">$DebugPrintTemplateList</a></li>
+<li>$DefaultNetstreamDriver &lt;drivername&gt;, the default <a href="netstream.html">network stream driver</a> to use. Defaults to&nbsp;ptcp.$DefaultNetstreamDriverCAFile &lt;/path/to/cafile.pem&gt;</li>
+<li>$DefaultNetstreamDriverCertFile &lt;/path/to/certfile.pem&gt;</li>
+<li>$DefaultNetstreamDriverKeyFile &lt;/path/to/keyfile.pem&gt;</li>
+<li><b>$DefaultRuleset</b> <i>name</i> - changes the default ruleset for unbound inputs to
+the provided <i>name</i> (the default default ruleset is named
+&quot;RSYSLOG_DefaultRuleset&quot;). It is advised to also read
+our paper on <a href="multi_ruleset.html">using multiple rule sets in rsyslog</a>.</li>
+<li><b>$CreateDirs</b> [<b>on</b>/off] - create directories on an as-needed basis</li>
+<li><a href="rsconf1_dircreatemode.html">$DirCreateMode</a></li>
+<li><a href="rsconf1_dirgroup.html">$DirGroup</a></li>
+<li><a href="rsconf1_dirowner.html">$DirOwner</a></li>
+<li><a href="rsconf1_dropmsgswithmaliciousdnsptrrecords.html">$DropMsgsWithMaliciousDnsPTRRecords</a></li>
+<li><a href="rsconf1_droptrailinglfonreception.html">$DropTrailingLFOnReception</a></li>
+<li><a href="rsconf1_dynafilecachesize.html">$DynaFileCacheSize</a></li>
+<li><a href="rsconf1_escapecontrolcharactersonreceive.html">$EscapeControlCharactersOnReceive</a></li>
+<li>$ErrorMessagesToStderr [<b>on</b>|off] - direct rsyslogd error message to stderr (in addition to other targets)</li>
+<li><a href="rsconf1_failonchownfailure.html">$FailOnChownFailure</a></li>
+<li><a href="rsconf1_filecreatemode.html">$FileCreateMode</a></li>
+<li><a href="rsconf1_filegroup.html">$FileGroup</a></li>
+<li><a href="rsconf1_fileowner.html">$FileOwner</a></li>
+<li><a href="rsconf1_generateconfiggraph.html">$GenerateConfigGraph</a></li>
+<li><a href="rsconf1_gssforwardservicename.html">$GssForwardServiceName</a></li>
+<li><a href="rsconf1_gsslistenservicename.html">$GssListenServiceName</a></li>
+<li><a href="rsconf1_gssmode.html">$GssMode</a></li>
+<li>$HUPisRestart [on/<b>off</b>] - if set to on, a HUP is a full daemon restart. This means any queued messages are discarded (depending
+on queue configuration, of course) all modules are unloaded and reloaded. This mode keeps compatible with sysklogd, but is
+not recommended for use with rsyslog. To do a full restart, simply stop and start the daemon. The default (since 4.5.1) is "off".
+If it is set to "off", a HUP will only close open files. This is a much quicker action and usually
+the only one that is needed e.g. for log rotation. <b>Restart-type HUPs (value "on") are depricated</b>
+and will go away in rsyslog v5. So it is a good idea to change anything that needs it, now.
+Usually that should not be a big issue, as the restart-type HUP can easily be replaced by
+something along the lines of &quot;/etc/init.d/rsyslog restart&quot;.
+</li>
+<li><a href="rsconf1_includeconfig.html">$IncludeConfig</a></li><li>MainMsgQueueCheckpointInterval &lt;number&gt;</li>
+<li>$MainMsgQueueDequeueSlowdown &lt;number&gt; [number
+is timeout in <i> micro</i>seconds (1000000us is 1sec!),
+default 0 (no delay). Simple rate-limiting!]</li>
+<li>$MainMsgQueueDiscardMark &lt;number&gt; [default 9750]</li>
+<li>$MainMsgQueueDiscardSeverity &lt;severity&gt;
+[either a textual or numerical severity! default 4 (warning)]</li>
+<li>$MainMsgQueueFileName &lt;name&gt;</li>
+<li>$MainMsgQueueHighWaterMark &lt;number&gt; [default
+8000]</li>
+<li>$MainMsgQueueImmediateShutdown [on/<b>off</b>]</li>
+<li><a href="rsconf1_mainmsgqueuesize.html">$MainMsgQueueSize</a></li>
+<li>$MainMsgQueueLowWaterMark &lt;number&gt; [default
+2000]</li>
+<li>$MainMsgQueueMaxFileSize &lt;size_nbr&gt;, default
+1m</li>
+<li>$MainMsgQueueTimeoutActionCompletion
+&lt;number&gt; [number is timeout in ms (1000ms is 1sec!),
+default
+1000, 0 means immediate!]</li>
+<li>$MainMsgQueueTimeoutEnqueue &lt;number&gt; [number
+is timeout in ms (1000ms is 1sec!), default 2000, 0 means indefinite]</li>
+<li>$MainMsgQueueTimeoutShutdown &lt;number&gt; [number
+is timeout in ms (1000ms is 1sec!), default 0 (indefinite)]</li>
+<li>$MainMsgQueueWorkerTimeoutThreadShutdown
+&lt;number&gt; [number is timeout in ms (1000ms is 1sec!),
+default 60000 (1 minute)]</li>
+<li>$MainMsgQueueType [<b>FixedArray</b>/LinkedList/Direct/Disk]</li>
+<li>$MainMsgQueueSaveOnShutdown&nbsp; [on/<b>off</b>]
+</li>
+<li>$MainMsgQueueWorkerThreads &lt;number&gt;, num
+worker threads, default 1, recommended 1</li>
+<li>$MainMsgQueueWorkerThreadMinumumMessages &lt;number&gt;, default 100</li>
+<li><a href="rsconf1_markmessageperiod.html">$MarkMessagePeriod</a> (immark)</li>
+<li><b><i>$MaxMessageSize</i></b> &lt;size_nbr&gt;, default 2k - allows to specify maximum supported message size
+(both for sending and receiving). The default
+should be sufficient for almost all cases. Do not set this below 1k, as it would cause
+interoperability problems with other syslog implementations.<br>
+Change the setting to e.g. 32768 if you would like to
+support large message sizes for IHE (32k is the current maximum
+needed for IHE). I was initially tempted to set the default to 32k,
+but there is a some memory footprint with the current
+implementation in rsyslog.
+<br>If you intend to receive Windows Event Log data (e.g. via
+<a href="http://www.eventreporter.com/">EventReporter</a>), you might want to
+increase this number to an even higher value, as event
+log messages can be very lengthy ("$MaxMessageSize 64k" is not a bad idea).
+Note: testing showed that 4k seems to be
+the typical maximum for <b>UDP</b> based syslog. This is an IP stack
+restriction. Not always ... but very often. If you go beyond
+that value, be sure to test that rsyslogd actually does what
+you think it should do ;) It is highly suggested to use a TCP based transport
+instead of UDP (plain TCP syslog, RELP). This resolves the UDP stack size restrictions.
+<br>Note that 2k, the current default, is the smallest size that must be
+supported in order to be compliant to the upcoming new syslog RFC series.
+</li>
+<li><a href="rsconf1_maxopenfiles.html">$MaxOpenFiles</a></li>
+<li><a href="rsconf1_moddir.html">$ModDir</a></li>
+<li><a href="rsconf1_modload.html">$ModLoad</a></li>
+<li><b>$OMFileAsyncWriting</b> [on/<b>off</b>], if turned on, the files will be written
+in asynchronous mode via a separate thread. In that case, double buffers will be used so
+that one buffer can be filled while the other buffer is being written. Note that in order
+to enable $OMFileFlushInterval, $OMFileAsyncWriting must be set to "on". Otherwise, the flush
+interval will be ignored. Also note that when $OMFileFlushOnTXEnd is "on" but
+$OMFileAsyncWriting is off, output will only be written when the buffer is full. This may take
+several hours, or even require a rsyslog shutdown. However, a buffer flush can be forced
+in that case by sending rsyslogd a HUP signal.
+<li><b>$OMFileZipLevel</b> 0..9 [default 0] - if greater 0, turns on gzip compression
+of the output file. The higher the number, the better the compression, but also the
+more CPU is required for zipping.</li>
+<li><b>$OMFileIOBufferSize</b> &lt;size_nbr&gt;, default 4k, size of the buffer used to writing output data. The larger the buffer, the potentially better performance is. The default of 4k is quite conservative, it is useful to go up to 64k, and 128K if you used gzip compression (then, even higher sizes may make sense)</li>
+<li><b>$OMFileFlushOnTXEnd</b> &lt;[<b>on</b>/off]&gt;, default on. Omfile has the
+capability to
+write output using a buffered writer. Disk writes are only done when the buffer is
+full. So if an error happens during that write, data is potentially lost. In cases where
+this is unacceptable, set $OMFileFlushOnTXEnd to on. Then, data is written at the end
+of each transaction (for pre-v5 this means after <b>each</b> log message) and the usual
+error recovery thus can handle write errors without data loss. Note that this option
+severely reduces the effect of zip compression and should be switched to off
+for that use case. Note that the default -on- is primarily an aid to preserve
+the traditional syslogd behaviour.</li>
+<li><b>$RepeatedMsgContainsOriginalMsg</b> [on/<b>off</b>] - "last message repeated n times" messages, if generated,
+have a different format that contains the message that is being repeated.
+Note that only the first "n" characters are included, with n to be at least 80 characters, most
+probably more (this may change from version to version, thus no specific limit is given). The bottom
+line is that n is large enough to get a good idea which message was repeated but it is not necessarily
+large enough for the whole message. (Introduced with 4.1.5). Once set, it affects all following actions.</li>
+<li><a href="rsconf1_repeatedmsgreduction.html">$RepeatedMsgReduction</a></li>
+<li><a href="rsconf1_resetconfigvariables.html">$ResetConfigVariables</a></li>
+<li><b>$Ruleset</b> <i>name</i> - starts a new ruleset or switches back to one already defined.
+All following actions belong to that new rule set.
+the <i>name</i> does not yet exist, it is created. To swith back to rsyslog's
+default ruleset, specify &quot;RSYSLOG_DefaultRuleset&quot;) as the name.
+All following actions belong to that new rule set. It is advised to also read
+our paper on <a href="multi_ruleset.html">using multiple rule sets in rsyslog</a>.</li>
+<li><b>$OptimizeForUniprocessor</b> [on/<b>off</b>] - turns on optimizatons which lead to better
+performance on uniprocessors. If you run on multicore-machiens, turning this off lessens CPU load. The
+default may change as uniprocessor systems become less common. [available since 4.1.0]</li>
+<li>$PreserveFQDN [on/<b>off</b>) - if set to off (legacy default to remain compatible
+to sysklogd), the domain part from a name that is within the same domain as the receiving
+system is stripped. If set to on, full names are always used.</li>
+<li>$WorkDirectory &lt;name&gt; (directory for spool and other work files.
+Do <b>not</b> use trailing slashes)</li>
+<li>$UDPServerAddress &lt;IP&gt; (imudp) -- local IP
+address (or name) the UDP listens should bind to</li>
+<li>$UDPServerRun &lt;port&gt; (imudp) -- former
+-r&lt;port&gt; option, default 514, start UDP server on this
+port, "*" means all addresses</li>
+<li>$UDPServerTimeRequery &lt;nbr-of-times&gt; (imudp) -- this is a performance
+optimization. Getting the system time is very costly. With this setting, imudp can
+be instructed to obtain the precise time only once every n-times. This logic is
+only activated if messages come in at a very fast rate, so doing less frequent
+time calls should usually be acceptable. The default value is two, because we have
+seen that even without optimization the kernel often returns twice the identical time.
+You can set this value as high as you like, but do so at your own risk. The higher
+the value, the less precise the timestamp.
+<li><a href="droppriv.html">$PrivDropToGroup</a></li>
+<li><a href="droppriv.html">$PrivDropToGroupID</a></li>
+<li><a href="droppriv.html">$PrivDropToUser</a></li>
+<li><a href="droppriv.html">$PrivDropToUserID</a></li>
+<li><a href="rsconf1_umask.html">$UMASK</a></li>
+</ul>
+<p><b>Where &lt;size_nbr&gt; is specified above,</b>
+modifiers can be used after the number part. For example, 1k means
+1024. Supported are k(ilo), m(ega), g(iga), t(era), p(eta) and e(xa).
+Lower case letters refer to the traditional binary defintion (e.g. 1m
+equals 1,048,576) whereas upper case letters refer to their new
+1000-based definition (e.g 1M equals 1,000,000).</p>
+<p>Numbers may include '.' and ',' for readability. So you can
+for example specify either "1000" or "1,000" with the same result.
+Please note that rsyslogd simply ignores the punctuation. Form it's
+point of view, "1,,0.0.,.,0" also has the value 1000. </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, 2009 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
new file mode 100644
index 00000000..675b8bb3
--- /dev/null
+++ b/doc/rsyslog_conf_modules.html
@@ -0,0 +1,72 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><title>Modules - 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>Modules</h1>
+<p>Rsyslog has a modular design. This enables functionality to be
+dynamically loaded from modules, which may also be written by any
+third party. Rsyslog itself offers all non-core functionality as
+modules. Consequently, there is a growing
+number of modules. Here is the entry point to their documentation and
+what they do (list is currently not complete)</p>
+<p>Please note that each module provides configuration
+directives, which are NOT necessarily being listed below. Also
+remember, that a modules configuration directive (and functionality) is
+only available if it has been loaded (using $ModLoad).</p>
+<p>It is relatively easy to write a rsyslog module. <b>If none of the provided
+modules solve your need, you may consider writing one or have one written
+for you by
+<a href="http://www.rsyslog.com/professional-services">Adiscon's professional services for rsyslog</a>
+</b>(this often is a very cost-effective and efficient way of getting what you need).
+
+<h2>Input Modules</h2>
+<p>Input modules are used to gather messages from various sources. They interface
+to message generators.
+<ul>
+<li><a href="imfile.html">imfile</a> -&nbsp; input module for text files</li>
+<li><a href="imrelp.html">imrelp</a> - RELP input module</li>
+<li>imudp - udp syslog message input</li>
+<li><a href="imtcp.html">imtcp</a> - input plugin for plain tcp syslog</li>
+<li><a href="imgssapi.html">imgssapi</a> - input plugin for plain tcp and GSS-enabled syslog</li>
+<li>immark - support for mark messages</li>
+<li><a href="imklog.html">imklog</a> - kernel logging</li>
+<li><a href="imuxsock.html">imuxsock</a> - unix sockets, including the system log socket</li>
+<li><a href="im3195.html">im3195</a> - accepts syslog messages via RFC 3195</li>
+</ul>
+
+<h2>Output Modules</h2>
+<p>Output modules process messages. With them, message formats can be transformed
+and messages be transmitted to various different targets.
+<ul>
+<li><a href="omsnmp.html">omsnmp</a> - SNMP trap output module</li>
+<li><a href="omstdout.html">omtdout</a> - stdout output module (mainly a test tool)</li>
+<li><a href="omrelp.html">omrelp</a> - RELP output module</li>
+<li>omgssapi - output module for GSS-enabled syslog</li>
+<li><a href="ommysql.html">ommysql</a> - output module for MySQL</li>
+<li>ompgsql - output module for PostgreSQL</li>
+<li><a href="omlibdbi.html">omlibdbi</a> -
+generic database output module (Firebird/Interbase, MS SQL, Sybase,
+SQLLite, Ingres, Oracle, mSQL)</li>
+<li><a href="ommail.html">ommail</a> -
+permits rsyslog to alert folks by mail if something important happens</li>
+<li><a href="omoracle.html">omoracle</a> - output module for Oracle (native OCI interface)</li>
+</ul>
+
+<h2>Library Modules</h2>
+<p>Library modules provide dynamically loadable functionality for parts of rsyslog,
+most often for other loadable modules. They can not be user-configured and are loaded
+automatically by some components. They are just mentioned so that error messages that
+point to library moduls can be understood. No module list is provided.
+
+<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, 2009 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_nomatch.html b/doc/rsyslog_conf_nomatch.html
new file mode 100644
index 00000000..5f25f3e4
--- /dev/null
+++ b/doc/rsyslog_conf_nomatch.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><title>nomatch mode - property replacer - rsyslog.conf</title></head>
+<body>
+<h1>nomatch mode - property replacer - rsyslog.con</h1>
+<p>This is a part of the <a href="rsyslog_conf.html">rsyslog.conf documentation</a>
+of the <a href="property_replacer.html">property replacer</a>.</p>
+<p><b>The "nomatch-Mode" specifies which string the property replacer
+shall return if a regular expression did not find the search string.</b>. Traditionally,
+the string "**NO MATCH**" was returned, but many people complained this was almost never useful.
+Still, this mode is support as "<b>DFLT</b>" for legacy configurations.
+<p>Three additional and potentially useful modes exist: in one (<b>BLANK</b>) a blank string
+is returned. This is probably useful for inserting values into databases where no
+value shall be inserted if the expression could not be found.
+<p>A similar mode is "<b>ZERO</b>" where the string "0" is returned. This is suitable
+for numerical values. A use case may be
+that you record a traffic log based on firewall rules and the "bytes transmitted" counter
+is extracted via a regular expression. If no "bytes transmitted" counter is available
+in the current message, it is probably a good idea to return an empty string, which the
+database layer can turn into a zero.
+<p>The other mode is "<b>FIELD</b>", in which the complete field is returned. This may be useful
+in cases where absense of a match is considered a failure and the message that triggered
+it shall be logged.
+<p>If in doubt, <b>it is highly suggested to use the
+<a href="http://www.rsyslog.com/tool-regex">rsyslog online regular expression
+checker and generator</a> to see these options in action</b>. With that online tool,
+you can craft regular expressions based on samples and try out the different modes.
+
+<h2>Summary of nomatch Modes</h2>
+<table border="1" cellspacing="0">
+<tr><td><b>Mode</b></td><td><b>Returned</b></td></tr>
+<tr><td>DFLT</td><td>"**NO MATCH**"</td></tr>
+<tr><td>BLANK</td><td>"" (empty string)</td></tr>
+<tr><td>ZERO</td><td>"0"</td></tr>
+<tr><td>FIELD</td><td>full content of original field</td></tr>
+<tr><td>&nbsp;</td><td><a href="http://www.rsyslog.com/tool-regex">Interactive Tool</a></td></tr>
+</table>
+<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 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 2 or higher.</font></p>
+</body>
+</html>
+
+
diff --git a/doc/rsyslog_conf_output.html b/doc/rsyslog_conf_output.html
new file mode 100644
index 00000000..c52aaa5e
--- /dev/null
+++ b/doc/rsyslog_conf_output.html
@@ -0,0 +1,81 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><title>Output Channels - rsyslog.conf</title></head>
+<body>
+<p>This is a part of the rsyslog.conf documentation.</p>
+<a href="rsyslog_conf.html">back</a>
+<h2>Output Channels</h2>
+<p>Output Channels are a new concept first introduced in rsyslog
+0.9.0. <b>As of this writing, it is most likely that they will
+be replaced by something different in the future.</b> So if you
+use them, be prepared to change you configuration file syntax when you
+upgrade to a later release.<br>
+<br>
+The idea behind output channel definitions is that it shall provide an
+umbrella for any type of output that the user might want. In essence,<br>
+this is the "file" part of selector lines (and this is why we are not
+sure output channel syntax will stay after the next review). There is a<br>
+difference, though: selector channels both have filter conditions
+(currently facility and severity) as well as the output destination.
+they can only be used to write to files - not pipes, ttys or whatever
+Output channels define the output definition, only. As of this build,
+else. If we stick with output channels, this will change over time.</p>
+<p>In concept, an output channel includes everything needed to
+know about an output actions. In practice, the current implementation
+only carries<br>
+a filename, a maximum file size and a command to be issued when this
+file size is reached. More things might be present in future version,
+which might also change the syntax of the directive.</p>
+<p>Output channels are defined via an $outchannel directive. It's
+syntax is as follows:<br>
+<br>
+$outchannel name,file-name,max-size,action-on-max-size<br>
+<br>
+name is the name of the output channel (not the file), file-name is the
+file name to be written to, max-size the maximum allowed size and
+action-on-max-size a command to be issued when the max size is reached.
+This command always has exactly one parameter. The binary is that part
+of action-on-max-size before the first space, its parameter is
+everything behind that space.<br>
+<br>
+Please note that max-size is queried BEFORE writing the log message to
+the file. So be sure to set this limit reasonably low so that any
+message might fit. For the current release, setting it 1k lower than
+you expected is helpful. The max-size must always be specified in bytes
+- there are no special symbols (like 1k, 1m,...) at this point of
+development.<br>
+<br>
+Keep in mind that $outchannel just defines a channel with "name". It
+does not activate it. To do so, you must use a selector line (see
+below). That selector line includes the channel name plus an $ sign in
+front of it. A sample might be:<br>
+<br>
+*.* $mychannel<br>
+<br>
+In its current form, output channels primarily provide the ability to
+size-limit an output file. To do so, specify a maximum size. When this
+size is reached, rsyslogd will execute the action-on-max-size command
+and then reopen the file and retry. The command should be something
+like a <a href="log_rotation_fix_size.html">log rotation
+script</a> or a similar thing.</p>
+<p>If there is no action-on-max-size command or the command did
+not resolve the situation, the file is closed and never reopened by
+rsyslogd (except, of course, by huping it). This logic was integrated
+when we first experienced severe issues with files larger 2gb, which
+could lead to rsyslogd dumping core. In such cases, it is more
+appropriate to stop writing to a single file. Meanwhile, rsyslogd has
+been fixed to support files larger 2gb, but obviously only on file
+systems and operating system versions that do so. So it can still make
+sense to enforce a 2gb file size limit.</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 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 2 or higher.</font></p>
+</body>
+</html>
+
+
diff --git a/doc/rsyslog_conf_templates.html b/doc/rsyslog_conf_templates.html
new file mode 100644
index 00000000..baa4ce29
--- /dev/null
+++ b/doc/rsyslog_conf_templates.html
@@ -0,0 +1,150 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><title>Templates - rsyslog.conf</title></head>
+<body>
+<p>This is a part of the rsyslog.conf - documentation.</p>
+<a href="rsyslog_conf.html">back</a>
+<h2>Templates</h2>
+<p>Templates are a key feature of rsyslog. They allow to specify
+any
+format a user might want. They are also used for dynamic file name
+generation. Every output in rsyslog uses templates - this holds true
+for files, user messages and so on. The database writer expects its
+template to be a proper SQL statement - so this is highly customizable
+too. You might ask how does all of this work when no templates at all
+are specified. Good question ;) The answer is simple, though. Templates
+compatible with the stock syslogd formats are hardcoded into rsyslogd.
+So if no template is specified, we use one of these hardcoded
+templates. Search for "template_" in syslogd.c and you will find the
+hardcoded ones.</p>
+<p>A template consists of a template directive, a name, the
+actual template text and optional options. A sample is:</p>
+<blockquote><code>$template MyTemplateName,"\7Text
+%property% some more text\n",&lt;options&gt;</code></blockquote>
+<p>The "$template" is the template directive. It tells rsyslog
+that this line contains a template. "MyTemplateName" is the template
+name. All
+other config lines refer to this name. The text within quotes is the
+actual template text. The backslash is an escape character, much as it
+is in C. It does all these "cool" things. For example, \7 rings the
+bell (this is an ASCII value), \n is a new line. C programmers and perl
+coders have the advantage of knowing this, but the set in rsyslog is a
+bit restricted currently.
+</p>
+<p>All text in the template is used literally, except for things
+within percent signs. These are properties and allow you access to the
+contents of the syslog message. Properties are accessed via the
+<a href="property_replacer.html">property replacer</a>
+(nice name, huh) and it can do cool things, too. For
+example, it can pick a substring or do date-specific formatting. More
+on this is below, on some lines of the property replacer.<br>
+<br>
+The &lt;options&gt; part is optional. It carries options
+influencing the template as whole. See details below. Be sure NOT to
+mistake template options with property options - the later ones are
+processed by the property replacer and apply to a SINGLE property, only
+(and not the whole template).<br>
+<br>
+Template options are case-insensitive. Currently defined are: </p>
+<p><b>sql</b> - format the string suitable for a SQL
+statement in MySQL format. This will replace single quotes ("'") and
+the backslash character by their backslash-escaped counterpart ("\'"
+and "\\") inside each field. Please note that in MySQL configuration,
+the <code class="literal">NO_BACKSLASH_ESCAPES</code>
+mode must be turned off for this format to work (this is the default).</p>
+<p><b>stdsql</b> - format the string suitable for a
+SQL statement that is to be sent to a standards-compliant sql server.
+This will replace single quotes ("'") by two single quotes ("''")
+inside each field. You must use stdsql together with MySQL if in MySQL
+configuration the
+<code class="literal">NO_BACKSLASH_ESCAPES</code> is
+turned on.</p>
+<p>Either the <b>sql</b> or <b>stdsql</b>&nbsp;
+option <b>must</b> be specified when a template is used
+for writing to a database, otherwise injection might occur. Please note
+that due to the unfortunate fact that several vendors have violated the
+sql standard and introduced their own escape methods, it is impossible
+to have a single option doing all the work.&nbsp; So you yourself
+must make sure you are using the right format. <b>If you choose
+the wrong one, you are still vulnerable to sql injection.</b><br>
+<br>
+Please note that the database writer *checks* that the sql option is
+present in the template. If it is not present, the write database
+action is disabled. This is to guard you against accidental forgetting
+it and then becoming vulnerable to SQL injection. The sql option can
+also be useful with files - especially if you want to import them into
+a database on another machine for performance reasons. However, do NOT
+use it if you do not have a real need for it - among others, it takes
+some toll on the processing time. Not much, but on a really busy system
+you might notice it ;)</p>
+<p>The default template for the write to database action has the
+sql option set. As we currently support only MySQL and the sql option
+matches the default MySQL configuration, this is a good choice.
+However, if you have turned on
+<code class="literal">NO_BACKSLASH_ESCAPES</code> in
+your MySQL config, you need to supply a template with the stdsql
+option. Otherwise you will become vulnerable to SQL injection. <br>
+<br>
+To escape:<br>
+% = \%<br>
+\ = \\ --&gt; '\' is used to escape (as in C)<br>
+$template TraditionalFormat,"%timegenerated% %HOSTNAME% %syslogtag%%msg%\n"<br>
+<br>
+Properties can be accessed by the <a href="property_replacer.html">property
+replacer</a> (see there for details).</p>
+<p><b>Please note that templates can also by
+used to generate selector lines with dynamic file names.</b> For
+example, if you would like to split syslog messages from different
+hosts to different files (one per host), you can define the following
+template:</p>
+<blockquote><code>$template
+DynFile,"/var/log/system-%HOSTNAME%.log"</code></blockquote>
+<p>This template can then be used when defining an output
+selector line. It will result in something like
+"/var/log/system-localhost.log"</p>
+<p>Template
+names beginning with "RSYSLOG_" are reserved for rsyslog use. Do NOT
+use them if, otherwise you may receive a conflict in the future (and
+quite unpredictable behaviour). There is a small set of pre-defined
+templates that you can use without the need to define it:</p>
+<ul>
+<li><span style="font-weight: bold;">RSYSLOG_TraditionalFileFormat</span>
+- the "old style" default log file format with low-precision timestamps</li>
+<li><span style="font-weight: bold;">RSYSLOG_FileFormat</span>
+- a modern-style logfile format similar to TraditionalFileFormat, buth
+with high-precision timestamps and timezone information</li>
+<li><span style="font-weight: bold;">RSYSLOG_TraditionalForwardFormat</span>
+- the traditional forwarding format with low-precision timestamps. Most
+useful if you send&nbsp;messages to other syslogd's or rsyslogd
+below
+version 3.12.5.</li>
+<li><span style="font-weight: bold;">RSYSLOG_ForwardFormat</span>
+- a new high-precision forwarding format very similar to the
+traditional one, but with high-precision timestamps and timezone
+information. Recommended to be used when sending messages to rsyslog
+3.12.5 or above.</li>
+<li><span style="font-weight: bold;">RSYSLOG_SyslogProtocol23Format</span>
+- the format specified in IETF's internet-draft
+ietf-syslog-protocol-23, which is assumed to be come the new syslog
+standard RFC. This format includes several improvements. The rsyslog
+message parser understands this format, so you can use it together with
+all relatively recent versions of rsyslog. Other syslogd's may get
+hopelessly confused if receiving that format, so check before you use
+it. Note that the format is unlikely to change when the final RFC comes
+out, but this may happen.</li>
+<li><span style="font-weight: bold;">RSYSLOG_DebugFormat</span>
+- a special format used for troubleshooting property problems. This format
+is meant to be written to a log file. Do <b>not</b> use for production or remote
+forwarding.</li>
+</ul>
+
+<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 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 2 or higher.</font></p>
+</body>
+</html>
+
diff --git a/doc/rsyslog_confgraph_complex.conf b/doc/rsyslog_confgraph_complex.conf
new file mode 100644
index 00000000..3d7ec0a3
--- /dev/null
+++ b/doc/rsyslog_confgraph_complex.conf
@@ -0,0 +1,108 @@
+$DebugPrintTemplateList off
+$DebugPrintCfSysLineHandlerList off
+$DebugPrintModuleList off
+#$ResetConfigVariables
+$ErrorMessagesToStderr off
+$ModLoad /home/rger/proj/rsyslog/plugins/imuxsock/.libs/imuxsock.so
+#$ModLoad /home/rger/proj/rsyslog/plugins/imklog/.libs/imklog
+#$ModLoad /home/rger/proj/rsyslog/plugins/imtcp/.libs/imtcp
+$ModLoad /home/rger/proj/rsyslog/plugins/imtcp/.libs/imtcp
+$ModLoad /home/rger/proj/rsyslog/plugins/imudp/.libs/imudp
+$ModLoad /home/rger/proj/rsyslog/plugins/omstdout/.libs/omstdout
+$ModLoad /home/rger/proj/rsyslog/plugins/omprog/.libs/omprog
+$ModLoad /home/rger/proj/rsyslog/plugins/omtesting/.libs/omtesting
+#$ModLoad /home/rger/proj/rsyslog/plugins/ommail/.libs/ommail
+#
+#
+# PGSQL testing
+$ModLoad /home/rger/proj/rsyslog/plugins/ompgsql/.libs/ompgsql.so
+$template pgfmt,"insert into SystemEvents (Message, Facility, FromHost, Priority, DeviceReportedTime, ReceivedAt, InfoUnitID, SysLogTag) values ('%msg%', %syslogfacility%, '%HOSTNAME%', %syslogpriority%, '%timereported:::date-pgsql%', '%timegenerated:::date-pgsql%', %iut%, '%syslogtag%');",STDSQL
+#$ActionQueueType linkedlist
+#*.* :ompgsql:127.0.0.1,rsyslog,postgres,;pgfmt
+
+#$ActionOMStdoutArrayInterface on
+#*.* :omstdout:
+
+$ActionResumeInterval 4
+$ActionResumeRetryCount 3
+$ActionQueueType LinkedList # run asynchronously
+$ActionName Forward to 172.19.3.9
+*.* @@172.19.3.9:10514
+#*.* :omtesting:randfail
+#*.* :omtesting:always_suspend
+#*.* :omtesting:fail 2 2
+
+#$UDPServerTimeRequery 10
+$UDPServerRun 514
+$inputtcpmaxsessions 2000
+$InputTCPServerRun 12514
+
+#$PrivDropToUser rger
+#$InputTCPServerInputName tcp/514
+#$InputTCPServerAddtlFrameDelimiter 10
+#$InputTCPServerRun 514
+#$AllowedSender UDP,127.0.0.1/32
+#$AllowedSender TCP,127.0.0.1/32
+
+$PreserveFQDN off
+
+#$HUPisRestart on
+
+#$MainMsgQueueType direct
+$MainMsgQueueType linkedlist
+$MainMsgQueueDequeueBatchSize 200
+#$MainMsgQueueWorkerTimeoutThreadShutdown -1
+
+#---- test DA mode
+# set spool locations and switch queue to disk assisted mode
+$WorkDirectory spool
+$MainMsgQueueSize 200 # this *should* trigger moving on to DA mode...
+# note: we must set QueueSize sufficiently high, so that 70% (light delay mark)
+# is high enough above HighWatermark!
+$MainMsgQueueHighWatermark 80
+$MainMsgQueueLowWatermark 40
+$MainMsgQueueFilename mainq
+$MainMsgQueueType linkedlist
+# ucomment, as we now have an issue (finally the test case works ;))
+#$MainMsgQueueDequeueBatchSize 80
+#---- end test DA mode
+
+#$template test,"%timereported:::date-rfc3339%,%timereported:::date-mysql%,%timereported:::date-subseconds%, %timegenerated:::date-mysql%, %timegenerated:::date-subseconds%, msg: %msg%\n"
+#$template db,"re: '%msg:R,ERE,1,FIELD:dsn=([0-9]+\.[0-9]+\.[0-9])--end%', msg: '%msg%'\n"
+#$template db,"re: '%msg:R,ERE,1,ZERO:dsn=([0-9]+\.[0-9]+\.[0-9])--end%', msg: '%msg%'\n"
+#$template DEBUG,"Debug line with all properties:\nFROMHOST: '%FROMHOST%', fromhost-ip: '%fromhost-ip%, HOSTNAME: '%HOSTNAME%', PRI: %PRI%,\nsyslogtag '%syslogtag%', programname: '%programname%', APP-NAME: '%APP-NAME%', PROCID: '%PROCID%', MSGID: '%MSGID%',\nTIMESTAMP: '%TIMESTAMP%', STRUCTURED-DATA: '%STRUCTURED-DATA%',\nmsg: '%msg%'\nescaped msg: '%msg:::drop-cc%'\nrawmsg: '%rawmsg%'\n\n"
+$template csv,"%syslogtag:::csv%,%msg:::upppercase,csv%,%msg%\n"
+*.* -/home/rger/proj/rsyslog/logfile
+kern.* -/home/rger/proj/rsyslog/logfile
+$ActionExecOnlyWhenPreviousIsSuspended on
+& -/tmp/xyz/uuu
+$ActionExecOnlyWhenPreviousIsSuspended off
+& ~
+& -/tmp/xyz/uuu2
+& -/tmp/xyz/uuu3
+
+
+#$template dynfile,"/home/rger/proj/rsyslog/test-%syslogtag%"
+#*.* -?dynfile
+#:msg, ereregex, "test|tast" /home/rger/proj/rsyslog/ere
+#if strlen($syslogtag & strlen($msg)) > 10 then /home/rger/proj/rsyslog/longlog
+#if strlen($msg) > 10 then /home/rger/proj/rsyslog/longlog
+#if tolower($msg) contains 'test' then /home/rger/proj/rsyslog/longlog
+#if $msg contains 'test' then /home/rger/proj/rsyslog/longlog
+
+#$ActionOMProgBinary /home/rger/proj/rsyslog/consumer
+#*.* :omprog:
+
+#$actionresumeretryCount -1
+#$actionResumeInterval 4
+#$template dynfile,"/mnt2/logs/logfile.log"
+#*.* /mnt2/logs/logfile.log
+#if $msg contains 'test' then ?dynfile
+#*.* ?dynfile
+:msg, contains, "test " /tmpo/sdafsdf
+
+$ActionName write_system_log_2
+if $msg == 'test' then /tmpo/sdafsdf2
+& /tmpo/234234
+*.* @@(o,z9)172.19.3.21:10514
+$GenerateConfigGraph /home/rger/proj/rsyslog/rsyslog.dot
diff --git a/doc/rsyslog_confgraph_complex.png b/doc/rsyslog_confgraph_complex.png
new file mode 100644
index 00000000..21c04c57
--- /dev/null
+++ b/doc/rsyslog_confgraph_complex.png
Binary files differ
diff --git a/doc/rsyslog_confgraph_std.conf b/doc/rsyslog_confgraph_std.conf
new file mode 100644
index 00000000..64c9a18a
--- /dev/null
+++ b/doc/rsyslog_confgraph_std.conf
@@ -0,0 +1,79 @@
+#rsyslog v3 config file
+
+# if you experience problems, check
+# http://www.rsyslog.com/troubleshoot for assistance
+
+#### MODULES ####
+
+$ModLoad imuxsock.so # provides support for local system logging (e.g. via logger command)
+$ModLoad imklog.so # provides kernel logging support (previously done by rklogd)
+#$ModLoad immark.so # provides --MARK-- message capability
+
+# Provides UDP syslog reception
+#$ModLoad imudp.so
+#$UDPServerRun 514
+
+# Provides TCP syslog reception
+#$ModLoad imtcp.so
+#$InputTCPServerRun 514
+
+
+#### GLOBAL DIRECTIVES ####
+
+# Use default timestamp format
+$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
+
+# File syncing capability is disabled by default. This feature is usually not required,
+# not useful and an extreme performance hit
+#$ActionFileEnableSync on
+
+
+#### RULES ####
+
+# Log all kernel messages to the console.
+# Logging much else clutters up the screen.
+#kern.* /dev/console
+
+# Log anything (except mail) of level info or higher.
+# Don't log private authentication messages!
+*.info;mail.none;authpriv.none;cron.none /var/log/messages
+
+# The authpriv file has restricted access.
+authpriv.* /var/log/secure
+
+# Log all the mail messages in one place.
+mail.* -/var/log/maillog
+
+
+# Log cron stuff
+cron.* /var/log/cron
+
+# Everybody gets emergency messages
+*.emerg *
+
+# Save news errors of level crit and higher in a special file.
+uucp,news.crit /var/log/spooler
+
+# Save boot messages also to boot.log
+local7.* /var/log/boot.log
+
+
+
+# ### 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 ###
+$GenerateConfigGraph /home/rger/proj/rsyslog/rsyslog.dot
diff --git a/doc/rsyslog_confgraph_std.png b/doc/rsyslog_confgraph_std.png
new file mode 100644
index 00000000..655a7f82
--- /dev/null
+++ b/doc/rsyslog_confgraph_std.png
Binary files differ
diff --git a/doc/rsyslog_high_database_rate.html b/doc/rsyslog_high_database_rate.html
index 158a4df6..2bae58c6 100644
--- a/doc/rsyslog_high_database_rate.html
+++ b/doc/rsyslog_high_database_rate.html
@@ -7,6 +7,7 @@
</head>
<body>
+<a href="features.html">back</a>
<h1>Handling a massive syslog database insert rate with Rsyslog</h1>
@@ -171,6 +172,14 @@ comments or find bugs (I *do* bugs - no way... ;)), please
http://www.gnu.org/copyleft/fdl.html</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>
+<p><font size="2">This documentation is part of the
+<a href="http://www.rsyslog.com/">rsyslog</a> project.<br>
+Copyright &copy; 2008 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 2 or higher.</font></p>
</body>
diff --git a/doc/rsyslog_mysql.html b/doc/rsyslog_mysql.html
index 753c86ec..a27bd59e 100644
--- a/doc/rsyslog_mysql.html
+++ b/doc/rsyslog_mysql.html
@@ -1,6 +1,6 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head><title>Writing syslog Data to MySQL</title>
-
+<a href="features.html">back</a>
<meta name="KEYWORDS" content="syslog, mysql, syslog to mysql, howto"></head>
<body>
<h1>Writing syslog messages to MySQL</h1>
@@ -259,4 +259,13 @@ document under the terms of the GNU Free Documentation License, Version
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
Texts. A copy of the license can be viewed at <a href="http://www.gnu.org/copyleft/fdl.html">
http://www.gnu.org/copyleft/fdl.html</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>
+<p><font size="2">This documentation is part of the
+<a href="http://www.rsyslog.com/">rsyslog</a> project.<br>
+Copyright &copy; 2008 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 2 or higher.</font></p>
+
</body></html>
diff --git a/doc/rsyslog_ng_comparison.html b/doc/rsyslog_ng_comparison.html
index 2f383f78..8e121a8d 100644
--- a/doc/rsyslog_ng_comparison.html
+++ b/doc/rsyslog_ng_comparison.html
@@ -1,6 +1,7 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head><title>rsyslog vs. syslog-ng - a comparison</title></head>
<body>
+<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>
@@ -584,4 +585,13 @@ 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>
+<p><font size="2">This documentation is part of the
+<a href="http://www.rsyslog.com/">rsyslog</a> project.<br>
+Copyright &copy; 2008 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 2 or higher.</font></p>
+
</body></html>
diff --git a/doc/rsyslog_pgsql.html b/doc/rsyslog_pgsql.html
new file mode 100644
index 00000000..dcb9dc3a
--- /dev/null
+++ b/doc/rsyslog_pgsql.html
@@ -0,0 +1,336 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=utf-8">
+ <TITLE></TITLE>
+ <META NAME="GENERATOR" CONTENT="OpenOffice.org 3.1 (Unix)">
+ <META NAME="AUTHOR" CONTENT="Marc Schiffbauer">
+ <META NAME="CREATED" CONTENT="20100129;15054500">
+ <META NAME="CHANGEDBY" CONTENT="Marc Schiffbauer">
+ <META NAME="CHANGED" CONTENT="20100129;16035000">
+ <META NAME="Info 1" CONTENT="">
+ <META NAME="Info 2" CONTENT="">
+ <META NAME="Info 3" CONTENT="">
+ <META NAME="Info 4" CONTENT="">
+ <STYLE TYPE="text/css">
+ <!--
+ @page { size: 8.27in 11.69in; margin: 0.79in }
+ P { margin-bottom: 0.08in }
+ P.western { font-family: "Arial", sans-serif }
+ H1 { margin-bottom: 0.08in }
+ H1.western { font-family: "Times New Roman", serif }
+ H1.cjk { font-family: "DejaVu Sans" }
+ H1.ctl { font-family: "DejaVu Sans" }
+ H2 { margin-bottom: 0.08in }
+ H2.western { font-family: "Times New Roman", serif }
+ BLOCKQUOTE.western { font-family: "Arial", sans-serif }
+ H3 { margin-bottom: 0.08in }
+ H3.western { font-family: "Times New Roman", serif }
+ A:link { so-language: zxx }
+ -->
+ </STYLE>
+</HEAD>
+<BODY LANG="de-DE" DIR="LTR">
+<H1 CLASS="western"><SPAN LANG="en-US">Writing </SPAN>syslog messages
+to MySQL, PostgreSQL or any other supported Database</H1>
+<P CLASS="western"><FONT SIZE=2><I>Written by </I></FONT><A HREF="http://www.adiscon.com/en/people/rainer-gerhards.php"><FONT SIZE=2><I>Rainer
+Gerhards</I></FONT></A><FONT SIZE=2><I> with some additions by Marc
+Schiffbauer (2008-02-28)</I></FONT></P>
+<H2 CLASS="western">Abstract</H2>
+<P CLASS="western"><SPAN LANG="en-US"><I><B>In this paper, I describe
+how to write </B></I></SPAN><A HREF="http://www.monitorware.com/en/topics/syslog/">syslog</A><SPAN LANG="en-US"><I><B>
+messages to a </B></I></SPAN><A HREF="http://www.mysql.com/">MySQL</A><SPAN LANG="en-US"><I><B>
+or </B></I></SPAN><A HREF="http://www.postgresql.org/">PostgreSQL</A><SPAN LANG="en-US"><I><B>
+database.</B></I></SPAN><SPAN LANG="en-US"><I> Having syslog messages
+in a database is often handy, especially when you intend to set up a
+front-end for viewing them. This paper describes an approach with
+</I></SPAN><A HREF="http://www.rsyslog.com/">rsyslogd</A><SPAN LANG="en-US"><I>,
+an alternative enhanced syslog daemon natively supporting MySQL and
+PostgreSQL. I describe the components needed to be installed and how
+to configure them. Please note that as of this writing, rsyslog
+supports a variety of databases. While this guide is still MySQL- and
+PostgreSQL-focused, you can probably use it together with other ones
+too. You just need to modify a few settings.</I></SPAN></P>
+<H2 CLASS="western">Background</H2>
+<P LANG="en-US" CLASS="western">In many cases, syslog data is simply
+written to text files. This approach has some advantages, most
+notably it is very fast and efficient. However, data stored in text
+files is not readily accessible for real-time viewing and analysis.
+To do that, the messages need to be in a database. There are various
+ways to store syslog messages in a database. For example, some have
+the syslogd write text files which are later feed via a separate
+script into the database. Others have written scripts taking the data
+(via a pipe) from a non-database-aware syslogd and store them as they
+appear. Some others use database-aware syslogds and make them write
+the data directly to the database. In this paper, I use that &quot;direct
+write&quot; approach. I think it is superior, because the syslogd
+itself knows the status of the database connection and thus can
+handle it intelligently (well ... hopefully ;)). I use rsyslogd to
+acomplish this, simply because I have initiated the rsyslog project
+with database-awareness as one goal.</P>
+<P CLASS="western"><SPAN LANG="en-US"><B>One word of caution:</B></SPAN><SPAN LANG="en-US">
+while message storage in the database provides an excellent
+foundation for interactive analysis, it comes at a cost. Database i/o
+is considerably slower than text file i/o. As such, directly writing
+to the database makes sense only if your message volume is low enough
+to allow a) the syslogd, b) the network, and c) the database server
+to catch up with it. Some time ago, I have written a paper on
+</SPAN><A HREF="http://www.monitorware.com/Common/en/Articles/performance-optimizing-syslog-server.php">optimizing
+syslog server performance</A><SPAN LANG="en-US">. While this paper
+talks about Window-based solutions, the ideas in it are generic
+enough to apply here, too. So it might be worth reading if you
+anticipate medium high to high traffic. If you anticipate really high
+traffic (or very large traffic spikes), you should seriously consider
+forgetting about direct database writes - in my opinion, such a
+situation needs either a very specialized system or a different
+approach (the text-file-to-database approach might work better for
+you in this case). </SPAN>
+</P>
+<H2 CLASS="western">Overall System Setup</H2>
+<P CLASS="western"><SPAN LANG="en-US">In this paper, I concentrate on
+the server side. If you are thinking about interactive syslog message
+review, you probably want to centralize syslog. In such a scenario,
+you have multiple machines (the so-called clients) send their data to
+a central machine (called server in this context). While I expect
+such a setup to be typical when you are interested in storing
+messages in the database, I do not describe how to set it up. This is
+beyond the scope of this paper. If you search a little, you will
+probably find many good descriptions on </SPAN><SPAN LANG="en-US">how
+to centralize syslog. If you do that, it might be a good idea to do
+it securely, so you might also be interested in my paper on
+</SPAN><A HREF="http://www.rsyslog.com/doc-rsyslog_stunnel.html">ssl-encrypting
+syslog message transfer</A><SPAN LANG="en-US">.</SPAN></P>
+<P LANG="en-US" CLASS="western">No matter how the messages arrive at
+the server, their processing is always the same. So you can use this
+paper in combination with any description for centralized syslog
+reporting.</P>
+<P CLASS="western"><SPAN LANG="en-US">As I already said, I use
+rsyslogd on the server. It has intrinsic support for talking to the
+supported databases. For obvious reasons, we also need an instance of
+MySQL or PostgreSQL running. To keep us focused, the setup of the
+database itself is also beyond the scope of this paper. I assume that
+you have successfully installed the database and also have a
+front-end at hand to work with it (for example, </SPAN><A HREF="http://www.phpmyadmin.net/">phpMyAdmin</A><SPAN LANG="en-US">
+or </SPAN><A HREF="http://phppgadmin.sourceforge.net/">phpPgAdmin</A><SPAN LANG="en-US">.
+Please make sure that this is installed, actually working and you
+have a basic understanding of how to handle it.</SPAN></P>
+<H2 CLASS="western">Setting up the system</H2>
+<P CLASS="western"><SPAN LANG="en-US">You need to download and
+install rsyslogd first. Obtain it from the </SPAN><A HREF="http://www.rsyslog.com/">rsyslog
+site</A><SPAN LANG="en-US">. Make sure that you disable stock
+syslogd, otherwise you will experience some difficulties. On some
+distributions &nbsp;(Fedora 8 and above, for example), rsyslog may
+already by the default syslogd, in which case you obviously do not
+need to do anything specific. For many others, there are prebuild
+packages available. If you use either, please make sure that you have
+the required database plugins for your database available. It usually
+is a separate package and typically </SPAN><SPAN LANG="en-US"><B>not</B></SPAN><SPAN LANG="en-US">
+installed by default.</SPAN></P>
+<P CLASS="western"><SPAN LANG="en-US">It is important to understand
+how rsyslogd talks to the database. In rsyslogd, there is the concept
+of &quot;templates&quot;. Basically, a template is a string that
+includes some replacement characters, which are called &quot;properties&quot;
+in rsyslog. Properties are accessed via the &quot;</SPAN><A HREF="http://www.rsyslog.com/doc-property_replacer.html">Property
+Replacer</A><SPAN LANG="en-US">&quot;. Simply said, you access
+properties by including their name between percent signs inside the
+template. For example, if the syslog message is &quot;Test&quot;, the
+template &quot;%msg%&quot; would be expanded to &quot;Test&quot;.
+Rsyslogd supports sending template text as a SQL statement to the
+database. As such, the template must be a valid SQL statement. There
+is no limit in what the statement might be, but there are some
+obvious and not so obvious choices. For example, a template &quot;drop
+table xxx&quot; is possible, but does not make an awful lot of sense.
+In practice, you will always use an &quot;insert&quot; statement
+inside the template.</SPAN></P>
+<P LANG="en-US" CLASS="western">An example: if you would just like to
+store the msg part of the full syslog message, you have probably
+created a table &quot;syslog&quot; with a single column &quot;message&quot;.
+In such a case, a good template would be &quot;insert into
+syslog(message) values ('%msg%')&quot;. With the example above, that
+would be expanded to &quot;insert into syslog(message)
+values('Test')&quot;. This expanded string is then sent to the
+database. It's that easy, no special magic. The only thing you must
+ensure is that your template expands to a proper SQL statement and
+that this statement matches your database design.</P>
+<P CLASS="western"><SPAN LANG="en-US">Does that mean you need to
+create database schema yourself and also must fully understand
+rsyslogd's properties? No, that's not needed. Because we anticipated
+that folks are probably more interested in getting things going
+instead of designing them from scratch. So we have provided a default
+schema as well as build-in support for it. This schema also offers an
+additional benefit: rsyslog is part of </SPAN><A HREF="http://www.adiscon.com/en/">Adiscon</A><SPAN LANG="en-US">'s
+</SPAN><A HREF="http://www.monitorware.com/en/">MonitorWare product
+line</A><SPAN LANG="en-US"> (which includes open source and closed
+source members). All of these tools share the same default schema and
+know how to operate on it. For this reason, the default schema is
+also called the &quot;MonitorWare Schema&quot;. If you use it, you
+can simply add </SPAN><A HREF="http://www.phplogcon.org/">phpLogCon,
+a GPLed syslog web interface</A><SPAN LANG="en-US">, to your system
+and have instant interactive access to your database. So there are
+some benefits in using the provided schema.</SPAN></P>
+<P LANG="en-US" CLASS="western">The schema definition is contained in
+the file &quot;createDB.sql&quot;. It comes with the rsyslog package
+and one can be found for each supported database type (in the plugins
+directory). Review it to check that the database name is acceptable
+for you. Be sure to leave the table and field names unmodified,
+because otherwise you need to customize rsyslogd's default sql
+template, which we do not do in this paper. Then, run the script with
+your favorite SQL client. Double-check that the table was
+successfully created.</P>
+<P LANG="en-US" CLASS="western">It is important to note that the
+correct database encoding must be used so that the database will
+accept strings independend of the string encoding. This is an
+important part because it can not be guarantied that all syslog
+messages will have a defined character encoding. This is especially
+true if the rsyslog-Server will collect messages from different
+clients and different products.
+</P>
+<P LANG="en-US" CLASS="western">For example PostgreSQL may refuse to
+accept messages if you would set the database encoding to “UTF8â€
+while a client is sending invalid byte sequences for that encoding.
+</P>
+<P LANG="en-US" CLASS="western">Database support in rsyslog is
+integrated via loadable plugin modules. To use the database
+functionality, the database plugin must be enabled in the config file
+BEFORE the first database table action is used. This is done by
+placing the</P>
+<BLOCKQUOTE CLASS="western"><CODE>$ModLoad ommysql</CODE></BLOCKQUOTE>
+<P CLASS="western">directive at the begining of /etc/rsyslog.conf for
+MySQL and</P>
+<BLOCKQUOTE CLASS="western"><CODE>$ModLoad ompgsql</CODE></BLOCKQUOTE>
+<P CLASS="western"><CODE><FONT FACE="Arial, sans-serif">for
+PostgreSQL.</FONT></CODE></P>
+<P LANG="en-US" CLASS="western"><FONT FACE="Arial, sans-serif">For
+other databases, use their plugin name (e.g. omoracle).</FONT></P>
+<P CLASS="western">Next, we need to tell rsyslogd to write data to
+the database. As we use the default schema, we do NOT need to define
+a template for this. We can use the hardcoded one (rsyslogd handles
+the proper template linking). So all we need to do e.g. for MySQL is
+add a simple selector line to /etc/rsyslog.conf:</P>
+<BLOCKQUOTE CLASS="western"><CODE>*.*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+:ommysql:database-server,database-name,database-userid,database-password</CODE></BLOCKQUOTE>
+<P CLASS="western">Again, other databases have other selector names,
+e.g. &quot;:ompgsql:&quot; instead of &quot;:ommysql:&quot;. See the
+output plugin's documentation for details.</P>
+<P LANG="en-US" CLASS="western">In many cases, the database will run
+on the local machine. In this case, you can simply use &quot;127.0.0.1&quot;
+for <I>database-server</I>. This can be especially advisable, if you
+do not need to expose the database to any process outside of the
+local machine. In this case, you can simply bind it to 127.0.0.1,
+which provides a quite secure setup. Of course, rsyslog also supports
+remote database instances. In that case, use the remote server name
+(e.g. mydb.example.com) or IP-address. The <I>database-name</I> by
+default is &quot;Syslog&quot;. If you have modified the default, use
+your name here. <I>Database-userid</I> and <I>-password</I> are the
+credentials used to connect to the database. As they are stored in
+clear text in rsyslog.conf, that user should have only the least
+possible privileges. It is sufficient to grant it INSERT privileges
+to the systemevents table, only. As a side note, it is strongly
+advisable to make the rsyslog.conf file readable by root only - if
+you make it world-readable, everybody could obtain the password (and
+eventually other vital information from it). In our example, let's
+assume you have created a database user named &quot;syslogwriter&quot;
+with a password of &quot;topsecret&quot; (just to say it bluntly:
+such a password is NOT a good idea...). If your database is on the
+local machine, your rsyslog.conf line might look like in this sample:</P>
+<BLOCKQUOTE CLASS="western"><CODE>*.*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+:ommysql:127.0.0.1,Syslog,syslogwriter,topsecret</CODE></BLOCKQUOTE>
+<P CLASS="western">Save rsyslog.conf, restart rsyslogd - and you
+should see syslog messages being stored in the &quot;systemevents&quot;
+table!</P>
+<P LANG="en-US" CLASS="western">The example line stores every message
+to the database. Especially if you have a high traffic volume, you
+will probably limit the amount of messages being logged. This is easy
+to accomplish: the &quot;write database&quot; action is just a
+regular selector line. As such, you can apply normal selector-line
+filtering. If, for example, you are only interested in messages from
+the mail subsystem, you can use the following selector line:</P>
+<BLOCKQUOTE CLASS="western"><CODE>mail.*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:ommysql:127.0.0.1,syslog,syslogwriter,topsecret</CODE></BLOCKQUOTE>
+<P CLASS="western">Review the <A HREF="http://www.rsyslog.com/doc-rsyslog_conf.html">rsyslog.conf</A>
+documentation for details on selector lines and their filtering.</P>
+<P CLASS="western"><SPAN LANG="en-US"><B>You have now completed
+everything necessary to store syslog messages to the a database.</B></SPAN><SPAN LANG="en-US">
+If you would like to try out a front-end, you might want to look at
+</SPAN><A HREF="http://www.phplogcon.org/">phpLogCon</A><SPAN LANG="en-US">,
+which displays syslog data in a browser. As of this writing,
+phpLogCon is not yet a powerful tool, but it's open source, so it
+might be a starting point for your own solution.</SPAN></P>
+<H2 CLASS="western">On Reliability...</H2>
+<P LANG="en-US" CLASS="western">Rsyslogd writes syslog messages
+directly to the database. This implies that the database must be
+available at the time of message arrival. If the database is offline,
+no space is left or something else goes wrong - rsyslogd can not
+write the database record. If rsyslogd is unable to store a message,
+it performs one retry. This is helpful if the database server was
+restarted. In this case, the previous connection was broken but a
+reconnect immediately succeeds. However, if the database is down for
+an extended period of time, an immediate retry does not help.</P>
+<P CLASS="western"><SPAN LANG="en-US">Message loss in this scenario
+can easily be prevented with rsyslog. All you need to do is run the
+database writer in queued mode. This is now described in a generic
+way and I do not intend to duplicate it here. So please be sure to
+read &quot;</SPAN><A HREF="http://www.rsyslog.com/doc-rsyslog_high_database_rate.html">Handling
+a massive syslog database insert rate with Rsyslog</A><SPAN LANG="en-US">&quot;,
+which describes the scenario and also includes configuration
+examples.</SPAN></P>
+<H2 CLASS="western">Conclusion</H2>
+<P LANG="en-US" CLASS="western">With minimal effort, you can use
+rsyslogd to write syslog messages to a database. You can even make it
+absolutely fail-safe and protect it against database server downtime.
+Once the messages are arrived there, you can interactively review and
+analyze them. In practice, the messages are also stored in text files
+for longer-term archival and the databases are cleared out after some
+time (to avoid becoming too slow). If you expect an extremely high
+syslog message volume, storing it in real-time to the database may
+outperform your database server. In such cases, either filter out
+some messages or used queued mode (which in general is recommended
+with databases).</P>
+<P LANG="en-US" CLASS="western">The method outlined in this paper
+provides an easy to setup and maintain solution for most use cases.</P>
+<H3 CLASS="western">Feedback Requested</H3>
+<P CLASS="western">I would appreciate feedback on this paper. If you
+have additional ideas, comments or find bugs, please <A HREF="mailto:rgerhards@adiscon.com">let
+me know</A>.</P>
+<H2 CLASS="western">References and Additional Material</H2>
+<UL>
+ <LI><P CLASS="western" STYLE="margin-bottom: 0in"><A HREF="http://www.rsyslog.com/">www.rsyslog.com</A>
+ - the rsyslog site
+ </P>
+ <LI><P CLASS="western"><A HREF="http://www.monitorware.com/Common/en/Articles/performance-optimizing-syslog-server.php">Paper
+ on Syslog Server Optimization</A>
+ </P>
+</UL>
+<H2 CLASS="western">Revision History</H2>
+<UL>
+ <LI><P CLASS="western" STYLE="margin-bottom: 0in">2005-08-02 *
+ <A HREF="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer
+ Gerhards</A> * initial version created
+ </P>
+ <LI><P CLASS="western" STYLE="margin-bottom: 0in">2005-08-03 *
+ <A HREF="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer
+ Gerhards</A> * added references to demo site
+ </P>
+ <LI><P CLASS="western" STYLE="margin-bottom: 0in">2007-06-13 *
+ <A HREF="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer
+ Gerhards</A> * removed demo site - was torn down because too
+ expensive for usage count
+ </P>
+ <LI><P CLASS="western" STYLE="margin-bottom: 0in">2008-02-21 *
+ <A HREF="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer
+ Gerhards</A> * updated reliability section, can now be done with
+ on-demand disk queues</P>
+ <LI><P CLASS="western">2008-02-28 * <A HREF="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer
+ Gerhards</A> * added info on other databases, updated syntax to more
+ recent one
+ </P>
+ <LI><P CLASS="western">2010-01-29 * Marc Schiffbauer * added some
+ PostgreSQL stuff, made wording more database generic, fixed some
+ typos</P>
+</UL>
+<H2 CLASS="western">Copyright</H2>
+<P CLASS="western">Copyright (c) 2005-2010 <A HREF="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer
+Gerhards</A>, Marc Schiffbauer and <A HREF="http://www.adiscon.com/en/">Adiscon</A>.</P>
+<P CLASS="western"><BR><BR>
+</P>
+</BODY>
+</HTML> \ No newline at end of file
diff --git a/doc/rsyslog_php_syslog_ng.html b/doc/rsyslog_php_syslog_ng.html
index bf48a1eb..ed4d72fc 100644
--- a/doc/rsyslog_php_syslog_ng.html
+++ b/doc/rsyslog_php_syslog_ng.html
@@ -7,8 +7,10 @@
<P><small><i>Written by
<a href="http://www.adiscon.com/en/people/rainer-gerhards.php">Rainer
Gerhards</a> (2005-08-04)</i></small></P>
-<p><b>Note: it has been reported that this guide is somewhat outdated. Please
-use with care. </b></p>
+<p>Note: it has been reported that this guide is somewhat outdated. Please
+use with care. Also, please note that <b>rsyslog's "native" web frontend is
+<a href="http://www.phplogcon.org">phpLogCon</a></b>, which provides best integration
+and a lot of extra functionality.</p>
<h2>Abstract</h2>
<p><i><b>In this paper, I describe how to use
<a href="http://www.vermeer.org/projects/php-syslog-ng">php-syslog-ng</a> with
@@ -116,11 +118,11 @@ those unfamiliar with syslog-ng, this configuration is probably easier to set up
then switching to syslog-ng. For existing rsyslogd users, php-syslog-ng might be a nice
add-on to their logging infrastructure.</P>
<P>Please note that the <a href="http://www.monitorware.com/en/">MonitorWare family</a> (to which rsyslog belongs) also
-offers a web-interface: <a href="http://www.phplogcon.org/">phpLogCon</a>. At the time of this writing, phpLogCon's code
-is by far not as clean as I would like it to be. Also the user-interface is
-definitely not as intutive as pp-syslog-ng. From a functionality point of view,
-however, I think it already is a bit ahead. So you might
-consider using it. I have set up a <a href="http://demo.rsyslog.com/">demo server</a>.,
+offers a web-interface: <a href="http://www.phplogcon.org/">phpLogCon</a>.
+From my point of view, obviously, <b>phpLogCon is the more natural choice for a web interface
+to be used together with rsyslog</b>. It also offers superb functionality and provides,
+for example,native display of Windows event log entries.
+I have set up a <a href="http://demo.phplogcon.org/">demo server</a>.,
You can have a peek at it
without installing anything.</P>
<h2>Feedback Requested</h2>
diff --git a/doc/rsyslog_stunnel.html b/doc/rsyslog_stunnel.html
index 1d024934..f0c0b3af 100644
--- a/doc/rsyslog_stunnel.html
+++ b/doc/rsyslog_stunnel.html
@@ -1,5 +1,6 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
+<a href="features.html">back</a>
<title>SSL Encrypting syslog with stunnel</title><meta name="KEYWORDS" content="syslog encryption, rsyslog, stunnel, secure syslog, tcp, reliable, howto, ssl"></head><body>
<h1>SSL Encrypting Syslog with Stunnel</h1>
@@ -236,5 +237,13 @@ comments or find bugs (I *do* bugs - no way... ;)), please
Texts. A copy of the license can be viewed at
<a href="http://www.gnu.org/copyleft/fdl.html">
http://www.gnu.org/copyleft/fdl.html</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>
+<p><font size="2">This documentation is part of the
+<a href="http://www.rsyslog.com/">rsyslog</a> project.<br>
+Copyright &copy; 2008 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 2 or higher.</font></p>
</body></html>
diff --git a/doc/rsyslog_tls.html b/doc/rsyslog_tls.html
index 0a27b90e..bb312c77 100644
--- a/doc/rsyslog_tls.html
+++ b/doc/rsyslog_tls.html
@@ -1,5 +1,6 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head><title>TLS (SSL) Encrypting syslog</title>
+<a href="features.html">back</a>
<meta name="KEYWORDS" content="syslog encryption, rsyslog, secure syslog, tcp, reliable, howto, ssl, tls">
</head>
@@ -304,4 +305,13 @@ document under the terms of the GNU Free Documentation License, Version
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
Texts. A copy of the license can be viewed at
<a href="http://www.gnu.org/copyleft/fdl.html">http://www.gnu.org/copyleft/fdl.html</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>
+<p><font size="2">This documentation is part of the
+<a href="http://www.rsyslog.com/">rsyslog</a> project.<br>
+Copyright &copy; 2008 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 2 or higher.</font></p>
+
</body></html>
diff --git a/doc/src/dataflow.dia b/doc/src/dataflow.dia
new file mode 100644
index 00000000..3875fc61
--- /dev/null
+++ b/doc/src/dataflow.dia
Binary files differ
diff --git a/doc/src/direct_queue0.dia b/doc/src/direct_queue0.dia
new file mode 100644
index 00000000..4446619b
--- /dev/null
+++ b/doc/src/direct_queue0.dia
Binary files differ
diff --git a/doc/src/direct_queue1.dia b/doc/src/direct_queue1.dia
new file mode 100644
index 00000000..7a64ea09
--- /dev/null
+++ b/doc/src/direct_queue1.dia
Binary files differ
diff --git a/doc/src/direct_queue2.dia b/doc/src/direct_queue2.dia
new file mode 100644
index 00000000..b0c394c0
--- /dev/null
+++ b/doc/src/direct_queue2.dia
Binary files differ
diff --git a/doc/src/direct_queue3.dia b/doc/src/direct_queue3.dia
new file mode 100644
index 00000000..bc477b25
--- /dev/null
+++ b/doc/src/direct_queue3.dia
Binary files differ
diff --git a/doc/src/direct_queue_directq.dia b/doc/src/direct_queue_directq.dia
new file mode 100644
index 00000000..37fdb44c
--- /dev/null
+++ b/doc/src/direct_queue_directq.dia
Binary files differ
diff --git a/doc/src/direct_queue_rsyslog.dia b/doc/src/direct_queue_rsyslog.dia
new file mode 100644
index 00000000..9a030117
--- /dev/null
+++ b/doc/src/direct_queue_rsyslog.dia
Binary files differ
diff --git a/doc/src/direct_queue_rsyslog2.dia b/doc/src/direct_queue_rsyslog2.dia
new file mode 100644
index 00000000..c596f39f
--- /dev/null
+++ b/doc/src/direct_queue_rsyslog2.dia
Binary files differ
diff --git a/doc/src/queue_analogy_tv.dia b/doc/src/queue_analogy_tv.dia
new file mode 100644
index 00000000..00fbdeb5
--- /dev/null
+++ b/doc/src/queue_analogy_tv.dia
Binary files differ
diff --git a/doc/src/rsyslog_pgsql.odt b/doc/src/rsyslog_pgsql.odt
new file mode 100644
index 00000000..5034c5fb
--- /dev/null
+++ b/doc/src/rsyslog_pgsql.odt
Binary files differ
diff --git a/doc/status.html b/doc/status.html
index 5c8409ec..4e8f1a5f 100644
--- a/doc/status.html
+++ b/doc/status.html
@@ -2,29 +2,30 @@
<html><head><title>rsyslog status page</title></head>
<body>
<h2>rsyslog status page</h2>
-<p>This page reflects the status as of 2008-10-22.</p>
+<p>This page reflects the status as of 2009-05-25.</p>
<h2>Current Releases</h2>
-<p><b>development:</b> 3.21.6 [2008-10-22] -
-<a href="http://www.rsyslog.com/Article292.phtml">change log</a> -
-<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-135.phtml">download</a>
+<p><b>development:</b> 4.3.1 [2009-05-25] -
+<a href="http://www.rsyslog.com/Article372.phtml">change log</a> -
+<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-159.phtml">download</a>
-<br><b>beta:</b> 3.19.12 [2008-10-16] -
-<a href="http://www.rsyslog.com/Article283.phtml">change log</a> -
-<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-134.phtml">download</a></p>
+<br><b>beta:</b> 3.21.11 [2009-04-03] -
+<a href="http://www.rsyslog.com/Article358.phtml">change log</a> -
+<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-152.phtml">download</a></p>
-<p><b>v3 stable:</b> 3.18.5 [2008-10-09] - <a href="http://www.rsyslog.com/Article281.phtml">change log</a> -
-<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-133.phtml">download</a>
+<p><b>v3 stable:</b> 3.22.0 [2009-04-21] - <a href="http://www.rsyslog.com/Article368.phtml">change log</a> -
+<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-157.phtml">download</a>
-<br><b>v2 stable:</b> 2.0.6 [2008-08-07] - <a href="http://www.rsyslog.com/Article266.phtml">change log</a> -
-<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-125.phtml">download</a>
+<br><b>v2 stable:</b> 2.0.7 [2009-04-14] - <a href="http://www.rsyslog.com/Article362.phtml">change log</a> -
+<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-154.phtml">download</a>
<br>v0 and v1 are deprecated and no longer supported. If you absolutely do not like to
upgrade, you may consider purchasing a
<a href="professional_support.html">commercial rsyslog support package</a>. Just let us point
out that it is really not a good idea to still run a v0 version.
<p><a href="v3compatibility.html">If you updgrade from version 2, be sure to read the rsyslog v3
-compatibility document.</a></p>
+compatibility document.</a> There are no additional compatibility concerns at this time for
+upgrading from v3 to v4. If some occur, we will post an additional compatiblity document.</p>
<p>(<a href="version_naming.html">How are versions named?</a>)</p>
<h2>Platforms</h2>
diff --git a/doc/syslog_protocol.html b/doc/syslog_protocol.html
index 72de5c27..57eb9ffe 100644
--- a/doc/syslog_protocol.html
+++ b/doc/syslog_protocol.html
@@ -3,6 +3,7 @@
<title>syslog-protocol support in rsyslog</title>
</head>
<body>
+<a href="features.html">back</a>
<h1>syslog-protocol support in rsyslog</h1>
<p><b><a href="http://www.rsyslog.com/">Rsyslog</a>&nbsp; provides a trial
implementation of the proposed
@@ -191,6 +192,14 @@ discussed ;)</p>
syslog-protocol should be further evaluated and be fully understood</li>
</ul>
<p>&nbsp;</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 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 2 or higher.</font></p>
</body>
</html>
diff --git a/doc/tls_cert_machine.html b/doc/tls_cert_machine.html
index 5ecde0d1..095e15c2 100644
--- a/doc/tls_cert_machine.html
+++ b/doc/tls_cert_machine.html
@@ -75,7 +75,15 @@ Locality name: <font color="red">Somewhere</font>
State or province name: <font color="red">CA</font>
Common name: <font color="red">machine.example.net</font>
UID:
-Enter a challenge password:
+Enter a dnsName of the subject of the certificate:
+Enter the IP address of the subject of the certificate:
+Enter the e-mail of the subject of the certificate:
+Enter a challange password:
+Does the certificate belong to an authority? (y/N): <font color="red">n</font>
+Will the certificate be used for signing (DHE and RSA-EXPORT ciphersuites)? (y/N):
+Will the certificate be used for encryption (RSA ciphersuites)? (y/N):
+Is this a TLS web client certificate? (y/N): <font color="red">y</font>
+Is this also a TLS web server certificate? (y/N): <font color="red">y</font>
[root@rgf9dev sample]# <font color="red">certtool --generate-certificate --load-request request.pem --outfile cert.pem --load-ca-certificate ca.pem --load-ca-privkey ca-key.pem</font>
Generating a signed certificate...
Enter the certificate's serial number (decimal):
@@ -86,10 +94,12 @@ The certificate will expire in (days): 1000
Extensions.
+Do you want to honour the extensions from the request? (y/N):
Does the certificate belong to an authority? (Y/N): <font color="red">n</font>
Is this a TLS web client certificate? (Y/N): <font color="red">y</font>
Is this also a TLS web server certificate? (Y/N): <font color="red">y</font>
Enter the dnsName of the subject of the certificate: <font color="red">machine.example.net</font> <i>{This is the name of the machine that will use the certificate}</i>
+Enter the IP address of the subject of certificate:
Will the certificate be used for signing (DHE and RSA-EXPORT ciphersuites)? (Y/N):
Will the certificate be used for encryption (RSA ciphersuites)? (Y/N):
X.509 Certificate Information:
diff --git a/doc/troubleshoot.html b/doc/troubleshoot.html
index e655c2ef..a8855fd4 100644
--- a/doc/troubleshoot.html
+++ b/doc/troubleshoot.html
@@ -18,7 +18,14 @@ is a rsyslog-doc package, that often needs to be installed separately.
<p><b>Malformed Messages and Message Properties</b>
<p>A common trouble source are <a href="syslog_parsing.html">ill-formed syslog messages</a>, which
lead to to all sorts of interesting problems, including malformed hostnames and dates.
-Read the quoted guide to find relief.
+Read the quoted guide to find relief. A common symptom is that the %HOSTNAME% property is
+used for generating dynafile names, but some glibberish shows up. This is caused by the
+malformed syslog messages, so be sure to read the
+<a href="syslog_parsing.html">guide</a> if you face that problem. Just let me add that the
+common work-around is to use %FROMHOST% or %FROMHOST-IP% instead. These do not take the
+hostname from the message, but rather use the host that sent the message (taken from
+the socket layer). Of course, this does not work over NAT or relay chains, where the
+only cure is to make sure senders emit well-formed messages.
<p><b>Configuration Problems</b>
<p>Rsyslog 3.21.1 and above has been enhanced to support extended configuration checking.
It offers a special command line switch (-N1) that puts it into "config verfication mode".
@@ -28,6 +35,15 @@ mode can be used in parallel to a running instance of rsyslogd.
<p><b><i>/path/to/rsyslogd -f/path/to/config-file -N1</i></b>
<p>You should also specify other options you usually give (like -c3 and whatever else).
Any problems experienced are reported to stderr [aka "your screen" (if not redirected)].
+<p><b>Configuration Graphs</b>
+<p>Starting with rsyslog 4.3.1, the
+&quot;<a href="rsconf1_generateconfiggraph.html">$GenerateConfigGraph</a>&quot;
+command is supported, a very valuable troubleshooting tool. It permits to
+generate a graph of how rsyslogd understood its configuration file. It is assumed that
+many configuration issues can easily be detected just by looking at the configuration graph.
+Full details of how to generate the graphs, and what to look for can be found in the
+&quot;<a href="rsconf1_generateconfiggraph.html">$GenerateConfigGraph</a>&quot;
+manual page.
<p><b>Asking for Help</b>
<p>If you can't find the answer yourself, you should look at these places for
community help.
diff --git a/doc/v4compatibility.html b/doc/v4compatibility.html
new file mode 100644
index 00000000..5d877af1
--- /dev/null
+++ b/doc/v4compatibility.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><title>Compatibility notes for rsyslog v4</title>
+</head>
+<body>
+<h1>Compatibility Notes for rsyslog v4</h1>
+<p><small><i>Written by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a>
+(2009-07-15)</i></small></p>
+<p>The changes introduced in rsyslog v4 are numerous, but not very intrusive.
+This document describes things to keep in mind when moving from v3 to v4. It
+does not list enhancements nor does it talk about compatibility concerns introduced
+by v3 (for this, see the <a href="v3compatibility.html">rsyslog v3 compatibility notes</a>).
+<h2>HUP processing</h2>
+<p>With v3 and below, rsyslog used the traditional HUP behaviour. That meant that
+all output files are closed and the configuration file is re-read and the new configuration
+applied.
+<p>With a program as simple and static as sysklogd, this was not much of an issue. The
+most important config settings (like udp reception) of a traditional syslogd can not be
+modified via the configuration file. So a config file reload only meant setting up a new set of filters. It also didn't account as problem that while doing so messages may be lost - without
+any threading and queuing model, a traditional syslogd will potentially always loose
+messages, so it is irrelevant if this happens, too, during the short config re-read
+phase.
+<p>In rsyslog, things are quite different: the program is more or less a framework into
+which loadable modules are loaded as needed for a particular configuration. The software
+that will acutally be running is taylored via the config file. Thus, a re-read of
+the config file requires a full, very heavy restart, because the software acutally
+running with the new config can be totally different from what ran with the old config.
+<p>Consequently, the traditional HUP is a very heavy operation and may even cause some
+data loss because queues must be shut down, listeners stopped and so on. Some of these
+operations (depending on their configuration) involve intentional message loss. The operation
+also takes up a lot of system resources and needs quite some time (maybe seconds) to be
+completed. During this restart period, the syslog subsytem is not fully available.
+<p>From the software developer's point of view, the full restart done by a HUP is rather complex,
+especially if user-timeout limits set on action completion are taken into consideration (for
+those in the know: at the extreme ends this means we need to cancel threads as a last resort,
+but than we need to make sure that such cancellation does not happen at points where it
+would be fatal for a restart). A regular restart, where the process is actually terminated, is
+much less complex, because the operating system does a full cleanup after process termination,
+so rsyslogd does not need to take care for exotic cleanup cases and leave that to the OS.
+In the end result, restart-type HUPs clutter the code, increase complexity (read: add bugs)
+and cost performance.
+<p>On the contrary, a HUP is typically needed for log rotation, and the real desire is
+to close files. This is a non-disruptive and very lightweigth operation.
+<p>Many people have said that they are used to HUP the syslogd to apply configuration
+changes. This is true, but it is questionable if that really justifies all the cost that
+comes with it. After all, it is the difference between typing
+<pre>
+$ kill -HUP `cat /var/run/rsyslogd.pid`
+</pre>
+versus
+<pre>
+$ /etc/init.d/rsyslog restart
+</pre>
+Semantically, both is mostly the same thing. The only difference is that with the restart
+command rsyslogd can spit config error message to stderr, so that the user is able to see
+any problems and fix them. With a HUP, we do not have access to stderr and thus can log
+error messages only to their configured destinations; exprience tells that most users
+will never find them there. What, by the way, is another strong argument against
+restarting rsyslogd by HUPing it.
+<p>So a restart via HUP is not strictly necessary
+and most other deamons require that a restart command is typed in if a restart is required.
+<p>Rsyslog will follow this paradigm in the next versions, resulting in many benefits. In v4,
+we provide some support for the old-style semantics. We introduced a setting $HUPisRestart
+which may be set to &quot;on&quot; (tradional, heavy operationg)
+or &quot;off&quot; (new, lightweight &quot;file close only&quot; operation).
+The initial versions had the default set to traditional behavior, but starting with 4.5.1
+we are now using the new behavior as the default.
+<p>Most importantly, <b>this may break some scripts</b>, but my sincere belief is that
+there are very few scripts that automatically <b>change</b> rsyslog's config and then do a
+HUP to reload it. Anyhow, if you have some of these, it may be a good idea to change
+them now instead of turning restart-type HUPs on. Other than that, one mainly needs
+to change the habit of how to restart rsyslog after a configuration change.
+<p><b>Please note that restart-type HUP is depricated and will go away in rsyslog v5.</b>
+So it is a good idea to become ready for the new version now and also enjoy some of the
+benefits of the &quot;real restart&quot;, like the better error-reporting capability.
+<p>Note that code complexity reduction (and thus performance improvement) needs the restart-type
+HUP code to be removed, so these changes can (and will) only happen in version 5.
+</body></html>
diff --git a/m4/ax_check_off64_t.m4 b/m4/ax_check_off64_t.m4
new file mode 100644
index 00000000..05d6f45d
--- /dev/null
+++ b/m4/ax_check_off64_t.m4
@@ -0,0 +1,69 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_check_off64_t.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_CHECK_OFF64_T
+#
+# DESCRIPTION
+#
+# Check if off64_t is defined. On true define HAVE_OFF64_T, also define
+# __LARGEFILE64_SOURCE where one is needed. (Note that an appropriative
+# entry must be in config.h.in.)
+#
+# LICENSE
+#
+# Copyright (c) 2008 Ruslan Shevchenko <Ruslan@Shevchenko.Kiev.UA>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 6
+
+AU_ALIAS([RSSH_CHECK_OFF64_T], [AX_CHECK_OFF64_T])
+AC_DEFUN([AX_CHECK_OFF64_T], [
+AC_REQUIRE([AC_SYS_LARGEFILE])dnl
+AC_CHECK_HEADER(unistd.h)
+AC_CACHE_CHECK([whether type off64_t support],
+ [ax_cv_check_off64_t],
+ [
+ AC_COMPILE_IFELSE(
+AC_LANG_SOURCE([
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+extern off64_t x1;
+])
+,ax_have_off64t=1)
+ if test "x$ax_have_off64t" = "x"
+ then
+ AC_COMPILE_IFELSE(
+AC_LANG_SOURCE([
+#define _LARGEFILE64_SOURCE
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+extern off64_t x1;
+]),
+ ax_cv_check_off64_t="_LARGEFILE64_SOURCE",
+ ax_cv_check_off64_t="no"
+)dnl
+
+ else
+ ax_cv_check_off64_t=yes
+ fi
+ ])dnl
+
+if test "x$ax_cv_check_off64_t" = "x_LARGEFILE64_SOURCE"
+then
+ AC_DEFINE(_LARGEFILE64_SOURCE)
+ AC_DEFINE(HAVE_OFF64_T)
+elif test "x$ax_cv_check_off64_t" = "xyes"
+then
+ AC_DEFINE(HAVE_OFF64_T)
+fi
+])dnl
+
diff --git a/m4/shave.m4 b/m4/shave.m4
new file mode 100644
index 00000000..e647e579
--- /dev/null
+++ b/m4/shave.m4
@@ -0,0 +1,99 @@
+dnl Make automake/libtool output more friendly to humans
+dnl
+dnl Copyright (c) 2009, Damien Lespiau <damien.lespiau@gmail.com>
+dnl
+dnl Permission is hereby granted, free of charge, to any person
+dnl obtaining a copy of this software and associated documentation
+dnl files (the "Software"), to deal in the Software without
+dnl restriction, including without limitation the rights to use,
+dnl copy, modify, merge, publish, distribute, sublicense, and/or sell
+dnl copies of the Software, and to permit persons to whom the
+dnl Software is furnished to do so, subject to the following
+dnl conditions:
+dnl
+dnl The above copyright notice and this permission notice shall be
+dnl included in all copies or substantial portions of the Software.
+dnl
+dnl THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+dnl EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+dnl OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+dnl NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+dnl HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+dnl WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+dnl FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+dnl OTHER DEALINGS IN THE SOFTWARE.
+dnl
+dnl SHAVE_INIT([shavedir],[default_mode])
+dnl
+dnl shavedir: the directory where the shave scripts are, it defaults to
+dnl $(top_builddir)
+dnl default_mode: (enable|disable) default shave mode. This parameter
+dnl controls shave's behaviour when no option has been
+dnl given to configure. It defaults to disable.
+dnl
+dnl * SHAVE_INIT should be called late in your configure.(ac|in) file (just
+dnl before AC_CONFIG_FILE/AC_OUTPUT is perfect. This macro rewrites CC and
+dnl LIBTOOL, you don't want the configure tests to have these variables
+dnl re-defined.
+dnl * This macro requires GNU make's -s option.
+
+AC_DEFUN([_SHAVE_ARG_ENABLE],
+[
+ AC_ARG_ENABLE([shave],
+ AS_HELP_STRING(
+ [--enable-shave],
+ [use shave to make the build pretty [[default=$1]]]),,
+ [enable_shave=$1]
+ )
+])
+
+AC_DEFUN([SHAVE_INIT],
+[
+ dnl you can tweak the default value of enable_shave
+ m4_if([$2], [enable], [_SHAVE_ARG_ENABLE(yes)], [_SHAVE_ARG_ENABLE(no)])
+
+ if test x"$enable_shave" = xyes; then
+ dnl where can we find the shave scripts?
+ m4_if([$1],,
+ [shavedir="$ac_pwd"],
+ [shavedir="$ac_pwd/$1"])
+ AC_SUBST(shavedir)
+
+ dnl make is now quiet
+ AC_SUBST([MAKEFLAGS], [-s])
+ AC_SUBST([AM_MAKEFLAGS], ['`test -z $V && echo -s`'])
+
+ dnl we need sed
+ AC_CHECK_PROG(SED,sed,sed,false)
+
+ dnl substitute libtool
+ SHAVE_SAVED_LIBTOOL=$LIBTOOL
+ LIBTOOL="${SHELL} ${shavedir}/shave-libtool '${SHAVE_SAVED_LIBTOOL}'"
+ AC_SUBST(LIBTOOL)
+
+ dnl substitute cc/cxx
+ SHAVE_SAVED_CC=$CC
+ SHAVE_SAVED_CXX=$CXX
+ SHAVE_SAVED_FC=$FC
+ SHAVE_SAVED_F77=$F77
+ SHAVE_SAVED_OBJC=$OBJC
+ CC="${SHELL} ${shavedir}/shave cc ${SHAVE_SAVED_CC}"
+ CXX="${SHELL} ${shavedir}/shave cxx ${SHAVE_SAVED_CXX}"
+ FC="${SHELL} ${shavedir}/shave fc ${SHAVE_SAVED_FC}"
+ F77="${SHELL} ${shavedir}/shave f77 ${SHAVE_SAVED_F77}"
+ OBJC="${SHELL} ${shavedir}/shave objc ${SHAVE_SAVED_OBJC}"
+ AC_SUBST(CC)
+ AC_SUBST(CXX)
+ AC_SUBST(FC)
+ AC_SUBST(F77)
+ AC_SUBST(OBJC)
+
+ V=@
+ else
+ V=1
+ fi
+ Q='$(V:1=)'
+ AC_SUBST(V)
+ AC_SUBST(Q)
+])
+
diff --git a/outchannel.c b/outchannel.c
index 5c348b63..74c18218 100644
--- a/outchannel.c
+++ b/outchannel.c
@@ -105,22 +105,21 @@ static rsRetVal get_Field(uchar **pp, uchar **pField)
skip_Comma((char**)pp);
p = *pp;
- CHKiRet(rsCStrConstruct(&pStrB));
- rsCStrSetAllocIncrement(pStrB, 32);
+ CHKiRet(cstrConstruct(&pStrB));
/* copy the field */
while(*p && *p != ' ' && *p != ',') {
- CHKiRet(rsCStrAppendChar(pStrB, *p++));
+ CHKiRet(cstrAppendChar(pStrB, *p++));
}
*pp = p;
- CHKiRet(rsCStrFinish(pStrB));
- CHKiRet(rsCStrConvSzStrAndDestruct(pStrB, pField, 0));
+ CHKiRet(cstrFinalize(pStrB));
+ CHKiRet(cstrConvSzStrAndDestruct(pStrB, pField, 0));
finalize_it:
if(iRet != RS_RET_OK) {
if(pStrB != NULL)
- rsCStrDestruct(&pStrB);
+ cstrDestruct(&pStrB);
}
RETiRet;
@@ -174,22 +173,21 @@ static inline rsRetVal get_restOfLine(uchar **pp, uchar **pBuf)
skip_Comma((char**)pp);
p = *pp;
- CHKiRet(rsCStrConstruct(&pStrB));
- rsCStrSetAllocIncrement(pStrB, 32);
+ CHKiRet(cstrConstruct(&pStrB));
/* copy the field */
while(*p) {
- CHKiRet(rsCStrAppendChar(pStrB, *p++));
+ CHKiRet(cstrAppendChar(pStrB, *p++));
}
*pp = p;
- CHKiRet(rsCStrFinish(pStrB));
- CHKiRet(rsCStrConvSzStrAndDestruct(pStrB, pBuf, 0));
+ CHKiRet(cstrFinalize(pStrB));
+ CHKiRet(cstrConvSzStrAndDestruct(pStrB, pBuf, 0));
finalize_it:
if(iRet != RS_RET_OK) {
if(pStrB != NULL)
- rsCStrDestruct(&pStrB);
+ cstrDestruct(&pStrB);
}
RETiRet;
diff --git a/parse.c b/parse.c
index 58458d62..5288c8b4 100644
--- a/parse.c
+++ b/parse.c
@@ -256,7 +256,7 @@ 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) {
- CHKiRet(rsCStrAppendChar(pCStr, bConvLower ? tolower(*pC) : *pC));
+ CHKiRet(cstrAppendChar(pCStr, bConvLower ? tolower(*pC) : *pC));
++pThis->iCurrPos;
++pC;
}
@@ -268,10 +268,10 @@ rsRetVal parsDelimCStr(rsParsObj *pThis, cstr_t **ppCStr, char cDelim, int bTrim
/* We got the string, now take it and see if we need to
* remove anything at its end.
*/
- CHKiRet(rsCStrFinish(pCStr));
+ CHKiRet(cstrFinalize(pCStr));
if(bTrimTrailing) {
- CHKiRet(rsCStrTrimTrailingWhiteSpace(pCStr));
+ CHKiRet(cstrTrimTrailingWhiteSpace(pCStr));
}
/* done! */
@@ -313,23 +313,23 @@ rsRetVal parsQuotedCStr(rsParsObj *pThis, cstr_t **ppCStr)
pC = rsCStrGetBufBeg(pThis->pCStr) + pThis->iCurrPos;
/* OK, we most probably can obtain a value... */
- CHKiRet(rsCStrConstruct(&pCStr));
+ CHKiRet(cstrConstruct(&pCStr));
- while(pThis->iCurrPos < rsCStrLen(pThis->pCStr)) {
+ while(pThis->iCurrPos < cstrLen(pThis->pCStr)) {
if(*pC == '"') {
break; /* we are done! */
} else if(*pC == '\\') {
++pThis->iCurrPos;
++pC;
- if(pThis->iCurrPos < rsCStrLen(pThis->pCStr)) {
+ if(pThis->iCurrPos < cstrLen(pThis->pCStr)) {
/* in this case, we copy the escaped character
* to the output buffer (but do not rely on this,
* we might later introduce other things, like \007!
*/
- CHKiRet(rsCStrAppendChar(pCStr, *pC));
+ CHKiRet(cstrAppendChar(pCStr, *pC));
}
} else { /* regular character */
- CHKiRet(rsCStrAppendChar(pCStr, *pC));
+ CHKiRet(cstrAppendChar(pCStr, *pC));
}
++pThis->iCurrPos;
++pC;
@@ -339,12 +339,12 @@ rsRetVal parsQuotedCStr(rsParsObj *pThis, cstr_t **ppCStr)
++pThis->iCurrPos; /* 'eat' trailing quote */
} else {
/* error - improperly quoted string! */
- rsCStrDestruct(&pCStr);
+ cstrDestruct(&pCStr);
ABORT_FINALIZE(RS_RET_MISSING_TRAIL_QUOTE);
}
/* We got the string, let's finish it... */
- CHKiRet(rsCStrFinish(pCStr));
+ CHKiRet(cstrFinalize(pCStr));
/* done! */
*ppCStr = pCStr;
@@ -352,7 +352,7 @@ rsRetVal parsQuotedCStr(rsParsObj *pThis, cstr_t **ppCStr)
finalize_it:
if(iRet != RS_RET_OK) {
if(pCStr != NULL)
- rsCStrDestruct(&pCStr);
+ cstrDestruct(&pCStr);
}
RETiRet;
@@ -380,7 +380,7 @@ rsRetVal parsAddrWithBits(rsParsObj *pThis, struct NetAddr **pIP, int *pBits)
assert(pIP != NULL);
assert(pBits != NULL);
- CHKiRet(rsCStrConstruct(&pCStr));
+ CHKiRet(cstrConstruct(&pCStr));
parsSkipWhitespace(pThis);
pC = rsCStrGetBufBeg(pThis->pCStr) + pThis->iCurrPos;
@@ -390,8 +390,8 @@ rsRetVal parsAddrWithBits(rsParsObj *pThis, struct NetAddr **pIP, int *pBits)
*/
while(pThis->iCurrPos < rsCStrLen(pThis->pCStr)
&& *pC != '/' && *pC != ',' && !isspace((int)*pC)) {
- if((iRet = rsCStrAppendChar(pCStr, *pC)) != RS_RET_OK) {
- rsCStrDestruct (&pCStr);
+ if((iRet = cstrAppendChar(pCStr, *pC)) != RS_RET_OK) {
+ cstrDestruct (&pCStr);
FINALIZE;
}
++pThis->iCurrPos;
@@ -399,15 +399,15 @@ rsRetVal parsAddrWithBits(rsParsObj *pThis, struct NetAddr **pIP, int *pBits)
}
/* We got the string, let's finish it... */
- if((iRet = rsCStrFinish(pCStr)) != RS_RET_OK) {
- rsCStrDestruct (&pCStr);
+ if((iRet = cstrFinalize(pCStr)) != RS_RET_OK) {
+ cstrDestruct(&pCStr);
FINALIZE;
}
/* now we have the string and must check/convert it to
* an NetAddr structure.
*/
- CHKiRet(rsCStrConvSzStrAndDestruct(pCStr, &pszIP, 0));
+ CHKiRet(cstrConvSzStrAndDestruct(pCStr, &pszIP, 0));
*pIP = calloc(1, sizeof(struct NetAddr));
diff --git a/plugins/cust1/Makefile.am b/plugins/cust1/Makefile.am
new file mode 100644
index 00000000..d2e075f9
--- /dev/null
+++ b/plugins/cust1/Makefile.am
@@ -0,0 +1,6 @@
+pkglib_LTLIBRARIES = cust1.la
+
+cust1_la_SOURCES = cust1.c
+cust1_la_CPPFLAGS = -I$(top_srcdir) $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
+cust1_la_LDFLAGS = -module -avoid-version
+cust1_la_LIBADD =
diff --git a/plugins/im3195/im3195.c b/plugins/im3195/im3195.c
index 1c2502fe..106da2c8 100644
--- a/plugins/im3195/im3195.c
+++ b/plugins/im3195/im3195.c
@@ -47,6 +47,7 @@
#include "liblogging/syslogmessage.h"
#include "module-template.h"
#include "cfsysline.h"
+#include "msg.h"
#include "errmsg.h"
MODULE_TYPE_INPUT
@@ -83,7 +84,7 @@ void OnReceive(srAPIObj __attribute__((unused)) *pMyAPI, srSLMGObj* pSLMG)
srSLMGGetRawMSG(pSLMG, &pszRawMsg);
parseAndSubmitMessage(fromHost, fromHostIP, pszRawMsg, strlen((char*)pszRawMsg),
- MSG_PARSE_HOSTNAME, NOFLAG, eFLOWCTL_FULL_DELAY, (uchar*)"im3195");
+ PARSE_HOSTNAME, eFLOWCTL_FULL_DELAY, (uchar*)"im3195", NULL, 0);
}
diff --git a/plugins/imdiag/imdiag.c b/plugins/imdiag/imdiag.c
index 77e99236..bf972191 100644
--- a/plugins/imdiag/imdiag.c
+++ b/plugins/imdiag/imdiag.c
@@ -1,5 +1,3 @@
-#warning "imdiag is NOT supported in this version of rsyslog"
-#if 0
/* imdiag.c
* This is a diagnostics module, primarily meant for troubleshooting
* and information about the runtime state of rsyslog. It is implemented
@@ -9,7 +7,7 @@
*
* File begun on 2008-07-25 by RGerhards
*
- * Copyright 2008 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2008, 2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -28,8 +26,8 @@
*
* A copy of the GPL can be found in the file "COPYING" in this distribution.
*/
-
#include "config.h"
+#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
@@ -45,117 +43,402 @@
#include <fcntl.h>
#endif
#include "rsyslog.h"
-//#include "dirty.h"
+#include "dirty.h"
#include "cfsysline.h"
#include "module-template.h"
+#include "unicode-helper.h"
#include "net.h"
#include "netstrm.h"
#include "errmsg.h"
+#include "tcpsrv.h"
+#include "srUtils.h"
+#include "msg.h"
+#include "datetime.h"
+#include "net.h" /* for permittedPeers, may be removed when this is removed */
MODULE_TYPE_INPUT
/* static data */
DEF_IMOD_STATIC_DATA
+DEFobjCurrIf(tcpsrv)
+DEFobjCurrIf(tcps_sess)
DEFobjCurrIf(net)
DEFobjCurrIf(netstrm)
DEFobjCurrIf(errmsg)
+DEFobjCurrIf(datetime)
+DEFobjCurrIf(prop)
/* Module static data */
-netstrms_t *pNS; /**< pointer to network stream subsystem */
-netstrm_t **ppLstn[10]; /**< our netstream listners */
-int iLstnMax = 0; /**< max nbr of listeners currently supported */
+static tcpsrv_t *pOurTcpsrv = NULL; /* our TCP server(listener) TODO: change for multiple instances */
+static permittedPeers_t *pPermPeersRoot = NULL;
+static prop_t *pInputName = NULL; /* there is only one global inputName for all messages generated by this input */
+static prop_t *pRcvDummy = NULL;
+static prop_t *pRcvIPDummy = NULL;
/* config settings */
+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 */
+static uchar *pszInputName = NULL; /* value for inputname property, NULL is OK and handled by core engine */
+
+
+/* callbacks */
+/* this shall go into a specific ACL module! */
+static int
+isPermittedHost(struct sockaddr __attribute__((unused)) *addr, char __attribute__((unused)) *fromHostFQDN,
+ void __attribute__((unused)) *pUsrSrv, void __attribute__((unused)) *pUsrSess)
+{
+ return 1; /* TODO: implement ACLs ... or via some other way? */
+}
+
+
+static rsRetVal
+doOpenLstnSocks(tcpsrv_t *pSrv)
+{
+ ISOBJ_TYPE_assert(pSrv, tcpsrv);
+ return tcpsrv.create_tcp_socket(pSrv);
+}
+
+
+static rsRetVal
+doRcvData(tcps_sess_t *pSess, char *buf, size_t lenBuf, ssize_t *piLenRcvd)
+{
+ DEFiRet;
+ assert(pSess != NULL);
+ assert(piLenRcvd != NULL);
+
+ *piLenRcvd = lenBuf;
+ CHKiRet(netstrm.Rcv(pSess->pStrm, (uchar*) buf, piLenRcvd));
+finalize_it:
+ RETiRet;
+}
+
+static rsRetVal
+onRegularClose(tcps_sess_t *pSess)
+{
+ DEFiRet;
+ assert(pSess != NULL);
+ /* process any incomplete frames left over */
+ tcps_sess.PrepareClose(pSess);
+ /* Session closed */
+ tcps_sess.Close(pSess);
+ RETiRet;
+}
-/* add a listen socket to our listen socket array. This is a callback
- * invoked from the netstrm class. -- rgerhards, 2008-04-23
+
+static rsRetVal
+onErrClose(tcps_sess_t *pSess)
+{
+ DEFiRet;
+ assert(pSess != NULL);
+
+ tcps_sess.Close(pSess);
+ RETiRet;
+}
+
+/* ------------------------------ end callbacks ------------------------------ */
+
+
+/* get the first word delimited by space from a given string. The pointer is
+ * advanced to after the word. Any leading spaces are discarded. If the
+ * output buffer is too small, parsing ends on buffer full condition.
+ * An empty buffer is returned if there is no more data inside the string.
+ * rgerhards, 2009-05-27
+ */
+#define TO_LOWERCASE 1
+#define NO_MODIFY 0
+static void
+getFirstWord(uchar **ppszSrc, uchar *pszBuf, size_t lenBuf, int options)
+{
+ uchar c;
+ uchar *pszSrc = *ppszSrc;
+
+ while(*pszSrc && *pszSrc == ' ')
+ ++pszSrc; /* skip to first non-space */
+
+ while(*pszSrc && *pszSrc != ' ' && lenBuf > 1) {
+ c = *pszSrc++;
+ if(options & TO_LOWERCASE)
+ c = tolower(c);
+ *pszBuf++ = c;
+ lenBuf--;
+ }
+
+ *pszBuf = '\0';
+ *ppszSrc = pszSrc;
+}
+
+
+/* send a response back to the originator
+ * rgerhards, 2009-05-27
+ */
+static rsRetVal __attribute__((format(printf, 2, 3)))
+sendResponse(tcps_sess_t *pSess, char *fmt, ...)
+{
+ va_list ap;
+ ssize_t len;
+ uchar buf[1024];
+ DEFiRet;
+
+ va_start(ap, fmt);
+ len = vsnprintf((char*)buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ CHKiRet(netstrm.Send(pSess->pStrm, buf, &len));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* actually submit a message to the rsyslog core
*/
static rsRetVal
-addTcpLstn(void *pUsr, netstrm_t *pLstn)
+doInjectMsg(int iNum)
{
+ uchar szMsg[1024];
+ msg_t *pMsg;
+ struct syslogTime stTime;
+ time_t ttGenTime;
DEFiRet;
- ISOBJ_TYPE_assert(pLstn, netstrm);
+ snprintf((char*)szMsg, sizeof(szMsg)/sizeof(uchar),
+ "<167>Mar 1 01:00:00 172.20.245.8 tag msgnum:%8.8d:\n", iNum);
+
+ datetime.getCurrTime(&stTime, &ttGenTime);
+ /* we now create our own message object and submit it to the queue */
+ CHKiRet(msgConstructWithTime(&pMsg, &stTime, ttGenTime));
+ MsgSetRawMsg(pMsg, (char*) szMsg, ustrlen(szMsg));
+ MsgSetInputName(pMsg, pInputName);
+ MsgSetFlowControlType(pMsg, eFLOWCTL_NO_DELAY);
+ pMsg->msgFlags = NEEDS_PARSING | PARSE_HOSTNAME;
+ pMsg->bParseHOSTNAME = 1;
+ MsgSetRcvFrom(pMsg, pRcvDummy);
+ CHKiRet(MsgSetRcvFromIP(pMsg, pRcvIPDummy));
+ CHKiRet(submitMsg(pMsg));
- if(iLstnMax >= sizeof(ppLstn)/sizeof(netstrm_t))
- ABORT_FINALIZE(RS_RET_MAX_LSTN_REACHED);
+finalize_it:
+ RETiRet;
+}
- ppLstn[pThis->iLstnMax] = pLstn;
- ++iLstnMax;
+
+/* This function injects messages. Command format:
+ * injectmsg <fromnbr> <number-of-messages>
+ * rgerhards, 2009-05-27
+ */
+static rsRetVal
+injectMsg(uchar *pszCmd, tcps_sess_t *pSess)
+{
+ uchar wordBuf[1024];
+ int iFrom;
+ int nMsgs;
+ int i;
+ DEFiRet;
+
+ /* we do not check errors here! */
+ getFirstWord(&pszCmd, wordBuf, sizeof(wordBuf)/sizeof(uchar), TO_LOWERCASE);
+ iFrom = atoi((char*)wordBuf);
+ getFirstWord(&pszCmd, wordBuf, sizeof(wordBuf)/sizeof(uchar), TO_LOWERCASE);
+ nMsgs = atoi((char*)wordBuf);
+
+ for(i = 0 ; i < nMsgs ; ++i) {
+ doInjectMsg(i + iFrom);
+ }
+
+ CHKiRet(sendResponse(pSess, "messages injected\n"));
finalize_it:
RETiRet;
}
-/* initialize network stream subsystem */
+/* This function waits until the main queue is drained (size = 0)
+ */
static rsRetVal
-initNetstrm(void)
+waitMainQEmpty(tcps_sess_t *pSess)
{
+ int iMsgQueueSize;
DEFiRet;
- /* prepare network stream subsystem */
- CHKiRet(netstrms.Construct(&pNS));
- CHKiRet(netstrms.SetDrvrMode(pNS, 0)); /* always plain text */
- //CHKiRet(netstrms.SetDrvrAuthMode(pThis->pNS, pThis->pszDrvrAuthMode));
- //CHKiRet(netstrms.SetDrvrPermPeers(pThis->pNS, pThis->pPermPeers));
- // TODO: set driver!
- CHKiRet(netstrms.ConstructFinalize(pThis->pNS));
+ CHKiRet(diagGetMainMsgQSize(&iMsgQueueSize));
+ while(iMsgQueueSize > 0) {
+ srSleep(0,2); /* wait a little bit */
+ CHKiRet(diagGetMainMsgQSize(&iMsgQueueSize));
+ }
- /* set up listeners */
- CHKiRet(netstrm.LstnInit(pNS, NULL, addTcpLstn, "127.0.0.1", "44514", 1));
+ CHKiRet(sendResponse(pSess, "mainqueue empty\n"));
finalize_it:
- if(iRet != RS_RET_OK) {
- if(pThis->pNS != NULL)
- netstrms.Destruct(&pThis->pNS);
+ RETiRet;
+}
+
+/* Function to handle received messages. This is our core function!
+ * rgerhards, 2009-05-24
+ */
+static rsRetVal
+OnMsgReceived(tcps_sess_t *pSess, uchar *pRcv, int iLenMsg)
+{
+ int iMsgQueueSize;
+ uchar *pszMsg;
+ uchar cmdBuf[1024];
+ DEFiRet;
+
+ assert(pSess != NULL);
+ assert(pRcv != NULL);
+
+ /* NOTE: pRcv is NOT a C-String but rather an array of characters
+ * WITHOUT a termination \0 char. So we need to convert it to one
+ * before proceeding.
+ */
+ CHKmalloc(pszMsg = malloc(sizeof(uchar) * (iLenMsg + 1)));
+ memcpy(pszMsg, pRcv, iLenMsg);
+ pszMsg[iLenMsg] = '\0';
+
+ getFirstWord(&pszMsg, cmdBuf, sizeof(cmdBuf)/sizeof(uchar), TO_LOWERCASE);
+
+ if(!ustrcmp(cmdBuf, UCHAR_CONSTANT("getmainmsgqueuesize"))) {
+ CHKiRet(diagGetMainMsgQSize(&iMsgQueueSize));
+ CHKiRet(sendResponse(pSess, "%d\n", iMsgQueueSize));
+ } else if(!ustrcmp(cmdBuf, UCHAR_CONSTANT("waitmainqueueempty"))) {
+ CHKiRet(waitMainQEmpty(pSess));
+ } else if(!ustrcmp(cmdBuf, UCHAR_CONSTANT("injectmsg"))) {
+ CHKiRet(injectMsg(pszMsg, pSess));
+ } else {
+ dbgprintf("imdiag unkown command '%s'\n", cmdBuf);
+ CHKiRet(sendResponse(pSess, "unkown command '%s'\n", cmdBuf));
}
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* set permitted peer -- rgerhards, 2008-05-19
+ */
+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;
}
-/* This function is called to gather input. In our case, it is a bit abused
- * to drive the listener loop for the diagnostics code.
+static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVal)
+{
+ DEFiRet;
+
+ if(pOurTcpsrv == NULL) {
+ CHKiRet(tcpsrv.Construct(&pOurTcpsrv));
+ CHKiRet(tcpsrv.SetSessMax(pOurTcpsrv, iTCPSessMax));
+ 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.SetOnMsgReceive(pOurTcpsrv, OnMsgReceived));
+ /* now set optional params, but only if they were actually configured */
+ if(pszStrmDrvrAuthMode != NULL) {
+ CHKiRet(tcpsrv.SetDrvrAuthMode(pOurTcpsrv, pszStrmDrvrAuthMode));
+ }
+ if(pPermPeersRoot != NULL) {
+ CHKiRet(tcpsrv.SetDrvrPermPeers(pOurTcpsrv, pPermPeersRoot));
+ }
+ }
+
+ /* initialized, now add socket */
+ CHKiRet(tcpsrv.SetInputName(pOurTcpsrv, pszInputName == NULL ?
+ UCHAR_CONSTANT("imdiag") : pszInputName));
+ tcpsrv.configureTCPListen(pOurTcpsrv, pNewVal);
+
+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);
+ }
+ RETiRet;
+}
+
+/* This function is called to gather input.
*/
BEGINrunInput
CODESTARTrunInput
+ CHKiRet(tcpsrv.ConstructFinalize(pOurTcpsrv));
+ iRet = tcpsrv.Run(pOurTcpsrv);
+finalize_it:
ENDrunInput
/* initialize and return if will run or not */
BEGINwillRun
CODESTARTwillRun
- iRet = initNetstrm();
+ /* first apply some config settings */
+ if(pOurTcpsrv == 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("imdiag"), sizeof("imdiag") - 1));
+ CHKiRet(prop.ConstructFinalize(pInputName));
+
+ CHKiRet(prop.Construct(&pRcvDummy));
+ CHKiRet(prop.SetString(pRcvDummy, UCHAR_CONSTANT("127.0.0.1"), sizeof("127.0.0.1") - 1));
+ CHKiRet(prop.ConstructFinalize(pRcvDummy));
+
+ CHKiRet(prop.Construct(&pRcvIPDummy));
+ CHKiRet(prop.SetString(pRcvIPDummy, UCHAR_CONSTANT("127.0.0.1"), sizeof("127.0.0.1") - 1));
+ CHKiRet(prop.ConstructFinalize(pRcvIPDummy));
+
+finalize_it:
ENDwillRun
BEGINafterRun
CODESTARTafterRun
- /* do cleanup here */
- /* finally close our listen streams */
- for(i = 0 ; i < iLstnMax ; ++i) {
- netstrm.Destruct(ppLstn + i);
- }
-
- /* destruct netstream subsystem */
- netstrms.Destruct(pNS);
+ if(pInputName != NULL)
+ prop.Destruct(&pInputName);
+ if(pRcvDummy != NULL)
+ prop.Destruct(&pRcvDummy);
+ if(pRcvIPDummy != NULL)
+ prop.Destruct(&pRcvIPDummy);
ENDafterRun
BEGINmodExit
CODESTARTmodExit
+ if(pOurTcpsrv != NULL)
+ iRet = tcpsrv.Destruct(&pOurTcpsrv);
+
+ if(pPermPeersRoot != NULL) {
+ net.DestructPermittedPeers(&pPermPeersRoot);
+ }
+
/* release objects we used */
objRelease(net, LM_NET_FILENAME);
objRelease(netstrm, LM_NETSTRMS_FILENAME);
+ objRelease(tcps_sess, LM_TCPSRV_FILENAME);
+ objRelease(tcpsrv, LM_TCPSRV_FILENAME);
objRelease(errmsg, CORE_COMPONENT);
+ objRelease(datetime, CORE_COMPONENT);
+ objRelease(prop, CORE_COMPONENT);
ENDmodExit
static rsRetVal
resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
+ iTCPSessMax = 200;
+ iStrmDrvrMode = 0;
+ free(pszInputName);
+ pszInputName = NULL;
+ if(pszStrmDrvrAuthMode != NULL) {
+ free(pszStrmDrvrAuthMode);
+ pszStrmDrvrAuthMode = NULL;
+ }
return RS_RET_OK;
}
@@ -175,25 +458,28 @@ CODEmodInit_QueryRegCFSLineHdlr
/* request objects we use */
CHKiRet(objUse(net, LM_NET_FILENAME));
CHKiRet(objUse(netstrm, LM_NETSTRMS_FILENAME));
+ CHKiRet(objUse(tcps_sess, LM_TCPSRV_FILENAME));
+ CHKiRet(objUse(tcpsrv, LM_TCPSRV_FILENAME));
CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
+ CHKiRet(objUse(prop, CORE_COMPONENT));
-#if 0
/* register config file handlers */
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverrun", 0, eCmdHdlrGetWord,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("imdiagserverrun"), 0, eCmdHdlrGetWord,
addTCPListener, NULL, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpmaxsessions", 0, eCmdHdlrInt,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("imdiagmaxsessions"), 0, eCmdHdlrInt,
NULL, &iTCPSessMax, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverstreamdrivermode", 0,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("imdiagserverstreamdrivermode"), 0,
eCmdHdlrInt, NULL, &iStrmDrvrMode, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverstreamdriverauthmode", 0,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("imdiagserverstreamdriverauthmode"), 0,
eCmdHdlrGetWord, NULL, &pszStrmDrvrAuthMode, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverstreamdriverpermittedpeer", 0,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("imdiagserverstreamdriverpermittedpeer"), 0,
eCmdHdlrGetWord, setPermittedPeer, NULL, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("imdiagserverinputname"), 0,
+ eCmdHdlrGetWord, NULL, &pszInputName, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("resetconfigvariables"), 1, eCmdHdlrCustomHandler,
resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
-#endif
ENDmodInit
-#endif
/* vim:set ai:
diff --git a/plugins/imfile/imfile.c b/plugins/imfile/imfile.c
index b0211bf6..3981f9f7 100644
--- a/plugins/imfile/imfile.c
+++ b/plugins/imfile/imfile.c
@@ -45,6 +45,8 @@
#include "errmsg.h"
#include "glbl.h"
#include "datetime.h"
+#include "unicode-helper.h"
+#include "prop.h"
MODULE_TYPE_INPUT /* must be present for input modules, do not remove */
@@ -55,10 +57,13 @@ DEF_IMOD_STATIC_DATA /* must be present, starts static data */
DEFobjCurrIf(errmsg)
DEFobjCurrIf(glbl)
DEFobjCurrIf(datetime)
+DEFobjCurrIf(strm)
+DEFobjCurrIf(prop)
typedef struct fileInfo_s {
uchar *pszFileName;
uchar *pszTag;
+ size_t lenTag;
uchar *pszStateFile; /* file in which state between runs is to be stored */
int iFacility;
int iSeverity;
@@ -78,6 +83,7 @@ static int iFilPtr = 0; /* number of files to be monitored; pointer to next fre
#define MAX_INPUT_FILES 100
static fileInfo_t files[MAX_INPUT_FILES];
+static prop_t *pInputName = NULL; /* there is only one global inputName for all messages generated by this input */
/* enqueue the read file line as a message. The provided string is
* not freed - thuis must be done by the caller.
@@ -94,12 +100,11 @@ static rsRetVal enqLine(fileInfo_t *pInfo, cstr_t *cstrLine)
CHKiRet(msgConstruct(&pMsg));
MsgSetFlowControlType(pMsg, eFLOWCTL_FULL_DELAY);
- MsgSetInputName(pMsg, "imfile");
- MsgSetUxTradMsg(pMsg, (char*)rsCStrGetSzStr(cstrLine));
- MsgSetRawMsg(pMsg, (char*)rsCStrGetSzStr(cstrLine));
- MsgSetMSG(pMsg, (char*)rsCStrGetSzStr(cstrLine));
- MsgSetHOSTNAME(pMsg, (char*)glbl.GetLocalHostName());
- MsgSetTAG(pMsg, (char*)pInfo->pszTag);
+ MsgSetInputName(pMsg, pInputName);
+ MsgSetRawMsg(pMsg, (char*)rsCStrGetSzStr(cstrLine), cstrLen(cstrLine));
+ MsgSetMSGoffs(pMsg, 0); /* we do not have a header... */
+ MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName(), ustrlen(glbl.GetLocalHostName()));
+ MsgSetTAG(pMsg, pInfo->pszTag, pInfo->lenTag);
pMsg->iFacility = LOG_FAC(pInfo->iFacility);
pMsg->iSeverity = LOG_PRI(pInfo->iSeverity);
pMsg->bParseHOSTNAME = 0;
@@ -138,16 +143,16 @@ openFile(fileInfo_t *pThis)
/* If we reach this point, we have a .si file */
- CHKiRet(strmConstruct(&psSF));
- CHKiRet(strmSettOperationsMode(psSF, STREAMMODE_READ));
- CHKiRet(strmSetsType(psSF, STREAMTYPE_FILE_SINGLE));
- CHKiRet(strmSetFName(psSF, pszSFNam, lenSFNam));
- CHKiRet(strmConstructFinalize(psSF));
+ CHKiRet(strm.Construct(&psSF));
+ CHKiRet(strm.SettOperationsMode(psSF, STREAMMODE_READ));
+ CHKiRet(strm.SetsType(psSF, STREAMTYPE_FILE_SINGLE));
+ CHKiRet(strm.SetFName(psSF, pszSFNam, lenSFNam));
+ CHKiRet(strm.ConstructFinalize(psSF));
/* read back in the object */
CHKiRet(obj.Deserialize(&pThis->pStrm, (uchar*) "strm", psSF, NULL, pThis));
- CHKiRet(strmSeekCurrOffs(pThis->pStrm));
+ CHKiRet(strm.SeekCurrOffs(pThis->pStrm));
/* OK, we could successfully read the file, so we now can request that it be deleted.
* If we need it again, it will be written on the next shutdown.
@@ -156,14 +161,14 @@ openFile(fileInfo_t *pThis)
finalize_it:
if(psSF != NULL)
- strmDestruct(&psSF);
+ strm.Destruct(&psSF);
if(iRet != RS_RET_OK) {
- CHKiRet(strmConstruct(&pThis->pStrm));
- CHKiRet(strmSettOperationsMode(pThis->pStrm, STREAMMODE_READ));
- CHKiRet(strmSetsType(pThis->pStrm, STREAMTYPE_FILE_MONITOR));
- CHKiRet(strmSetFName(pThis->pStrm, pThis->pszFileName, strlen((char*) pThis->pszFileName)));
- CHKiRet(strmConstructFinalize(pThis->pStrm));
+ CHKiRet(strm.Construct(&pThis->pStrm));
+ CHKiRet(strm.SettOperationsMode(pThis->pStrm, STREAMMODE_READ));
+ CHKiRet(strm.SetsType(pThis->pStrm, STREAMTYPE_FILE_MONITOR));
+ CHKiRet(strm.SetFName(pThis->pStrm, pThis->pszFileName, strlen((char*) pThis->pszFileName)));
+ CHKiRet(strm.ConstructFinalize(pThis->pStrm));
}
RETiRet;
@@ -202,7 +207,7 @@ static rsRetVal pollFile(fileInfo_t *pThis, int *pbHadFileData)
/* loop below will be exited when strmReadLine() returns EOF */
while(1) {
- CHKiRet(strmReadLine(pThis->pStrm, &pCStr));
+ CHKiRet(strm.ReadLine(pThis->pStrm, &pCStr));
*pbHadFileData = 1; /* this is just a flag, so set it and forget it */
CHKiRet(enqLine(pThis, pCStr)); /* process line */
rsCStrDestruct(&pCStr); /* discard string (must be done by us!) */
@@ -334,6 +339,11 @@ CODESTARTwillRun
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("imfile"), sizeof("imfile") - 1));
+ CHKiRet(prop.ConstructFinalize(pInputName));
+
finalize_it:
ENDwillRun
@@ -349,25 +359,27 @@ persistStrmState(fileInfo_t *pInfo)
{
DEFiRet;
strm_t *psSF = NULL; /* state file (stream) */
+ size_t lenDir;
ASSERT(pInfo != NULL);
/* TODO: create a function persistObj in obj.c? */
- CHKiRet(strmConstruct(&psSF));
- CHKiRet(strmSetDir(psSF, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir())));
- CHKiRet(strmSettOperationsMode(psSF, STREAMMODE_WRITE));
- CHKiRet(strmSetiAddtlOpenFlags(psSF, O_TRUNC));
- CHKiRet(strmSetsType(psSF, STREAMTYPE_FILE_SINGLE));
- CHKiRet(strmSetFName(psSF, pInfo->pszStateFile, strlen((char*) pInfo->pszStateFile)));
- CHKiRet(strmConstructFinalize(psSF));
+ CHKiRet(strm.Construct(&psSF));
+ lenDir = strlen((char*)glbl.GetWorkDir());
+ if(lenDir > 0)
+ CHKiRet(strm.SetDir(psSF, glbl.GetWorkDir(), lenDir));
+ CHKiRet(strm.SettOperationsMode(psSF, STREAMMODE_WRITE_TRUNC));
+ CHKiRet(strm.SetsType(psSF, STREAMTYPE_FILE_SINGLE));
+ CHKiRet(strm.SetFName(psSF, pInfo->pszStateFile, strlen((char*) pInfo->pszStateFile)));
+ CHKiRet(strm.ConstructFinalize(psSF));
- CHKiRet(strmSerialize(pInfo->pStrm, psSF));
+ CHKiRet(strm.Serialize(pInfo->pStrm, psSF));
- CHKiRet(strmDestruct(&psSF));
+ CHKiRet(strm.Destruct(&psSF));
finalize_it:
if(psSF != NULL)
- strmDestruct(&psSF);
+ strm.Destruct(&psSF);
RETiRet;
}
@@ -387,9 +399,12 @@ CODESTARTafterRun
for(i = 0 ; i < iFilPtr ; ++i) {
if(files[i].pStrm != NULL) { /* stream open? */
persistStrmState(&files[i]);
- strmDestruct(&(files[i].pStrm));
+ strm.Destruct(&(files[i].pStrm));
}
}
+
+ if(pInputName != NULL)
+ prop.Destruct(&pInputName);
ENDafterRun
@@ -400,9 +415,11 @@ ENDafterRun
BEGINmodExit
CODESTARTmodExit
/* release objects we used */
+ objRelease(strm, CORE_COMPONENT);
objRelease(datetime, CORE_COMPONENT);
objRelease(glbl, CORE_COMPONENT);
objRelease(errmsg, CORE_COMPONENT);
+ objRelease(prop, CORE_COMPONENT);
ENDmodExit
@@ -470,6 +487,7 @@ static rsRetVal addMonitor(void __attribute__((unused)) *pVal, uchar *pNewVal)
ABORT_FINALIZE(RS_RET_CONFIG_ERROR);
} else {
pThis->pszTag = (uchar*) strdup((char*) pszFileTag);
+ pThis->lenTag = ustrlen(pThis->pszTag);
}
if(pszStateFile == NULL) {
@@ -511,6 +529,8 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(errmsg, CORE_COMPONENT));
CHKiRet(objUse(glbl, CORE_COMPONENT));
CHKiRet(objUse(datetime, CORE_COMPONENT));
+ CHKiRet(objUse(strm, CORE_COMPONENT));
+ CHKiRet(objUse(prop, CORE_COMPONENT));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfilename", 0, eCmdHdlrGetWord,
NULL, &pszFileName, STD_LOADABLE_MODULE_ID));
diff --git a/plugins/imgssapi/imgssapi.c b/plugins/imgssapi/imgssapi.c
index debe935e..d8791880 100644
--- a/plugins/imgssapi/imgssapi.c
+++ b/plugins/imgssapi/imgssapi.c
@@ -9,7 +9,7 @@
* NOTE: read comments in module-template.h to understand how this file
* works!
*
- * Copyright 2007 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007, 2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -249,7 +249,6 @@ onErrClose(tcps_sess_t *pSess)
static rsRetVal
doOpenLstnSocks(tcpsrv_t *pSrv)
{
- int *pRet = NULL;
gsssrv_t *pGSrv;
DEFiRet;
@@ -331,7 +330,7 @@ addGSSListener(void __attribute__((unused)) *pVal, uchar *pNewVal)
CHKiRet(tcpsrv.SetCBOnSessAccept(pOurTcpsrv, onSessAccept));
CHKiRet(tcpsrv.SetCBOnRegularClose(pOurTcpsrv, onRegularClose));
CHKiRet(tcpsrv.SetCBOnErrClose(pOurTcpsrv, onErrClose));
- tcpsrv.configureTCPListen(pOurTcpsrv, (char *) pNewVal);
+ tcpsrv.configureTCPListen(pOurTcpsrv, pNewVal);
CHKiRet(tcpsrv.ConstructFinalize(pOurTcpsrv));
}
diff --git a/plugins/imklog/bsd.c b/plugins/imklog/bsd.c
index 090c4e9b..cecf21c4 100644
--- a/plugins/imklog/bsd.c
+++ b/plugins/imklog/bsd.c
@@ -83,6 +83,11 @@ static int fklog = -1; /* /dev/klog */
# define _PATH_KLOG "/dev/klog"
#endif
+static uchar *GetPath(void)
+{
+ return pszPath ? pszPath : (uchar*) _PATH_KLOG;
+}
+
/* open the kernel log - will be called inside the willRun() imklog
* entry point. -- rgerhards, 2008-04-09
*/
@@ -91,9 +96,9 @@ klogWillRun(void)
{
DEFiRet;
- fklog = open(_PATH_KLOG, O_RDONLY, 0);
+ fklog = open((char*)GetPath(), O_RDONLY, 0);
if (fklog < 0) {
- dbgprintf("can't open %s (%d)\n", _PATH_KLOG, errno);
+ dbgprintf("can't open %s (%d)\n", GetPath(), errno);
iRet = RS_RET_ERR; // TODO: better error code
}
@@ -147,7 +152,7 @@ readklog(void)
break;
}
- for (p = pRcv; (q = strchr(p, '\n')) != NULL; p = q + 1) {
+ for (p = (char*)pRcv; (q = strchr(p, '\n')) != NULL; p = q + 1) {
*q = '\0';
Syslog(LOG_INFO, (uchar*) p);
}
diff --git a/plugins/imklog/imklog.c b/plugins/imklog/imklog.c
index 20bc34ab..7994c5eb 100644
--- a/plugins/imklog/imklog.c
+++ b/plugins/imklog/imklog.c
@@ -18,7 +18,7 @@
* Please note that this file replaces the klogd daemon that was
* also present in pre-v3 versions of rsyslog.
*
- * Copyright (C) 2008 by Rainer Gerhards and Adiscon GmbH
+ * Copyright (C) 2008, 2009 by Rainer Gerhards and Adiscon GmbH
*
* This file is part of rsyslog.
*
@@ -44,6 +44,7 @@
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
+#include <stdlib.h>
#include "dirty.h"
#include "cfsysline.h"
@@ -53,6 +54,8 @@
#include "datetime.h"
#include "imklog.h"
#include "glbl.h"
+#include "prop.h"
+#include "unicode-helper.h"
MODULE_TYPE_INPUT
@@ -60,6 +63,7 @@ MODULE_TYPE_INPUT
DEF_IMOD_STATIC_DATA
DEFobjCurrIf(datetime)
DEFobjCurrIf(glbl)
+DEFobjCurrIf(prop)
/* configuration settings */
int dbgPrintSymbols = 0; /* this one is extern so the helpers can access it! */
@@ -68,6 +72,8 @@ 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
@@ -75,9 +81,11 @@ int iFacilIntMsg; /* the facility to use for internal messages (set by driver) *
* changes resulting from that). -- rgerhards, 2007-12-20
*/
char *symfile = NULL;
-int console_log_level = -1;
+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 */
+
/* enqueue the the kernel message into the message queue.
* The provided msg string is not freed - thus must be done
* by the caller.
@@ -94,15 +102,13 @@ enqMsg(uchar *msg, uchar* pszTag, int iFacility, int iSeverity)
CHKiRet(msgConstruct(&pMsg));
MsgSetFlowControlType(pMsg, eFLOWCTL_LIGHT_DELAY);
- MsgSetInputName(pMsg, "imklog");
- MsgSetRawMsg(pMsg, (char*)msg);
- MsgSetUxTradMsg(pMsg, (char*)msg);
- MsgSetRawMsg(pMsg, (char*)msg);
- MsgSetMSG(pMsg, (char*)msg);
- MsgSetRcvFrom(pMsg, (char*)glbl.GetLocalHostName());
- MsgSetRcvFromIP(pMsg, (uchar*)"127.0.0.1");
- MsgSetHOSTNAME(pMsg, (char*)glbl.GetLocalHostName());
- MsgSetTAG(pMsg, (char*)pszTag);
+ MsgSetInputName(pMsg, pInputName);
+ MsgSetRawMsgWOSize(pMsg, (char*)msg);
+ MsgSetMSGoffs(pMsg, 0); /* we do not have a header... */
+ MsgSetRcvFrom(pMsg, glbl.GetLocalHostNameProp());
+ MsgSetRcvFromIP(pMsg, pLocalHostIP);
+ MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName(), ustrlen(glbl.GetLocalHostName()));
+ MsgSetTAG(pMsg, pszTag, ustrlen(pszTag));
pMsg->iFacility = LOG_FAC(iFacility);
pMsg->iSeverity = LOG_PRI(iSeverity);
pMsg->bParseHOSTNAME = 0;
@@ -229,13 +235,23 @@ ENDrunInput
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();
+
+ if(pInputName != NULL)
+ prop.Destruct(&pInputName);
+ if(pLocalHostIP != NULL)
+ prop.Destruct(&pLocalHostIP);
ENDafterRun
@@ -244,6 +260,9 @@ CODESTARTmodExit
/* release objects we used */
objRelease(glbl, CORE_COMPONENT);
objRelease(datetime, CORE_COMPONENT);
+ objRelease(prop, CORE_COMPONENT);
+ if(pszPath != NULL)
+ free(pszPath);
ENDmodExit
@@ -260,6 +279,10 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
symfile = NULL;
symbol_lookup = 0;
bPermitNonKernel = 0;
+ if(pszPath != NULL) {
+ free(pszPath);
+ pszPath = NULL;
+ }
iFacilIntMsg = klogFacilIntMsg();
return RS_RET_OK;
}
@@ -270,14 +293,17 @@ CODESTARTmodInit
CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(datetime, CORE_COMPONENT));
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));
ENDmodInit
diff --git a/plugins/imklog/imklog.h b/plugins/imklog/imklog.h
index 37bd58b0..c183026d 100644
--- a/plugins/imklog/imklog.h
+++ b/plugins/imklog/imklog.h
@@ -52,6 +52,7 @@ extern int symbol_lookup;
extern char *symfile;
extern int console_log_level;
extern int dbgPrintSymbols;
+extern uchar *pszPath;
/* the functions below may be called by the drivers */
rsRetVal imklogLogIntMsg(int priority, char *fmt, ...) __attribute__((format(printf,2, 3)));
diff --git a/plugins/imklog/ksym.c b/plugins/imklog/ksym.c
index f636a7bb..ca708ba6 100644
--- a/plugins/imklog/ksym.c
+++ b/plugins/imklog/ksym.c
@@ -650,8 +650,7 @@ static void FreeSymbols(void)
**************************************************************************/
extern char *ExpandKadds(char *line, char *el)
{
- auto char dlm,
- *kp,
+ auto char *kp,
*sl = line,
*elp = el,
*symbol;
@@ -781,7 +780,6 @@ extern char *ExpandKadds(char *line, char *el)
strcpy(el, sl);
return(el);
}
- dlm = *kp;
strncpy(num,sl+1,kp-sl-1);
num[kp-sl-1] = '\0';
value = strtoul(num, (char **) 0, 16);
diff --git a/plugins/imklog/ksym_mod.c b/plugins/imklog/ksym_mod.c
index 6e48e89e..be5fdee9 100644
--- a/plugins/imklog/ksym_mod.c
+++ b/plugins/imklog/ksym_mod.c
@@ -1,9 +1,8 @@
-/*
- * ksym_mod.c - functions for building symbol lookup tables for klogd
+/* ksym_mod.c - functions for building symbol lookup tables for klogd
* Copyright (c) 1995, 1996 Dr. G.W. Wettstein <greg@wind.rmcc.com>
* Copyright (c) 1996 Enjellic Systems Development
* Copyright (c) 1998-2007 Martin Schulze <joey@infodrom.org>
- * Copyright (C) 2007-2008 Rainer Gerhards <rgerhards@adiscon.com>
+ * Copyright (C) 2007-2009 Rainer Gerhards <rgerhards@adiscon.com>
*
* This file is part of rsyslog.
*
@@ -83,7 +82,6 @@
* Changed llseek() to lseek64() in order to skip a libc warning.
*/
-
/* Includes. */
#include "config.h"
#include <stdio.h>
@@ -112,7 +110,7 @@
#define KSYMS "/proc/kallsyms"
static int num_modules = 0;
-struct Module *sym_array_modules = (struct Module *) 0;
+struct Module *sym_array_modules = (struct Module *) NULL;
static int have_modules = 0;
@@ -266,7 +264,7 @@ static void FreeModules()
}
free(sym_array_modules);
- sym_array_modules = (struct Module *) 0;
+ sym_array_modules = (struct Module *) NULL;
num_modules = 0;
return;
}
@@ -390,11 +388,11 @@ static int AddSymbol(line)
mp->sym_array = (struct sym_table *) realloc(mp->sym_array, \
(mp->num_syms+1) * sizeof(struct sym_table));
- if ( mp->sym_array == (struct sym_table *) 0 )
+ if ( mp->sym_array == (struct sym_table *) NULL )
return(0);
mp->sym_array[mp->num_syms].name = strdup(p);
- if ( mp->sym_array[mp->num_syms].name == (char *) 0 )
+ if ( mp->sym_array[mp->num_syms].name == (char *) NULL )
return(0);
/* Stuff interesting information into the module. */
@@ -424,15 +422,21 @@ static int AddSymbol(line)
* If a match cannot be found a diagnostic string is printed.
* If a match is found the pointer to the symbolic name most
* closely matching the address is returned.
+ *
+ * TODO: We are using int values for the offset, but longs for the value
+ * values. This may create some trouble in the future (on 64 Bit OS?).
+ * Anyhow, I have not changed this, because we do not seem to have any
+ * issue and my understanding of this code is limited (and I don't see
+ * need to invest more time to dig much deeper).
+ * rgerhards, 2009-04-17
**************************************************************************/
extern char * LookupModuleSymbol(value, sym)
unsigned long value;
struct symbol *sym;
{
- auto int nmod,
- nsym;
- auto struct sym_table *last;
- auto struct Module *mp;
+ int nmod, nsym;
+ struct sym_table *last;
+ struct Module *mp;
static char ret[100];
sym->size = 0;
@@ -443,8 +447,7 @@ extern char * LookupModuleSymbol(value, sym)
for (nmod = 0; nmod < num_modules; ++nmod) {
mp = &sym_array_modules[nmod];
- /*
- * Run through the list of symbols in this module and
+ /* Run through the list of symbols in this module and
* see if the address can be resolved.
*/
for(nsym = 1, last = &mp->sym_array[0];
@@ -453,13 +456,12 @@ extern char * LookupModuleSymbol(value, sym)
if ( mp->sym_array[nsym].value > value )
{
if ( sym->size == 0 ||
- (value - last->value) < sym->offset ||
- ( (sym->offset == (value - last->value)) &&
- (mp->sym_array[nsym].value-last->value) < sym->size ) )
+ (int) (value - last->value) < sym->offset ||
+ ( (sym->offset == (int) (value - last->value)) &&
+ (int) (mp->sym_array[nsym].value-last->value) < sym->size ) )
{
sym->offset = value - last->value;
- sym->size = mp->sym_array[nsym].value - \
- last->value;
+ sym->size = mp->sym_array[nsym].value - last->value;
ret[sizeof(ret)-1] = '\0';
if ( mp->name == NULL )
snprintf(ret, sizeof(ret)-1,
@@ -478,5 +480,5 @@ extern char * LookupModuleSymbol(value, sym)
return(ret);
/* It has been a hopeless exercise. */
- return((char *) 0);
+ return(NULL);
}
diff --git a/plugins/imklog/ksyms.h b/plugins/imklog/ksyms.h
index b5362ff3..a168947b 100644
--- a/plugins/imklog/ksyms.h
+++ b/plugins/imklog/ksyms.h
@@ -2,7 +2,7 @@
* Copyright (c) 1995, 1996 Dr. G.W. Wettstein <greg@wind.rmcc.com>
* Copyright (c) 1996 Enjellic Systems Development
* Copyright (c) 2004-7 Martin Schulze <joey@infodrom.org>
- * Copyright (c) 2007-2008 Rainer Gerhards <rgerhards@adiscon.com>
+ * Copyright (c) 2007-2009 Rainer Gerhards <rgerhards@adiscon.com>
*
* This file is part of rsyslog.
*
@@ -26,7 +26,7 @@
struct symbol
{
- char *name;
+ uchar *name;
int size;
int offset;
};
diff --git a/plugins/imklog/linux.c b/plugins/imklog/linux.c
index 198b7c0e..727708a5 100644
--- a/plugins/imklog/linux.c
+++ b/plugins/imklog/linux.c
@@ -37,6 +37,7 @@
#include "msg.h"
#include "module-template.h"
#include "imklog.h"
+#include "unicode-helper.h"
/* Includes. */
@@ -84,19 +85,21 @@ static enum LOGSRC {none, proc, kernel} logsrc;
extern int ksyslog(int type, char *buf, int len);
+static uchar *GetPath(void)
+{
+ return pszPath ? pszPath : UCHAR_CONSTANT(_PATH_KLOG);
+}
+
static void CloseLogSrc(void)
{
- /* Turn on logging of messages to console, but only if we had the -c
- * option -- rgerhards, 2007-08-01
- */
- if (console_log_level != -1)
+ /* Turn on logging of messages to console, but only if a log level was speficied */
+ if(console_log_level != -1)
ksyslog(7, NULL, 0);
/* Shutdown the log sources. */
- switch ( logsrc )
- {
+ switch(logsrc) {
case kernel:
- ksyslog(0, 0, 0);
+ ksyslog(0, NULL, 0);
imklogLogIntMsg(LOG_INFO, "Kernel logging (ksyslog) stopped.");
break;
case proc:
@@ -135,7 +138,7 @@ static enum LOGSRC GetKernelLogSrc(void)
* file system is available to get kernel messages from.
*/
if ( use_syscall ||
- ((stat(_PATH_KLOG, &sb) < 0) && (errno == ENOENT)) )
+ ((stat((char*)GetPath(), &sb) < 0) && (errno == ENOENT)) )
{
/* Initialize kernel logging. */
ksyslog(1, NULL, 0);
@@ -144,14 +147,14 @@ static enum LOGSRC GetKernelLogSrc(void)
return(kernel);
}
- if ( (kmsg = open(_PATH_KLOG, O_RDONLY)) < 0 )
+ if ( (kmsg = open((char*)GetPath(), O_RDONLY|O_CLOEXEC)) < 0 )
{
imklogLogIntMsg(LOG_ERR, "imklog: Cannot open proc file system, %d.\n", errno);
- ksyslog(7, NULL, 0); /* TODO: check this, implement more */
+ ksyslog(7, NULL, 0);
return(none);
}
- imklogLogIntMsg(LOG_INFO, "imklog %s, log source = %s started.", VERSION, _PATH_KLOG);
+ imklogLogIntMsg(LOG_INFO, "imklog %s, log source = %s started.", VERSION, GetPath());
return(proc);
}
diff --git a/plugins/immark/immark.c b/plugins/immark/immark.c
index 323da3fe..8504f872 100644
--- a/plugins/immark/immark.c
+++ b/plugins/immark/immark.c
@@ -41,6 +41,7 @@
#include "cfsysline.h"
#include "module-template.h"
#include "errmsg.h"
+#include "msg.h"
MODULE_TYPE_INPUT
diff --git a/plugins/imrelp/imrelp.c b/plugins/imrelp/imrelp.c
index f4e1444c..4d14b47a 100644
--- a/plugins/imrelp/imrelp.c
+++ b/plugins/imrelp/imrelp.c
@@ -4,7 +4,7 @@
*
* File begun on 2008-03-13 by RGerhards
*
- * Copyright 2008 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2008, 2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -42,20 +42,23 @@
#include "cfsysline.h"
#include "module-template.h"
#include "net.h"
+#include "msg.h"
+#include "unicode-helper.h"
+#include "prop.h"
MODULE_TYPE_INPUT
/* static data */
DEF_IMOD_STATIC_DATA
DEFobjCurrIf(net)
+DEFobjCurrIf(prop)
/* Module static data */
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 */
/* config settings */
-static int iTCPSessMax = 200; /* max number of sessions */
-
/* ------------------------------ callbacks ------------------------------ */
#if 0
@@ -83,8 +86,8 @@ static relpRetVal
onSyslogRcv(uchar *pHostname, uchar *pIP, uchar *pMsg, size_t lenMsg)
{
DEFiRet;
- parseAndSubmitMessage(pHostname, pIP, pMsg, lenMsg, MSG_PARSE_HOSTNAME,
- NOFLAG, eFLOWCTL_LIGHT_DELAY, (uchar*)"imrelp");
+ parseAndSubmitMessage(pHostname, pIP, pMsg, lenMsg, PARSE_HOSTNAME,
+ eFLOWCTL_LIGHT_DELAY, pInputName, NULL, 0);
RETiRet;
}
@@ -129,6 +132,11 @@ CODESTARTwillRun
//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
@@ -142,6 +150,9 @@ CODESTARTafterRun
net.pAllowedSenders_TCP = NULL;
}
#endif
+
+ if(pInputName != NULL)
+ prop.Destruct(&pInputName);
ENDafterRun
@@ -151,6 +162,7 @@ CODESTARTmodExit
iRet = relpEngineDestruct(&pRelpEngine);
/* release objects we used */
+ objRelease(prop, CORE_COMPONENT);
objRelease(net, LM_NET_FILENAME);
ENDmodExit
@@ -158,7 +170,6 @@ ENDmodExit
static rsRetVal
resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
- iTCPSessMax = 200;
return RS_RET_OK;
}
@@ -176,13 +187,12 @@ CODESTARTmodInit
CODEmodInit_QueryRegCFSLineHdlr
pRelpEngine = NULL;
/* request objects we use */
+ CHKiRet(objUse(prop, CORE_COMPONENT));
CHKiRet(objUse(net, LM_NET_FILENAME));
/* register config file handlers */
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputrelpserverrun", 0, eCmdHdlrGetWord,
addListener, NULL, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputrelpmaxsessions", 0, eCmdHdlrInt,
- NULL, &iTCPSessMax, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
diff --git a/plugins/imtcp/imtcp.c b/plugins/imtcp/imtcp.c
index 7b3eeda5..d122e976 100644
--- a/plugins/imtcp/imtcp.c
+++ b/plugins/imtcp/imtcp.c
@@ -3,7 +3,7 @@
*
* File begun on 2007-12-21 by RGerhards (extracted from syslogd.c)
*
- * Copyright 2007 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007, 2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -56,10 +56,12 @@
#include "dirty.h"
#include "cfsysline.h"
#include "module-template.h"
+#include "unicode-helper.h"
#include "net.h"
#include "netstrm.h"
#include "errmsg.h"
#include "tcpsrv.h"
+#include "ruleset.h"
#include "net.h" /* for permittedPeers, may be removed when this is removed */
MODULE_TYPE_INPUT
@@ -71,6 +73,7 @@ DEFobjCurrIf(tcps_sess)
DEFobjCurrIf(net)
DEFobjCurrIf(netstrm)
DEFobjCurrIf(errmsg)
+DEFobjCurrIf(ruleset)
/* Module static data */
static tcpsrv_t *pOurTcpsrv = NULL; /* our TCP server(listener) TODO: change for multiple instances */
@@ -79,8 +82,13 @@ static permittedPeers_t *pPermPeersRoot = NULL;
/* config settings */
static int iTCPSessMax = 200; /* max number of sessions */
+static int iTCPLstnMax = 20; /* max number of sessions */
static int iStrmDrvrMode = 0; /* mode for stream driver, driver-dependent (0 mostly means plain tcp) */
+static int bEmitMsgOnClose = 0; /* emit an informational message on close by remote peer */
+static int iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER; /* addtl frame delimiter, e.g. for netscreen, default none */
static 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) */
/* callbacks */
@@ -89,7 +97,7 @@ static int
isPermittedHost(struct sockaddr *addr, char *fromHostFQDN, void __attribute__((unused)) *pUsrSrv,
void __attribute__((unused)) *pUsrSess)
{
- return net.isAllowedSender((uchar*) "TCP", addr, fromHostFQDN);
+ return net.isAllowedSender(UCHAR_CONSTANT("TCP"), addr, fromHostFQDN);
}
@@ -154,6 +162,27 @@ 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)
+{
+ 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("imtcp 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;
@@ -161,12 +190,15 @@ static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVa
if(pOurTcpsrv == NULL) {
CHKiRet(tcpsrv.Construct(&pOurTcpsrv));
CHKiRet(tcpsrv.SetSessMax(pOurTcpsrv, iTCPSessMax));
+ CHKiRet(tcpsrv.SetLstnMax(pOurTcpsrv, iTCPLstnMax));
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.SetAddtlFrameDelim(pOurTcpsrv, iAddtlFrameDelim));
+ CHKiRet(tcpsrv.SetNotificationOnRemoteClose(pOurTcpsrv, bEmitMsgOnClose));
/* now set optional params, but only if they were actually configured */
if(pszStrmDrvrAuthMode != NULL) {
CHKiRet(tcpsrv.SetDrvrAuthMode(pOurTcpsrv, pszStrmDrvrAuthMode));
@@ -174,11 +206,14 @@ static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVa
if(pPermPeersRoot != NULL) {
CHKiRet(tcpsrv.SetDrvrPermPeers(pOurTcpsrv, pPermPeersRoot));
}
- /* most params set, now start listener */
- tcpsrv.configureTCPListen(pOurTcpsrv, (char *) pNewVal);
- CHKiRet(tcpsrv.ConstructFinalize(pOurTcpsrv));
}
+ /* 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);
+
finalize_it:
if(iRet != RS_RET_OK) {
errmsg.LogError(0, NO_ERRCODE, "error %d trying to add listener", iRet);
@@ -195,7 +230,9 @@ 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
@@ -213,7 +250,7 @@ ENDwillRun
BEGINafterRun
CODESTARTafterRun
/* do cleanup here */
- net.clearAllowedSenders((uchar*)"TCP");
+ net.clearAllowedSenders(UCHAR_CONSTANT("TCP"));
ENDafterRun
@@ -232,6 +269,7 @@ CODESTARTmodExit
objRelease(tcps_sess, LM_TCPSRV_FILENAME);
objRelease(tcpsrv, LM_TCPSRV_FILENAME);
objRelease(errmsg, CORE_COMPONENT);
+ objRelease(ruleset, CORE_COMPONENT);
ENDmodExit
@@ -239,7 +277,14 @@ static rsRetVal
resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
iTCPSessMax = 200;
+ iTCPLstnMax = 20;
iStrmDrvrMode = 0;
+ bEmitMsgOnClose = 0;
+ iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER;
+ free(pszInputName);
+ pszInputName = NULL;
+ free(pszStrmDrvrAuthMode);
+ pszStrmDrvrAuthMode = NULL;
return RS_RET_OK;
}
@@ -262,19 +307,30 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(tcps_sess, LM_TCPSRV_FILENAME));
CHKiRet(objUse(tcpsrv, LM_TCPSRV_FILENAME));
CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(ruleset, CORE_COMPONENT));
/* register config file handlers */
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverrun", 0, eCmdHdlrGetWord,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverrun"), 0, eCmdHdlrGetWord,
addTCPListener, NULL, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpmaxsessions", 0, eCmdHdlrInt,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpmaxsessions"), 0, eCmdHdlrInt,
NULL, &iTCPSessMax, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverstreamdrivermode", 0,
+ 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 *)"inputtcpserverstreamdriverauthmode", 0,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverstreamdriverauthmode"), 0,
eCmdHdlrGetWord, NULL, &pszStrmDrvrAuthMode, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverstreamdriverpermittedpeer", 0,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverstreamdriverpermittedpeer"), 0,
eCmdHdlrGetWord, setPermittedPeer, NULL, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserveraddtlframedelimiter"), 0, eCmdHdlrInt,
+ NULL, &iAddtlFrameDelim, 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("resetconfigvariables"), 1, eCmdHdlrCustomHandler,
resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
diff --git a/plugins/imudp/imudp.c b/plugins/imudp/imudp.c
index 79d51263..f8555f00 100644
--- a/plugins/imudp/imudp.c
+++ b/plugins/imudp/imudp.c
@@ -6,7 +6,7 @@
*
* File begun on 2007-12-21 by RGerhards (extracted from syslogd.c)
*
- * Copyright 2007 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007-2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -40,6 +40,11 @@
#include "srUtils.h"
#include "errmsg.h"
#include "glbl.h"
+#include "msg.h"
+#include "parser.h"
+#include "datetime.h"
+#include "prop.h"
+#include "unicode-helper.h"
MODULE_TYPE_INPUT
@@ -50,6 +55,8 @@ DEF_IMOD_STATIC_DATA
DEFobjCurrIf(errmsg)
DEFobjCurrIf(glbl)
DEFobjCurrIf(net)
+DEFobjCurrIf(datetime)
+DEFobjCurrIf(prop)
static int iMaxLine; /* maximum UDP message size supported */
static time_t ttLastDiscard = 0; /* timestamp when a message from a non-permitted sender was last discarded
@@ -63,6 +70,10 @@ static uchar *pRcvBuf = NULL; /* receive buffer (for a single packet). We use a
* 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 */
+// TODO: 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 */
@@ -90,7 +101,7 @@ static rsRetVal addListner(void __attribute__((unused)) *pVal, uchar *pNewVal)
else
bindAddr = pszBindAddr;
- dbgprintf("Trying to open syslog UDP ports at %s:%s.\n",
+ DBGPRINTF("Trying to open syslog UDP ports at %s:%s.\n",
(bindAddr == NULL) ? (uchar*)"*" : bindAddr, pNewVal);
newSocks = net.create_udp_socket(bindAddr, (pNewVal == NULL || *pNewVal == '\0') ? (uchar*) "514" : pNewVal, 1);
@@ -130,20 +141,164 @@ finalize_it:
}
+#if 0 /* TODO: implement when tehre is time, requires restructure of socket array! */
+/* 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(&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;
+}
+#endif
+
+
+/* This function is a helper to runInput. I have extracted it
+ * from the main loop just so that we do not have that large amount of code
+ * in a single place. This function takes a socket and pulls messages from
+ * it until the socket does not have any more waiting.
+ * rgerhards, 2008-01-08
+ * We try to read from the file descriptor until there
+ * is no more data. This is done in the hope to get better performance
+ * out of the system. However, this also means that a descriptor
+ * monopolizes processing while it contains data. This can lead to
+ * data loss in other descriptors. However, if the system is incapable of
+ * handling the workload, we will loss data in any case. So it doesn't really
+ * matter where the actual loss occurs - it is always random, because we depend
+ * on scheduling order. -- rgerhards, 2008-10-02
+ */
+static inline rsRetVal
+processSocket(int fd, struct sockaddr_storage *frominetPrev, int *pbIsPermitted,
+ uchar *fromHost, uchar *fromHostFQDN, uchar *fromHostIP)
+{
+ DEFiRet;
+ int iNbrTimeUsed;
+ time_t ttGenTime;
+ struct syslogTime stTime;
+ socklen_t socklen;
+ ssize_t lenRcvBuf;
+ struct sockaddr_storage frominet;
+ msg_t *pMsg;
+ prop_t *propFromHost = NULL;
+ prop_t *propFromHostIP = NULL;
+ char errStr[1024];
+
+ iNbrTimeUsed = 0;
+ while(1) { /* loop is terminated if we have a bad receive, done below in the body */
+ socklen = sizeof(struct sockaddr_storage);
+ lenRcvBuf = recvfrom(fd, (char*) pRcvBuf, iMaxLine, 0, (struct sockaddr *)&frominet, &socklen);
+ if(lenRcvBuf < 0) {
+ if(errno != EINTR && errno != EAGAIN) {
+ rs_strerror_r(errno, errStr, sizeof(errStr));
+ DBGPRINTF("INET socket error: %d = %s.\n", errno, errStr);
+ errmsg.LogError(errno, NO_ERRCODE, "recvfrom inet");
+ }
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+ if(lenRcvBuf == 0)
+ continue; /* this looks a bit strange, but practice shows it happens... */
+
+ /* if we reach this point, we had a good receive and can process the packet received */
+ /* check if we have a different sender than before, if so, we need to query some new values */
+ if(net.CmpHost(&frominet, frominetPrev, socklen) != 0) {
+ CHKiRet(net.cvthname(&frominet, fromHost, fromHostFQDN, fromHostIP));
+ memcpy(frominetPrev, &frominet, socklen); /* update cache indicator */
+ /* Here we check if a host is permitted to send us
+ * syslog messages. If it isn't, we do not further
+ * process the message but log a warning (if we are
+ * configured to do this).
+ * rgerhards, 2005-09-26
+ */
+ *pbIsPermitted = net.isAllowedSender((uchar*)"UDP",
+ (struct sockaddr *)&frominet, (char*)fromHostFQDN);
+
+ if(!*pbIsPermitted) {
+ DBGPRINTF("%s is not an allowed sender\n", (char*)fromHostFQDN);
+ if(glbl.GetOption_DisallowWarning) {
+ time_t tt;
+
+ time(&tt);
+ if(tt > ttLastDiscard + 60) {
+ ttLastDiscard = tt;
+ errmsg.LogError(0, NO_ERRCODE,
+ "UDP message from disallowed sender %s discarded",
+ (char*)fromHost);
+ }
+ }
+ }
+ }
+
+ DBGPRINTF("recv(%d,%d)/%s,acl:%d,msg:%s\n", fd, (int) lenRcvBuf, fromHost, *pbIsPermitted, pRcvBuf);
+
+ if(*pbIsPermitted) {
+ if((iTimeRequery == 0) || (iNbrTimeUsed++ % iTimeRequery) == 0) {
+ datetime.getCurrTime(&stTime, &ttGenTime);
+ }
+ /* we now create our own message object and submit it to the queue */
+ CHKiRet(msgConstructWithTime(&pMsg, &stTime, ttGenTime));
+ MsgSetRawMsg(pMsg, (char*)pRcvBuf, lenRcvBuf);
+ MsgSetInputName(pMsg, pInputName);
+ MsgSetFlowControlType(pMsg, eFLOWCTL_NO_DELAY);
+ pMsg->msgFlags = NEEDS_PARSING | PARSE_HOSTNAME;
+ pMsg->bParseHOSTNAME = 1;
+ MsgSetRcvFromStr(pMsg, fromHost, ustrlen(fromHost), &propFromHost);
+ CHKiRet(MsgSetRcvFromIPStr(pMsg, fromHostIP, ustrlen(fromHostIP), &propFromHostIP));
+ CHKiRet(submitMsg(pMsg));
+ }
+ }
+
+finalize_it:
+ if(propFromHost != NULL)
+ prop.Destruct(&propFromHost);
+ if(propFromHostIP != NULL)
+ prop.Destruct(&propFromHostIP);
+
+ RETiRet;
+}
+
+
/* This function is called to gather input.
+ * Note that udpLstnSocks 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
+ * rgerhards, 2008-10-07: I have implemented a very simple, yet in most cases probably
+ * highly efficient "name caching". Before querying a name, I now check if the name to be
+ * queried is the same as the one queried in the last message processed. If that is the
+ * case, we can simple re-use the previous value. This algorithm works quite well with
+ * few sender, especially if they emit messages in bursts. The more sender and the
+ * more intermixed messages arrive, the less this algorithm works, but the overhead
+ * is so minimal (a simple memory compare and move) that this does not hurt. Even
+ * with a real name lookup cache, this optimization here is useful as it is quicker
+ * than even a cache lookup).
*/
BEGINrunInput
int maxfds;
int nfds;
int i;
fd_set readfds;
- struct sockaddr_storage frominet;
- socklen_t socklen;
+ struct sockaddr_storage frominetPrev;
+ int bIsPermitted;
uchar fromHost[NI_MAXHOST];
uchar fromHostIP[NI_MAXHOST];
uchar fromHostFQDN[NI_MAXHOST];
- ssize_t l;
CODESTARTrunInput
+ /* start "name caching" algo by making sure the previous system indicator
+ * is invalidated.
+ */
+ bIsPermitted = 0;
+ memset(&frominetPrev, 0, sizeof(frominetPrev));
/* this is an endless loop - it is terminated when the thread is
* signalled to do so. This, however, is handled by the framework,
* right into the sleep below.
@@ -158,17 +313,14 @@ CODESTARTrunInput
maxfds = 0;
FD_ZERO (&readfds);
- /* Add the UDP listen sockets to the list of read descriptors.
- */
- if(udpLstnSocks != NULL) {
- for (i = 0; i < *udpLstnSocks; i++) {
- if (udpLstnSocks[i+1] != -1) {
- if(Debug)
- net.debugListenInfo(udpLstnSocks[i+1], "UDP");
- FD_SET(udpLstnSocks[i+1], &readfds);
- if(udpLstnSocks[i+1]>maxfds) maxfds=udpLstnSocks[i+1];
- }
- }
+ /* Add the UDP listen sockets to the list of read descriptors. */
+ for (i = 0; i < *udpLstnSocks; i++) {
+ if (udpLstnSocks[i+1] != -1) {
+ if(Debug)
+ net.debugListenInfo(udpLstnSocks[i+1], "UDP");
+ FD_SET(udpLstnSocks[i+1], &readfds);
+ if(udpLstnSocks[i+1]>maxfds) maxfds=udpLstnSocks[i+1];
+ }
}
if(Debug) {
dbgprintf("--------imUDP calling select, active file descriptors (max %d): ", maxfds);
@@ -181,53 +333,14 @@ CODESTARTrunInput
/* wait for io to become ready */
nfds = select(maxfds+1, (fd_set *) &readfds, NULL, NULL, NULL);
- if(udpLstnSocks != NULL) {
- for (i = 0; nfds && i < *udpLstnSocks; i++) {
- if (FD_ISSET(udpLstnSocks[i+1], &readfds)) {
- socklen = sizeof(frominet);
- l = recvfrom(udpLstnSocks[i+1], (char*) pRcvBuf, iMaxLine, 0,
- (struct sockaddr *)&frominet, &socklen);
- if (l > 0) {
- if(net.cvthname(&frominet, fromHost, fromHostFQDN, fromHostIP) == RS_RET_OK) {
- dbgprintf("Message from inetd socket: #%d, host: %s\n",
- udpLstnSocks[i+1], fromHost);
- /* Here we check if a host is permitted to send us
- * syslog messages. If it isn't, we do not further
- * process the message but log a warning (if we are
- * configured to do this).
- * rgerhards, 2005-09-26
- */
- if(net.isAllowedSender((uchar*) "UDP",
- (struct sockaddr *)&frominet, (char*)fromHostFQDN)) {
- parseAndSubmitMessage(fromHost, fromHostIP, pRcvBuf, l,
- MSG_PARSE_HOSTNAME, NOFLAG, eFLOWCTL_NO_DELAY, (uchar*)"imudp");
- } else {
- dbgprintf("%s is not an allowed sender\n", (char*)fromHostFQDN);
- if(glbl.GetOption_DisallowWarning) {
- time_t tt;
-
- time(&tt);
- if(tt > ttLastDiscard + 60) {
- ttLastDiscard = tt;
- errmsg.LogError(0, NO_ERRCODE,
- "UDP message from disallowed sender %s discarded",
- (char*)fromHost);
- }
- }
- }
- }
- } else if (l < 0 && errno != EINTR && errno != EAGAIN) {
- char errStr[1024];
- rs_strerror_r(errno, errStr, sizeof(errStr));
- dbgprintf("INET socket error: %d = %s.\n", errno, errStr);
- errmsg.LogError(errno, NO_ERRCODE, "recvfrom inet");
- /* should be harmless */
- sleep(1);
- }
- --nfds; /* indicate we have processed one */
- }
- }
- }
+ for(i = 0; nfds && i < *udpLstnSocks; i++) {
+ if(FD_ISSET(udpLstnSocks[i+1], &readfds)) {
+ processSocket(udpLstnSocks[i+1], &frominetPrev, &bIsPermitted,
+ fromHost, fromHostFQDN, fromHostIP);
+ --nfds; /* indicate we have processed one descriptor */
+ }
+ }
+ /* end of a run, back to loop for next recv() */
}
return iRet;
@@ -237,6 +350,11 @@ 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 */
/* if we could not set up any listners, there is no point in running... */
@@ -264,6 +382,8 @@ CODESTARTafterRun
free(pRcvBuf);
pRcvBuf = NULL;
}
+ if(pInputName != NULL)
+ prop.Destruct(&pInputName);
ENDafterRun
@@ -272,6 +392,8 @@ CODESTARTmodExit
/* release what we no longer need */
objRelease(errmsg, CORE_COMPONENT);
objRelease(glbl, CORE_COMPONENT);
+ objRelease(datetime, CORE_COMPONENT);
+ objRelease(prop, CORE_COMPONENT);
objRelease(net, LM_NET_FILENAME);
ENDmodExit
@@ -291,6 +413,7 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
net.closeUDPListenSockets(udpLstnSocks);
udpLstnSocks = NULL;
}
+ iTimeRequery = TIME_REQUERY_DFLT;/* the default is to query only every second time */
return RS_RET_OK;
}
@@ -301,16 +424,23 @@ CODESTARTmodInit
CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(errmsg, CORE_COMPONENT));
CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
+ CHKiRet(objUse(prop, CORE_COMPONENT));
CHKiRet(objUse(net, LM_NET_FILENAME));
/* register config file handlers */
+ /* TODO: add - but this requires more changes, no time right now...
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"udpserverbindruleset", 0, eCmdHdlrGetWord,
+ setRuleset, NULL, STD_LOADABLE_MODULE_ID));
+ */
CHKiRet(omsdRegCFSLineHdlr((uchar *)"udpserverrun", 0, eCmdHdlrGetWord,
addListner, NULL, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"udpserveraddress", 0, eCmdHdlrGetWord,
NULL, &pszBindAddr, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"udpservertimerequery", 0, eCmdHdlrInt,
+ NULL, &iTimeRequery, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
-/*
- * vi:set ai:
+/* vim:set ai:
*/
diff --git a/plugins/imuxsock/imuxsock.c b/plugins/imuxsock/imuxsock.c
index 55b8b2df..5567a405 100644
--- a/plugins/imuxsock/imuxsock.c
+++ b/plugins/imuxsock/imuxsock.c
@@ -37,11 +37,14 @@
#include <sys/un.h>
#include "dirty.h"
#include "cfsysline.h"
+#include "unicode-helper.h"
#include "module-template.h"
#include "srUtils.h"
#include "errmsg.h"
#include "net.h"
#include "glbl.h"
+#include "msg.h"
+#include "prop.h"
MODULE_TYPE_INPUT
@@ -65,7 +68,9 @@ MODULE_TYPE_INPUT
DEF_IMOD_STATIC_DATA
DEFobjCurrIf(errmsg)
DEFobjCurrIf(glbl)
+DEFobjCurrIf(prop)
+static prop_t *pInputName = NULL; /* our inputName currently is always "imuxsock", and this will hold it */
static int startIndexUxLocalSockets; /* process funix from that index on (used to
* suppress local logging. rgerhards 2005-08-01
* read-only after startup
@@ -221,7 +226,8 @@ static rsRetVal readSocket(int fd, int iSock)
if (iRcvd > 0) {
parseAndSubmitMessage(funixHName[iSock] == NULL ? glbl.GetLocalHostName() : funixHName[iSock],
(uchar*)"127.0.0.1", pRcv,
- iRcvd, funixParseHost[iSock], funixFlags[iSock], funixFlowCtl[iSock], (uchar*)"imuxsock");
+ iRcvd, funixParseHost[iSock] ? (funixFlags[iSock] | PARSE_HOSTNAME) : funixFlags[iSock],
+ funixFlowCtl[iSock], pInputName, NULL, 0);
} else if (iRcvd < 0 && errno != EINTR) {
char errStr[1024];
rs_strerror_r(errno, errStr, sizeof(errStr));
@@ -304,7 +310,12 @@ CODESTARTwillRun
dbgprintf("Opened UNIX socket '%s' (fd %d).\n", funixn[i], funix[i]);
}
- RETiRet;
+ /* 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
@@ -330,6 +341,9 @@ CODESTARTafterRun
discardFunixn();
nfunix = 1;
+
+ if(pInputName != NULL)
+ prop.Destruct(&pInputName);
ENDafterRun
@@ -337,6 +351,7 @@ BEGINmodExit
CODESTARTmodExit
objRelease(glbl, CORE_COMPONENT);
objRelease(errmsg, CORE_COMPONENT);
+ objRelease(prop, CORE_COMPONENT);
ENDmodExit
@@ -373,6 +388,7 @@ CODESTARTmodInit
CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(errmsg, CORE_COMPONENT));
CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(prop, CORE_COMPONENT));
dbgprintf("imuxsock version %s initializing\n", PACKAGE_VERSION);
diff --git a/plugins/omgssapi/omgssapi.c b/plugins/omgssapi/omgssapi.c
index e0cc8af6..782ac22f 100644
--- a/plugins/omgssapi/omgssapi.c
+++ b/plugins/omgssapi/omgssapi.c
@@ -44,6 +44,7 @@
#include <pthread.h>
#include <gssapi/gssapi.h>
#include "dirty.h"
+#include "conf.h"
#include "syslogd-types.h"
#include "srUtils.h"
#include "net.h"
@@ -376,7 +377,7 @@ CODESTARTtryResume
ENDtryResume
BEGINdoAction
- char *psz; /* temporary buffering */
+ char *psz = NULL; /* temporary buffering */
register unsigned l;
int iMaxLine;
CODESTARTdoAction
@@ -430,11 +431,14 @@ CODESTARTdoAction
* rgerhards, 2006-11-30
*/
dbgprintf("Compression failed, sending uncompressed message\n");
+ free(out);
} else if(destLen+1 < l) {
/* only use compression if there is a gain in using it! */
dbgprintf("there is gain in compression, so we do it\n");
psz = (char*) out;
l = destLen + 1; /* take care for the "z" at message start! */
+ } else {
+ free(out);
}
++destLen;
}
@@ -444,11 +448,17 @@ CODESTARTdoAction
/* error! */
dbgprintf("error forwarding via tcp, suspending\n");
pData->eDestState = eDestFORW_SUSP;
- iRet = RS_RET_SUSPENDED;
+ ABORT_FINALIZE(RS_RET_SUSPENDED);
}
break;
}
finalize_it:
+# ifdef USE_NETZIP
+ if((psz != NULL) && (psz != (char*) ppString[0])) {
+ /* we need to free temporary buffer, alloced above - Naoya Nakazawa, 2010-01-11 */
+ free(psz);
+ }
+# endif
ENDdoAction
diff --git a/plugins/omlibdbi/omlibdbi.c b/plugins/omlibdbi/omlibdbi.c
index 6f130f54..67b1edf9 100644
--- a/plugins/omlibdbi/omlibdbi.c
+++ b/plugins/omlibdbi/omlibdbi.c
@@ -287,7 +287,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
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*)"")) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ if((pData->pwd = (uchar*) strdup((char*)pwd)) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_RQD_TPL_OPT_SQL, (uchar*) " StdDBFmt"));
diff --git a/plugins/ommail/ommail.c b/plugins/ommail/ommail.c
index 5faadce3..3a7669c9 100644
--- a/plugins/ommail/ommail.c
+++ b/plugins/ommail/ommail.c
@@ -44,7 +44,7 @@
#include <netdb.h>
#include <time.h>
#include <sys/socket.h>
-#include "dirty.h"
+#include "conf.h"
#include "syslogd-types.h"
#include "srUtils.h"
#include "cfsysline.h"
diff --git a/plugins/ommysql/ommysql.c b/plugins/ommysql/ommysql.c
index ecf738a9..d6870a7b 100644
--- a/plugins/ommysql/ommysql.c
+++ b/plugins/ommysql/ommysql.c
@@ -36,7 +36,7 @@
#include <errno.h>
#include <time.h>
#include <mysql.h>
-#include "dirty.h"
+#include "conf.h"
#include "syslogd-types.h"
#include "srUtils.h"
#include "template.h"
diff --git a/plugins/omoracle/Makefile.am b/plugins/omoracle/Makefile.am
new file mode 100644
index 00000000..11257fb2
--- /dev/null
+++ b/plugins/omoracle/Makefile.am
@@ -0,0 +1,8 @@
+pkglib_LTLIBRARIES = omoracle.la
+
+omoracle_la_SOURCES = omoracle.c omoracle.h
+omoracle_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) $(ORACLE_CFLAGS)
+omoracle_la_LDFLAGS = -module -avoid-version
+omoracle_la_LIBADD = $(ORACLE_LIBS)
+
+#EXTRA_DIST =
diff --git a/plugins/omoracle/omoracle.c b/plugins/omoracle/omoracle.c
new file mode 100644
index 00000000..331b7dd4
--- /dev/null
+++ b/plugins/omoracle/omoracle.c
@@ -0,0 +1,572 @@
+/** omoracle.c
+
+ This is an output module feeding directly to an Oracle
+ database. It uses Oracle Call Interface, a propietary module
+ provided by Oracle.
+
+ Selector lines to be used are of this form:
+
+ :omoracle:;TemplateName
+
+ The module gets its configuration via rsyslog $... directives,
+ namely:
+
+ $OmoracleDBUser: user name to log in on the database.
+
+ $OmoracleDBPassword: password to log in on the database.
+
+ $OmoracleDB: connection string (an Oracle easy connect or a db
+ name as specified by tnsnames.ora)
+
+ $OmoracleBatchSize: Number of elements to send to the DB on each
+ transaction.
+
+ $OmoracleBatchItemSize: Number of characters each property may
+ have. Make it as big as the longest value you expect for *any*
+ property in the sentence. For instance, if you expect 5 arguments
+ to the statement, 4 have 10 bytes and the 5th may be up to 3KB,
+ then specify $OmoracleBatchItemSize 3072. Please, remember to
+ leave space to the trailing \0!!
+
+ $OmoracleStatementTemplate: Name of the template containing the
+ statement to be prepared and executed in batches. Please note that
+ Oracle's prepared statements have their placeholders as
+ ':identifier', and this module uses the colon to guess how many
+ placeholders there will be.
+
+ All these directives are mandatory. The dbstring can be an Oracle
+ easystring or a DB name, as present in the tnsnames.ora file.
+
+ The form of the template is just a list of strings you want
+ inserted to the DB, for instance:
+
+ $template TestStmt,"%hostname%%msg%"
+
+ Will provide the arguments to a statement like
+
+ $OmoracleStatement \
+ insert into foo(hostname,message)values(:host,:message)
+
+ Also note that identifiers to placeholders are arbitrarry. You
+ need to define the properties on the template in the correct order
+ you want them passed to the statement!
+
+ This file is licensed under the terms of the GPL version 3 or, at
+ your choice, any later version. Exceptionally (perhaps), you are
+ allowed to link to the Oracle Call Interface in your derived work
+
+ Author: Luis Fernando Muñoz Mejías
+ <Luis.Fernando.Munoz.Mejias@cern.ch>
+
+ This file is part of rsyslog.
+*/
+#include "config.h"
+#include "rsyslog.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <oci.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <time.h>
+#include <assert.h>
+#include <ctype.h>
+#include "dirty.h"
+#include "syslogd-types.h"
+#include "srUtils.h"
+#include "template.h"
+#include "module-template.h"
+#include "errmsg.h"
+#include "cfsysline.h"
+#include "omoracle.h"
+
+MODULE_TYPE_OUTPUT
+
+/** */
+DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+
+/** */
+struct oracle_batch
+{
+ /* Batch size */
+ int size;
+ /* Last element inserted in the buffer. The batch will be
+ * executed when n == size */
+ int n;
+ /* Number of arguments the statement takes */
+ int arguments;
+ /** Maximum size of each parameter */
+ int param_size;
+ /* Parameters to pass to the statement on this transaction */
+ char*** parameters;
+ /* Binding parameters */
+ OCIBind** bindings;
+};
+
+typedef struct _instanceData {
+ /* Environment handler, the base for any OCI work. */
+ OCIEnv* environment;
+ /* Session handler, the actual DB connection object. */
+ OCISession* session;
+ /* Error handler for OCI calls. */
+ OCIError* error;
+ /* Prepared statement. */
+ OCIStmt* statement;
+ /* Service handler. */
+ OCISvcCtx* service;
+ /* Credentials object for the connection. */
+ OCIAuthInfo* authinfo;
+ /* Connection string, kept here for possible retries. */
+ char* connection;
+ /* Statement to be prepared. */
+ char* txt_statement;
+ /* Batch */
+ struct oracle_batch batch;
+} instanceData;
+
+/** Database name, to be filled by the $OmoracleDB directive */
+static char* db_name;
+/** Database user name, to be filled by the $OmoracleDBUser
+ * directive */
+static char* db_user;
+/** Database password, to be filled by the $OmoracleDBPassword */
+static char* db_password;
+/** Batch size. */
+static int batch_size;
+/** Size of each element in the batch. */
+static int batch_item_size;
+/** Statement to prepare and execute */
+static char* db_statement;
+
+/** Generic function for handling errors from OCI.
+
+ It will be called only inside CHECKERR and CHECKENV macros.
+
+ Arguments: handle The error or environment handle to check.
+ htype: OCI_HTYPE_* constant, usually OCI_HTYPE_ERROR or
+ OCI_HTYPE_ENV
+ status: status code to check, usually the return value of an OCI
+ function.
+
+ Returns OCI_SUCCESS on success, OCI_ERROR otherwise.
+*/
+static int oci_errors(void* handle, ub4 htype, sword status)
+{
+ sb4 errcode;
+ unsigned char buf[MAX_BUFSIZE];
+
+ switch (status) {
+ case OCI_SUCCESS:
+ return OCI_SUCCESS;
+ break;
+ case OCI_SUCCESS_WITH_INFO:
+ errmsg.LogError(0, NO_ERRCODE, "OCI SUCCESS - With info\n");
+ break;
+ case OCI_NEED_DATA:
+ errmsg.LogError(0, NO_ERRCODE, "OCI NEEDS MORE DATA\n");
+ break;
+ case OCI_ERROR:
+ dbgprintf ("OCI GENERAL ERROR\n");
+ if (handle) {
+ OCIErrorGet(handle, 1, NULL, &errcode, buf,
+ sizeof buf, htype);
+ errmsg.LogError(0, NO_ERRCODE, "Error message: %s", buf);
+ } else
+ errmsg.LogError(0, NO_ERRCODE, "NULL handle\n"
+ "Unable to extract further "
+ "information");
+ break;
+ case OCI_INVALID_HANDLE:
+ errmsg.LogError(0, NO_ERRCODE, "OCI INVALID HANDLE\n");
+ break;
+ case OCI_STILL_EXECUTING:
+ errmsg.LogError(0, NO_ERRCODE, "Still executing...\n");
+ break;
+ case OCI_CONTINUE:
+ errmsg.LogError(0, NO_ERRCODE, "OCI CONTINUE\n");
+ break;
+ }
+ return OCI_ERROR;
+}
+
+/** Callback for OCIBindDynamic.
+ *
+ * OCI doesn't insert an array of char* by itself (although it can
+ * handle arrays of int), so we must either run in batches of size one
+ * (no way) or bind all parameters with OCI_DATA_AT_EXEC instead of
+ * OCI_DEFAULT, and then give this function as an argument to
+ * OCIBindDynamic so that it is able to handle all strings in a single
+ * server trip.
+ *
+ * See the documentation of OCIBindDynamic
+ * (http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28395/oci16rel003.htm#i444015)
+ * for more details.
+ */
+static int bind_dynamic (char** in, OCIBind __attribute__((unused))* bind,
+ int iter, int __attribute__((unused)) idx,
+ char** out, int* buflen, unsigned char* piece,
+ void** bd)
+{
+ *out = in[iter];
+ *buflen = strlen(*out) + 1;
+ dbgprintf ("omoracle bound line %d, length %d: %s\n", iter, *buflen,
+ *out);
+ *piece = OCI_ONE_PIECE;
+ *bd = NULL;
+ return OCI_CONTINUE;
+}
+
+
+/** Returns the number of bind parameters for the statement given as
+ * an argument. It counts the number of appearances of ':', as in
+ *
+ * insert into foo(bar, baz) values(:bar, :baz)
+ *
+ * while taking in account that string literals must not be parsed. */
+static int count_bind_parameters(char* p)
+{
+ int n = 0;
+ int enable = 1;
+
+ for (; *p; p++)
+ if (enable && *p == BIND_MARK )
+ n++;
+ else if (*p == '\'')
+ enable ^= 1;
+ dbgprintf ("omoracle statement has %d parameters\n", n);
+ return n;
+}
+
+/** Prepares the statement, binding all its positional parameters */
+static int prepare_statement(instanceData* pData)
+{
+ int i;
+ DEFiRet;
+
+ CHECKERR(pData->error,
+ OCIStmtPrepare(pData->statement,
+ pData->error,
+ pData->txt_statement,
+ strlen(pData->txt_statement),
+ OCI_NTV_SYNTAX, OCI_DEFAULT));
+ for (i = 0; i < pData->batch.arguments; i++) {
+ CHECKERR(pData->error,
+ OCIBindByPos(pData->statement,
+ pData->batch.bindings+i,
+ pData->error, i+1, NULL,
+ pData->batch.param_size,
+ SQLT_STR, NULL, NULL, NULL,
+ 0, 0, OCI_DATA_AT_EXEC));
+ CHECKERR(pData->error,
+ OCIBindDynamic(pData->batch.bindings[i],
+ pData->error,
+ pData->batch.parameters[i],
+ bind_dynamic, NULL, NULL));
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Resource allocation */
+BEGINcreateInstance
+ int i, j;
+ struct template* tpl;
+CODESTARTcreateInstance
+
+ ASSERT(pData != NULL);
+
+ CHECKENV(pData->environment,
+ OCIEnvCreate((void*) &(pData->environment), OCI_DEFAULT,
+ NULL, NULL, NULL, NULL, 0, NULL));
+ CHECKENV(pData->environment,
+ OCIHandleAlloc(pData->environment, (void*) &(pData->error),
+ OCI_HTYPE_ERROR, 0, NULL));
+ CHECKENV(pData->environment,
+ OCIHandleAlloc(pData->environment, (void*) &(pData->authinfo),
+ OCI_HTYPE_AUTHINFO, 0, NULL));
+ CHECKENV(pData->environment,
+ OCIHandleAlloc(pData->environment, (void*) &(pData->statement),
+ OCI_HTYPE_STMT, 0, NULL));
+ tpl = tplFind(db_statement, strlen(db_statement));
+ pData->txt_statement = strdup(tpl->pEntryRoot->data.constant.pConstant);
+ CHKmalloc(pData->txt_statement);
+ dbgprintf("omoracle will run stored statement: %s\n",
+ pData->txt_statement);
+
+ pData->batch.n = 0;
+ pData->batch.size = batch_size;
+ pData->batch.param_size = batch_item_size *
+ sizeof ***pData->batch.parameters;
+ pData->batch.arguments = count_bind_parameters(pData->txt_statement);
+
+ /* I know, this can be done with a single malloc() call but this is
+ * easier to read. :) */
+ pData->batch.parameters = calloc(pData->batch.arguments,
+ sizeof *pData->batch.parameters);
+ CHKmalloc(pData->batch.parameters);
+ for (i = 0; i < pData->batch.arguments; i++) {
+ pData->batch.parameters[i] = calloc(pData->batch.size,
+ sizeof **pData->batch.parameters);
+ CHKmalloc(pData->batch.parameters[i]);
+ for (j = 0; j < pData->batch.size; j++) {
+ /* Each entry has at most
+ * pData->batch.param_size bytes because OCI
+ * doesn't like null-terminated strings when
+ * operating with batches, and the maximum
+ * size of each entry must be provided when
+ * binding parameters. pData->batch.param_size
+ * is long enough for usual entries. */
+ pData->batch.parameters[i][j] = malloc(pData->batch.param_size);
+ CHKmalloc(pData->batch.parameters[i][j]);
+ }
+ }
+
+ pData->batch.bindings = calloc(pData->batch.arguments,
+ sizeof *pData->batch.bindings);
+ CHKmalloc(pData->batch.bindings);
+
+finalize_it:
+ENDcreateInstance
+
+/* Inserts all stored statements into the database, releasing any
+ * allocated memory. */
+static int insert_to_db(instanceData* pData)
+{
+ DEFiRet;
+
+ CHECKERR(pData->error,
+ OCIStmtExecute(pData->service,
+ pData->statement,
+ pData->error,
+ pData->batch.n, 0, NULL, NULL,
+ OCI_BATCH_ERRORS));
+
+finalize_it:
+ pData->batch.n = 0;
+ OCITransCommit(pData->service, pData->error, 0);
+ dbgprintf ("omoracle insertion to DB %s\n", iRet == RS_RET_OK ?
+ "succeeded" : "did not succeed");
+ RETiRet;
+}
+
+/** Close the session and free anything allocated by
+ createInstance. */
+BEGINfreeInstance
+ int i, j;
+CODESTARTfreeInstance
+
+/* Before actually releasing our resources, let's try to commit
+ * anything pending so that we don't lose any messages. */
+ insert_to_db(pData);
+ OCISessionRelease(pData->service, pData->error, NULL, 0, OCI_DEFAULT);
+ OCIHandleFree(pData->environment, OCI_HTYPE_ENV);
+ OCIHandleFree(pData->error, OCI_HTYPE_ERROR);
+ OCIHandleFree(pData->service, OCI_HTYPE_SVCCTX);
+ OCIHandleFree(pData->authinfo, OCI_HTYPE_AUTHINFO);
+ OCIHandleFree(pData->statement, OCI_HTYPE_STMT);
+ free(pData->connection);
+ free(pData->txt_statement);
+ for (i = 0; i < pData->batch.arguments; i++) {
+ for (j = 0; j < pData->batch.size; j++)
+ free(pData->batch.parameters[i][j]);
+ free(pData->batch.parameters[i]);
+ }
+ free(pData->batch.parameters);
+ free(pData->batch.bindings);
+ dbgprintf ("omoracle freed all its resources\n");
+
+ENDfreeInstance
+
+BEGINtryResume
+CODESTARTtryResume
+ /* Here usually only a reconnect is done. The rsyslog core will call
+ * this entry point from time to time when the action suspended itself.
+ * Note that the rsyslog core expects that if the plugin suspended itself
+ * the action was not carried out during that invocation. Thus, rsyslog
+ * will call the action with *the same* data item again AFTER a resume
+ * was successful. As such, tryResume should NOT write the failed data
+ * item. If it needs to for some reason, it must delete the item again,
+ * otherwise, it will get duplicated.
+ * This handling inside the rsyslog core is important to be able to
+ * preserve data over rsyslog restarts. With it, the core can ensure that
+ * it queues all not-yet-processed messages without the plugin needing
+ * to take care about that.
+ * So in essence, it is recommended that just a reconnet is tried, but
+ * the last action not restarted. Note that it is not a real problem
+ * (but causes a slight performance degradation) if tryResume returns
+ * successfully but the next call to doAction() immediately returns
+ * RS_RET_SUSPENDED. So it is OK to do the actual restart inside doAction().
+ * ... of course I don't know why Oracle might need a full restart...
+ * rgerhards, 2009-03-26
+ */
+ dbgprintf("omoracle attempting to reconnect to DB server\n");
+ OCISessionRelease(pData->service, pData->error, NULL, 0, OCI_DEFAULT);
+ OCIHandleFree(pData->service, OCI_HTYPE_SVCCTX);
+ CHECKERR(pData->error, OCISessionGet(pData->environment, pData->error,
+ &pData->service, pData->authinfo,
+ pData->connection,
+ strlen(pData->connection), NULL, 0,
+ NULL, NULL, NULL, OCI_DEFAULT));
+ CHKiRet(prepare_statement(pData));
+
+finalize_it:
+ENDtryResume
+
+static rsRetVal startSession(instanceData* pData, char* connection, char* user,
+ char * password)
+{
+ DEFiRet;
+ CHECKERR(pData->error,
+ OCIAttrSet(pData->authinfo, OCI_HTYPE_AUTHINFO, user,
+ strlen(user), OCI_ATTR_USERNAME, pData->error));
+ CHECKERR(pData->error,
+ OCIAttrSet(pData->authinfo, OCI_HTYPE_AUTHINFO, password,
+ strlen(password), OCI_ATTR_PASSWORD, pData->error));
+ CHECKERR(pData->error,
+ OCISessionGet(pData->environment, pData->error,
+ &pData->service, pData->authinfo, connection,
+ strlen(connection), NULL, 0, NULL, NULL, NULL,
+ OCI_DEFAULT));
+finalize_it:
+ if (iRet != RS_RET_OK)
+ errmsg.LogError(0, NO_ERRCODE,
+ "Unable to start Oracle session\n");
+ RETiRet;
+}
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ /* Right now, this module is compatible with nothing. */
+ iRet = RS_RET_INCOMPATIBLE;
+ENDisCompatibleWithFeature
+
+BEGINparseSelectorAct
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(1);
+
+ if (strncmp((char*) p, ":omoracle:", sizeof ":omoracle:" - 1)) {
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+ }
+
+ p += sizeof ":omoracle:" - 1;
+
+ if (*p == '\0' || *p == ',') {
+ errmsg.LogError(0, NO_ERRCODE,
+ "Wrong char processing module arguments: %c\n",
+ *p);
+ ABORT_FINALIZE(RS_RET_INVALID_PARAMS);
+ }
+
+ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0,
+ OMSR_TPL_AS_ARRAY, " StdFmt"));
+ CHKiRet(createInstance(&pData));
+ CHKmalloc(pData->connection = strdup(db_name));
+ CHKiRet(startSession(pData, db_name, db_user, db_password));
+
+ CHKiRet(prepare_statement(pData));
+
+ dbgprintf ("omoracle module got all its resources allocated "
+ "and connected to the DB\n");
+
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+BEGINdoAction
+ int i;
+ char **params = (char**) ppString[0];
+CODESTARTdoAction
+
+ if (pData->batch.n == pData->batch.size) {
+ dbgprintf("omoracle batch size limit hit, sending into DB\n");
+ CHKiRet(insert_to_db(pData));
+ }
+
+ for (i = 0; i < pData->batch.arguments && params[i]; i++) {
+ dbgprintf("batch[%d][%d]=%s\n", i, pData->batch.n, params[i]);
+ strncpy(pData->batch.parameters[i][pData->batch.n], params[i],
+ pData->batch.param_size);
+ CHKmalloc(pData->batch.parameters[i][pData->batch.n]);
+ }
+ pData->batch.n++;
+
+finalize_it:
+ENDdoAction
+
+BEGINmodExit
+CODESTARTmodExit
+ENDmodExit
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ENDdbgPrintInstInfo
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_OMOD_QUERIES
+ENDqueryEtryPt
+
+static rsRetVal
+resetConfigVariables(uchar __attribute__((unused)) *pp,
+ void __attribute__((unused)) *pVal)
+{
+ int n;
+ DEFiRet;
+ if(db_user != NULL)
+ free(db_user);
+ if(db_name != NULL)
+ free(db_name);
+ if (db_password != NULL) {
+ n = strlen(db_password);
+ memset(db_password, 0, n);
+ free(db_password);
+ }
+ if (db_statement != NULL)
+ free(db_statement);
+ db_name = db_user = db_password = db_statement = NULL;
+ batch_size = batch_item_size = 0;
+ RETiRet;
+}
+
+BEGINmodInit()
+ rsRetVal (*supported_options)(unsigned long *pOpts);
+ unsigned long opts;
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION;
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(omsdRegCFSLineHdlr((uchar*) "resetconfigvariables", 1,
+ eCmdHdlrCustomHandler, resetConfigVariables,
+ NULL, STD_LOADABLE_MODULE_ID));
+
+ CHKiRet(omsdRegCFSLineHdlr((uchar*) "omoracledbuser", 0,
+ eCmdHdlrGetWord, NULL, &db_user,
+ STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar*) "omoracledbpassword", 0,
+ eCmdHdlrGetWord, NULL, &db_password,
+ STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar*) "omoracledb", 0,
+ eCmdHdlrGetWord, NULL, &db_name,
+ STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar*) "omoraclebatchsize", 0,
+ eCmdHdlrInt, NULL, &batch_size,
+ STD_LOADABLE_MODULE_ID));
+ CHKiRet(pHostQueryEtryPt((uchar*)"OMSRgetSupportedTplOpts", &supported_options));
+ CHKiRet((*supported_options)(&opts));
+ if (!(opts & OMSR_TPL_AS_ARRAY))
+ ABORT_FINALIZE(RS_RET_RSCORE_TOO_OLD);
+
+ CHKiRet(omsdRegCFSLineHdlr((uchar*) "omoraclestatementtemplate", 0,
+ eCmdHdlrGetWord, NULL,
+ &db_statement, STD_LOADABLE_MODULE_ID));
+
+ CHKiRet(omsdRegCFSLineHdlr((uchar*) "omoraclebatchitemsize", 0,
+ eCmdHdlrInt, NULL,
+ &batch_item_size, STD_LOADABLE_MODULE_ID));
+
+ENDmodInit
diff --git a/plugins/omoracle/omoracle.h b/plugins/omoracle/omoracle.h
new file mode 100644
index 00000000..0ff879b3
--- /dev/null
+++ b/plugins/omoracle/omoracle.h
@@ -0,0 +1,31 @@
+/** Definitions for the Oracle output module.
+
+ This module needs OCI to be installed (on Red Hat-like systems
+ this is usually the oracle-instantclient-devel RPM).
+
+ This file is part of rsyslog.
+
+ This file is licensed under the terms of the GPL version 3 or, at
+ your choice, any later version. Exceptionally (perhaps), you are
+ allowed to link to the Oracle Call Interface in your derived work
+
+ Author: Luis Fernando Muñoz Mejías <Luis.Fernando.Munoz.Mejias@cern.ch>
+*/
+#ifndef __OMORACLEH__
+#define __OMORACLEH__
+
+/** Macros to make error handling easier. */
+
+/** Checks for errors on the OCI handling. */
+#define CHECKERR(handle,status) CHKiRet(oci_errors((handle), \
+ OCI_HTYPE_ERROR, (status)))
+
+/** Checks for errors when handling the environment of OCI. */
+#define CHECKENV(handle,status) CHKiRet(oci_errors((handle), \
+ OCI_HTYPE_ENV, (status)))
+
+enum { MAX_BUFSIZE = 2048 };
+
+#define BIND_MARK ':'
+
+#endif
diff --git a/plugins/omoracle/omoracle.te b/plugins/omoracle/omoracle.te
new file mode 100644
index 00000000..81eb6cf1
--- /dev/null
+++ b/plugins/omoracle/omoracle.te
@@ -0,0 +1,13 @@
+
+module omoracle 1.0;
+
+require {
+ type syslogd_t;
+ type port_t;
+ class process { execstack execmem };
+ class tcp_socket name_connect;
+}
+
+#============= syslogd_t ==============
+allow syslogd_t port_t:tcp_socket name_connect;
+allow syslogd_t self:process { execstack execmem };
diff --git a/plugins/ompgsql/createDB.sql b/plugins/ompgsql/createDB.sql
index facdcaa0..2f72a0a6 100644
--- a/plugins/ompgsql/createDB.sql
+++ b/plugins/ompgsql/createDB.sql
@@ -1,4 +1,4 @@
-CREATE DATABASE "Syslog";
+CREATE DATABASE 'Syslog' WITH ENCODING 'SQL_ASCII';
\c Syslog;
CREATE TABLE SystemEvents
(
diff --git a/plugins/ompgsql/ompgsql.c b/plugins/ompgsql/ompgsql.c
index 6daac1c7..eb774835 100644
--- a/plugins/ompgsql/ompgsql.c
+++ b/plugins/ompgsql/ompgsql.c
@@ -40,7 +40,7 @@
#include <errno.h>
#include <time.h>
#include <libpq-fe.h>
-#include "dirty.h"
+#include "conf.h"
#include "syslogd-types.h"
#include "srUtils.h"
#include "template.h"
diff --git a/plugins/omprog/Makefile.am b/plugins/omprog/Makefile.am
new file mode 100644
index 00000000..63fe09b8
--- /dev/null
+++ b/plugins/omprog/Makefile.am
@@ -0,0 +1,8 @@
+pkglib_LTLIBRARIES = omprog.la
+
+omprog_la_SOURCES = omprog.c
+omprog_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS)
+omprog_la_LDFLAGS = -module -avoid-version
+omprog_la_LIBADD =
+
+EXTRA_DIST =
diff --git a/plugins/omprog/omprog.c b/plugins/omprog/omprog.c
new file mode 100644
index 00000000..01fa7cea
--- /dev/null
+++ b/plugins/omprog/omprog.c
@@ -0,0 +1,357 @@
+/* omprog.c
+ * This output plugin enables rsyslog to execute a program and
+ * feed it the message stream as standard input.
+ *
+ * NOTE: read comments in module-template.h for more specifics!
+ *
+ * File begun on 2009-04-01 by RGerhards
+ *
+ * Copyright 2009 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <wait.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
+
+/* internal structures
+ */
+DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+
+typedef struct _instanceData {
+ uchar *szBinary; /* name of binary to call */
+ pid_t pid; /* pid of currently running process */
+ int fdPipe; /* file descriptor to write to */
+ int bIsRunning; /* is binary currently running? 0-no, 1-yes */
+} instanceData;
+
+/* config settings */
+static uchar *szBinary = NULL; /* name of binary to call */
+
+BEGINcreateInstance
+CODESTARTcreateInstance
+ENDcreateInstance
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURERepeatedMsgReduction)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ if(pData->szBinary != NULL)
+ free(pData->szBinary);
+ENDfreeInstance
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ENDdbgPrintInstInfo
+
+
+BEGINtryResume
+CODESTARTtryResume
+ENDtryResume
+
+
+/* execute the child process (must be called in child context
+ * after fork).
+ */
+
+static void execBinary(instanceData *pData, int fdStdin)
+{
+ int i;
+ struct sigaction sigAct;
+ char *newargv[] = { NULL };
+ char *newenviron[] = { NULL };
+
+ assert(pData != NULL);
+
+ fclose(stdin);
+ dup(fdStdin);
+ //fclose(stdout);
+
+ /* we close all file handles as we fork soon
+ * Is there a better way to do this? - mail me! rgerhards@adiscon.com
+ */
+# ifndef VALGRIND /* we can not use this with valgrind - too many errors... */
+ for(i = 3 ; i <= 65535 ; ++i)
+ close(i);
+# endif
+
+ /* reset signal handlers to default */
+ memset(&sigAct, 0, sizeof(sigAct));
+ sigfillset(&sigAct.sa_mask);
+ sigAct.sa_handler = SIG_DFL;
+ for(i = 1 ; i < NSIG ; ++i)
+ sigaction(i, &sigAct, NULL);
+
+ alarm(0);
+
+ /* finally exec child */
+ execve((char*)pData->szBinary, newargv, newenviron);
+ /* switch to?
+ execlp((char*)program, (char*) program, (char*)arg, NULL);
+ */
+
+ /* we should never reach this point, but if we do, we terminate */
+ exit(1);
+}
+
+
+/* creates a pipe and starts program, uses pipe as stdin for program.
+ * rgerhards, 2009-04-01
+ */
+static rsRetVal
+openPipe(instanceData *pData)
+{
+ int pipefd[2];
+ pid_t cpid;
+ DEFiRet;
+
+ assert(pData != NULL);
+
+ if(pipe(pipefd) == -1) {
+ ABORT_FINALIZE(RS_RET_ERR_CREAT_PIPE);
+ }
+
+ DBGPRINTF("executing program '%s'\n", pData->szBinary);
+
+ /* NO OUTPUT AFTER FORK! */
+
+ cpid = fork();
+ if(cpid == -1) {
+ ABORT_FINALIZE(RS_RET_ERR_FORK);
+ }
+
+ if(cpid == 0) {
+ /* we are now the child, just set the right selectors and
+ * exec the binary. If that fails, there is not much we can do.
+ */
+ close(pipefd[1]);
+ execBinary(pData, pipefd[0]);
+ /*NO CODE HERE - WILL NEVER BE REACHED!*/
+ }
+
+ DBGPRINTF("child has pid %d\n", cpid);
+ pData->fdPipe = pipefd[1];
+ pData->pid = cpid;
+ close(pipefd[0]);
+ pData->bIsRunning = 1;
+finalize_it:
+ RETiRet;
+}
+
+
+/* clean up after a terminated child
+ */
+static inline rsRetVal
+cleanup(instanceData *pData)
+{
+ int status;
+ int ret;
+ char errStr[1024];
+ DEFiRet;
+
+ assert(pData != NULL);
+ assert(pData->bIsRunning == 1);
+RUNLOG_VAR("%d", pData->pid);
+ ret = waitpid(pData->pid, &status, 0);
+ if(ret != pData->pid) {
+ /* if waitpid() fails, we can not do much - try to ignore it... */
+ DBGPRINTF("waitpid() returned state %d[%s], future malfunction may happen\n", ret,
+ rs_strerror_r(errno, errStr, sizeof(errStr)));
+ } else {
+ /* check if we should print out some diagnostic information */
+ DBGPRINTF("waitpid status return for program '%s': %2.2x\n",
+ pData->szBinary, status);
+ if(WIFEXITED(status)) {
+ errmsg.LogError(0, NO_ERRCODE, "program '%s' exited normally, state %d",
+ pData->szBinary, WEXITSTATUS(status));
+ } else if(WIFSIGNALED(status)) {
+ errmsg.LogError(0, NO_ERRCODE, "program '%s' terminated by signal %d.",
+ pData->szBinary, WTERMSIG(status));
+ }
+ }
+
+ pData->bIsRunning = 0;
+ RETiRet;
+}
+
+
+/* try to restart the binary when it has stopped.
+ */
+static inline rsRetVal
+tryRestart(instanceData *pData)
+{
+ DEFiRet;
+ assert(pData != NULL);
+ assert(pData->bIsRunning == 0);
+
+ iRet = openPipe(pData);
+ RETiRet;
+}
+
+
+/* write to pipe
+ * note that we do not try to run block-free. If the users fears something
+ * may block (and this not be acceptable), the action should be run on its
+ * own action queue.
+ */
+static rsRetVal
+writePipe(instanceData *pData, uchar *szMsg)
+{
+ int lenWritten;
+ int lenWrite;
+ int writeOffset;
+ char errStr[1024];
+ DEFiRet;
+
+ assert(pData != NULL);
+
+ lenWrite = strlen((char*)szMsg);
+ writeOffset = 0;
+
+ do
+ {
+ lenWritten = write(pData->fdPipe, ((char*)szMsg)+writeOffset, lenWrite);
+ if(lenWritten == -1) {
+ switch(errno) {
+ case EPIPE:
+ DBGPRINTF("Program '%s' terminated, trying to restart\n",
+ pData->szBinary);
+ CHKiRet(cleanup(pData));
+ CHKiRet(tryRestart(pData));
+ break;
+ default:
+ DBGPRINTF("error %d writing to pipe: %s\n", errno,
+ rs_strerror_r(errno, errStr, sizeof(errStr)));
+ ABORT_FINALIZE(RS_RET_ERR_WRITE_PIPE);
+ break;
+ }
+ } else {
+ writeOffset += lenWritten;
+ }
+ } while(lenWritten != lenWrite);
+
+
+finalize_it:
+ RETiRet;
+}
+
+
+BEGINdoAction
+CODESTARTdoAction
+ if(pData->bIsRunning == 0) {
+ openPipe(pData);
+ }
+
+ iRet = writePipe(pData, ppString[0]);
+
+ if(iRet != RS_RET_OK)
+ iRet = RS_RET_SUSPENDED;
+ENDdoAction
+
+
+BEGINparseSelectorAct
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(1)
+ /* first check if this config line is actually for us */
+ if(strncmp((char*) p, ":omprog:", sizeof(":omprog:") - 1)) {
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+ }
+
+ /* ok, if we reach this point, we have something for us */
+ p += sizeof(":omprog:") - 1; /* eat indicator sequence (-1 because of '\0'!) */
+ CHKiRet(createInstance(&pData));
+
+ CHKmalloc(pData->szBinary = (uchar*) strdup((char*)szBinary));
+ /* check if a non-standard template is to be applied */
+ if(*(p-1) == ';')
+ --p;
+ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, 0, (uchar*) "RSYSLOG_FileFormat"));
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+
+BEGINmodExit
+CODESTARTmodExit
+ if(szBinary != NULL) {
+ free(szBinary);
+ szBinary = NULL;
+ }
+ CHKiRet(objRelease(errmsg, CORE_COMPONENT));
+finalize_it:
+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;
+
+ if(szBinary != NULL) {
+ free(szBinary);
+ szBinary = NULL;
+ }
+
+ RETiRet;
+}
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *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 *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+CODEmodInit_QueryRegCFSLineHdlr
+ENDmodInit
+
+/* vi:set ai:
+ */
diff --git a/plugins/omrelp/omrelp.c b/plugins/omrelp/omrelp.c
index 8d74c82f..4a21627d 100644
--- a/plugins/omrelp/omrelp.c
+++ b/plugins/omrelp/omrelp.c
@@ -36,7 +36,7 @@
#include <errno.h>
#include <ctype.h>
#include <librelp.h>
-#include "dirty.h"
+#include "conf.h"
#include "syslogd-types.h"
#include "srUtils.h"
#include "cfsysline.h"
@@ -249,8 +249,18 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
/* extract the host first (we do a trick - we replace the ';' or ':' with a '\0')
* now skip to port and then template name. rgerhards 2005-07-06
*/
- for(q = p ; *p && *p != ';' && *p != ':' ; ++p)
- /* JUST SKIP */;
+ if(*p == '[') { /* everything is hostname upto ']' */
+ ++p; /* skip '[' */
+ for(q = p ; *p && *p != ']' ; ++p)
+ /* JUST SKIP */;
+ if(*p == ']') {
+ *p = '\0'; /* trick to obtain hostname (later)! */
+ ++p; /* eat it */
+ }
+ } else { /* traditional view of hostname */
+ for(q = p ; *p && *p != ';' && *p != ':' && *p != '#' ; ++p)
+ /* JUST SKIP */;
+ }
pData->port = NULL;
if(*p == ':') { /* process port */
diff --git a/plugins/omsnmp/omsnmp.c b/plugins/omsnmp/omsnmp.c
index 72fa8d64..4db60e62 100644
--- a/plugins/omsnmp/omsnmp.c
+++ b/plugins/omsnmp/omsnmp.c
@@ -36,7 +36,7 @@
#include <netdb.h>
#include <ctype.h>
#include <assert.h>
-#include "dirty.h"
+#include "conf.h"
#include "syslogd-types.h"
#include "cfsysline.h"
#include "module-template.h"
diff --git a/plugins/omstdout/Makefile.am b/plugins/omstdout/Makefile.am
new file mode 100644
index 00000000..9f5d497f
--- /dev/null
+++ b/plugins/omstdout/Makefile.am
@@ -0,0 +1,8 @@
+pkglib_LTLIBRARIES = omstdout.la
+
+omstdout_la_SOURCES = omstdout.c
+omstdout_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS)
+omstdout_la_LDFLAGS = -module -avoid-version
+omstdout_la_LIBADD =
+
+EXTRA_DIST =
diff --git a/plugins/omstdout/omstdout.c b/plugins/omstdout/omstdout.c
new file mode 100644
index 00000000..b3ec6287
--- /dev/null
+++ b/plugins/omstdout/omstdout.c
@@ -0,0 +1,214 @@
+/* omstdout.c
+ * send all output to stdout - this is primarily a test driver (but may
+ * be used for weired use cases). Not tested for robustness!
+ *
+ * NOTE: read comments in module-template.h for more specifics!
+ *
+ * File begun on 2009-03-19 by RGerhards
+ *
+ * Copyright 2009 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include "conf.h"
+#include "syslogd-types.h"
+#include "srUtils.h"
+#include "template.h"
+#include "module-template.h"
+#include "errmsg.h"
+#include "cfsysline.h"
+
+MODULE_TYPE_OUTPUT
+
+/* 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 {
+ int bUseArrayInterface; /* uses action use array instead of string template interface? */
+ int bEnsureLFEnding; /* ensure that a linefeed is written at the end of EACH record (test aid for nettester) */
+} instanceData;
+
+BEGINcreateInstance
+CODESTARTcreateInstance
+ENDcreateInstance
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURERepeatedMsgReduction)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ENDfreeInstance
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ENDdbgPrintInstInfo
+
+
+BEGINtryResume
+CODESTARTtryResume
+ENDtryResume
+
+BEGINdoAction
+ char **szParams;
+ char *toWrite;
+ int iParamVal;
+ int iParam;
+ int iBuf;
+ char szBuf[65564];
+ size_t len;
+CODESTARTdoAction
+ if(pData->bUseArrayInterface) {
+ /* if we use array passing, we need to put together a string
+ * ourselves. At this point, please keep in mind that omstdout is
+ * primarily a testing aid. Other modules may do different processing
+ * if they would like to support downlevel versions which do not support
+ * array-passing, but also use that interface on cores who do...
+ * So this code here is also more or less an example of how to do that.
+ * rgerhards, 2009-04-03
+ */
+ szParams = (char**) (ppString[0]);
+ /* In array-passing mode, ppString[] contains a NULL-terminated array
+ * of char *pointers.
+ */
+ iParam = 0;
+ iBuf = 0;
+ while(szParams[iParam] != NULL) {
+ if(iParam > 0)
+ szBuf[iBuf++] = ','; /* all but first need a delimiter */
+ iParamVal = 0;
+ while(szParams[iParam][iParamVal] != '\0' && iBuf < (int) sizeof(szBuf)) {
+ szBuf[iBuf++] = szParams[iParam][iParamVal++];
+ }
+ ++iParam;
+ }
+ szBuf[iBuf] = '\0';
+ toWrite = szBuf;
+ } else {
+ toWrite = (char*) ppString[0];
+ }
+ len = strlen(toWrite);
+ write(1, toWrite, len); /* 1 is stdout! */
+ if(pData->bEnsureLFEnding && toWrite[len-1] != '\n') {
+ write(1, "\n", 1); /* write missing LF */
+ }
+ENDdoAction
+
+
+BEGINparseSelectorAct
+ int iTplOpts;
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(1)
+ /* first check if this config line is actually for us */
+ if(strncmp((char*) p, ":omstdout:", sizeof(":omstdout:") - 1)) {
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+ }
+
+ /* ok, if we reach this point, we have something for us */
+ p += sizeof(":omstdout:") - 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;
+ iTplOpts = (bUseArrayInterface == 0) ? 0 : OMSR_TPL_AS_ARRAY;
+ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, iTplOpts, (uchar*) "RSYSLOG_FileFormat"));
+ pData->bUseArrayInterface = bUseArrayInterface;
+ pData->bEnsureLFEnding = bEnsureLFEnding;
+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;
+ bUseArrayInterface = 0;
+ bEnsureLFEnding = 1;
+ RETiRet;
+}
+
+
+BEGINmodInit()
+ rsRetVal localRet;
+ rsRetVal (*pomsrGetSupportedTplOpts)(unsigned long *pOpts);
+ unsigned long opts;
+ int bArrayPassingSupported; /* does core support template passing as an array? */
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ /* check if the rsyslog core supports parameter passing code */
+ bArrayPassingSupported = 0;
+ localRet = pHostQueryEtryPt((uchar*)"OMSRgetSupportedTplOpts", &pomsrGetSupportedTplOpts);
+ if(localRet == RS_RET_OK) {
+ /* found entry point, so let's see if core supports array passing */
+ CHKiRet((*pomsrGetSupportedTplOpts)(&opts));
+ if(opts & OMSR_TPL_AS_ARRAY)
+ bArrayPassingSupported = 1;
+ } else if(localRet != RS_RET_ENTRY_POINT_NOT_FOUND) {
+ ABORT_FINALIZE(localRet); /* Something else went wrong, what is not acceptable */
+ }
+ DBGPRINTF("omstdout: array-passing is %ssupported by rsyslog core.\n", bArrayPassingSupported ? "" : "not ");
+
+ if(bArrayPassingSupported) {
+ /* enable config comand only if core supports it */
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionomstdoutarrayinterface", 0, eCmdHdlrBinary, NULL,
+ &bUseArrayInterface, STD_LOADABLE_MODULE_ID));
+ }
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionomstdoutensurelfending", 0, eCmdHdlrBinary, NULL,
+ &bEnsureLFEnding, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
+ resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+
+/* vi:set ai:
+ */
diff --git a/plugins/omtemplate/Makefile.am b/plugins/omtemplate/Makefile.am
new file mode 100644
index 00000000..e816c7c6
--- /dev/null
+++ b/plugins/omtemplate/Makefile.am
@@ -0,0 +1,8 @@
+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
new file mode 100644
index 00000000..5577f8c6
--- /dev/null
+++ b/plugins/omtemplate/omtemplate.c
@@ -0,0 +1,220 @@
+/* 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
+
+/* 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/runtime/Makefile.am b/runtime/Makefile.am
index 2e6c4041..14abe722 100644
--- a/runtime/Makefile.am
+++ b/runtime/Makefile.am
@@ -7,6 +7,7 @@ pkglib_LTLIBRARIES =
librsyslog_la_SOURCES = \
rsyslog.c \
rsyslog.h \
+ unicode-helper.h \
atomic.h \
syslogd-types.h \
module-template.h \
@@ -16,6 +17,8 @@ librsyslog_la_SOURCES = \
glbl.c \
conf.c \
conf.h \
+ parser.h \
+ parser.c \
msg.c \
msg.h \
linkedlist.c \
@@ -36,6 +39,8 @@ librsyslog_la_SOURCES = \
obj.h \
modules.c \
modules.h \
+ apc.c \
+ apc.h \
sync.c \
sync.h \
expr.c \
@@ -64,6 +69,12 @@ librsyslog_la_SOURCES = \
vmop.h \
queue.c \
queue.h \
+ ruleset.c \
+ ruleset.h \
+ rule.c \
+ rule.h \
+ prop.c \
+ prop.h \
cfsysline.c \
cfsysline.h \
\
@@ -102,6 +113,17 @@ lmregexp_la_LDFLAGS = -module -avoid-version
lmregexp_la_LIBADD =
endif
+#
+# zlib support
+#
+if ENABLE_ZLIB
+pkglib_LTLIBRARIES += lmzlibw.la
+lmzlibw_la_SOURCES = zlibw.c zlibw.h
+lmzlibw_la_CPPFLAGS = $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
+lmzlibw_la_LDFLAGS = -module -avoid-version
+lmzlibw_la_LIBADD =
+endif
+
if ENABLE_INET
pkglib_LTLIBRARIES += lmnet.la lmnetstrms.la
#
@@ -118,6 +140,13 @@ lmnetstrms_la_CPPFLAGS = $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
lmnetstrms_la_LDFLAGS = -module -avoid-version
lmnetstrms_la_LIBADD =
+# generic stream server framework
+pkglib_LTLIBRARIES += lmstrmsrv.la
+lmstrmsrv_la_SOURCES = strmsrv.c strmsrv.h strms_sess.c strms_sess.h
+lmstrmsrv_la_CPPFLAGS = $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
+lmstrmsrv_la_LDFLAGS = -module -avoid-version
+lmstrmsrv_la_LIBADD =
+
# netstream drivers
# plain tcp driver - main driver
diff --git a/runtime/apc.c b/runtime/apc.c
new file mode 100644
index 00000000..bc330e39
--- /dev/null
+++ b/runtime/apc.c
@@ -0,0 +1,405 @@
+/* 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"
+
+/* static data */
+DEFobjStaticHelpers
+
+/* 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);
+
+ time(&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;
+ DEFVARS_mutexProtection_uncond;
+ DEFiRet;
+
+ BEGIN_MTX_PROTECTED_OPERATIONS_UNCOND(&listMutex);
+ iRet = unlistCurrent(&pExecList);
+ END_MTX_PROTECTED_OPERATIONS_UNCOND(&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)
+{
+ DEFVARS_mutexProtection_uncond;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, apc);
+ assert(pID != NULL);
+ BEGIN_MTX_PROTECTED_OPERATIONS_UNCOND(&listMutex);
+ insertApc(pThis, pID);
+ END_MTX_PROTECTED_OPERATIONS_UNCOND(&listMutex);
+RUNLOG_STR("apcConstructFinalize post mutex unlock\n");
+ 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)
+{
+ DEFVARS_mutexProtection_uncond;
+
+ BEGINfunc
+ BEGIN_MTX_PROTECTED_OPERATIONS_UNCOND(&listMutex);
+ deleteApc(id);
+ END_MTX_PROTECTED_OPERATIONS_UNCOND(&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(apcstk, 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(apcstk, 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
new file mode 100644
index 00000000..7c679b97
--- /dev/null
+++ b/runtime/apc.h
@@ -0,0 +1,56 @@
+/* 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/atomic.h b/runtime/atomic.h
index 10dbac90..d5aaf56b 100644
--- a/runtime/atomic.h
+++ b/runtime/atomic.h
@@ -42,9 +42,16 @@
*/
#ifdef HAVE_ATOMIC_BUILTINS
# define ATOMIC_INC(data) ((void) __sync_fetch_and_add(&(data), 1))
+# define ATOMIC_INC_AND_FETCH(data) __sync_fetch_and_add(&(data), 1)
+# define ATOMIC_DEC(data) ((void) __sync_sub_and_fetch(&(data), 1))
# define ATOMIC_DEC_AND_FETCH(data) __sync_sub_and_fetch(&(data), 1)
# define ATOMIC_FETCH_32BIT(data) ((unsigned) __sync_fetch_and_and(&(data), 0xffffffff))
# define ATOMIC_STORE_1_TO_32BIT(data) __sync_lock_test_and_set(&(data), 1)
+# define ATOMIC_STORE_0_TO_INT(data) __sync_fetch_and_and(&(data), 0)
+# define ATOMIC_STORE_1_TO_INT(data) __sync_fetch_and_or(&(data), 1)
+# define ATOMIC_STORE_INT_TO_INT(data, val) __sync_fetch_and_or(&(data), (val))
+# define ATOMIC_CAS(data, oldVal, newVal) __sync_bool_compare_and_swap(&(data), (oldVal), (newVal));
+# define ATOMIC_CAS_VAL(data, oldVal, newVal) __sync_val_compare_and_swap(&(data), (oldVal), (newVal));
#else
/* note that we gained parctical proof that theoretical problems DO occur
* if we do not properly address them. See this blog post for details:
@@ -55,6 +62,7 @@
*/
# warning "atomic builtins not available, using nul operations - rsyslogd will probably be racy!"
# define ATOMIC_INC(data) (++(data))
+# define ATOMIC_DEC(data) (--(data))
# define ATOMIC_DEC_AND_FETCH(data) (--(data))
# define ATOMIC_FETCH_32BIT(data) (data)
# define ATOMIC_STORE_1_TO_32BIT(data) (data) = 1
diff --git a/runtime/cfsysline.c b/runtime/cfsysline.c
index c4490b48..1dd5905e 100644
--- a/runtime/cfsysline.c
+++ b/runtime/cfsysline.c
@@ -217,9 +217,9 @@ static rsRetVal doGetSize(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *
case 'K': i *= 1000; ++(*pp); break;
case 'M': i *= 1000000; ++(*pp); break;
case 'G': i *= 1000000000; ++(*pp); break;
- case 'T': i *= 1000000000000; ++(*pp); break; /* tera */
- case 'P': i *= 1000000000000000; ++(*pp); break; /* peta */
- case 'E': i *= 1000000000000000000; ++(*pp); break; /* exa */
+ case 'T': i *= (long long) 1000000000000; ++(*pp); break; /* tera */
+ case 'P': i *= (long long) 1000000000000000; ++(*pp); break; /* peta */
+ case 'E': i *= (long long) 1000000000000000000; ++(*pp); break; /* exa */
}
/* done */
@@ -364,7 +364,7 @@ static rsRetVal doGetGID(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *p
/* we set value via a set function */
CHKiRet(pSetHdlr(pVal, pgBuf->gr_gid));
}
- dbgprintf("gid %d obtained for group '%s'\n", pgBuf->gr_gid, szName);
+ dbgprintf("gid %d obtained for group '%s'\n", (int) pgBuf->gr_gid, szName);
}
skipWhiteSpace(pp); /* skip over any whitespace */
@@ -406,7 +406,7 @@ static rsRetVal doGetUID(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *p
/* we set value via a set function */
CHKiRet(pSetHdlr(pVal, ppwBuf->pw_uid));
}
- dbgprintf("uid %d obtained for user '%s'\n", ppwBuf->pw_uid, szName);
+ dbgprintf("uid %d obtained for user '%s'\n", (int) ppwBuf->pw_uid, szName);
}
skipWhiteSpace(pp); /* skip over any whitespace */
@@ -462,7 +462,7 @@ getWord(uchar **pp, cstr_t **ppStrB)
ASSERT(*pp != NULL);
ASSERT(ppStrB != NULL);
- CHKiRet(rsCStrConstruct(ppStrB));
+ CHKiRet(cstrConstruct(ppStrB));
skipWhiteSpace(pp); /* skip over any whitespace */
@@ -470,9 +470,9 @@ getWord(uchar **pp, cstr_t **ppStrB)
p = *pp;
while(*p && !isspace((int) *p)) {
- CHKiRet(rsCStrAppendChar(*ppStrB, *p++));
+ CHKiRet(cstrAppendChar(*ppStrB, *p++));
}
- CHKiRet(rsCStrFinish(*ppStrB));
+ CHKiRet(cstrFinalize(*ppStrB));
*pp = p;
@@ -506,7 +506,7 @@ static rsRetVal doGetWord(uchar **pp, rsRetVal (*pSetHdlr)(void*, uchar*), void
ASSERT(*pp != NULL);
CHKiRet(getWord(pp, &pStrB));
- CHKiRet(rsCStrConvSzStrAndDestruct(pStrB, &pNewVal, 0));
+ CHKiRet(cstrConvSzStrAndDestruct(pStrB, &pNewVal, 0));
pStrB = NULL;
/* we got the word, now set it */
@@ -525,7 +525,7 @@ static rsRetVal doGetWord(uchar **pp, rsRetVal (*pSetHdlr)(void*, uchar*), void
finalize_it:
if(iRet != RS_RET_OK) {
if(pStrB != NULL)
- rsCStrDestruct(&pStrB);
+ cstrDestruct(&pStrB);
}
RETiRet;
@@ -548,7 +548,7 @@ doSyslogName(uchar **pp, rsRetVal (*pSetHdlr)(void*, int), void *pVal, syslogNam
ASSERT(*pp != NULL);
CHKiRet(getWord(pp, &pStrB)); /* get word */
- iNewVal = decodeSyslogName(rsCStrGetSzStr(pStrB), pNameTable);
+ iNewVal = decodeSyslogName(cstrGetSzStr(pStrB), pNameTable);
if(pSetHdlr == NULL) {
/* we should set value directly to var */
@@ -814,7 +814,7 @@ rsRetVal regCfSysLineHdlr(uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlTy
CHKiRet(cslcConstruct(&pThis, bChainingPermitted));
CHKiRet_Hdlr(cslcAddHdlr(pThis, eType, pHdlr, pData, pOwnerCookie)) {
cslcDestruct(pThis);
- goto finalize_it;
+ FINALIZE;
}
/* important: add to list, AFTER everything else is OK. Else
* we mess up things in the error case.
@@ -825,7 +825,7 @@ rsRetVal regCfSysLineHdlr(uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlTy
}
CHKiRet_Hdlr(llAppend(&llCmdList, pMyCmdName, (void*) pThis)) {
cslcDestruct(pThis);
- goto finalize_it;
+ FINALIZE;
}
} else {
/* command already exists, are we allowed to chain? */
@@ -834,7 +834,7 @@ rsRetVal regCfSysLineHdlr(uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlTy
}
CHKiRet_Hdlr(cslcAddHdlr(pThis, eType, pHdlr, pData, pOwnerCookie)) {
cslcDestruct(pThis);
- goto finalize_it;
+ FINALIZE;
}
}
@@ -961,11 +961,11 @@ void dbgPrintCfSysLineHandlers(void)
dbgprintf("Sytem Line Configuration Commands:\n");
llCookieCmd = NULL;
- while((iRet = llGetNextElt(&llCmdList, &llCookieCmd, (void*)&pCmd)) == RS_RET_OK) {
+ while(llGetNextElt(&llCmdList, &llCookieCmd, (void*)&pCmd) == RS_RET_OK) {
llGetKey(llCookieCmd, (void*) &pKey); /* TODO: using the cookie is NOT clean! */
dbgprintf("\tCommand '%s':\n", pKey);
llCookieCmdHdlr = NULL;
- while((iRet = llGetNextElt(&pCmd->llCmdHdlrs, &llCookieCmdHdlr, (void*)&pCmdHdlr)) == RS_RET_OK) {
+ while(llGetNextElt(&pCmd->llCmdHdlrs, &llCookieCmdHdlr, (void*)&pCmdHdlr) == RS_RET_OK) {
dbgprintf("\t\ttype : %d\n", pCmdHdlr->eType);
dbgprintf("\t\tpData: 0x%lx\n", (unsigned long) pCmdHdlr->pData);
dbgprintf("\t\tHdlr : 0x%lx\n", (unsigned long) pCmdHdlr->cslCmdHdlr);
diff --git a/runtime/conf.c b/runtime/conf.c
index efe9c516..bbd2147a 100644
--- a/runtime/conf.c
+++ b/runtime/conf.c
@@ -46,7 +46,9 @@
#include <glob.h>
#include <sys/types.h>
#ifdef HAVE_LIBGEN_H
-# include <libgen.h>
+# ifndef OS_SOLARIS
+# include <libgen.h>
+# endif
#endif
#include "rsyslog.h"
@@ -67,10 +69,16 @@
#include "expr.h"
#include "ctok.h"
#include "ctok_token.h"
+#include "rule.h"
+#include "ruleset.h"
+#include "unicode-helper.h"
+#ifdef OS_SOLARIS
+# define NAME_MAX MAXNAMELEN
+#endif
/* forward definitions */
-static rsRetVal cfline(uchar *line, selector_t **pfCurr);
+static rsRetVal cfline(uchar *line, rule_t **pfCurr);
static rsRetVal processConfFile(uchar *pConfFile);
@@ -82,6 +90,8 @@ DEFobjCurrIf(ctok_token)
DEFobjCurrIf(module)
DEFobjCurrIf(errmsg)
DEFobjCurrIf(net)
+DEFobjCurrIf(rule)
+DEFobjCurrIf(ruleset)
static int iNbrActions; /* number of actions the running config has. Needs to be init on ReInitConf() */
@@ -387,15 +397,17 @@ finalize_it:
static rsRetVal
processConfFile(uchar *pConfFile)
{
- DEFiRet;
int iLnNbr = 0;
FILE *cf;
- selector_t *fCurr = NULL;
+ rule_t *pCurrRule = NULL;
uchar *p;
uchar cbuf[CFGLNSIZ];
uchar *cline;
int i;
int bHadAnError = 0;
+ uchar *pszOrgLine = NULL;
+ size_t lenLine;
+ DEFiRet;
ASSERT(pConfFile != NULL);
if((cf = fopen((char*)pConfFile, "r")) == NULL) {
@@ -408,9 +420,12 @@ processConfFile(uchar *pConfFile)
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 */
- if(cline[strlen((char*)cline)-1] == '\n') {
- cline[strlen((char*)cline) -1] = '\0';
+ 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.
*/
@@ -424,7 +439,6 @@ processConfFile(uchar *pConfFile)
* TODO: review the code at whole - this is highly suspect (but will go away
* once we do the rest of RainerScript).
*/
- /* was: strcpy((char*)cline, (char*)p); */
for( i = 0 ; p[i] != '\0' ; ++i) {
cline[i] = p[i];
}
@@ -448,7 +462,7 @@ processConfFile(uchar *pConfFile)
/* we now have the complete line, and are positioned at the first non-whitespace
* character. So let's process it
*/
- if(cfline(cbuf, &fCurr) != RS_RET_OK) {
+ if(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
*/
@@ -456,28 +470,32 @@ processConfFile(uchar *pConfFile)
dbgprintf("config line NOT successfully processed\n");
snprintf((char*)szErrLoc, sizeof(szErrLoc) / sizeof(uchar),
"%s, line %d", pConfFile, iLnNbr);
- errmsg.LogError(0, NO_ERRCODE, "the last error occured in %s", (char*)szErrLoc);
+ errmsg.LogError(0, NO_ERRCODE, "the last error occured in %s:\"%s\"", (char*)szErrLoc, (char*)pszOrgLine);
bHadAnError = 1;
}
}
/* we probably have one selector left to be added - so let's do that now */
- CHKiRet(selectorAddList(fCurr));
+ if(pCurrRule != NULL) {
+ CHKiRet(ruleset.AddRule(rule.GetAssRuleset(pCurrRule), &pCurrRule));
+ }
/* close the configuration file */
- (void) fclose(cf);
+ fclose(cf);
finalize_it:
if(iRet != RS_RET_OK) {
char errStr[1024];
- if(fCurr != NULL)
- selectorDestruct(fCurr);
+ 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;
}
@@ -495,7 +513,7 @@ finalize_it:
rsRetVal cflineParseTemplateName(uchar** pp, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts, uchar *dfltTplName)
{
uchar *p;
- uchar *tplName;
+ uchar *tplName = NULL;
cstr_t *pStrB;
DEFiRet;
@@ -521,23 +539,23 @@ rsRetVal cflineParseTemplateName(uchar** pp, omodStringRequest_t *pOMSR, int iEn
tplName = (uchar*) strdup((char*)dfltTplName);
} else {
/* template specified, pick it up */
- if(rsCStrConstruct(&pStrB) != RS_RET_OK) {
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- }
+ CHKiRet(cstrConstruct(&pStrB));
/* now copy the string */
while(*p && *p != '#' && !isspace((int) *p)) {
- CHKiRet(rsCStrAppendChar(pStrB, *p));
+ CHKiRet(cstrAppendChar(pStrB, *p));
++p;
}
- CHKiRet(rsCStrFinish(pStrB));
- CHKiRet(rsCStrConvSzStrAndDestruct(pStrB, &tplName, 0));
+ CHKiRet(cstrFinalize(pStrB));
+ CHKiRet(cstrConvSzStrAndDestruct(pStrB, &tplName, 0));
}
- iRet = OMSRsetEntry(pOMSR, iEntry, tplName, iTplOpts);
- if(iRet != RS_RET_OK) goto finalize_it;
+ CHKiRet(OMSRsetEntry(pOMSR, iEntry, tplName, iTplOpts));
finalize_it:
+ if(iRet != RS_RET_OK)
+ free(tplName);
+
*pp = p;
RETiRet;
@@ -552,6 +570,7 @@ finalize_it:
* rgerhards, 2007-07-25
* updated to include OMSR pointer -- rgerhards, 2007-07-27
* updated to include template name -- rgerhards, 2008-03-28
+ * rgerhards, 2010-01-19: file names end at the first space
*/
rsRetVal
cflineParseFileName(uchar* p, uchar *pFileName, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts, uchar *pszTpl)
@@ -564,7 +583,7 @@ cflineParseFileName(uchar* p, uchar *pFileName, omodStringRequest_t *pOMSR, int
pName = pFileName;
i = 1; /* we start at 1 so that we reseve space for the '\0'! */
- while(*p && *p != ';' && i < MAXFNAME) {
+ while(*p && *p != ';' && *p != ' ' && i < MAXFNAME) {
*pName++ = *p++;
++i;
}
@@ -583,7 +602,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 selector_t *f)
+static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register rule_t *pRule)
{
uchar *p;
register uchar *q;
@@ -598,17 +617,17 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register selector_t *f
ASSERT(pline != NULL);
ASSERT(*pline != NULL);
- ASSERT(f != NULL);
+ ISOBJ_TYPE_assert(pRule, rule);
dbgprintf(" - traditional PRI filter\n");
errno = 0; /* keep strerror_r() stuff out of logerror messages */
- f->f_filter_type = FILTER_PRI;
+ pRule->f_filter_type = FILTER_PRI;
/* Note: file structure is pre-initialized to zero because it was
* created with calloc()!
*/
for (i = 0; i <= LOG_NFACILITIES; i++) {
- f->f_filterData.f_pmask[i] = TABLE_NOPRI;
+ pRule->f_filterData.f_pmask[i] = TABLE_NOPRI;
}
/* scan through the list of selectors */
@@ -663,32 +682,32 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register selector_t *f
for (i = 0; i <= LOG_NFACILITIES; i++) {
if ( pri == INTERNAL_NOPRI ) {
if ( ignorepri )
- f->f_filterData.f_pmask[i] = TABLE_ALLPRI;
+ pRule->f_filterData.f_pmask[i] = TABLE_ALLPRI;
else
- f->f_filterData.f_pmask[i] = TABLE_NOPRI;
+ pRule->f_filterData.f_pmask[i] = TABLE_NOPRI;
}
else if ( singlpri ) {
if ( ignorepri )
- f->f_filterData.f_pmask[i] &= ~(1<<pri);
+ pRule->f_filterData.f_pmask[i] &= ~(1<<pri);
else
- f->f_filterData.f_pmask[i] |= (1<<pri);
+ pRule->f_filterData.f_pmask[i] |= (1<<pri);
}
else
{
if ( pri == TABLE_ALLPRI ) {
if ( ignorepri )
- f->f_filterData.f_pmask[i] = TABLE_NOPRI;
+ pRule->f_filterData.f_pmask[i] = TABLE_NOPRI;
else
- f->f_filterData.f_pmask[i] = TABLE_ALLPRI;
+ pRule->f_filterData.f_pmask[i] = TABLE_ALLPRI;
}
else
{
if ( ignorepri )
for (i2= 0; i2 <= pri; ++i2)
- f->f_filterData.f_pmask[i] &= ~(1<<i2);
+ pRule->f_filterData.f_pmask[i] &= ~(1<<i2);
else
for (i2= 0; i2 <= pri; ++i2)
- f->f_filterData.f_pmask[i] |= (1<<i2);
+ pRule->f_filterData.f_pmask[i] |= (1<<i2);
}
}
}
@@ -703,27 +722,27 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register selector_t *f
if ( pri == INTERNAL_NOPRI ) {
if ( ignorepri )
- f->f_filterData.f_pmask[i >> 3] = TABLE_ALLPRI;
+ pRule->f_filterData.f_pmask[i >> 3] = TABLE_ALLPRI;
else
- f->f_filterData.f_pmask[i >> 3] = TABLE_NOPRI;
+ pRule->f_filterData.f_pmask[i >> 3] = TABLE_NOPRI;
} else if ( singlpri ) {
if ( ignorepri )
- f->f_filterData.f_pmask[i >> 3] &= ~(1<<pri);
+ pRule->f_filterData.f_pmask[i >> 3] &= ~(1<<pri);
else
- f->f_filterData.f_pmask[i >> 3] |= (1<<pri);
+ pRule->f_filterData.f_pmask[i >> 3] |= (1<<pri);
} else {
if ( pri == TABLE_ALLPRI ) {
if ( ignorepri )
- f->f_filterData.f_pmask[i >> 3] = TABLE_NOPRI;
+ pRule->f_filterData.f_pmask[i >> 3] = TABLE_NOPRI;
else
- f->f_filterData.f_pmask[i >> 3] = TABLE_ALLPRI;
+ pRule->f_filterData.f_pmask[i >> 3] = TABLE_ALLPRI;
} else {
if ( ignorepri )
for (i2= 0; i2 <= pri; ++i2)
- f->f_filterData.f_pmask[i >> 3] &= ~(1<<i2);
+ pRule->f_filterData.f_pmask[i >> 3] &= ~(1<<i2);
else
for (i2= 0; i2 <= pri; ++i2)
- f->f_filterData.f_pmask[i >> 3] |= (1<<i2);
+ pRule->f_filterData.f_pmask[i >> 3] |= (1<<i2);
}
}
}
@@ -749,7 +768,7 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register selector_t *f
* A pointer to that beginnig is passed back to the caller.
* rgerhards 2008-01-19
*/
-static rsRetVal cflineProcessIfFilter(uchar **pline, register selector_t *f)
+static rsRetVal cflineProcessIfFilter(uchar **pline, register rule_t *f)
{
DEFiRet;
ctok_t *tok;
@@ -762,7 +781,6 @@ static rsRetVal cflineProcessIfFilter(uchar **pline, register selector_t *f)
dbgprintf(" - general expression-based filter\n");
errno = 0; /* keep strerror_r() stuff out of logerror messages */
-dbgprintf("calling expression parser, pp %p ('%s')\n", *pline, *pline);
f->f_filter_type = FILTER_EXPR;
/* if we come to over here, pline starts with "if ". We just skip that part. */
@@ -795,6 +813,9 @@ dbgprintf("calling expression parser, pp %p ('%s')\n", *pline, *pline);
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
*/
@@ -815,10 +836,11 @@ finalize_it:
* 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 selector_t *f)
+static rsRetVal cflineProcessPropFilter(uchar **pline, register rule_t *f)
{
rsParsObj *pPars;
cstr_t *pCSCompOp;
+ cstr_t *pCSPropName;
rsRetVal iRet;
int iOffset; /* for compare operations */
@@ -838,12 +860,19 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register selector_t *f)
}
/* read property */
- iRet = parsDelimCStr(pPars, &f->f_filterData.prop.pCSPropName, ',', 1, 1, 1);
+ iRet = parsDelimCStr(pPars, &pCSPropName, ',', 1, 1, 1);
+ if(iRet != RS_RET_OK) {
+ errmsg.LogError(0, iRet, "error %d parsing filter property - ignoring selector", iRet);
+ rsParsDestruct(pPars);
+ return(iRet);
+ }
+ iRet = propNameToID(pCSPropName, &f->f_filterData.prop.propID);
if(iRet != RS_RET_OK) {
errmsg.LogError(0, iRet, "error %d parsing filter property - ignoring selector", iRet);
rsParsDestruct(pPars);
return(iRet);
}
+ cstrDestruct(&pCSPropName);
/* read operation */
iRet = parsDelimCStr(pPars, &pCSCompOp, ',', 1, 1, 1);
@@ -879,6 +908,8 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register selector_t *f)
f->f_filterData.prop.operation = FIOP_STARTSWITH;
} else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (unsigned char*) "regex", 5)) {
f->f_filterData.prop.operation = FIOP_REGEX;
+ } else if(!rsCStrOffsetSzStrCmp(pCSCompOp, iOffset, (unsigned char*) "ereregex", 8)) {
+ f->f_filterData.prop.operation = FIOP_EREREGEX;
} else {
errmsg.LogError(0, NO_ERRCODE, "error: invalid compare operation '%s' - ignoring selector",
(char*) rsCStrGetSzStrNoNULL(pCSCompOp));
@@ -915,7 +946,7 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register selector_t *f)
*/
static rsRetVal cflineProcessHostSelector(uchar **pline)
{
- rsRetVal iRet;
+ DEFiRet;
ASSERT(pline != NULL);
ASSERT(*pline != NULL);
@@ -941,21 +972,20 @@ static rsRetVal cflineProcessHostSelector(uchar **pline)
dbgprintf("resetting BSD-like hostname filter\n");
eDfltHostnameCmpMode = HN_NO_COMP;
if(pDfltHostnameCmp != NULL) {
- if((iRet = rsCStrSetSzStr(pDfltHostnameCmp, NULL)) != RS_RET_OK)
- return(iRet);
+ CHKiRet(rsCStrSetSzStr(pDfltHostnameCmp, NULL));
}
} else {
dbgprintf("setting BSD-like hostname filter to '%s'\n", *pline);
if(pDfltHostnameCmp == NULL) {
/* create string for parser */
- if((iRet = rsCStrConstructFromszStr(&pDfltHostnameCmp, *pline)) != RS_RET_OK)
- return(iRet);
+ CHKiRet(rsCStrConstructFromszStr(&pDfltHostnameCmp, *pline));
} else { /* string objects exists, just update... */
- if((iRet = rsCStrSetSzStr(pDfltHostnameCmp, *pline)) != RS_RET_OK)
- return(iRet);
+ CHKiRet(rsCStrSetSzStr(pDfltHostnameCmp, *pline));
}
}
- return RS_RET_OK;
+
+finalize_it:
+ RETiRet;
}
@@ -966,7 +996,7 @@ static rsRetVal cflineProcessHostSelector(uchar **pline)
*/
static rsRetVal cflineProcessTagSelector(uchar **pline)
{
- rsRetVal iRet;
+ DEFiRet;
ASSERT(pline != NULL);
ASSERT(*pline != NULL);
@@ -991,22 +1021,22 @@ static rsRetVal cflineProcessTagSelector(uchar **pline)
dbgprintf("setting programname filter to '%s'\n", *pline);
if(pDfltProgNameCmp == NULL) {
/* create string for parser */
- if((iRet = rsCStrConstructFromszStr(&pDfltProgNameCmp, *pline)) != RS_RET_OK)
- return(iRet);
+ CHKiRet(rsCStrConstructFromszStr(&pDfltProgNameCmp, *pline));
} else { /* string objects exists, just update... */
- if((iRet = rsCStrSetSzStr(pDfltProgNameCmp, *pline)) != RS_RET_OK)
- return(iRet);
+ CHKiRet(rsCStrSetSzStr(pDfltProgNameCmp, *pline));
}
}
- return RS_RET_OK;
+
+finalize_it:
+ RETiRet;
}
/* read the filter part of a configuration line and store the filter
- * in the supplied selector_t
+ * in the supplied rule_t
* rgerhards, 2007-08-01
*/
-static rsRetVal cflineDoFilter(uchar **pp, selector_t *f)
+static rsRetVal cflineDoFilter(uchar **pp, rule_t *f)
{
DEFiRet;
@@ -1099,17 +1129,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.
+ * 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, selector_t **pfCurr)
+static rsRetVal
+cflineClassic(uchar *p, rule_t **ppRule)
{
DEFiRet;
action_t *pAction;
- selector_t *fCurr;
-
- ASSERT(pfCurr != NULL);
-
- fCurr = *pfCurr;
/* lines starting with '&' have no new filters and just add
* new actions to the currently processed selector.
@@ -1127,16 +1155,19 @@ static rsRetVal cflineClassic(uchar *p, selector_t **pfCurr)
* selector is NULL, which means we do not need to care about it at
* all. -- rgerhards, 2007-08-01
*/
- CHKiRet(selectorAddList(fCurr));
- CHKiRet(selectorConstruct(&fCurr)); /* create "fresh" selector */
- CHKiRet(cflineDoFilter(&p, fCurr)); /* pull filters */
+ 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 */
}
CHKiRet(cflineDoAction(&p, &pAction));
- CHKiRet(llAppend(&fCurr->llActList, NULL, (void*) pAction));
+ CHKiRet(llAppend(&(*ppRule)->llActList, NULL, (void*) pAction));
finalize_it:
- *pfCurr = fCurr;
RETiRet;
}
@@ -1146,7 +1177,7 @@ finalize_it:
* rgerhards, 2007-08-01
*/
static rsRetVal
-cfline(uchar *line, selector_t **pfCurr)
+cfline(uchar *line, rule_t **pfCurr)
{
DEFiRet;
@@ -1243,6 +1274,8 @@ CODESTARTObjClassExit(conf)
objRelease(module, CORE_COMPONENT);
objRelease(errmsg, CORE_COMPONENT);
objRelease(net, LM_NET_FILENAME);
+ objRelease(rule, CORE_COMPONENT);
+ objRelease(ruleset, CORE_COMPONENT);
ENDObjClassExit(conf)
@@ -1258,6 +1291,8 @@ BEGINAbstractObjClassInit(conf, 1, OBJ_IS_CORE_MODULE) /* class, version - CHANG
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));
ENDObjClassInit(conf)
/* vi:set ai:
diff --git a/runtime/conf.h b/runtime/conf.h
index 2494d4dc..25b887be 100644
--- a/runtime/conf.h
+++ b/runtime/conf.h
@@ -35,7 +35,7 @@ BEGINinterface(conf) /* name must also be changed in ENDinterface macro! */
rsRetVal (*cfsysline)(uchar *p);
rsRetVal (*doModLoad)(uchar **pp, __attribute__((unused)) void* pVal);
rsRetVal (*doIncludeLine)(uchar **pp, __attribute__((unused)) void* pVal);
- rsRetVal (*cfline)(uchar *line, selector_t **pfCurr);
+ rsRetVal (*cfline)(uchar *line, rule_t **pfCurr);
rsRetVal (*processConfFile)(uchar *pConfFile);
rsRetVal (*ReInitConf)(void);
rsRetVal (*GetNbrActActions)(int *);
@@ -51,5 +51,9 @@ PROTOTYPEObj(conf);
extern EHostnameCmpMode eDfltHostnameCmpMode;
extern cstr_t *pDfltHostnameCmp;
extern cstr_t *pDfltProgNameCmp;
+/* TODO: the following 2 need to go in conf obj interface... */
+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);
+
#endif /* #ifndef INCLUDED_CONF_H */
diff --git a/runtime/ctok.c b/runtime/ctok.c
index 7dd5f8b0..99b0e095 100644
--- a/runtime/ctok.c
+++ b/runtime/ctok.c
@@ -281,13 +281,18 @@ ctokGetVar(ctok_t *pThis, ctok_token_t *pToken)
pToken->tok = ctok_MSGVAR;
}
- CHKiRet(rsCStrConstruct(&pstrVal));
- /* this loop is quite simple, a variable name is terminated by whitespace. */
- while(!isspace(c)) {
- CHKiRet(rsCStrAppendChar(pstrVal, tolower(c)));
+ CHKiRet(cstrConstruct(&pstrVal));
+ /* this loop is quite simple, a variable name is terminated when a non-supported
+ * character is detected. Note that we currently permit a numerical digit as the
+ * first char, which is not permitted by ABNF. -- rgerhards, 2009-03-10
+ */
+ while(isalpha(c) || isdigit(c) || (c == '_') || (c == '-')) {
+ CHKiRet(cstrAppendChar(pstrVal, tolower(c)));
CHKiRet(ctokGetCharFromStream(pThis, &c));
}
- CHKiRet(rsCStrFinish(pStrB));
+ CHKiRet(ctokUngetCharFromStream(pThis, c)); /* put not processed char back */
+
+ CHKiRet(cstrFinalize(pstrVal));
CHKiRet(var.SetString(pToken->pVar, pstrVal));
pstrVal = NULL;
@@ -295,7 +300,7 @@ ctokGetVar(ctok_t *pThis, ctok_token_t *pToken)
finalize_it:
if(iRet != RS_RET_OK) {
if(pstrVal != NULL) {
- rsCStrDestruct(&pstrVal);
+ cstrDestruct(&pstrVal);
}
}
@@ -319,25 +324,25 @@ ctokGetSimpStr(ctok_t *pThis, ctok_token_t *pToken)
pToken->tok = ctok_SIMPSTR;
- CHKiRet(rsCStrConstruct(&pstrVal));
+ CHKiRet(cstrConstruct(&pstrVal));
CHKiRet(ctokGetCharFromStreamNoComment(pThis, &c));
/* while we are in escape mode (had a backslash), no sequence
* terminates the loop. If outside, it is terminated by a single quote.
*/
while(bInEsc || c != '\'') {
if(bInEsc) {
- CHKiRet(rsCStrAppendChar(pstrVal, c));
+ CHKiRet(cstrAppendChar(pstrVal, c));
bInEsc = 0;
} else {
if(c == '\\') {
bInEsc = 1;
} else {
- CHKiRet(rsCStrAppendChar(pstrVal, c));
+ CHKiRet(cstrAppendChar(pstrVal, c));
}
}
CHKiRet(ctokGetCharFromStreamNoComment(pThis, &c));
}
- CHKiRet(rsCStrFinish(pStrB));
+ CHKiRet(cstrFinalize(pstrVal));
CHKiRet(var.SetString(pToken->pVar, pstrVal));
pstrVal = NULL;
@@ -345,7 +350,7 @@ ctokGetSimpStr(ctok_t *pThis, ctok_token_t *pToken)
finalize_it:
if(iRet != RS_RET_OK) {
if(pstrVal != NULL) {
- rsCStrDestruct(&pstrVal);
+ cstrDestruct(&pstrVal);
}
}
@@ -412,6 +417,7 @@ ctokGetToken(ctok_t *pThis, ctok_token_t **ppToken)
uchar c;
uchar szWord[128];
int bRetry = 0; /* retry parse? Only needed for inline comments... */
+ cstr_t *pstrVal;
ISOBJ_TYPE_assert(pThis, ctok);
ASSERT(ppToken != NULL);
@@ -535,7 +541,11 @@ ctokGetToken(ctok_t *pThis, ctok_token_t **ppToken)
/* push c back, higher level parser needs it */
CHKiRet(ctokUngetCharFromStream(pThis, c));
pToken->tok = ctok_FUNCTION;
- /* TODO: fill function name */
+ /* fill function name */
+ CHKiRet(cstrConstruct(&pstrVal));
+ CHKiRet(rsCStrSetSzStr(pstrVal, szWord));
+ CHKiRet(cstrFinalize(pstrVal));
+ CHKiRet(var.SetString(pToken->pVar, pstrVal));
} else { /* give up... */
dbgprintf("parser has an invalid word (token) '%s'\n", szWord);
pToken->tok = ctok_INVALID;
diff --git a/runtime/datetime.c b/runtime/datetime.c
index bed33127..593c3d5c 100644
--- a/runtime/datetime.c
+++ b/runtime/datetime.c
@@ -49,6 +49,8 @@
DEFobjStaticHelpers
DEFobjCurrIf(errmsg)
+/* the following table of ten powers saves us some computation */
+static const int tenPowers[6] = { 1, 10, 100, 1000, 10000, 100000 };
/* ------------------------------ methods ------------------------------ */
@@ -62,9 +64,14 @@ DEFobjCurrIf(errmsg)
* most portable and removes the need for additional structures
* (but I have to admit it is somewhat "bulky";)).
*
- * Obviously, all caller-provided pointers must not be NULL...
+ * Obviously, *t must not be NULL...
+ *
+ * rgerhards, 2008-10-07: added ttSeconds to provide a way to
+ * obtain the second-resolution UNIX timestamp. This is needed
+ * in some situations to minimize time() calls (namely when doing
+ * output processing). This can be left NULL if not needed.
*/
-static void getCurrTime(struct syslogTime *t)
+static void getCurrTime(struct syslogTime *t, time_t *ttSeconds)
{
struct timeval tp;
struct tm *tm;
@@ -83,6 +90,9 @@ static void getCurrTime(struct syslogTime *t)
# else
gettimeofday(&tp, NULL);
# endif
+ if(ttSeconds != NULL)
+ *ttSeconds = tp.tv_sec;
+
tm = localtime_r((time_t*) &(tp.tv_sec), &tmBuf);
t->year = tm->tm_year + 1900;
@@ -113,6 +123,7 @@ static void getCurrTime(struct syslogTime *t)
t->OffsetMode = '+';
t->OffsetHour = lBias / 3600;
t->OffsetMinute = (lBias % 3600) / 60;
+ t->timeType = TIME_TYPE_RFC5424; /* we have a high precision timestamp */
}
@@ -132,6 +143,7 @@ static void getCurrTime(struct syslogTime *t)
* DO NOT PUT ANY OTHER CODE IN THIS BEGIN ... END BLOCK!!!!
*/
+
/**
* Parse a 32 bit integer number from a string.
*
@@ -139,18 +151,21 @@ static void getCurrTime(struct syslogTime *t)
* must be positioned at the first digit. Will be updated
* so that on return it points to the first character AFTER
* the integer parsed.
+ * \param pLenStr pointer to string length, decremented on exit by
+ * characters processed
+ * Note that if an empty string (len < 1) is passed in,
+ * the method always returns zero.
* \retval The number parsed.
*/
-
-static int srSLMGParseInt32(char** ppsz)
+static int srSLMGParseInt32(uchar** ppsz, int *pLenStr)
{
- int i;
+ register int i;
i = 0;
- while(isdigit((int) **ppsz))
- {
+ while(*pLenStr > 0 && isdigit((int) **ppsz)) {
i = i * 10 + **ppsz - '0';
++(*ppsz);
+ --(*pLenStr);
}
return i;
@@ -162,11 +177,15 @@ static int srSLMGParseInt32(char** ppsz)
* updates the parse pointer position. The pTime parameter
* is guranteed to be updated only if a new valid timestamp
* could be obtained (restriction added 2008-09-16 by rgerhards).
+ * This method now also checks the maximum string length it is passed.
+ * If a *valid* timestamp is found, the string length is decremented
+ * by the number of characters processed. If it is not a valid timestamp,
+ * the length is kept unmodified. -- rgerhards, 2009-09-23
*/
static rsRetVal
-ParseTIMESTAMP3339(struct syslogTime *pTime, char** ppszTS)
+ParseTIMESTAMP3339(struct syslogTime *pTime, uchar** ppszTS, int *pLenStr)
{
- char *pszTS = *ppszTS;
+ uchar *pszTS = *ppszTS;
/* variables to temporarily hold time information while we parse */
int year;
int month;
@@ -179,6 +198,7 @@ ParseTIMESTAMP3339(struct syslogTime *pTime, char** ppszTS)
char OffsetMode; /* UTC offset + or - */
char OffsetHour; /* UTC offset in hours */
int OffsetMinute; /* UTC offset in minutes */
+ int lenStr;
/* end variables to temporarily hold time information while we parse */
DEFiRet;
@@ -186,48 +206,55 @@ ParseTIMESTAMP3339(struct syslogTime *pTime, char** ppszTS)
assert(ppszTS != NULL);
assert(pszTS != NULL);
- year = srSLMGParseInt32(&pszTS);
+ lenStr = *pLenStr;
+ year = srSLMGParseInt32(&pszTS, &lenStr);
/* We take the liberty to accept slightly malformed timestamps e.g. in
* the format of 2003-9-1T1:0:0. This doesn't hurt on receiving. Of course,
* with the current state of affairs, we would never run into this code
* here because at postion 11, there is no "T" in such cases ;)
*/
- if(*pszTS++ != '-')
+ if(lenStr == 0 || *pszTS++ != '-')
ABORT_FINALIZE(RS_RET_INVLD_TIME);
- month = srSLMGParseInt32(&pszTS);
+ --lenStr;
+ month = srSLMGParseInt32(&pszTS, &lenStr);
if(month < 1 || month > 12)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
- if(*pszTS++ != '-')
+ if(lenStr == 0 || *pszTS++ != '-')
ABORT_FINALIZE(RS_RET_INVLD_TIME);
- day = srSLMGParseInt32(&pszTS);
+ --lenStr;
+ day = srSLMGParseInt32(&pszTS, &lenStr);
if(day < 1 || day > 31)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
- if(*pszTS++ != 'T')
+ if(lenStr == 0 || *pszTS++ != 'T')
ABORT_FINALIZE(RS_RET_INVLD_TIME);
+ --lenStr;
- hour = srSLMGParseInt32(&pszTS);
+ hour = srSLMGParseInt32(&pszTS, &lenStr);
if(hour < 0 || hour > 23)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
- if(*pszTS++ != ':')
+ if(lenStr == 0 || *pszTS++ != ':')
ABORT_FINALIZE(RS_RET_INVLD_TIME);
- minute = srSLMGParseInt32(&pszTS);
+ --lenStr;
+ minute = srSLMGParseInt32(&pszTS, &lenStr);
if(minute < 0 || minute > 59)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
- if(*pszTS++ != ':')
+ if(lenStr == 0 || *pszTS++ != ':')
ABORT_FINALIZE(RS_RET_INVLD_TIME);
- second = srSLMGParseInt32(&pszTS);
+ --lenStr;
+ second = srSLMGParseInt32(&pszTS, &lenStr);
if(second < 0 || second > 60)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
/* Now let's see if we have secfrac */
- if(*pszTS == '.') {
- char *pszStart = ++pszTS;
- secfrac = srSLMGParseInt32(&pszTS);
+ if(lenStr > 0 && *pszTS == '.') {
+ --lenStr;
+ uchar *pszStart = ++pszTS;
+ secfrac = srSLMGParseInt32(&pszTS, &lenStr);
secfracPrecision = (int) (pszTS - pszStart);
} else {
secfracPrecision = 0;
@@ -235,23 +262,27 @@ ParseTIMESTAMP3339(struct syslogTime *pTime, char** ppszTS)
}
/* check the timezone */
- if(*pszTS == 'Z')
- {
+ if(lenStr == 0)
+ ABORT_FINALIZE(RS_RET_INVLD_TIME);
+
+ if(*pszTS == 'Z') {
+ --lenStr;
pszTS++; /* eat Z */
OffsetMode = 'Z';
OffsetHour = 0;
OffsetMinute = 0;
} else if((*pszTS == '+') || (*pszTS == '-')) {
OffsetMode = *pszTS;
+ --lenStr;
pszTS++;
- OffsetHour = srSLMGParseInt32(&pszTS);
+ OffsetHour = srSLMGParseInt32(&pszTS, &lenStr);
if(OffsetHour < 0 || OffsetHour > 23)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
- if(*pszTS++ != ':')
+ if(lenStr == 0 || *pszTS++ != ':')
ABORT_FINALIZE(RS_RET_INVLD_TIME);
- OffsetMinute = srSLMGParseInt32(&pszTS);
+ OffsetMinute = srSLMGParseInt32(&pszTS, &lenStr);
if(OffsetMinute < 0 || OffsetMinute > 59)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
} else {
@@ -260,10 +291,12 @@ ParseTIMESTAMP3339(struct syslogTime *pTime, char** ppszTS)
}
/* OK, we actually have a 3339 timestamp, so let's indicated this */
- if(*pszTS == ' ')
- ++pszTS;
- else
- ABORT_FINALIZE(RS_RET_INVLD_TIME);
+ if(lenStr > 0) {
+ if(*pszTS != ' ') /* if it is not a space, it can not be a "good" time - 2010-02-22 rgerhards */
+ ABORT_FINALIZE(RS_RET_INVLD_TIME);
+ ++pszTS; /* just skip past it */
+ --lenStr;
+ }
/* we had success, so update parse pointer and caller-provided timestamp */
*ppszTS = pszTS;
@@ -279,6 +312,7 @@ ParseTIMESTAMP3339(struct syslogTime *pTime, char** ppszTS)
pTime->OffsetMode = OffsetMode;
pTime->OffsetHour = OffsetHour;
pTime->OffsetMinute = OffsetMinute;
+ *pLenStr = lenStr;
finalize_it:
RETiRet;
@@ -297,24 +331,32 @@ finalize_it:
* permits us to use a pre-aquired timestamp and thus avoids to do
* a (costly) time() call. Thanks to David Lang for insisting on
* time() call reduction ;).
+ * This method now also checks the maximum string length it is passed.
+ * If a *valid* timestamp is found, the string length is decremented
+ * by the number of characters processed. If it is not a valid timestamp,
+ * the length is kept unmodified. -- rgerhards, 2009-09-23
*/
static rsRetVal
-ParseTIMESTAMP3164(struct syslogTime *pTime, char** ppszTS)
+ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS, int *pLenStr)
{
/* variables to temporarily hold time information while we parse */
int month;
int day;
+ int year = 0; /* 0 means no year provided */
int hour; /* 24 hour clock */
int minute;
int second;
/* end variables to temporarily hold time information while we parse */
- char *pszTS;
+ int lenStr;
+ uchar *pszTS;
DEFiRet;
assert(ppszTS != NULL);
pszTS = *ppszTS;
assert(pszTS != NULL);
assert(pTime != NULL);
+ assert(pLenStr != NULL);
+ lenStr = *pLenStr;
/* If we look at the month (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec),
* we may see the following character sequences occur:
@@ -324,6 +366,10 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, char** ppszTS)
* We will use this for parsing, as it probably is the
* fastest way to parse it.
*
+ * 2009-08-17: we now do case-insensitive comparisons, as some devices obviously do not
+ * obey to the RFC-specified case. As we need to guess in any case, we can ignore case
+ * in the first place -- rgerhards
+ *
* 2005-07-18, well sometimes it pays to be a bit more verbose, even in C...
* Fixed a bug that lead to invalid detection of the data. The issue was that
* we had an if(++pszTS == 'x') inside of some of the consturcts below. However,
@@ -333,22 +379,26 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, char** ppszTS)
* june, when it first manifested. This also lead to invalid parsing of the rest
* of the message, as the time stamp was not detected to be correct. - rgerhards
*/
+ if(lenStr < 3)
+ ABORT_FINALIZE(RS_RET_INVLD_TIME);
+
switch(*pszTS++)
{
+ case 'j':
case 'J':
- if(*pszTS == 'a') {
+ if(*pszTS == 'a' || *pszTS == 'A') {
++pszTS;
- if(*pszTS == 'n') {
+ if(*pszTS == 'n' || *pszTS == 'N') {
++pszTS;
month = 1;
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
- } else if(*pszTS == 'u') {
+ } else if(*pszTS == 'u' || *pszTS == 'U') {
++pszTS;
- if(*pszTS == 'n') {
+ if(*pszTS == 'n' || *pszTS == 'N') {
++pszTS;
month = 6;
- } else if(*pszTS == 'l') {
+ } else if(*pszTS == 'l' || *pszTS == 'L') {
++pszTS;
month = 7;
} else
@@ -356,10 +406,11 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, char** ppszTS)
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
break;
+ case 'f':
case 'F':
- if(*pszTS == 'e') {
+ if(*pszTS == 'e' || *pszTS == 'E') {
++pszTS;
- if(*pszTS == 'b') {
+ if(*pszTS == 'b' || *pszTS == 'B') {
++pszTS;
month = 2;
} else
@@ -367,13 +418,14 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, char** ppszTS)
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
break;
+ case 'm':
case 'M':
- if(*pszTS == 'a') {
+ if(*pszTS == 'a' || *pszTS == 'A') {
++pszTS;
- if(*pszTS == 'r') {
+ if(*pszTS == 'r' || *pszTS == 'R') {
++pszTS;
month = 3;
- } else if(*pszTS == 'y') {
+ } else if(*pszTS == 'y' || *pszTS == 'Y') {
++pszTS;
month = 5;
} else
@@ -381,17 +433,18 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, char** ppszTS)
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
break;
+ case 'a':
case 'A':
- if(*pszTS == 'p') {
+ if(*pszTS == 'p' || *pszTS == 'P') {
++pszTS;
- if(*pszTS == 'r') {
+ if(*pszTS == 'r' || *pszTS == 'R') {
++pszTS;
month = 4;
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
- } else if(*pszTS == 'u') {
+ } else if(*pszTS == 'u' || *pszTS == 'U') {
++pszTS;
- if(*pszTS == 'g') {
+ if(*pszTS == 'g' || *pszTS == 'G') {
++pszTS;
month = 8;
} else
@@ -399,10 +452,11 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, char** ppszTS)
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
break;
+ case 's':
case 'S':
- if(*pszTS == 'e') {
+ if(*pszTS == 'e' || *pszTS == 'E') {
++pszTS;
- if(*pszTS == 'p') {
+ if(*pszTS == 'p' || *pszTS == 'P') {
++pszTS;
month = 9;
} else
@@ -410,10 +464,11 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, char** ppszTS)
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
break;
+ case 'o':
case 'O':
- if(*pszTS == 'c') {
+ if(*pszTS == 'c' || *pszTS == 'C') {
++pszTS;
- if(*pszTS == 't') {
+ if(*pszTS == 't' || *pszTS == 'T') {
++pszTS;
month = 10;
} else
@@ -421,10 +476,11 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, char** ppszTS)
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
break;
+ case 'n':
case 'N':
- if(*pszTS == 'o') {
+ if(*pszTS == 'o' || *pszTS == 'O') {
++pszTS;
- if(*pszTS == 'v') {
+ if(*pszTS == 'v' || *pszTS == 'V') {
++pszTS;
month = 11;
} else
@@ -432,10 +488,11 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, char** ppszTS)
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
break;
+ case 'd':
case 'D':
- if(*pszTS == 'e') {
+ if(*pszTS == 'e' || *pszTS == 'E') {
++pszTS;
- if(*pszTS == 'c') {
+ if(*pszTS == 'c' || *pszTS == 'C') {
++pszTS;
month = 12;
} else
@@ -447,36 +504,61 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, char** ppszTS)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
}
+ lenStr -= 3;
+
/* done month */
- if(*pszTS++ != ' ')
+ if(lenStr == 0 || *pszTS++ != ' ')
ABORT_FINALIZE(RS_RET_INVLD_TIME);
+ --lenStr;
/* we accept a slightly malformed timestamp when receiving. This is
* we accept one-digit days
*/
- if(*pszTS == ' ')
+ if(*pszTS == ' ') {
+ --lenStr;
++pszTS;
+ }
- day = srSLMGParseInt32(&pszTS);
+ day = srSLMGParseInt32(&pszTS, &lenStr);
if(day < 1 || day > 31)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
- if(*pszTS++ != ' ')
+ if(lenStr == 0 || *pszTS++ != ' ')
ABORT_FINALIZE(RS_RET_INVLD_TIME);
- hour = srSLMGParseInt32(&pszTS);
+ --lenStr;
+
+ /* time part */
+ hour = srSLMGParseInt32(&pszTS, &lenStr);
+ if(hour > 1970 && hour < 2100) {
+ /* if so, we assume this actually is a year. This is a format found
+ * e.g. in Cisco devices.
+ * (if you read this 2100+ trying to fix a bug, congratulate me
+ * to how long the code survived - me no longer ;)) -- rgerhards, 2008-11-18
+ */
+ year = hour;
+
+ /* re-query the hour, this time it must be valid */
+ if(lenStr == 0 || *pszTS++ != ' ')
+ ABORT_FINALIZE(RS_RET_INVLD_TIME);
+ --lenStr;
+ hour = srSLMGParseInt32(&pszTS, &lenStr);
+ }
+
if(hour < 0 || hour > 23)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
- if(*pszTS++ != ':')
+ if(lenStr == 0 || *pszTS++ != ':')
ABORT_FINALIZE(RS_RET_INVLD_TIME);
- minute = srSLMGParseInt32(&pszTS);
+ --lenStr;
+ minute = srSLMGParseInt32(&pszTS, &lenStr);
if(minute < 0 || minute > 59)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
- if(*pszTS++ != ':')
+ if(lenStr == 0 || *pszTS++ != ':')
ABORT_FINALIZE(RS_RET_INVLD_TIME);
- second = srSLMGParseInt32(&pszTS);
+ --lenStr;
+ second = srSLMGParseInt32(&pszTS, &lenStr);
if(second < 0 || second > 60)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
@@ -484,8 +566,16 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, char** ppszTS)
* invalid format, it occurs frequently enough (e.g. with Cisco devices)
* to permit it as a valid case. -- rgerhards, 2008-09-12
*/
- if(*pszTS++ == ':')
+ if(lenStr > 0 && *pszTS == ':') {
++pszTS; /* just skip past it */
+ --lenStr;
+ }
+ if(lenStr > 0) {
+ if(*pszTS != ' ') /* if it is not a space, it can not be a "good" time - 2010-02-22 rgerhards */
+ ABORT_FINALIZE(RS_RET_INVLD_TIME);
+ ++pszTS; /* just skip past it */
+ --lenStr;
+ }
/* we had success, so update parse pointer and caller-provided timestamp
* fields we do not have are not updated in the caller's timestamp. This
@@ -494,12 +584,15 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, char** ppszTS)
*ppszTS = pszTS; /* provide updated parse position back to caller */
pTime->timeType = 1;
pTime->month = month;
+ if(year > 0)
+ pTime->year = year; /* persist year if detected */
pTime->day = day;
pTime->hour = hour;
pTime->minute = minute;
pTime->second = second;
pTime->secfracPrecision = 0;
pTime->secfrac = 0;
+ *pLenStr = lenStr;
finalize_it:
RETiRet;
@@ -518,7 +611,7 @@ finalize_it:
* returns the size of the timestamp written in bytes (without
* the string terminator). If 0 is returend, an error occured.
*/
-int formatTimestampToMySQL(struct syslogTime *ts, char* pDst, size_t iLenDst)
+int formatTimestampToMySQL(struct syslogTime *ts, char* pBuf)
{
/* currently we do not consider localtime/utc. This may later be
* added. If so, I recommend using a property replacer option
@@ -527,28 +620,54 @@ int formatTimestampToMySQL(struct syslogTime *ts, char* pDst, size_t iLenDst)
* rgerhards, 2007-06-26
*/
assert(ts != NULL);
- assert(pDst != NULL);
-
- if (iLenDst < 15) /* we need at least 14 bytes
- 14 digits for timestamp + '\n' */
- return(0);
+ assert(pBuf != NULL);
- return(snprintf(pDst, iLenDst, "%4.4d%2.2d%2.2d%2.2d%2.2d%2.2d",
- ts->year, ts->month, ts->day, ts->hour, ts->minute, ts->second));
+ pBuf[0] = (ts->year / 1000) % 10 + '0';
+ pBuf[1] = (ts->year / 100) % 10 + '0';
+ pBuf[2] = (ts->year / 10) % 10 + '0';
+ pBuf[3] = ts->year % 10 + '0';
+ pBuf[4] = (ts->month / 10) % 10 + '0';
+ pBuf[5] = ts->month % 10 + '0';
+ pBuf[6] = (ts->day / 10) % 10 + '0';
+ pBuf[7] = ts->day % 10 + '0';
+ pBuf[8] = (ts->hour / 10) % 10 + '0';
+ pBuf[9] = ts->hour % 10 + '0';
+ pBuf[10] = (ts->minute / 10) % 10 + '0';
+ pBuf[11] = ts->minute % 10 + '0';
+ pBuf[12] = (ts->second / 10) % 10 + '0';
+ pBuf[13] = ts->second % 10 + '0';
+ pBuf[14] = '\0';
+ return 15;
}
-int formatTimestampToPgSQL(struct syslogTime *ts, char *pDst, size_t iLenDst)
+int formatTimestampToPgSQL(struct syslogTime *ts, char *pBuf)
{
- /* see note in formatTimestampToMySQL, applies here as well */
- assert(ts != NULL);
- assert(pDst != NULL);
-
- if (iLenDst < 21) /* we need 20 bytes + '\n' */
- return(0);
+ /* see note in formatTimestampToMySQL, applies here as well */
+ assert(ts != NULL);
+ assert(pBuf != NULL);
- return(snprintf(pDst, iLenDst, "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d",
- ts->year, ts->month, ts->day, ts->hour, ts->minute, ts->second));
+ pBuf[0] = (ts->year / 1000) % 10 + '0';
+ pBuf[1] = (ts->year / 100) % 10 + '0';
+ pBuf[2] = (ts->year / 10) % 10 + '0';
+ pBuf[3] = ts->year % 10 + '0';
+ pBuf[4] = '-';
+ pBuf[5] = (ts->month / 10) % 10 + '0';
+ pBuf[6] = ts->month % 10 + '0';
+ pBuf[7] = '-';
+ pBuf[8] = (ts->day / 10) % 10 + '0';
+ pBuf[9] = ts->day % 10 + '0';
+ pBuf[10] = ' ';
+ pBuf[11] = (ts->hour / 10) % 10 + '0';
+ pBuf[12] = ts->hour % 10 + '0';
+ pBuf[13] = ':';
+ pBuf[14] = (ts->minute / 10) % 10 + '0';
+ pBuf[15] = ts->minute % 10 + '0';
+ pBuf[16] = ':';
+ pBuf[17] = (ts->second / 10) % 10 + '0';
+ pBuf[18] = ts->second % 10 + '0';
+ pBuf[19] = '\0';
+ return 19;
}
@@ -558,35 +677,36 @@ int formatTimestampToPgSQL(struct syslogTime *ts, char *pDst, size_t iLenDst)
* buffer that will receive the resulting string. The function
* returns the size of the timestamp written in bytes (without
* the string terminator). If 0 is returend, an error occured.
- * The buffer must be at least 10 bytes large.
+ * The buffer must be at least 7 bytes large.
* rgerhards, 2008-06-06
*/
-int formatTimestampSecFrac(struct syslogTime *ts, char* pBuf, size_t iLenBuf)
+int formatTimestampSecFrac(struct syslogTime *ts, char* pBuf)
{
- int lenRet;
- char szFmtStr[64];
+ int iBuf;
+ int power;
+ int secfrac;
+ short digit;
assert(ts != NULL);
assert(pBuf != NULL);
- assert(iLenBuf >= 10);
+ iBuf = 0;
if(ts->secfracPrecision > 0)
- { /* We must look at
- * the precision specified. For example, if we have millisec precision (3 digits), a
- * secFrac value of 12 is not equivalent to ".12" but ".012". Obviously, this
- * is a huge difference ;). To avoid this, we first create a format string with
- * the specific precision and *then* use that format string to do the actual formating.
- */
- /* be careful: there is ONE actual %d in the format string below ;) */
- snprintf(szFmtStr, sizeof(szFmtStr), "%%0%dd", ts->secfracPrecision);
- lenRet = snprintf(pBuf, iLenBuf, szFmtStr, ts->secfrac);
+ {
+ power = tenPowers[(ts->secfracPrecision - 1) % 6];
+ secfrac = ts->secfrac;
+ while(power > 0) {
+ digit = secfrac / power;
+ secfrac -= digit * power;
+ power /= 10;
+ pBuf[iBuf++] = digit + '0';
+ }
} else {
- pBuf[0] = '0';
- pBuf[1] = '\0';
- lenRet = 1;
+ pBuf[iBuf++] = '0';
}
+ pBuf[iBuf] = '\0';
- return(lenRet);
+ return iBuf;
}
@@ -598,48 +718,73 @@ int formatTimestampSecFrac(struct syslogTime *ts, char* pBuf, size_t iLenBuf)
* returns the size of the timestamp written in bytes (without
* the string terminator). If 0 is returend, an error occured.
*/
-int formatTimestamp3339(struct syslogTime *ts, char* pBuf, size_t iLenBuf)
+int formatTimestamp3339(struct syslogTime *ts, char* pBuf)
{
- int iRet;
- char szTZ[7]; /* buffer for TZ information */
+ int iBuf;
+ int power;
+ int secfrac;
+ short digit;
+ BEGINfunc
assert(ts != NULL);
assert(pBuf != NULL);
-
- if(iLenBuf < 20)
- return(0); /* we NEED at least 20 bytes */
- /* do TZ information first, this is easier to take care of "Z" zone in rfc3339 */
+ /* start with fixed parts */
+ /* year yyyy */
+ pBuf[0] = (ts->year / 1000) % 10 + '0';
+ pBuf[1] = (ts->year / 100) % 10 + '0';
+ pBuf[2] = (ts->year / 10) % 10 + '0';
+ pBuf[3] = ts->year % 10 + '0';
+ pBuf[4] = '-';
+ /* month */
+ pBuf[5] = (ts->month / 10) % 10 + '0';
+ pBuf[6] = ts->month % 10 + '0';
+ pBuf[7] = '-';
+ /* day */
+ pBuf[8] = (ts->day / 10) % 10 + '0';
+ pBuf[9] = ts->day % 10 + '0';
+ pBuf[10] = 'T';
+ /* hour */
+ pBuf[11] = (ts->hour / 10) % 10 + '0';
+ pBuf[12] = ts->hour % 10 + '0';
+ pBuf[13] = ':';
+ /* minute */
+ pBuf[14] = (ts->minute / 10) % 10 + '0';
+ pBuf[15] = ts->minute % 10 + '0';
+ pBuf[16] = ':';
+ /* second */
+ pBuf[17] = (ts->second / 10) % 10 + '0';
+ pBuf[18] = ts->second % 10 + '0';
+
+ iBuf = 19; /* points to next free entry, now it becomes dynamic! */
+
+ if(ts->secfracPrecision > 0) {
+ pBuf[iBuf++] = '.';
+ power = tenPowers[(ts->secfracPrecision - 1) % 6];
+ secfrac = ts->secfrac;
+ while(power > 0) {
+ digit = secfrac / power;
+ secfrac -= digit * power;
+ power /= 10;
+ pBuf[iBuf++] = digit + '0';
+ }
+ }
+
if(ts->OffsetMode == 'Z') {
- szTZ[0] = 'Z';
- szTZ[1] = '\0';
+ pBuf[iBuf++] = 'Z';
} else {
- snprintf(szTZ, sizeof(szTZ) / sizeof(char), "%c%2.2d:%2.2d",
- ts->OffsetMode, ts->OffsetHour, ts->OffsetMinute);
+ pBuf[iBuf++] = ts->OffsetMode;
+ pBuf[iBuf++] = (ts->OffsetHour / 10) % 10 + '0';
+ pBuf[iBuf++] = ts->OffsetHour % 10 + '0';
+ pBuf[iBuf++] = ':';
+ pBuf[iBuf++] = (ts->OffsetMinute / 10) % 10 + '0';
+ pBuf[iBuf++] = ts->OffsetMinute % 10 + '0';
}
- if(ts->secfracPrecision > 0)
- { /* we now need to include fractional seconds. While doing so, we must look at
- * the precision specified. For example, if we have millisec precision (3 digits), a
- * secFrac value of 12 is not equivalent to ".12" but ".012". Obviously, this
- * is a huge difference ;). To avoid this, we first create a format string with
- * the specific precision and *then* use that format string to do the actual
- * formating (mmmmhhh... kind of self-modifying code... ;)).
- */
- char szFmtStr[64];
- /* be careful: there is ONE actual %d in the format string below ;) */
- snprintf(szFmtStr, sizeof(szFmtStr),
- "%%04d-%%02d-%%02dT%%02d:%%02d:%%02d.%%0%dd%%s",
- ts->secfracPrecision);
- iRet = snprintf(pBuf, iLenBuf, szFmtStr, ts->year, ts->month, ts->day,
- ts->hour, ts->minute, ts->second, ts->secfrac, szTZ);
- }
- else
- iRet = snprintf(pBuf, iLenBuf,
- "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d%s",
- ts->year, ts->month, ts->day,
- ts->hour, ts->minute, ts->second, szTZ);
- return(iRet);
+ pBuf[iBuf] = '\0';
+
+ ENDfunc
+ return iBuf;
}
/**
@@ -648,47 +793,40 @@ int formatTimestamp3339(struct syslogTime *ts, char* pBuf, size_t iLenBuf)
* buffer that will receive the resulting string. The function
* returns the size of the timestamp written in bytes (without
* the string termnator). If 0 is returend, an error occured.
+ * rgerhards, 2010-03-05: Added support to for buggy 3164 dates,
+ * where a zero-digit is written instead of a space for the first
+ * day character if day < 10. syslog-ng seems to do that, and some
+ * parsing scripts (in migration cases) rely on that.
*/
-int formatTimestamp3164(struct syslogTime *ts, char* pBuf, size_t iLenBuf)
+int formatTimestamp3164(struct syslogTime *ts, char* pBuf, int bBuggyDay)
{
- static char* monthNames[13] = {"ERR", "Jan", "Feb", "Mar",
- "Apr", "May", "Jun", "Jul",
- "Aug", "Sep", "Oct", "Nov", "Dec"};
+ static char* monthNames[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+ int iDay;
assert(ts != NULL);
assert(pBuf != NULL);
- if(iLenBuf < 16)
- return(0); /* we NEED 16 bytes */
- return(snprintf(pBuf, iLenBuf, "%s %2d %2.2d:%2.2d:%2.2d",
- monthNames[ts->month], ts->day, ts->hour,
- ts->minute, ts->second
- ));
+ pBuf[0] = monthNames[(ts->month - 1)% 12][0];
+ pBuf[1] = monthNames[(ts->month - 1) % 12][1];
+ pBuf[2] = monthNames[(ts->month - 1) % 12][2];
+ pBuf[3] = ' ';
+ iDay = (ts->day / 10) % 10; /* we need to write a space if the first digit is 0 */
+ pBuf[4] = (bBuggyDay || iDay > 0) ? iDay + '0' : ' ';
+ pBuf[5] = ts->day % 10 + '0';
+ pBuf[6] = ' ';
+ pBuf[7] = (ts->hour / 10) % 10 + '0';
+ pBuf[8] = ts->hour % 10 + '0';
+ pBuf[9] = ':';
+ pBuf[10] = (ts->minute / 10) % 10 + '0';
+ pBuf[11] = ts->minute % 10 + '0';
+ pBuf[12] = ':';
+ pBuf[13] = (ts->second / 10) % 10 + '0';
+ pBuf[14] = ts->second % 10 + '0';
+ pBuf[15] = '\0';
+ return 16; /* traditional: number of bytes written */
}
-/**
- * Format a syslogTimestamp to a text format.
- * The caller must provide the timestamp as well as a character
- * buffer that will receive the resulting string. The function
- * returns the size of the timestamp written in bytes (without
- * the string termnator). If 0 is returend, an error occured.
- */
-#if 0 /* This method is currently not called, be we like to preserve it */
-static int formatTimestamp(struct syslogTime *ts, char* pBuf, size_t iLenBuf)
-{
- assert(ts != NULL);
- assert(pBuf != NULL);
-
- if(ts->timeType == 1) {
- return(formatTimestamp3164(ts, pBuf, iLenBuf));
- }
-
- if(ts->timeType == 2) {
- return(formatTimestamp3339(ts, pBuf, iLenBuf));
- }
- return(0);
-}
-#endif
/* queryInterface function
* rgerhards, 2008-03-05
*/
@@ -722,7 +860,6 @@ ENDobjQueryInterface(datetime)
BEGINAbstractObjClassInit(datetime, 1, OBJ_IS_CORE_MODULE) /* class, version */
/* request objects we use */
CHKiRet(objUse(errmsg, CORE_COMPONENT));
-
ENDObjClassInit(datetime)
/* vi:set ai:
diff --git a/runtime/datetime.h b/runtime/datetime.h
index 2210af02..9dcce3c5 100644
--- a/runtime/datetime.h
+++ b/runtime/datetime.h
@@ -23,8 +23,6 @@
#ifndef INCLUDED_DATETIME_H
#define INCLUDED_DATETIME_H
-#include "datetime.h"
-
/* TODO: define error codes */
#define NO_ERRCODE -1
@@ -35,21 +33,23 @@ typedef struct datetime_s {
/* interfaces */
BEGINinterface(datetime) /* name must also be changed in ENDinterface macro! */
- void (*getCurrTime)(struct syslogTime *t);
- rsRetVal (*ParseTIMESTAMP3339)(struct syslogTime *pTime, char** ppszTS);
- rsRetVal (*ParseTIMESTAMP3164)(struct syslogTime *pTime, char** pszTS);
- int (*formatTimestampToMySQL)(struct syslogTime *ts, char* pDst, size_t iLenDst);
- int (*formatTimestampToPgSQL)(struct syslogTime *ts, char *pDst, size_t iLenDst);
- int (*formatTimestamp3339)(struct syslogTime *ts, char* pBuf, size_t iLenBuf);
- int (*formatTimestamp3164)(struct syslogTime *ts, char* pBuf, size_t iLenBuf);
- int (*formatTimestampSecFrac)(struct syslogTime *ts, char* pBuf, size_t iLenBuf);
+ void (*getCurrTime)(struct syslogTime *t, time_t *ttSeconds);
+ rsRetVal (*ParseTIMESTAMP3339)(struct syslogTime *pTime, uchar** ppszTS, int*);
+ rsRetVal (*ParseTIMESTAMP3164)(struct syslogTime *pTime, uchar** pszTS, int*);
+ int (*formatTimestampToMySQL)(struct syslogTime *ts, char* pDst);
+ int (*formatTimestampToPgSQL)(struct syslogTime *ts, char *pDst);
+ int (*formatTimestamp3339)(struct syslogTime *ts, char* pBuf);
+ int (*formatTimestamp3164)(struct syslogTime *ts, char* pBuf, int);
+ int (*formatTimestampSecFrac)(struct syslogTime *ts, char* pBuf);
ENDinterface(datetime)
-#define datetimeCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */
+#define datetimeCURR_IF_VERSION 4 /* increment whenever you change the interface structure! */
/* interface changes:
* 1 - initial version
* 2 - not compatible to 1 - bugfix required ParseTIMESTAMP3164 to accept char ** as
* last parameter. Did not try to remain compatible as this is not something any
* third-party module should call. -- rgerhards, 2008.-09-12
+ * 3 - taken by v5 branch!
+ * 4 - formatTimestamp3164 takes a third int parameter
*/
/* prototypes */
diff --git a/runtime/debug.c b/runtime/debug.c
index 62e2a687..6bb8d743 100644
--- a/runtime/debug.c
+++ b/runtime/debug.c
@@ -50,6 +50,7 @@
#include "rsyslog.h"
#include "debug.h"
#include "atomic.h"
+#include "cfsysline.h"
#include "obj.h"
@@ -432,14 +433,13 @@ dbgMutLog_t *dbgMutLogFindHolder(pthread_mutex_t *pmut)
static inline void dbgMutexPreLockLog(pthread_mutex_t *pmut, dbgFuncDB_t *pFuncDB, int ln)
{
dbgMutLog_t *pHolder;
- dbgMutLog_t *pLog;
char pszBuf[128];
char pszHolderThrdName[64];
char *pszHolder;
pthread_mutex_lock(&mutMutLog);
pHolder = dbgMutLogFindHolder(pmut);
- pLog = dbgMutLogAddEntry(pmut, MUTOP_LOCKWAIT, pFuncDB, ln);
+ dbgMutLogAddEntry(pmut, MUTOP_LOCKWAIT, pFuncDB, ln);
if(pHolder == NULL)
pszHolder = "[NONE]";
@@ -480,14 +480,13 @@ static inline void dbgMutexLockLog(pthread_mutex_t *pmut, dbgFuncDB_t *pFuncDB,
static inline void dbgMutexPreTryLockLog(pthread_mutex_t *pmut, dbgFuncDB_t *pFuncDB, int ln)
{
dbgMutLog_t *pHolder;
- dbgMutLog_t *pLog;
char pszBuf[128];
char pszHolderThrdName[64];
char *pszHolder;
pthread_mutex_lock(&mutMutLog);
pHolder = dbgMutLogFindHolder(pmut);
- pLog = dbgMutLogAddEntry(pmut, MUTOP_TRYLOCK, pFuncDB, ln);
+ dbgMutLogAddEntry(pmut, MUTOP_TRYLOCK, pFuncDB, ln);
if(pHolder == NULL)
pszHolder = "[NONE]";
@@ -548,6 +547,7 @@ if(pLog == NULL) {
return; /* if we don't know it yet, we can not clean up... */
}
#endif
+#include <sys/syscall.h>
/* we found the last lock entry. We now need to see from which FuncDB we need to
* remove it. This is recorded inside the mutex log entry.
@@ -731,6 +731,8 @@ static void dbgGetThrdName(char *pszBuf, size_t lenBuf, pthread_t thrd, int bInc
*/
void dbgSetThrdName(uchar *pszName)
{
+return;
+
dbgThrdInfo_t *pThrd = dbgGetThrdInfo();
if(pThrd->pszThrdName != NULL)
free(pThrd->pszThrdName);
@@ -775,7 +777,7 @@ static void dbgCallStackPrint(dbgThrdInfo_t *pThrd)
/* print all threads call stacks
*/
-static void dbgCallStackPrintAll(void)
+void dbgCallStackPrintAll(void)
{
dbgThrdInfo_t *pThrd;
/* stack info */
@@ -838,7 +840,7 @@ dbgprint(obj_t *pObj, char *pszMsg, size_t lenMsg)
static pthread_t ptLastThrdID = 0;
static int bWasNL = 0;
char pszThrdName[64]; /* 64 is to be on the safe side, anything over 20 is bad... */
- char pszWriteBuf[1024];
+ char pszWriteBuf[32*1024];
size_t lenWriteBuf;
struct timespec t;
uchar *pszObjName = NULL;
@@ -886,7 +888,9 @@ dbgprint(obj_t *pObj, char *pszMsg, size_t lenMsg)
if(stddbg != -1) write(stddbg, pszWriteBuf, lenWriteBuf);
if(altdbg != -1) write(altdbg, pszWriteBuf, lenWriteBuf);
}
+
lenWriteBuf = snprintf(pszWriteBuf, sizeof(pszWriteBuf), "%s: ", pszThrdName);
+ // use for testing: lenWriteBuf = snprintf(pszWriteBuf, sizeof(pszWriteBuf), "{%ld}%s: ", (long) syscall(SYS_gettid), pszThrdName);
if(stddbg != -1) write(stddbg, pszWriteBuf, lenWriteBuf);
if(altdbg != -1) write(altdbg, pszWriteBuf, lenWriteBuf);
/* print object name header if we have an object */
@@ -916,7 +920,7 @@ void
dbgoprint(obj_t *pObj, char *fmt, ...)
{
va_list ap;
- char pszWriteBuf[1024];
+ char pszWriteBuf[32*1024];
size_t lenWriteBuf;
if(!(Debug && debugging_on))
@@ -946,7 +950,7 @@ void
dbgprintf(char *fmt, ...)
{
va_list ap;
- char pszWriteBuf[1024];
+ char pszWriteBuf[20480];
size_t lenWriteBuf;
if(!(Debug && debugging_on))
@@ -955,6 +959,15 @@ dbgprintf(char *fmt, ...)
va_start(ap, fmt);
lenWriteBuf = vsnprintf(pszWriteBuf, sizeof(pszWriteBuf), fmt, ap);
va_end(ap);
+ if(lenWriteBuf >= sizeof(pszWriteBuf)) {
+ /* prevent buffer overrruns and garbagge display */
+ pszWriteBuf[sizeof(pszWriteBuf) - 5] = '.';
+ pszWriteBuf[sizeof(pszWriteBuf) - 4] = '.';
+ pszWriteBuf[sizeof(pszWriteBuf) - 3] = '.';
+ pszWriteBuf[sizeof(pszWriteBuf) - 2] = '\n';
+ pszWriteBuf[sizeof(pszWriteBuf) - 1] = '\0';
+ lenWriteBuf = sizeof(pszWriteBuf);
+ }
dbgprint(NULL, pszWriteBuf, lenWriteBuf);
}
@@ -1258,6 +1271,7 @@ dbgGetRuntimeOptions(void)
"NoLogTimestamp\n"
"Nostdoout\n"
"filetrace=file (may be provided multiple times)\n"
+ "DebugOnDemand - enables debugging on USR1, but does not turn on output\n"
"\nSee debug.html in your doc set or http://www.rsyslog.com for details\n");
exit(1);
} else if(!strcasecmp((char*)optname, "debug")) {
@@ -1266,6 +1280,13 @@ dbgGetRuntimeOptions(void)
*/
Debug = 1;
debugging_on = 1;
+ } else if(!strcasecmp((char*)optname, "debugondemand")) {
+ /* Enables debugging, but turns off debug output */
+ Debug = 1;
+ debugging_on = 1;
+ dbgprintf("Note: debug on demand turned on via configuraton file, "
+ "use USR1 signal to activate.\n");
+ debugging_on = 0;
} else if(!strcasecmp((char*)optname, "logfuncflow")) {
bLogFuncFlow = 1;
} else if(!strcasecmp((char*)optname, "logallocfree")) {
@@ -1340,7 +1361,7 @@ rsRetVal dbgClassInit(void)
if(pszAltDbgFileName != NULL) {
/* we have a secondary file, so let's open it) */
- if((altdbg = open(pszAltDbgFileName, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY, S_IRUSR|S_IWUSR)) == -1) {
+ if((altdbg = open(pszAltDbgFileName, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY|O_CLOEXEC, S_IRUSR|S_IWUSR)) == -1) {
fprintf(stderr, "alternate debug file could not be opened, ignoring. Error: %s\n", strerror(errno));
}
}
diff --git a/runtime/debug.h b/runtime/debug.h
index 43836024..dcbfb930 100644
--- a/runtime/debug.h
+++ b/runtime/debug.h
@@ -101,6 +101,8 @@ void dbgSetThrdName(uchar *pszName);
void dbgPrintAllDebugInfo(void);
/* macros */
+#define DBGPRINTF(...) if(Debug) { dbgprintf(__VA_ARGS__); }
+#define DBGOPRINT(...) if(Debug) { dbgoprint(__VA_ARGS__); }
#ifdef RTINST
# define BEGINfunc static dbgFuncDB_t *pdbgFuncDB; int dbgCALLStaCK_POP_POINT = dbgEntrFunc(&pdbgFuncDB, __FILE__, __func__, __LINE__);
# define ENDfunc dbgExitFunc(pdbgFuncDB, dbgCALLStaCK_POP_POINT, RS_RET_NO_IRET);
@@ -132,8 +134,7 @@ void dbgPrintAllDebugInfo(void);
/* debug aides */
-//#ifdef RTINST
-#if 0 // temporarily removed for helgrind
+#ifdef RTINST
#define d_pthread_mutex_lock(x) dbgMutexLock(x, pdbgFuncDB, __LINE__, dbgCALLStaCK_POP_POINT )
#define d_pthread_mutex_trylock(x) dbgMutexTryLock(x, pdbgFuncDB, __LINE__, dbgCALLStaCK_POP_POINT )
#define d_pthread_mutex_unlock(x) dbgMutexUnlock(x, pdbgFuncDB, __LINE__, dbgCALLStaCK_POP_POINT )
diff --git a/runtime/expr.c b/runtime/expr.c
index ee5b9e2c..e449d1c7 100644
--- a/runtime/expr.c
+++ b/runtime/expr.c
@@ -55,11 +55,63 @@ DEFobjCurrIf(ctok)
* rgerhards, 2008-02-19
*/
-/* forward definiton - thanks to recursive ABNF, we can not avoid at least one ;) */
+/* forward definition - thanks to recursive ABNF, we can not avoid at least one ;) */
static rsRetVal expr(expr_t *pThis, ctok_t *tok);
static rsRetVal
+function(expr_t *pThis, ctok_t *tok)
+{
+ DEFiRet;
+ ctok_token_t *pToken = NULL;
+ int iNumArgs = 0;
+ var_t *pVar;
+
+ ISOBJ_TYPE_assert(pThis, expr);
+ ISOBJ_TYPE_assert(tok, ctok);
+
+ CHKiRet(ctok.GetToken(tok, &pToken));
+ /* note: pToken is destructed in finalize_it */
+
+ if(pToken->tok == ctok_LPAREN) {
+ CHKiRet(ctok_token.Destruct(&pToken)); /* token processed, "eat" it */
+ CHKiRet(ctok.GetToken(tok, &pToken)); /* get next one */
+ } else
+ ABORT_FINALIZE(RS_RET_FUNC_NO_LPAREN);
+
+ /* we first push all the params on the stack. Then we call the function */
+ while(pToken->tok != ctok_RPAREN) {
+ ++iNumArgs;
+ CHKiRet(ctok.UngetToken(tok, pToken)); /* not for us, so let others process it */
+ CHKiRet(expr(pThis, tok));
+ CHKiRet(ctok.GetToken(tok, &pToken)); /* get next one, needed for while() check */
+ if(pToken->tok == ctok_COMMA) {
+ CHKiRet(ctok_token.Destruct(&pToken)); /* token processed, "eat" it */
+ CHKiRet(ctok.GetToken(tok, &pToken)); /* get next one */
+ if(pToken->tok == ctok_RPAREN) {
+ ABORT_FINALIZE(RS_RET_FUNC_MISSING_EXPR);
+ }
+ }
+ }
+
+
+ /* now push number of arguments - this must be on top of the stack */
+ CHKiRet(var.Construct(&pVar));
+ CHKiRet(var.ConstructFinalize(pVar));
+ CHKiRet(var.SetNumber(pVar, iNumArgs));
+ CHKiRet(vmprg.AddVarOperation(pThis->pVmprg, opcode_PUSHCONSTANT, pVar)); /* add to program */
+
+
+finalize_it:
+ if(pToken != NULL) {
+ ctok_token.Destruct(&pToken); /* "eat" processed token */
+ }
+
+ RETiRet;
+}
+
+
+static rsRetVal
terminal(expr_t *pThis, ctok_t *tok)
{
DEFiRet;
@@ -85,8 +137,14 @@ terminal(expr_t *pThis, ctok_t *tok)
break;
case ctok_FUNCTION:
dbgoprint((obj_t*) pThis, "function\n");
- /* TODO: vm: call - well, need to implement that first */
- ABORT_FINALIZE(RS_RET_NOT_IMPLEMENTED);
+ CHKiRet(function(pThis, tok)); /* this creates the stack call frame */
+ /* ... but we place the call instruction onto the stack ourselfs (because
+ * we have all relevant information)
+ */
+ CHKiRet(ctok_token.UnlinkVar(pToken, &pVar));
+ CHKiRet(var.ConvToString(pVar)); /* make sure we have a string */
+ CHKiRet(vmprg.AddCallOperation(pThis->pVmprg, pVar->val.pStr)); /* add to program */
+ CHKiRet(var.Destruct(&pVar));
break;
case ctok_MSGVAR:
dbgoprint((obj_t*) pThis, "MSGVAR\n");
@@ -406,6 +464,7 @@ ENDobjQueryInterface(expr)
*/
BEGINObjClassInit(expr, 1, OBJ_IS_CORE_MODULE) /* class, version */
/* request objects we use */
+ CHKiRet(objUse(var, CORE_COMPONENT));
CHKiRet(objUse(vmprg, CORE_COMPONENT));
CHKiRet(objUse(var, CORE_COMPONENT));
CHKiRet(objUse(ctok_token, CORE_COMPONENT));
diff --git a/runtime/glbl.c b/runtime/glbl.c
index 1114fcd3..7fa61963 100644
--- a/runtime/glbl.c
+++ b/runtime/glbl.c
@@ -35,8 +35,10 @@
#include "rsyslog.h"
#include "obj.h"
+#include "unicode-helper.h"
#include "cfsysline.h"
#include "glbl.h"
+#include "prop.h"
/* some defaults */
#ifndef DFLT_NETSTRM_DRVR
@@ -45,18 +47,24 @@
/* static data */
DEFobjStaticHelpers
+DEFobjCurrIf(prop)
/* static data
* For this object, these variables are obviously what makes the "meat" of the
* class...
*/
static uchar *pszWorkDir = NULL;
+static int bOptimizeUniProc = 1; /* enable uniprocessor optimizations */
+static int bHUPisRestart = 0; /* should SIGHUP cause a full system restart? */
+static int bPreserveFQDN = 0; /* should FQDNs always be preserved? */
static int iMaxLine = 2048; /* maximum length of a syslog message */
static int iDefPFFamily = PF_UNSPEC; /* protocol family (IPv4, IPv6 or both) */
static int bDropMalPTRMsgs = 0;/* Drop messages which have malicious PTR records during DNS lookup */
static int option_DisallowWarning = 1; /* complain if message from disallowed sender is received */
static int bDisableDNS = 0; /* don't look up IP addresses of remote messages */
+static prop_t *propLocalHostName = NULL;/* our hostname as FQDN - read-only after startup */
static uchar *LocalHostName = NULL;/* our hostname - read-only after startup */
+static uchar *LocalFQDNName = NULL;/* our hostname as FQDN - read-only after startup */
static uchar *LocalDomain; /* our local domain name - read-only after startup */
static char **StripDomains = NULL;/* these domains may be stripped before writing logs - r/o after s.u., never touched by init */
static char **LocalHosts = NULL;/* these hosts are logged with their hostname - read-only after startup, never touched by init */
@@ -85,6 +93,9 @@ static dataType Get##nameFunc(void) \
return(nameVar); \
}
+SIMP_PROP(OptimizeUniProc, bOptimizeUniProc, int)
+SIMP_PROP(PreserveFQDN, bPreserveFQDN, int)
+SIMP_PROP(HUPisRestart, bHUPisRestart, int)
SIMP_PROP(MaxLine, iMaxLine, int)
SIMP_PROP(DefPFFamily, iDefPFFamily, int) /* note that in the future we may check the family argument */
SIMP_PROP(DropMalPTRMsgs, bDropMalPTRMsgs, int)
@@ -94,6 +105,7 @@ SIMP_PROP(LocalDomain, LocalDomain, uchar*)
SIMP_PROP(StripDomains, StripDomains, char**)
SIMP_PROP(LocalHosts, LocalHosts, char**)
+SIMP_PROP_SET(LocalFQDNName, LocalFQDNName, uchar*)
SIMP_PROP_SET(LocalHostName, LocalHostName, uchar*)
SIMP_PROP_SET(DfltNetstrmDrvr, pszDfltNetstrmDrvr, uchar*) /* TODO: use custom function which frees existing value */
SIMP_PROP_SET(DfltNetstrmDrvrCAF, pszDfltNetstrmDrvrCAF, uchar*) /* TODO: use custom function which frees existing value */
@@ -110,7 +122,65 @@ SIMP_PROP_SET(DfltNetstrmDrvrCertFile, pszDfltNetstrmDrvrCertFile, uchar*) /* TO
static uchar*
GetLocalHostName(void)
{
- return(LocalHostName == NULL ? (uchar*) "[localhost]" : LocalHostName);
+ uchar *pszRet;
+
+ if(LocalHostName == NULL)
+ pszRet = (uchar*) "[localhost]";
+ else {
+ if(GetPreserveFQDN() == 1)
+ pszRet = LocalFQDNName;
+ else
+ pszRet = LocalHostName;
+ }
+ return(pszRet);
+}
+
+
+/* generate the local hostname property. This must be done after the hostname info
+ * has been set as well as PreserveFQDN.
+ * rgerhards, 2009-06-30
+ */
+static rsRetVal
+GenerateLocalHostNameProperty(void)
+{
+ DEFiRet;
+ uchar *pszName;
+
+ if(propLocalHostName != NULL)
+ prop.Destruct(&propLocalHostName);
+
+ CHKiRet(prop.Construct(&propLocalHostName));
+ if(LocalHostName == NULL)
+ pszName = (uchar*) "[localhost]";
+ else {
+ if(GetPreserveFQDN() == 1)
+ pszName = LocalFQDNName;
+ else
+ pszName = LocalHostName;
+ }
+ CHKiRet(prop.SetString(propLocalHostName, pszName, ustrlen(pszName)));
+ CHKiRet(prop.ConstructFinalize(propLocalHostName));
+
+finalize_it:
+ RETiRet;
+}
+
+/* return our local hostname as a string property
+ */
+static prop_t*
+GetLocalHostNameProp(void)
+{
+ return(propLocalHostName);
+}
+
+
+/* return the current localhost name as FQDN (requires FQDN to be set)
+ * TODO: we should set the FQDN ourselfs in here!
+ */
+static uchar*
+GetLocalFQDNName(void)
+{
+ return(LocalFQDNName == NULL ? (uchar*) "[localhost]" : LocalFQDNName);
}
@@ -169,14 +239,20 @@ CODESTARTobjQueryInterface(glbl)
* of course, also affects the "if" above).
*/
pIf->GetWorkDir = GetWorkDir;
+ pIf->GenerateLocalHostNameProperty = GenerateLocalHostNameProperty;
+ pIf->GetLocalHostNameProp = GetLocalHostNameProp;
#define SIMP_PROP(name) \
pIf->Get##name = Get##name; \
pIf->Set##name = Set##name;
SIMP_PROP(MaxLine);
+ SIMP_PROP(OptimizeUniProc);
+ SIMP_PROP(PreserveFQDN);
+ SIMP_PROP(HUPisRestart);
SIMP_PROP(DefPFFamily);
SIMP_PROP(DropMalPTRMsgs);
SIMP_PROP(Option_DisallowWarning);
SIMP_PROP(DisableDNS);
+ SIMP_PROP(LocalFQDNName)
SIMP_PROP(LocalHostName)
SIMP_PROP(LocalDomain)
SIMP_PROP(StripDomains)
@@ -216,6 +292,9 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
pszWorkDir = NULL;
}
bDropMalPTRMsgs = 0;
+ bOptimizeUniProc = 1;
+ bHUPisRestart = 0;
+ bPreserveFQDN = 0;
return RS_RET_OK;
}
@@ -227,6 +306,7 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
*/
BEGINAbstractObjClassInit(glbl, 1, OBJ_IS_CORE_MODULE) /* class, version */
/* request objects we use */
+ CHKiRet(objUse(prop, CORE_COMPONENT));
/* register config handlers (TODO: we need to implement a way to unregister them) */
CHKiRet(regCfSysLineHdlr((uchar *)"workdirectory", 0, eCmdHdlrGetWord, NULL, &pszWorkDir, NULL));
@@ -235,6 +315,9 @@ BEGINAbstractObjClassInit(glbl, 1, OBJ_IS_CORE_MODULE) /* class, version */
CHKiRet(regCfSysLineHdlr((uchar *)"defaultnetstreamdrivercafile", 0, eCmdHdlrGetWord, NULL, &pszDfltNetstrmDrvrCAF, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"defaultnetstreamdriverkeyfile", 0, eCmdHdlrGetWord, NULL, &pszDfltNetstrmDrvrKeyFile, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"defaultnetstreamdrivercertfile", 0, eCmdHdlrGetWord, NULL, &pszDfltNetstrmDrvrCertFile, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"optimizeforuniprocessor", 0, eCmdHdlrBinary, NULL, &bOptimizeUniProc, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"hupisrestart", 0, eCmdHdlrBinary, NULL, &bHUPisRestart, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"preservefqdn", 0, eCmdHdlrBinary, NULL, &bPreserveFQDN, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, NULL));
ENDObjClassInit(glbl)
@@ -255,6 +338,9 @@ BEGINObjClassExit(glbl, OBJ_IS_CORE_MODULE) /* class, version */
free(pszWorkDir);
if(LocalHostName != NULL)
free(LocalHostName);
+ if(LocalFQDNName != NULL)
+ free(LocalFQDNName);
+ objRelease(prop, CORE_COMPONENT);
ENDObjClassExit(glbl)
/* vi:set ai:
diff --git a/runtime/glbl.h b/runtime/glbl.h
index 0c83bdd5..dcfb6d5f 100644
--- a/runtime/glbl.h
+++ b/runtime/glbl.h
@@ -32,6 +32,8 @@
#ifndef GLBL_H_INCLUDED
#define GLBL_H_INCLUDED
+#include "prop.h"
+
#define glblGetIOBufSize() 4096 /* size of the IO buffer, e.g. for strm class */
/* interfaces */
@@ -41,10 +43,14 @@ BEGINinterface(glbl) /* name must also be changed in ENDinterface macro! */
dataType (*Get##name)(void); \
rsRetVal (*Set##name)(dataType);
SIMP_PROP(MaxLine, int)
+ SIMP_PROP(OptimizeUniProc, int)
+ SIMP_PROP(HUPisRestart, int)
+ SIMP_PROP(PreserveFQDN, int)
SIMP_PROP(DefPFFamily, int)
SIMP_PROP(DropMalPTRMsgs, int)
SIMP_PROP(Option_DisallowWarning, int)
SIMP_PROP(DisableDNS, int)
+ SIMP_PROP(LocalFQDNName, uchar*)
SIMP_PROP(LocalHostName, uchar*)
SIMP_PROP(LocalDomain, uchar*)
SIMP_PROP(StripDomains, char**)
@@ -53,9 +59,13 @@ BEGINinterface(glbl) /* name must also be changed in ENDinterface macro! */
SIMP_PROP(DfltNetstrmDrvrCAF, uchar*)
SIMP_PROP(DfltNetstrmDrvrKeyFile, uchar*)
SIMP_PROP(DfltNetstrmDrvrCertFile, uchar*)
+ /* added v3, 2009-06-30 */
+ rsRetVal (*GenerateLocalHostNameProperty)(void);
+ prop_t* (*GetLocalHostNameProp)(void);
#undef SIMP_PROP
ENDinterface(glbl)
-#define glblCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+#define glblCURR_IF_VERSION 3 /* increment whenever you change the interface structure! */
+/* version 2 had PreserveFQDN added - rgerhards, 2008-12-08 */
/* the remaining prototypes */
PROTOTYPEObj(glbl);
diff --git a/runtime/linkedlist.c b/runtime/linkedlist.c
index 8f842e43..cc095f6e 100644
--- a/runtime/linkedlist.c
+++ b/runtime/linkedlist.c
@@ -398,7 +398,7 @@ rsRetVal llExecFunc(linkedList_t *pThis, rsRetVal (*pFunc)(void*, void*), void*
*/
llCookie = llCookiePrev;
} else if (iRet != RS_RET_OK) {
- goto finalize_it;
+ FINALIZE;
}
llCookiePrev = llCookie;
}
diff --git a/runtime/module-template.h b/runtime/module-template.h
index eb39b587..3e963199 100644
--- a/runtime/module-template.h
+++ b/runtime/module-template.h
@@ -39,7 +39,8 @@
#define DEF_OMOD_STATIC_DATA \
DEF_MOD_STATIC_DATA \
- DEFobjCurrIf(obj)
+ DEFobjCurrIf(obj) \
+ static __attribute__((unused)) int bCoreSupportsBatching;
#define DEF_IMOD_STATIC_DATA \
DEF_MOD_STATIC_DATA \
DEFobjCurrIf(obj)
@@ -160,6 +161,37 @@ static rsRetVal isCompatibleWithFeature(syslogFeature __attribute__((unused)) eF
RETiRet;\
}
+
+/* beginTransaction()
+ * introduced in v4.3.3 -- rgerhards, 2009-04-27
+ */
+#define BEGINbeginTransaction \
+static rsRetVal beginTransaction(instanceData __attribute__((unused)) *pData)\
+{\
+ DEFiRet;
+
+#define CODESTARTbeginTransaction /* currently empty, but may be extended */
+
+#define ENDbeginTransaction \
+ RETiRet;\
+}
+
+
+/* endTransaction()
+ * introduced in v4.3.3 -- rgerhards, 2009-04-27
+ */
+#define BEGINendTransaction \
+static rsRetVal endTransaction(instanceData __attribute__((unused)) *pData)\
+{\
+ DEFiRet;
+
+#define CODESTARTendTransaction /* currently empty, but may be extended */
+
+#define ENDendTransaction \
+ RETiRet;\
+}
+
+
/* doAction()
*/
#define BEGINdoAction \
@@ -324,6 +356,18 @@ static rsRetVal queryEtryPt(uchar *name, rsRetVal (**pEtryPoint)())\
*pEtryPoint = tryResume;\
}
+
+/* the following definition is queryEtryPt block that must be added
+ * if an output module supports the transactional interface.
+ * rgerhards, 2009-04-27
+ */
+#define CODEqueryEtryPt_TXIF_OMOD_QUERIES \
+ else if(!strcmp((char*) name, "beginTransaction")) {\
+ *pEtryPoint = beginTransaction;\
+ } else if(!strcmp((char*) name, "endTransaction")) {\
+ *pEtryPoint = endTransaction;\
+ }
+
/* the following definition is the standard block for queryEtryPt for INPUT
* modules. This can be used if no specific handling (e.g. to cover version
* differences) is needed.
@@ -393,6 +437,32 @@ finalize_it:\
}
+/* now come some check functions, which enable a standard way of obtaining feature
+ * information from the core. feat is the to-be-tested feature and featVar is a
+ * variable that receives the result (0-not support, 1-supported).
+ * This must be a macro, so that it is put into the output's code. Otherwise, we
+ * would need to rely on a library entry point, which is what we intend to avoid ;)
+ * rgerhards, 2009-04-27
+ */
+#define INITChkCoreFeature(featVar, feat) \
+{ \
+ rsRetVal MACRO_Ret; \
+ rsRetVal (*pQueryCoreFeatureSupport)(int*, unsigned); \
+ int bSupportsIt; \
+ featVar = 0; \
+ MACRO_Ret = pHostQueryEtryPt((uchar*)"queryCoreFeatureSupport", &pQueryCoreFeatureSupport); \
+ if(MACRO_Ret == RS_RET_OK) { \
+ /* found entry point, so let's see if core supports it */ \
+ CHKiRet((*pQueryCoreFeatureSupport)(&bSupportsIt, feat)); \
+ if(bSupportsIt) \
+ featVar = 1; \
+ } else if(MACRO_Ret != RS_RET_ENTRY_POINT_NOT_FOUND) { \
+ ABORT_FINALIZE(MACRO_Ret); /* Something else went wrong, what is not acceptable */ \
+ } \
+}
+
+
+
/* definitions for host API queries */
#define CODEmodInit_QueryRegCFSLineHdlr \
CHKiRet(pHostQueryEtryPt((uchar*)"regCfSysLineHdlr", &omsdRegCFSLineHdlr));
@@ -481,6 +551,33 @@ static rsRetVal afterRun(void)\
}
-/*
- * vi:set ai:
+/* doHUP()
+ * This function is optional. Currently, it is available to output plugins
+ * only, but may be made available to other types of plugins in the future.
+ * A plugin does not need to define this entry point. If if does, it gets
+ * called when a non-restart type of HUP is done. A plugin should register
+ * this function so that it can close files, connection or other ressources
+ * on HUP - if it can be assume the user wanted to do this as a part of HUP
+ * processing. Note that the name "HUP" has historical reasons, it stems back
+ * to the infamous SIGHUP which was sent to restart a syslogd. We still retain
+ * that legacy, but may move this to a different signal.
+ * rgerhards, 2008-10-22
+ */
+#define CODEqueryEtryPt_doHUP \
+ else if(!strcmp((char*) name, "doHUP")) {\
+ *pEtryPoint = doHUP;\
+ }
+#define BEGINdoHUP \
+static rsRetVal doHUP(instanceData __attribute__((unused)) *pData)\
+{\
+ DEFiRet;
+
+#define CODESTARTdoHUP
+
+#define ENDdoHUP \
+ RETiRet;\
+}
+
+
+/* vim:set ai:
*/
diff --git a/runtime/modules.c b/runtime/modules.c
index d5730ede..871f356a 100644
--- a/runtime/modules.c
+++ b/runtime/modules.c
@@ -40,6 +40,7 @@
#include <time.h>
#include <assert.h>
#include <errno.h>
+#include <pthread.h>
#ifdef OS_BSD
# include "libgen.h"
#endif
@@ -49,6 +50,10 @@
#include <unistd.h>
#include <sys/file.h>
+#ifdef OS_SOLARIS
+# define PATH_MAX MAXPATHLEN
+#endif
+
#include "cfsysline.h"
#include "modules.h"
#include "errmsg.h"
@@ -57,6 +62,14 @@
DEFobjStaticHelpers
DEFobjCurrIf(errmsg)
+/* we must ensure that only one thread at one time tries to load or unload
+ * modules, otherwise we may see race conditions. This first came up with
+ * imdiag/imtcp, which both use the same stream drivers. Below is the mutex
+ * for that handling.
+ * rgerhards, 2009-05-25
+ */
+static pthread_mutex_t mutLoadUnload;
+
static modInfo_t *pLoadedModules = NULL; /* list of currently-loaded modules */
static modInfo_t *pLoadedModulesLast = NULL; /* tail-pointer */
@@ -221,6 +234,8 @@ static rsRetVal queryHostEtryPt(uchar *name, rsRetVal (**pEtryPoint)())
*pEtryPoint = regCfSysLineHdlr;
} else if(!strcmp((char*) name, "objGetObjInterface")) {
*pEtryPoint = objGetObjInterface;
+ } else if(!strcmp((char*) name, "OMSRgetSupportedTplOpts")) {
+ *pEtryPoint = OMSRgetSupportedTplOpts;
} else {
*pEtryPoint = NULL; /* to be on the safe side */
ABORT_FINALIZE(RS_RET_ENTRY_POINT_NOT_FOUND);
@@ -347,6 +362,7 @@ static rsRetVal
doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)(), modInfo_t*), uchar *name, void *pModHdlr)
{
DEFiRet;
+ rsRetVal localRet;
modInfo_t *pNew = NULL;
rsRetVal (*modGetType)(eModType_t *pType);
@@ -367,7 +383,7 @@ doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)(), modInfo_
* can never change in the lifetime of an module. -- rgerhards, 2007-12-14
*/
CHKiRet((*pNew->modQueryEtryPt)((uchar*)"getType", &modGetType));
- CHKiRet((iRet = (*modGetType)(&pNew->eType)) != RS_RET_OK);
+ CHKiRet((*modGetType)(&pNew->eType));
dbgprintf("module of type %d being loaded.\n", pNew->eType);
/* OK, we know we can successfully work with the module. So we now fill the
@@ -383,6 +399,7 @@ doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)(), modInfo_
CHKiRet((*pNew->modQueryEtryPt)((uchar*)"runInput", &pNew->mod.im.runInput));
CHKiRet((*pNew->modQueryEtryPt)((uchar*)"willRun", &pNew->mod.im.willRun));
CHKiRet((*pNew->modQueryEtryPt)((uchar*)"afterRun", &pNew->mod.im.afterRun));
+ pNew->mod.im.bCanRun = 0;
break;
case eMOD_OUT:
CHKiRet((*pNew->modQueryEtryPt)((uchar*)"freeInstance", &pNew->freeInstance));
@@ -391,6 +408,10 @@ doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)(), modInfo_
CHKiRet((*pNew->modQueryEtryPt)((uchar*)"parseSelectorAct", &pNew->mod.om.parseSelectorAct));
CHKiRet((*pNew->modQueryEtryPt)((uchar*)"isCompatibleWithFeature", &pNew->isCompatibleWithFeature));
CHKiRet((*pNew->modQueryEtryPt)((uchar*)"tryResume", &pNew->tryResume));
+ /* try load optional interfaces */
+ localRet = (*pNew->modQueryEtryPt)((uchar*)"doHUP", &pNew->doHUP);
+ if(localRet != RS_RET_OK && localRet != RS_RET_MODULE_ENTRY_POINT_NOT_FOUND)
+ ABORT_FINALIZE(localRet);
break;
case eMOD_LIB:
break;
@@ -468,6 +489,8 @@ modUnlinkAndDestroy(modInfo_t **ppThis)
pThis = *ppThis;
assert(pThis != NULL);
+ pthread_mutex_lock(&mutLoadUnload);
+
/* first check if we are permitted to unload */
if(pThis->eType == eMOD_LIB) {
if(pThis->uRefCnt > 0) {
@@ -502,6 +525,7 @@ modUnlinkAndDestroy(modInfo_t **ppThis)
moduleDestruct(pThis);
finalize_it:
+ pthread_mutex_unlock(&mutLoadUnload);
RETiRet;
}
@@ -576,6 +600,8 @@ Load(uchar *pModName)
assert(pModName != NULL);
dbgprintf("Requested to load module '%s'\n", pModName);
+ pthread_mutex_lock(&mutLoadUnload);
+
iModNameLen = strlen((char *) pModName);
if(iModNameLen > 3 && !strcmp((char *) pModName + iModNameLen - 3, ".so")) {
iModNameLen -= 3;
@@ -599,7 +625,7 @@ Load(uchar *pModName)
iLoadCnt = 0;
do {
/* now build our load module name */
- if(*pModName == '/') {
+ if(*pModName == '/' || *pModName == '.') {
*szPath = '\0'; /* we do not need to append the path - its already in the module name */
iPathLen = 0;
} else {
@@ -685,6 +711,7 @@ Load(uchar *pModName)
}
finalize_it:
+ pthread_mutex_unlock(&mutLoadUnload);
RETiRet;
}
@@ -780,6 +807,15 @@ BEGINObjClassExit(module, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MA
CODESTARTObjClassExit(module)
/* release objects we no longer need */
objRelease(errmsg, CORE_COMPONENT);
+ /* We have a problem in our reference counting, which leads to this function
+ * being called too early. This usually is no problem, but if we destroy
+ * the mutex object, we get into trouble. So rather than finding the root cause,
+ * we do not release the mutex right now and have a very, very slight leak.
+ * We know that otherwise no bad effects happen, so this acceptable for the
+ * time being. -- rgerhards, 2009-05-25
+ *
+ * TODO: add again: pthread_mutex_destroy(&mutLoadUnload);
+ */
# ifdef DEBUG
modUsrPrintAll(); /* debug aid - TODO: integrate with debug.c, at least the settings! */
@@ -822,6 +858,7 @@ ENDobjQueryInterface(module)
*/
BEGINAbstractObjClassInit(module, 1, OBJ_IS_CORE_MODULE) /* class, version - CHANGE class also in END MACRO! */
uchar *pModPath;
+ pthread_mutexattr_t mutAttr;
/* use any module load path specified in the environment */
if((pModPath = (uchar*) getenv("RSYSLOG_MODDIR")) != NULL) {
@@ -839,6 +876,10 @@ BEGINAbstractObjClassInit(module, 1, OBJ_IS_CORE_MODULE) /* class, version - CHA
SetModDir(glblModPath);
}
+ pthread_mutexattr_init(&mutAttr);
+ pthread_mutexattr_settype(&mutAttr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&mutLoadUnload, &mutAttr);
+
/* request objects we use */
CHKiRet(objUse(errmsg, CORE_COMPONENT));
ENDObjClassInit(module)
diff --git a/runtime/modules.h b/runtime/modules.h
index 7d34bcf7..4d874019 100644
--- a/runtime/modules.h
+++ b/runtime/modules.h
@@ -44,8 +44,11 @@
* rgerhards, 2008-03-04
* version 3 adds modInfo_t ptr to call of modInit -- rgerhards, 2008-03-10
* version 4 removes needUDPSocket OM callback -- rgerhards, 2008-03-22
+ * version 5 changes the way parsing works for input modules. This is
+ * an important change, parseAndSubmitMessage() goes away. Other
+ * module types are not affected. -- rgerhards, 2008-10-09
*/
-#define CURR_MOD_IF_VERSION 4
+#define CURR_MOD_IF_VERSION 5
typedef enum eModType_ {
eMOD_IN, /* input module */
@@ -88,6 +91,7 @@ typedef struct modInfo_s {
rsRetVal (*tryResume)(void*);/* called to see if module actin can be resumed now */
rsRetVal (*modExit)(void); /* called before termination or module unload */
rsRetVal (*modGetID)(void **); /* get its unique ID from module */
+ rsRetVal (*doHUP)(void *); /* non-restart type HUP handler */
/* below: parse a configuration line - return if processed
* or not. If not, must be parsed to next module.
*/
@@ -102,6 +106,7 @@ typedef struct modInfo_s {
rsRetVal (*runInput)(thrdInfo_t*); /* function to gather input and submit to queue */
rsRetVal (*willRun)(void); /* function to gather input and submit to queue */
rsRetVal (*afterRun)(thrdInfo_t*); /* function to gather input and submit to queue */
+ int bCanRun; /* cached value of whether willRun() succeeded */
} im;
struct {/* data for output modules */
/* below: perform the configured action
diff --git a/runtime/msg.c b/runtime/msg.c
index 375b9861..a9a09143 100644
--- a/runtime/msg.c
+++ b/runtime/msg.c
@@ -35,6 +35,9 @@
#include <string.h>
#include <assert.h>
#include <ctype.h>
+#if HAVE_MALLOC_H
+# include <malloc.h>
+#endif
#include "rsyslog.h"
#include "srUtils.h"
#include "stringbuf.h"
@@ -45,6 +48,9 @@
#include "glbl.h"
#include "regexp.h"
#include "atomic.h"
+#include "unicode-helper.h"
+#include "ruleset.h"
+#include "prop.h"
/* static data */
DEFobjStaticHelpers
@@ -52,57 +58,433 @@ DEFobjCurrIf(var)
DEFobjCurrIf(datetime)
DEFobjCurrIf(glbl)
DEFobjCurrIf(regexp)
+DEFobjCurrIf(prop)
+
+static struct {
+ uchar *pszName;
+ short lenName;
+} syslog_pri_names[192] = {
+ { UCHAR_CONSTANT("0"), 3},
+ { UCHAR_CONSTANT("1"), 3},
+ { UCHAR_CONSTANT("2"), 3},
+ { UCHAR_CONSTANT("3"), 3},
+ { UCHAR_CONSTANT("4"), 3},
+ { UCHAR_CONSTANT("5"), 3},
+ { UCHAR_CONSTANT("6"), 3},
+ { UCHAR_CONSTANT("7"), 3},
+ { UCHAR_CONSTANT("8"), 3},
+ { UCHAR_CONSTANT("9"), 3},
+ { UCHAR_CONSTANT("10"), 4},
+ { UCHAR_CONSTANT("11"), 4},
+ { UCHAR_CONSTANT("12"), 4},
+ { UCHAR_CONSTANT("13"), 4},
+ { UCHAR_CONSTANT("14"), 4},
+ { UCHAR_CONSTANT("15"), 4},
+ { UCHAR_CONSTANT("16"), 4},
+ { UCHAR_CONSTANT("17"), 4},
+ { UCHAR_CONSTANT("18"), 4},
+ { UCHAR_CONSTANT("19"), 4},
+ { UCHAR_CONSTANT("20"), 4},
+ { UCHAR_CONSTANT("21"), 4},
+ { UCHAR_CONSTANT("22"), 4},
+ { UCHAR_CONSTANT("23"), 4},
+ { UCHAR_CONSTANT("24"), 4},
+ { UCHAR_CONSTANT("25"), 4},
+ { UCHAR_CONSTANT("26"), 4},
+ { UCHAR_CONSTANT("27"), 4},
+ { UCHAR_CONSTANT("28"), 4},
+ { UCHAR_CONSTANT("29"), 4},
+ { UCHAR_CONSTANT("30"), 4},
+ { UCHAR_CONSTANT("31"), 4},
+ { UCHAR_CONSTANT("32"), 4},
+ { UCHAR_CONSTANT("33"), 4},
+ { UCHAR_CONSTANT("34"), 4},
+ { UCHAR_CONSTANT("35"), 4},
+ { UCHAR_CONSTANT("36"), 4},
+ { UCHAR_CONSTANT("37"), 4},
+ { UCHAR_CONSTANT("38"), 4},
+ { UCHAR_CONSTANT("39"), 4},
+ { UCHAR_CONSTANT("40"), 4},
+ { UCHAR_CONSTANT("41"), 4},
+ { UCHAR_CONSTANT("42"), 4},
+ { UCHAR_CONSTANT("43"), 4},
+ { UCHAR_CONSTANT("44"), 4},
+ { UCHAR_CONSTANT("45"), 4},
+ { UCHAR_CONSTANT("46"), 4},
+ { UCHAR_CONSTANT("47"), 4},
+ { UCHAR_CONSTANT("48"), 4},
+ { UCHAR_CONSTANT("49"), 4},
+ { UCHAR_CONSTANT("50"), 4},
+ { UCHAR_CONSTANT("51"), 4},
+ { UCHAR_CONSTANT("52"), 4},
+ { UCHAR_CONSTANT("53"), 4},
+ { UCHAR_CONSTANT("54"), 4},
+ { UCHAR_CONSTANT("55"), 4},
+ { UCHAR_CONSTANT("56"), 4},
+ { UCHAR_CONSTANT("57"), 4},
+ { UCHAR_CONSTANT("58"), 4},
+ { UCHAR_CONSTANT("59"), 4},
+ { UCHAR_CONSTANT("60"), 4},
+ { UCHAR_CONSTANT("61"), 4},
+ { UCHAR_CONSTANT("62"), 4},
+ { UCHAR_CONSTANT("63"), 4},
+ { UCHAR_CONSTANT("64"), 4},
+ { UCHAR_CONSTANT("65"), 4},
+ { UCHAR_CONSTANT("66"), 4},
+ { UCHAR_CONSTANT("67"), 4},
+ { UCHAR_CONSTANT("68"), 4},
+ { UCHAR_CONSTANT("69"), 4},
+ { UCHAR_CONSTANT("70"), 4},
+ { UCHAR_CONSTANT("71"), 4},
+ { UCHAR_CONSTANT("72"), 4},
+ { UCHAR_CONSTANT("73"), 4},
+ { UCHAR_CONSTANT("74"), 4},
+ { UCHAR_CONSTANT("75"), 4},
+ { UCHAR_CONSTANT("76"), 4},
+ { UCHAR_CONSTANT("77"), 4},
+ { UCHAR_CONSTANT("78"), 4},
+ { UCHAR_CONSTANT("79"), 4},
+ { UCHAR_CONSTANT("80"), 4},
+ { UCHAR_CONSTANT("81"), 4},
+ { UCHAR_CONSTANT("82"), 4},
+ { UCHAR_CONSTANT("83"), 4},
+ { UCHAR_CONSTANT("84"), 4},
+ { UCHAR_CONSTANT("85"), 4},
+ { UCHAR_CONSTANT("86"), 4},
+ { UCHAR_CONSTANT("87"), 4},
+ { UCHAR_CONSTANT("88"), 4},
+ { UCHAR_CONSTANT("89"), 4},
+ { UCHAR_CONSTANT("90"), 4},
+ { UCHAR_CONSTANT("91"), 4},
+ { UCHAR_CONSTANT("92"), 4},
+ { UCHAR_CONSTANT("93"), 4},
+ { UCHAR_CONSTANT("94"), 4},
+ { UCHAR_CONSTANT("95"), 4},
+ { UCHAR_CONSTANT("96"), 4},
+ { UCHAR_CONSTANT("97"), 4},
+ { UCHAR_CONSTANT("98"), 4},
+ { UCHAR_CONSTANT("99"), 4},
+ { UCHAR_CONSTANT("100"), 5},
+ { UCHAR_CONSTANT("101"), 5},
+ { UCHAR_CONSTANT("102"), 5},
+ { UCHAR_CONSTANT("103"), 5},
+ { UCHAR_CONSTANT("104"), 5},
+ { UCHAR_CONSTANT("105"), 5},
+ { UCHAR_CONSTANT("106"), 5},
+ { UCHAR_CONSTANT("107"), 5},
+ { UCHAR_CONSTANT("108"), 5},
+ { UCHAR_CONSTANT("109"), 5},
+ { UCHAR_CONSTANT("110"), 5},
+ { UCHAR_CONSTANT("111"), 5},
+ { UCHAR_CONSTANT("112"), 5},
+ { UCHAR_CONSTANT("113"), 5},
+ { UCHAR_CONSTANT("114"), 5},
+ { UCHAR_CONSTANT("115"), 5},
+ { UCHAR_CONSTANT("116"), 5},
+ { UCHAR_CONSTANT("117"), 5},
+ { UCHAR_CONSTANT("118"), 5},
+ { UCHAR_CONSTANT("119"), 5},
+ { UCHAR_CONSTANT("120"), 5},
+ { UCHAR_CONSTANT("121"), 5},
+ { UCHAR_CONSTANT("122"), 5},
+ { UCHAR_CONSTANT("123"), 5},
+ { UCHAR_CONSTANT("124"), 5},
+ { UCHAR_CONSTANT("125"), 5},
+ { UCHAR_CONSTANT("126"), 5},
+ { UCHAR_CONSTANT("127"), 5},
+ { UCHAR_CONSTANT("128"), 5},
+ { UCHAR_CONSTANT("129"), 5},
+ { UCHAR_CONSTANT("130"), 5},
+ { UCHAR_CONSTANT("131"), 5},
+ { UCHAR_CONSTANT("132"), 5},
+ { UCHAR_CONSTANT("133"), 5},
+ { UCHAR_CONSTANT("134"), 5},
+ { UCHAR_CONSTANT("135"), 5},
+ { UCHAR_CONSTANT("136"), 5},
+ { UCHAR_CONSTANT("137"), 5},
+ { UCHAR_CONSTANT("138"), 5},
+ { UCHAR_CONSTANT("139"), 5},
+ { UCHAR_CONSTANT("140"), 5},
+ { UCHAR_CONSTANT("141"), 5},
+ { UCHAR_CONSTANT("142"), 5},
+ { UCHAR_CONSTANT("143"), 5},
+ { UCHAR_CONSTANT("144"), 5},
+ { UCHAR_CONSTANT("145"), 5},
+ { UCHAR_CONSTANT("146"), 5},
+ { UCHAR_CONSTANT("147"), 5},
+ { UCHAR_CONSTANT("148"), 5},
+ { UCHAR_CONSTANT("149"), 5},
+ { UCHAR_CONSTANT("150"), 5},
+ { UCHAR_CONSTANT("151"), 5},
+ { UCHAR_CONSTANT("152"), 5},
+ { UCHAR_CONSTANT("153"), 5},
+ { UCHAR_CONSTANT("154"), 5},
+ { UCHAR_CONSTANT("155"), 5},
+ { UCHAR_CONSTANT("156"), 5},
+ { UCHAR_CONSTANT("157"), 5},
+ { UCHAR_CONSTANT("158"), 5},
+ { UCHAR_CONSTANT("159"), 5},
+ { UCHAR_CONSTANT("160"), 5},
+ { UCHAR_CONSTANT("161"), 5},
+ { UCHAR_CONSTANT("162"), 5},
+ { UCHAR_CONSTANT("163"), 5},
+ { UCHAR_CONSTANT("164"), 5},
+ { UCHAR_CONSTANT("165"), 5},
+ { UCHAR_CONSTANT("166"), 5},
+ { UCHAR_CONSTANT("167"), 5},
+ { UCHAR_CONSTANT("168"), 5},
+ { UCHAR_CONSTANT("169"), 5},
+ { UCHAR_CONSTANT("170"), 5},
+ { UCHAR_CONSTANT("171"), 5},
+ { UCHAR_CONSTANT("172"), 5},
+ { UCHAR_CONSTANT("173"), 5},
+ { UCHAR_CONSTANT("174"), 5},
+ { UCHAR_CONSTANT("175"), 5},
+ { UCHAR_CONSTANT("176"), 5},
+ { UCHAR_CONSTANT("177"), 5},
+ { UCHAR_CONSTANT("178"), 5},
+ { UCHAR_CONSTANT("179"), 5},
+ { UCHAR_CONSTANT("180"), 5},
+ { UCHAR_CONSTANT("181"), 5},
+ { UCHAR_CONSTANT("182"), 5},
+ { UCHAR_CONSTANT("183"), 5},
+ { UCHAR_CONSTANT("184"), 5},
+ { UCHAR_CONSTANT("185"), 5},
+ { UCHAR_CONSTANT("186"), 5},
+ { UCHAR_CONSTANT("187"), 5},
+ { UCHAR_CONSTANT("188"), 5},
+ { UCHAR_CONSTANT("189"), 5},
+ { UCHAR_CONSTANT("190"), 5},
+ { UCHAR_CONSTANT("191"), 5}
+ };
+
+/*syslog facility names (as of RFC5424) */
+static char *syslog_fac_names[24] = { "kern", "user", "mail", "daemon", "auth", "syslog", "lpr",
+ "news", "uucp", "cron", "authpriv", "ftp", "ntp", "audit",
+ "alert", "clock", "local0", "local1", "local2", "local3",
+ "local4", "local5", "local6", "local7" };
+
+/* table of severity names (in numerical order)*/
+static char *syslog_severity_names[8] = { "emerg", "alert", "crit", "err", "warning", "notice", "info", "debug" };
+
+/* numerical values as string - this is the most efficient approach to convert severity
+ * and facility values to a numerical string... -- rgerhars, 2009-06-17
+ */
-static syslogCODE rs_prioritynames[] =
- {
- { "alert", LOG_ALERT },
- { "crit", LOG_CRIT },
- { "debug", LOG_DEBUG },
- { "emerg", LOG_EMERG },
- { "err", LOG_ERR },
- { "error", LOG_ERR }, /* DEPRECATED */
- { "info", LOG_INFO },
- { "none", INTERNAL_NOPRI }, /* INTERNAL */
- { "notice", LOG_NOTICE },
- { "panic", LOG_EMERG }, /* DEPRECATED */
- { "warn", LOG_WARNING }, /* DEPRECATED */
- { "warning", LOG_WARNING },
- { NULL, -1 }
- };
-
-#ifndef LOG_AUTHPRIV
-# define LOG_AUTHPRIV LOG_AUTH
-#endif
-static syslogCODE rs_facilitynames[] =
- {
- { "auth", LOG_AUTH },
- { "authpriv", LOG_AUTHPRIV },
- { "cron", LOG_CRON },
- { "daemon", LOG_DAEMON },
-#if defined(LOG_FTP)
- {"ftp", LOG_FTP},
-#endif
- { "kern", LOG_KERN },
- { "lpr", LOG_LPR },
- { "mail", LOG_MAIL },
- { "news", LOG_NEWS },
- { "security", LOG_AUTH }, /* DEPRECATED */
- { "syslog", LOG_SYSLOG },
- { "user", LOG_USER },
- { "uucp", LOG_UUCP },
- { "local0", LOG_LOCAL0 },
- { "local1", LOG_LOCAL1 },
- { "local2", LOG_LOCAL2 },
- { "local3", LOG_LOCAL3 },
- { "local4", LOG_LOCAL4 },
- { "local5", LOG_LOCAL5 },
- { "local6", LOG_LOCAL6 },
- { "local7", LOG_LOCAL7 },
- { NULL, -1 }
- };
+static char *syslog_number_names[24] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14",
+ "15", "16", "17", "18", "19", "20", "21", "22", "23" };
/* some forward declarations */
-static int getAPPNAMELen(msg_t *pM);
+static int getAPPNAMELen(msg_t *pM, bool bLockMutex);
+
+
+static inline int getProtocolVersion(msg_t *pM)
+{
+ return(pM->iProtocolVersion);
+}
+
+
+static inline void
+getInputName(msg_t *pM, uchar **ppsz, int *plen)
+{
+ BEGINfunc
+ if(pM == NULL) {
+ *ppsz = UCHAR_CONSTANT("");
+ *plen = 0;
+ } else {
+ prop.GetString(pM->pInputName, ppsz, plen);
+ }
+ ENDfunc
+}
+
+
+static inline uchar*
+getRcvFromIP(msg_t *pM)
+{
+ uchar *psz;
+ int len;
+ BEGINfunc
+ if(pM == NULL) {
+ psz = UCHAR_CONSTANT("");
+ } else {
+ if(pM->pRcvFromIP == NULL)
+ psz = UCHAR_CONSTANT("");
+ else
+ prop.GetString(pM->pRcvFromIP, &psz, &len);
+ }
+ ENDfunc
+ return psz;
+}
+
+
+
+/* map a property name (string) to a property ID */
+rsRetVal propNameToID(cstr_t *pCSPropName, propid_t *pPropID)
+{
+ uchar *pName;
+ DEFiRet;
+
+ assert(pCSPropName != NULL);
+ assert(pPropID != NULL);
+ pName = rsCStrGetSzStrNoNULL(pCSPropName);
+
+ /* sometimes there are aliases to the original MonitoWare
+ * property names. These come after || in the ifs below. */
+ if(!strcmp((char*) pName, "msg")) {
+ *pPropID = PROP_MSG;
+ } else if(!strcmp((char*) pName, "timestamp")
+ || !strcmp((char*) pName, "timereported")) {
+ *pPropID = PROP_TIMESTAMP;
+ } else if(!strcmp((char*) pName, "hostname") || !strcmp((char*) pName, "source")) {
+ *pPropID = PROP_HOSTNAME;
+ } else if(!strcmp((char*) pName, "syslogtag")) {
+ *pPropID = PROP_SYSLOGTAG;
+ } else if(!strcmp((char*) pName, "rawmsg")) {
+ *pPropID = PROP_RAWMSG;
+ /* enable this, if someone actually uses UxTradMsg, delete after some time has
+ * passed and nobody complained -- rgerhards, 2009-06-16
+ } else if(!strcmp((char*) pName, "uxtradmsg")) {
+ pRes = getUxTradMsg(pMsg);
+ */
+ } else if(!strcmp((char*) pName, "inputname")) {
+ *pPropID = PROP_INPUTNAME;
+ } else if(!strcmp((char*) pName, "fromhost")) {
+ *pPropID = PROP_FROMHOST;
+ } else if(!strcmp((char*) pName, "fromhost-ip")) {
+ *pPropID = PROP_FROMHOST_IP;
+ } else if(!strcmp((char*) pName, "pri")) {
+ *pPropID = PROP_PRI;
+ } else if(!strcmp((char*) pName, "pri-text")) {
+ *pPropID = PROP_PRI_TEXT;
+ } else if(!strcmp((char*) pName, "iut")) {
+ *pPropID = PROP_IUT;
+ } else if(!strcmp((char*) pName, "syslogfacility")) {
+ *pPropID = PROP_SYSLOGFACILITY;
+ } else if(!strcmp((char*) pName, "syslogfacility-text")) {
+ *pPropID = PROP_SYSLOGFACILITY_TEXT;
+ } else if(!strcmp((char*) pName, "syslogseverity") || !strcmp((char*) pName, "syslogpriority")) {
+ *pPropID = PROP_SYSLOGSEVERITY;
+ } else if(!strcmp((char*) pName, "syslogseverity-text") || !strcmp((char*) pName, "syslogpriority-text")) {
+ *pPropID = PROP_SYSLOGSEVERITY_TEXT;
+ } else if(!strcmp((char*) pName, "timegenerated")) {
+ *pPropID = PROP_TIMEGENERATED;
+ } else if(!strcmp((char*) pName, "programname")) {
+ *pPropID = PROP_PROGRAMNAME;
+ } else if(!strcmp((char*) pName, "protocol-version")) {
+ *pPropID = PROP_PROTOCOL_VERSION;
+ } else if(!strcmp((char*) pName, "structured-data")) {
+ *pPropID = PROP_STRUCTURED_DATA;
+ } else if(!strcmp((char*) pName, "app-name")) {
+ *pPropID = PROP_APP_NAME;
+ } else if(!strcmp((char*) pName, "procid")) {
+ *pPropID = PROP_PROCID;
+ } else if(!strcmp((char*) pName, "msgid")) {
+ *pPropID = PROP_MSGID;
+ /* here start system properties (those, that do not relate to the message itself */
+ } else if(!strcmp((char*) pName, "$now")) {
+ *pPropID = PROP_SYS_NOW;
+ } else if(!strcmp((char*) pName, "$year")) {
+ *pPropID = PROP_SYS_YEAR;
+ } else if(!strcmp((char*) pName, "$month")) {
+ *pPropID = PROP_SYS_MONTH;
+ } else if(!strcmp((char*) pName, "$day")) {
+ *pPropID = PROP_SYS_DAY;
+ } else if(!strcmp((char*) pName, "$hour")) {
+ *pPropID = PROP_SYS_HOUR;
+ } else if(!strcmp((char*) pName, "$hhour")) {
+ *pPropID = PROP_SYS_HHOUR;
+ } else if(!strcmp((char*) pName, "$qhour")) {
+ *pPropID = PROP_SYS_QHOUR;
+ } else if(!strcmp((char*) pName, "$minute")) {
+ *pPropID = PROP_SYS_MINUTE;
+ } else if(!strcmp((char*) pName, "$myhostname")) {
+ *pPropID = PROP_SYS_MYHOSTNAME;
+ } else {
+ *pPropID = PROP_INVALID;
+ iRet = RS_RET_VAR_NOT_FOUND;
+ }
+
+ RETiRet;
+}
+
+
+/* map a property ID to a name string (useful for displaying) */
+uchar *propIDToName(propid_t propID)
+{
+ switch(propID) {
+ case PROP_MSG:
+ return UCHAR_CONSTANT("msg");
+ case PROP_TIMESTAMP:
+ return UCHAR_CONSTANT("timestamp");
+ case PROP_HOSTNAME:
+ return UCHAR_CONSTANT("hostname");
+ case PROP_SYSLOGTAG:
+ return UCHAR_CONSTANT("syslogtag");
+ case PROP_RAWMSG:
+ return UCHAR_CONSTANT("rawmsg");
+ /* enable this, if someone actually uses UxTradMsg, delete after some time has
+ * passed and nobody complained -- rgerhards, 2009-06-16
+ case PROP_UXTRADMSG:
+ pRes = getUxTradMsg(pMsg);
+ break;
+ */
+ case PROP_INPUTNAME:
+ return UCHAR_CONSTANT("inputname");
+ case PROP_FROMHOST:
+ return UCHAR_CONSTANT("fromhost");
+ case PROP_FROMHOST_IP:
+ return UCHAR_CONSTANT("fromhost-ip");
+ case PROP_PRI:
+ return UCHAR_CONSTANT("pri");
+ case PROP_PRI_TEXT:
+ return UCHAR_CONSTANT("pri-text");
+ case PROP_IUT:
+ return UCHAR_CONSTANT("iut");
+ case PROP_SYSLOGFACILITY:
+ return UCHAR_CONSTANT("syslogfacility");
+ case PROP_SYSLOGFACILITY_TEXT:
+ return UCHAR_CONSTANT("syslogfacility-text");
+ case PROP_SYSLOGSEVERITY:
+ return UCHAR_CONSTANT("syslogseverity");
+ case PROP_SYSLOGSEVERITY_TEXT:
+ return UCHAR_CONSTANT("syslogseverity-text");
+ case PROP_TIMEGENERATED:
+ return UCHAR_CONSTANT("timegenerated");
+ case PROP_PROGRAMNAME:
+ return UCHAR_CONSTANT("programname");
+ case PROP_PROTOCOL_VERSION:
+ return UCHAR_CONSTANT("protocol-version");
+ case PROP_STRUCTURED_DATA:
+ return UCHAR_CONSTANT("structured-data");
+ case PROP_APP_NAME:
+ return UCHAR_CONSTANT("app-name");
+ case PROP_PROCID:
+ return UCHAR_CONSTANT("procid");
+ case PROP_MSGID:
+ return UCHAR_CONSTANT("msgid");
+ case PROP_SYS_NOW:
+ return UCHAR_CONSTANT("$NOW");
+ case PROP_SYS_YEAR:
+ return UCHAR_CONSTANT("$YEAR");
+ case PROP_SYS_MONTH:
+ return UCHAR_CONSTANT("$MONTH");
+ case PROP_SYS_DAY:
+ return UCHAR_CONSTANT("$DAY");
+ case PROP_SYS_HOUR:
+ return UCHAR_CONSTANT("$HOUR");
+ case PROP_SYS_HHOUR:
+ return UCHAR_CONSTANT("$HHOUR");
+ case PROP_SYS_QHOUR:
+ return UCHAR_CONSTANT("$QHOUR");
+ case PROP_SYS_MINUTE:
+ return UCHAR_CONSTANT("$MINUTE");
+ case PROP_SYS_MYHOSTNAME:
+ return UCHAR_CONSTANT("$MYHOSTNAME");
+ default:
+ return UCHAR_CONSTANT("*invalid property id*");
+ }
+}
+
/* The following functions will support advanced output module
* multithreading, once this is implemented. Currently, we
@@ -140,8 +522,8 @@ void (*funcMsgPrepareEnqueue)(msg_t *pMsg);
#define MsgLock(pMsg) funcLock(pMsg)
#define MsgUnlock(pMsg) funcUnlock(pMsg)
#else
-#define MsgLock(pMsg) {dbgprintf("line %d\n - ", __LINE__); funcLock(pMsg);; }
-#define MsgUnlock(pMsg) {dbgprintf("line %d - ", __LINE__); funcUnlock(pMsg); }
+#define MsgLock(pMsg) {dbgprintf("MsgLock line %d\n - ", __LINE__); funcLock(pMsg);; }
+#define MsgUnlock(pMsg) {dbgprintf("MsgUnlock line %d - ", __LINE__); funcUnlock(pMsg); }
#endif
/* the next function is a dummy to be used by the looking functions
@@ -164,32 +546,9 @@ static void MsgLockingDummy(msg_t __attribute__((unused)) *pMsg)
*/
static void MsgPrepareEnqueueLockingCase(msg_t *pThis)
{
- int iErr;
BEGINfunc
assert(pThis != NULL);
- iErr = pthread_mutexattr_init(&pThis->mutAttr);
- if(iErr != 0) {
- dbgprintf("error initializing mutex attribute in %s:%d, trying to continue\n",
- __FILE__, __LINE__);
- }
- iErr = pthread_mutexattr_settype(&pThis->mutAttr, PTHREAD_MUTEX_RECURSIVE);
- if(iErr != 0) {
- dbgprintf("ERROR setting mutex attribute to recursive in %s:%d, trying to continue "
- "but we will probably either abort or hang soon\n",
- __FILE__, __LINE__);
- /* TODO: it makes very little sense to continue here,
- * but it requires an iRet interface to gracefully shut
- * down. We should do that over time. -- rgerhards, 2008-07-14
- */
- }
- pthread_mutex_init(&pThis->mut, &pThis->mutAttr);
-
- /* we do no longer need the attribute. According to the
- * POSIX spec, we can destroy it without affecting the
- * initialized mutex (that used the attribute).
- * rgerhards, 2008-07-14
- */
- pthread_mutexattr_destroy(&pThis->mutAttr);
+ pthread_mutex_init(&pThis->mut, NULL);
pThis->bDoLock = 1;
ENDfunc
}
@@ -242,45 +601,148 @@ rsRetVal MsgEnableThreadSafety(void)
/* end locking functions */
-/* "Constructor" for a msg "object". Returns a pointer to
+/* This is common code for all Constructors. It is defined in an
+ * inline'able function so that we can save a function call in the
+ * actual constructors (otherwise, the msgConstruct would need
+ * to call msgConstructWithTime(), which would require a
+ * function call). Now, both can use this inline function. This
+ * enables us to be optimal, but still have the code just once.
* the new object or NULL if no such object could be allocated.
* An object constructed via this function should only be destroyed
- * via "msgDestruct()".
+ * via "msgDestruct()". This constructor does not query system time
+ * itself but rather uses a user-supplied value. This enables the caller
+ * to do some tricks to save processing time (done, for example, in the
+ * udp input).
+ * NOTE: this constructor does NOT call calloc(), as we have many bytes
+ * inside the structure which do not need to be cleared. bzero() will
+ * heavily thrash the cache, so we do the init manually (which also
+ * is the right thing to do with pointers, as they are not neccessarily
+ * a binary 0 on all machines [but today almost always...]).
+ * rgerhards, 2008-10-06
*/
-rsRetVal msgConstruct(msg_t **ppThis)
+static inline rsRetVal msgBaseConstruct(msg_t **ppThis)
{
DEFiRet;
msg_t *pM;
assert(ppThis != NULL);
- if((pM = calloc(1, sizeof(msg_t))) == NULL)
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ CHKmalloc(pM = malloc(sizeof(msg_t)));
+ objConstructSetObjInfo(pM); /* intialize object helper entities */
- /* initialize members that are non-zero */
+ /* initialize members in ORDER they appear in structure (think "cache line"!) */
+ pM->flowCtlType = 0;
+ pM->bDoLock = 0;
+ pM->bParseHOSTNAME = 0;
pM->iRefCount = 1;
pM->iSeverity = -1;
pM->iFacility = -1;
+ pM->offAfterPRI = 0;
+ pM->offMSG = -1;
+ pM->iProtocolVersion = 0;
+ pM->msgFlags = 0;
+ pM->iLenRawMsg = 0;
+ pM->iLenMSG = 0;
+ pM->iLenTAG = 0;
+ pM->iLenHOSTNAME = 0;
+ pM->pszRawMsg = NULL;
+ pM->pszHOSTNAME = NULL;
+ pM->pszRcvdAt3164 = NULL;
+ pM->pszRcvdAt3339 = NULL;
+ pM->pszRcvdAt_MySQL = NULL;
+ pM->pszRcvdAt_PgSQL = NULL;
+ pM->pszTIMESTAMP3164 = NULL;
+ pM->pszTIMESTAMP3339 = NULL;
+ pM->pszTIMESTAMP_MySQL = NULL;
+ pM->pszTIMESTAMP_PgSQL = NULL;
+ pM->pCSProgName = NULL;
+ pM->pCSStrucData = NULL;
+ pM->pCSAPPNAME = NULL;
+ pM->pCSPROCID = NULL;
+ pM->pCSMSGID = NULL;
+ pM->pInputName = NULL;
+ pM->pRcvFromIP = NULL;
+ pM->pRcvFrom = NULL;
+ pM->pRuleset = NULL;
+ memset(&pM->tRcvdAt, 0, sizeof(pM->tRcvdAt));
+ memset(&pM->tTIMESTAMP, 0, sizeof(pM->tTIMESTAMP));
+ pM->TAG.pszTAG = NULL;
+ pM->pszTimestamp3164[0] = '\0';
+ pM->pszTimestamp3339[0] = '\0';
+ pM->pszTIMESTAMP_SecFrac[0] = '\0';
+ pM->pszRcvdAt_SecFrac[0] = '\0';
+
+ /* DEV debugging only! dbgprintf("msgConstruct\t0x%x, ref 1\n", (int)pM);*/
+
+ *ppThis = pM;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* "Constructor" for a msg "object". Returns a pointer to
+ * the new object or NULL if no such object could be allocated.
+ * An object constructed via this function should only be destroyed
+ * via "msgDestruct()". This constructor does not query system time
+ * itself but rather uses a user-supplied value. This enables the caller
+ * to do some tricks to save processing time (done, for example, in the
+ * udp input).
+ * rgerhards, 2008-10-06
+ */
+rsRetVal msgConstructWithTime(msg_t **ppThis, struct syslogTime *stTime, time_t ttGenTime)
+{
+ DEFiRet;
+
+ CHKiRet(msgBaseConstruct(ppThis));
+ (*ppThis)->ttGenTime = ttGenTime;
+ memcpy(&(*ppThis)->tRcvdAt, stTime, sizeof(struct syslogTime));
+ memcpy(&(*ppThis)->tTIMESTAMP, stTime, sizeof(struct syslogTime));
+
+finalize_it:
+ RETiRet;
+}
+
+/* "Constructor" for a msg "object". Returns a pointer to
+ * the new object or NULL if no such object could be allocated.
+ * An object constructed via this function should only be destroyed
+ * via "msgDestruct()". This constructor, for historical reasons,
+ * also sets the two timestamps to the current time.
+ */
+rsRetVal msgConstruct(msg_t **ppThis)
+{
+ DEFiRet;
+
+ CHKiRet(msgBaseConstruct(ppThis));
/* we initialize both timestamps to contain the current time, so that they
* are consistent. Also, this saves us from doing any further time calls just
* to obtain a timestamp. The memcpy() should not really make a difference,
* especially as I think there is no codepath currently where it would not be
* required (after I have cleaned up the pathes ;)). -- rgerhards, 2008-10-02
*/
- datetime.getCurrTime(&(pM->tRcvdAt));
- memcpy(&pM->tTIMESTAMP, &pM->tRcvdAt, sizeof(struct syslogTime));
-
- objConstructSetObjInfo(pM);
-
- /* DEV debugging only! dbgprintf("msgConstruct\t0x%x, ref 1\n", (int)pM);*/
-
- *ppThis = pM;
+ datetime.getCurrTime(&((*ppThis)->tRcvdAt), &((*ppThis)->ttGenTime));
+ memcpy(&(*ppThis)->tTIMESTAMP, &(*ppThis)->tRcvdAt, sizeof(struct syslogTime));
finalize_it:
RETiRet;
}
+/* some free handlers for (slightly) complicated cases... All of them may be called
+ * with an empty element.
+ */
+static inline void freeTAG(msg_t *pThis)
+{
+ if(pThis->iLenTAG >= CONF_TAG_BUFSIZE)
+ free(pThis->TAG.pszTAG);
+}
+static inline void freeHOSTNAME(msg_t *pThis)
+{
+ if(pThis->iLenHOSTNAME >= CONF_HOSTNAME_BUFSIZE)
+ free(pThis->pszHOSTNAME);
+}
+
+
BEGINobjDestruct(msg) /* be sure to specify the object type also in END and CODESTART macros! */
int currRefCount;
CODESTARTobjDestruct(msg)
@@ -294,52 +756,22 @@ CODESTARTobjDestruct(msg)
if(currRefCount == 0)
{
/* DEV Debugging Only! dbgprintf("msgDestruct\t0x%lx, RefCount now 0, doing DESTROY\n", (unsigned long)pThis); */
- if(pThis->pszUxTradMsg != NULL)
- free(pThis->pszUxTradMsg);
- if(pThis->pszRawMsg != NULL)
+ if(pThis->pszRawMsg != pThis->szRawMsg)
free(pThis->pszRawMsg);
- if(pThis->pszTAG != NULL)
- free(pThis->pszTAG);
- if(pThis->pszHOSTNAME != NULL)
- free(pThis->pszHOSTNAME);
- if(pThis->pszInputName != NULL)
- free(pThis->pszInputName);
- if(pThis->pszRcvFrom != NULL)
- free(pThis->pszRcvFrom);
- if(pThis->pszRcvFromIP != NULL)
- free(pThis->pszRcvFromIP);
- if(pThis->pszMSG != NULL)
- free(pThis->pszMSG);
- if(pThis->pszFacility != NULL)
- free(pThis->pszFacility);
- if(pThis->pszFacilityStr != NULL)
- free(pThis->pszFacilityStr);
- if(pThis->pszSeverity != NULL)
- free(pThis->pszSeverity);
- if(pThis->pszSeverityStr != NULL)
- free(pThis->pszSeverityStr);
- if(pThis->pszRcvdAt3164 != NULL)
- free(pThis->pszRcvdAt3164);
- if(pThis->pszRcvdAt3339 != NULL)
- free(pThis->pszRcvdAt3339);
- if(pThis->pszRcvdAt_SecFrac != NULL)
- free(pThis->pszRcvdAt_SecFrac);
- if(pThis->pszRcvdAt_MySQL != NULL)
- free(pThis->pszRcvdAt_MySQL);
- if(pThis->pszRcvdAt_PgSQL != NULL)
- free(pThis->pszRcvdAt_PgSQL);
- if(pThis->pszTIMESTAMP3164 != NULL)
- free(pThis->pszTIMESTAMP3164);
- if(pThis->pszTIMESTAMP3339 != NULL)
- free(pThis->pszTIMESTAMP3339);
- if(pThis->pszTIMESTAMP_SecFrac != NULL)
- free(pThis->pszTIMESTAMP_SecFrac);
- if(pThis->pszTIMESTAMP_MySQL != NULL)
- free(pThis->pszTIMESTAMP_MySQL);
- if(pThis->pszTIMESTAMP_PgSQL != NULL)
- free(pThis->pszTIMESTAMP_PgSQL);
- if(pThis->pszPRI != NULL)
- free(pThis->pszPRI);
+ freeTAG(pThis);
+ freeHOSTNAME(pThis);
+ if(pThis->pInputName != NULL)
+ prop.Destruct(&pThis->pInputName);
+ if(pThis->pRcvFrom != NULL)
+ prop.Destruct(&pThis->pRcvFrom);
+ if(pThis->pRcvFromIP != NULL)
+ prop.Destruct(&pThis->pRcvFromIP);
+ free(pThis->pszRcvdAt3164);
+ free(pThis->pszRcvdAt3339);
+ free(pThis->pszRcvdAt_MySQL);
+ free(pThis->pszRcvdAt_PgSQL);
+ free(pThis->pszTIMESTAMP_MySQL);
+ free(pThis->pszTIMESTAMP_PgSQL);
if(pThis->pCSProgName != NULL)
rsCStrDestruct(&pThis->pCSProgName);
if(pThis->pCSStrucData != NULL)
@@ -354,6 +786,25 @@ CODESTARTobjDestruct(msg)
MsgUnlock(pThis);
# endif
funcDeleteMutex(pThis);
+ /* now we need to do our own optimization. Testing has shown that at least the glibc
+ * malloc() subsystem returns memory to the OS far too late in our case. So we need
+ * to help it a bit, by calling malloc_trim(), which will tell the alloc subsystem
+ * to consolidate and return to the OS. We keep 128K for our use, as a safeguard
+ * to too-frequent reallocs. But more importantly, we call this hook only every
+ * 100,000 messages (which is an approximation, as we do not work with atomic
+ * operations on the counter. --- rgerhards, 2009-06-22.
+ */
+# if HAVE_MALLOC_TRIM
+ { /* standard C requires a new block for a new variable definition!
+ * To simplify matters, we use modulo arithmetic and live with the fact
+ * that we trim too often when the counter wraps.
+ */
+ static unsigned iTrimCtr = 1;
+ if(ATOMIC_INC_AND_FETCH(iTrimCtr) % 100000 == 0) {
+ malloc_trim(128*1024);
+ }
+ }
+# endif
} else {
# ifndef HAVE_ATOMIC_BUILTINS
MsgUnlock(pThis);
@@ -402,7 +853,7 @@ msg_t* MsgDup(msg_t* pOld)
assert(pOld != NULL);
BEGINfunc
- if(msgConstruct(&pNew) != RS_RET_OK) {
+ if(msgConstructWithTime(&pNew, &pOld->tTIMESTAMP, pOld->ttGenTime) != RS_RET_OK) {
return NULL;
}
@@ -413,19 +864,51 @@ msg_t* MsgDup(msg_t* pOld)
pNew->bParseHOSTNAME = pOld->bParseHOSTNAME;
pNew->msgFlags = pOld->msgFlags;
pNew->iProtocolVersion = pOld->iProtocolVersion;
- memcpy(&pNew->tRcvdAt, &pOld->tRcvdAt, sizeof(struct syslogTime));
- memcpy(&pNew->tTIMESTAMP, &pOld->tTIMESTAMP, sizeof(struct syslogTime));
- tmpCOPYSZ(Severity);
- tmpCOPYSZ(SeverityStr);
- tmpCOPYSZ(Facility);
- tmpCOPYSZ(FacilityStr);
- tmpCOPYSZ(PRI);
- tmpCOPYSZ(RawMsg);
- tmpCOPYSZ(MSG);
- tmpCOPYSZ(UxTradMsg);
- tmpCOPYSZ(TAG);
- tmpCOPYSZ(HOSTNAME);
- tmpCOPYSZ(RcvFrom);
+ pNew->ttGenTime = pOld->ttGenTime;
+ pNew->offMSG = pOld->offMSG;
+ pNew->iLenRawMsg = pOld->iLenRawMsg;
+ pNew->iLenMSG = pOld->iLenMSG;
+ pNew->iLenTAG = pOld->iLenTAG;
+ pNew->iLenHOSTNAME = pOld->iLenHOSTNAME;
+ if(pOld->pRcvFrom != NULL) {
+ pNew->pRcvFrom = pOld->pRcvFrom;
+ prop.AddRef(pNew->pRcvFrom);
+ }
+ if(pOld->pRcvFromIP != NULL) {
+ pNew->pRcvFromIP = pOld->pRcvFromIP;
+ prop.AddRef(pNew->pRcvFromIP);
+ }
+ if(pOld->pInputName != NULL) {
+ pNew->pInputName = pOld->pInputName;
+ prop.AddRef(pNew->pInputName);
+ }
+ /* enable this, if someone actually uses UxTradMsg, delete after some time has
+ * passed and nobody complained -- rgerhards, 2009-06-16
+ pNew->offAfterPRI = pOld->offAfterPRI;
+ */
+ if(pOld->iLenTAG > 0) {
+ if(pOld->iLenTAG < CONF_TAG_BUFSIZE) {
+ memcpy(pNew->TAG.szBuf, pOld->TAG.szBuf, pOld->iLenTAG);
+ } else {
+ if((pNew->TAG.pszTAG = srUtilStrDup(pOld->TAG.pszTAG, pOld->iLenTAG)) == NULL) {
+ msgDestruct(&pNew);
+ return NULL;
+ }
+ pNew->iLenTAG = pOld->iLenTAG;
+ }
+ }
+ if(pOld->iLenRawMsg < CONF_RAWMSG_BUFSIZE) {
+ memcpy(pNew->szRawMsg, pOld->szRawMsg, pOld->iLenRawMsg + 1);
+ pNew->pszRawMsg = pNew->szRawMsg;
+ } else {
+ tmpCOPYSZ(RawMsg);
+ }
+ if(pOld->iLenHOSTNAME < CONF_HOSTNAME_BUFSIZE) {
+ memcpy(pNew->szHOSTNAME, pOld->szHOSTNAME, pOld->iLenHOSTNAME + 1);
+ pNew->pszHOSTNAME = pNew->szHOSTNAME;
+ } else {
+ tmpCOPYSZ(HOSTNAME);
+ }
tmpCOPYCSTR(ProgName);
tmpCOPYCSTR(StrucData);
@@ -458,33 +941,49 @@ msg_t* MsgDup(msg_t* pOld)
*/
static rsRetVal MsgSerialize(msg_t *pThis, strm_t *pStrm)
{
+ uchar *psz;
+ int len;
DEFiRet;
assert(pThis != NULL);
assert(pStrm != NULL);
+ /* then serialize elements */
CHKiRet(obj.BeginSerialize(pStrm, (obj_t*) pThis));
objSerializeSCALAR(pStrm, iProtocolVersion, SHORT);
objSerializeSCALAR(pStrm, iSeverity, SHORT);
objSerializeSCALAR(pStrm, iFacility, SHORT);
objSerializeSCALAR(pStrm, msgFlags, INT);
+ objSerializeSCALAR(pStrm, ttGenTime, INT);
objSerializeSCALAR(pStrm, tRcvdAt, SYSLOGTIME);
objSerializeSCALAR(pStrm, tTIMESTAMP, SYSLOGTIME);
+ /* enable this, if someone actually uses UxTradMsg, delete after some time has
+ * passed and nobody complained -- rgerhards, 2009-06-16
+ objSerializeSCALAR(pStrm, offsAfterPRI, SHORT);
+ */
+
+ CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("pszTAG"), PROPTYPE_PSZ, (void*)
+ ((pThis->iLenTAG < CONF_TAG_BUFSIZE) ? pThis->TAG.szBuf : pThis->TAG.pszTAG)));
objSerializePTR(pStrm, pszRawMsg, PSZ);
- objSerializePTR(pStrm, pszMSG, PSZ);
- objSerializePTR(pStrm, pszUxTradMsg, PSZ);
- objSerializePTR(pStrm, pszTAG, PSZ);
objSerializePTR(pStrm, pszHOSTNAME, PSZ);
- objSerializePTR(pStrm, pszInputName, PSZ);
- objSerializePTR(pStrm, pszRcvFrom, PSZ);
- objSerializePTR(pStrm, pszRcvFromIP, PSZ);
+ getInputName(pThis, &psz, &len);
+ CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("pszInputName"), PROPTYPE_PSZ, (void*) psz));
+ psz = getRcvFrom(pThis);
+ CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("pszRcvFrom"), PROPTYPE_PSZ, (void*) psz));
+ psz = getRcvFromIP(pThis);
+ CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("pszRcvFromIP"), PROPTYPE_PSZ, (void*) psz));
objSerializePTR(pStrm, pCSStrucData, CSTR);
objSerializePTR(pStrm, pCSAPPNAME, CSTR);
objSerializePTR(pStrm, pCSPROCID, CSTR);
objSerializePTR(pStrm, pCSMSGID, CSTR);
+ /* offset must be serialized after pszRawMsg, because we need that to obtain the correct
+ * MSG size.
+ */
+ objSerializeSCALAR(pStrm, offMSG, SHORT);
+
CHKiRet(obj.EndSerialize(pStrm));
finalize_it:
@@ -523,22 +1022,27 @@ msg_t *MsgAddRef(msg_t *pM)
* can obtain a PROCID. Take in mind that not every legacy syslog message
* actually has a PROCID.
* rgerhards, 2005-11-24
+ * THIS MUST be called with the message lock locked.
*/
static rsRetVal aquirePROCIDFromTAG(msg_t *pM)
{
register int i;
+ uchar *pszTag;
DEFiRet;
assert(pM != NULL);
+
if(pM->pCSPROCID != NULL)
return RS_RET_OK; /* we are already done ;) */
if(getProtocolVersion(pM) != 0)
return RS_RET_OK; /* we can only emulate if we have legacy format */
+ pszTag = (uchar*) ((pM->iLenTAG < CONF_TAG_BUFSIZE) ? pM->TAG.szBuf : pM->TAG.pszTAG);
+
/* find first '['... */
i = 0;
- while((i < pM->iLenTAG) && (pM->pszTAG[i] != '['))
+ while((i < pM->iLenTAG) && (pszTag[i] != '['))
++i;
if(!(i < pM->iLenTAG))
return RS_RET_OK; /* no [, so can not emulate... */
@@ -546,10 +1050,9 @@ static rsRetVal aquirePROCIDFromTAG(msg_t *pM)
++i; /* skip '[' */
/* now obtain the PROCID string... */
- CHKiRet(rsCStrConstruct(&pM->pCSPROCID));
- rsCStrSetAllocIncrement(pM->pCSPROCID, 16);
- while((i < pM->iLenTAG) && (pM->pszTAG[i] != ']')) {
- CHKiRet(rsCStrAppendChar(pM->pCSPROCID, pM->pszTAG[i]));
+ CHKiRet(cstrConstruct(&pM->pCSPROCID));
+ while((i < pM->iLenTAG) && (pszTag[i] != ']')) {
+ CHKiRet(cstrAppendChar(pM->pCSPROCID, pszTag[i]));
++i;
}
@@ -559,12 +1062,12 @@ static rsRetVal aquirePROCIDFromTAG(msg_t *pM)
* the buffer and simply return. Note that this is NOT an error
* case!
*/
- rsCStrDestruct(&pM->pCSPROCID);
+ cstrDestruct(&pM->pCSPROCID);
FINALIZE;
}
/* OK, finaally we could obtain a PROCID. So let's use it ;) */
- CHKiRet(rsCStrFinish(pM->pCSPROCID));
+ CHKiRet(cstrFinalize(pM->pCSPROCID));
finalize_it:
RETiRet;
@@ -584,57 +1087,35 @@ finalize_it:
* The program name is not parsed by default, because it is infrequently-used.
* If it is needed, this function should be called first. It checks if it is
* already set and extracts it, if not.
- * A message object must be provided, else a crash will occur.
+ *
+ * IMPORTANT: A locked message object must be provided, else a crash will occur.
* rgerhards, 2005-10-19
*/
static rsRetVal aquireProgramName(msg_t *pM)
{
- DEFiRet;
register int i;
+ uchar *pszTag;
+ DEFiRet;
assert(pM != NULL);
if(pM->pCSProgName == NULL) {
- /* ok, we do not yet have it. So let's parse the TAG
- * to obtain it.
- */
- CHKiRet(rsCStrConstruct(&pM->pCSProgName));
- rsCStrSetAllocIncrement(pM->pCSProgName, 33);
+ /* ok, we do not yet have it. So let's parse the TAG to obtain it. */
+ pszTag = (uchar*) ((pM->iLenTAG < CONF_TAG_BUFSIZE) ? pM->TAG.szBuf : pM->TAG.pszTAG);
+ CHKiRet(cstrConstruct(&pM->pCSProgName));
for( i = 0
- ; (i < pM->iLenTAG) && isprint((int) pM->pszTAG[i])
- && (pM->pszTAG[i] != '\0') && (pM->pszTAG[i] != ':')
- && (pM->pszTAG[i] != '[') && (pM->pszTAG[i] != '/')
+ ; (i < pM->iLenTAG) && isprint((int) pszTag[i])
+ && (pszTag[i] != '\0') && (pszTag[i] != ':')
+ && (pszTag[i] != '[') && (pszTag[i] != '/')
; ++i) {
- CHKiRet(rsCStrAppendChar(pM->pCSProgName, pM->pszTAG[i]));
+ CHKiRet(cstrAppendChar(pM->pCSProgName, pszTag[i]));
}
- CHKiRet(rsCStrFinish(pM->pCSProgName));
+ CHKiRet(cstrFinalize(pM->pCSProgName));
}
finalize_it:
RETiRet;
}
-/* This function moves the HOSTNAME inside the message object to the
- * TAG. It is a specialised function used to handle the condition when
- * a message without HOSTNAME is being processed. The missing HOSTNAME
- * is only detected at a later stage, during TAG processing, so that
- * we already had set the HOSTNAME property and now need to move it to
- * the TAG. Of course, we could do this via a couple of get/set methods,
- * but it is far more efficient to do it via this specialised method.
- * This is especially important as this can be a very common case, e.g.
- * when BSD syslog is acting as a sender.
- * rgerhards, 2005-11-10.
- */
-void moveHOSTNAMEtoTAG(msg_t *pM)
-{
- assert(pM != NULL);
- if(pM->pszTAG != NULL)
- free(pM->pszTAG);
- pM->pszTAG = pM->pszHOSTNAME;
- pM->iLenTAG = pM->iLenHOSTNAME;
- pM->pszHOSTNAME = NULL;
- pM->iLenHOSTNAME = 0;
-}
-
/* Access methods - dumb & easy, not a comment for each ;)
*/
void setProtocolVersion(msg_t *pM, int iNewVersion)
@@ -647,12 +1128,6 @@ void setProtocolVersion(msg_t *pM, int iNewVersion)
pM->iProtocolVersion = iNewVersion;
}
-int getProtocolVersion(msg_t *pM)
-{
- assert(pM != NULL);
- return(pM->iProtocolVersion);
-}
-
/* note: string is taken from constant pool, do NOT free */
char *getProtocolVersionString(msg_t *pM)
{
@@ -660,13 +1135,8 @@ char *getProtocolVersionString(msg_t *pM)
return(pM->iProtocolVersion ? "1" : "0");
}
-int getMSGLen(msg_t *pM)
-{
- return((pM == NULL) ? 0 : pM->iLenMSG);
-}
-
-char *getRawMsg(msg_t *pM)
+static char *getRawMsg(msg_t *pM)
{
if(pM == NULL)
return "";
@@ -677,78 +1147,78 @@ char *getRawMsg(msg_t *pM)
return (char*)pM->pszRawMsg;
}
+
+/* enable this, if someone actually uses UxTradMsg, delete after some time has
+ * passed and nobody complained -- rgerhards, 2009-06-16
char *getUxTradMsg(msg_t *pM)
{
if(pM == NULL)
return "";
else
- if(pM->pszUxTradMsg == NULL)
- return "";
- else
- return (char*)pM->pszUxTradMsg;
+ return (char*)pM->pszRawMsg + pM->offAfterPRI;
}
+*/
-char *getMSG(msg_t *pM)
+
+int getMSGLen(msg_t *pM)
{
- if(pM == NULL)
- return "";
- else
- if(pM->pszMSG == NULL)
- return "";
- else
- return (char*)pM->pszMSG;
+ return((pM == NULL) ? 0 : pM->iLenMSG);
}
-
-/* Get PRI value in text form */
-char *getPRI(msg_t *pM)
+uchar *getMSG(msg_t *pM)
{
- int pri;
-
+ uchar *ret;
if(pM == NULL)
- return "";
-
- MsgLock(pM);
- if(pM->pszPRI == NULL) {
- /* OK, we need to construct it... we use a 5 byte buffer - as of
- * RFC 3164, it can't be longer. Should it still be, snprintf will truncate...
- * Note that we do not use the LOG_MAKEPRI macro. This macro
- * is a simple add of the two values under FreeBSD 7. So we implement
- * the logic in our own code. This is a change from a bug
- * report. -- rgerhards, 2008-07-14
- */
- pri = pM->iFacility * 8 + pM->iSeverity;
- if((pM->pszPRI = malloc(5)) == NULL) return "";
- pM->iLenPRI = snprintf((char*)pM->pszPRI, 5, "%d", pri);
+ ret = UCHAR_CONSTANT("");
+ else {
+ if(pM->iLenMSG == 0)
+ ret = UCHAR_CONSTANT("");
+ else
+ ret = pM->pszRawMsg + pM->offMSG;
}
- MsgUnlock(pM);
-
- return (char*)pM->pszPRI;
+ return ret;
}
/* Get PRI value as integer */
-int getPRIi(msg_t *pM)
+static int getPRIi(msg_t *pM)
{
- assert(pM != NULL);
return (pM->iFacility << 3) + (pM->iSeverity);
}
-char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt)
+/* Get PRI value in text form
+ */
+static inline char *getPRI(msg_t *pM)
+{
+ /* PRI is a number in the range 0..191. Thus, we use a simple lookup table to obtain the
+ * string value. It looks a bit clumpsy here in code ;)
+ */
+ int iPRI;
+
+ if(pM == NULL)
+ return "";
+
+ iPRI = getPRIi(pM);
+ return (iPRI > 191) ? "invld" : (char*)syslog_pri_names[iPRI].pszName;
+}
+
+
+static inline char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt)
{
+ BEGINfunc
if(pM == NULL)
return "";
switch(eFmt) {
case tplFmtDefault:
+ case tplFmtRFC3164Date:
+ case tplFmtRFC3164BuggyDate:
MsgLock(pM);
if(pM->pszTIMESTAMP3164 == NULL) {
- if((pM->pszTIMESTAMP3164 = malloc(16)) == NULL) {
- MsgUnlock(pM);
- return "";
- }
- datetime.formatTimestamp3164(&pM->tTIMESTAMP, pM->pszTIMESTAMP3164, 16);
+ pM->pszTIMESTAMP3164 = pM->pszTimestamp3164;
+ datetime.formatTimestamp3164(&pM->tTIMESTAMP, pM->pszTIMESTAMP3164,
+ (eFmt == tplFmtRFC3164BuggyDate));
}
MsgUnlock(pM);
return(pM->pszTIMESTAMP3164);
@@ -759,7 +1229,7 @@ char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt)
MsgUnlock(pM);
return "";
}
- datetime.formatTimestampToMySQL(&pM->tTIMESTAMP, pM->pszTIMESTAMP_MySQL, 15);
+ datetime.formatTimestampToMySQL(&pM->tTIMESTAMP, pM->pszTIMESTAMP_MySQL);
}
MsgUnlock(pM);
return(pM->pszTIMESTAMP_MySQL);
@@ -770,49 +1240,36 @@ char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt)
MsgUnlock(pM);
return "";
}
- datetime.formatTimestampToPgSQL(&pM->tTIMESTAMP, pM->pszTIMESTAMP_PgSQL, 21);
+ datetime.formatTimestampToPgSQL(&pM->tTIMESTAMP, pM->pszTIMESTAMP_PgSQL);
}
MsgUnlock(pM);
return(pM->pszTIMESTAMP_PgSQL);
- case tplFmtRFC3164Date:
- MsgLock(pM);
- if(pM->pszTIMESTAMP3164 == NULL) {
- if((pM->pszTIMESTAMP3164 = malloc(16)) == NULL) {
- MsgUnlock(pM);
- return "";
- }
- datetime.formatTimestamp3164(&pM->tTIMESTAMP, pM->pszTIMESTAMP3164, 16);
- }
- MsgUnlock(pM);
- return(pM->pszTIMESTAMP3164);
case tplFmtRFC3339Date:
MsgLock(pM);
if(pM->pszTIMESTAMP3339 == NULL) {
- if((pM->pszTIMESTAMP3339 = malloc(33)) == NULL) {
- MsgUnlock(pM);
- return ""; /* TODO: check this: can it cause a free() of constant memory?) */
- }
- datetime.formatTimestamp3339(&pM->tTIMESTAMP, pM->pszTIMESTAMP3339, 33);
+ pM->pszTIMESTAMP3339 = pM->pszTimestamp3339;
+ datetime.formatTimestamp3339(&pM->tTIMESTAMP, pM->pszTIMESTAMP3339);
}
MsgUnlock(pM);
return(pM->pszTIMESTAMP3339);
case tplFmtSecFrac:
- MsgLock(pM);
- if(pM->pszTIMESTAMP_SecFrac == NULL) {
- if((pM->pszTIMESTAMP_SecFrac = malloc(10)) == NULL) {
- MsgUnlock(pM);
- return ""; /* TODO: check this: can it cause a free() of constant memory?) */
+ if(pM->pszTIMESTAMP_SecFrac[0] == '\0') {
+ MsgLock(pM);
+ /* re-check, may have changed while we did not hold lock */
+ if(pM->pszTIMESTAMP_SecFrac[0] == '\0') {
+ datetime.formatTimestampSecFrac(&pM->tTIMESTAMP, pM->pszTIMESTAMP_SecFrac);
}
- datetime.formatTimestampSecFrac(&pM->tTIMESTAMP, pM->pszTIMESTAMP_SecFrac, 10);
+ MsgUnlock(pM);
}
- MsgUnlock(pM);
return(pM->pszTIMESTAMP_SecFrac);
}
+ ENDfunc
return "INVALID eFmt OPTION!";
}
-char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt)
+static inline char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt)
{
+ BEGINfunc
if(pM == NULL)
return "";
@@ -824,7 +1281,7 @@ char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt)
MsgUnlock(pM);
return "";
}
- datetime.formatTimestamp3164(&pM->tRcvdAt, pM->pszRcvdAt3164, 16);
+ datetime.formatTimestamp3164(&pM->tRcvdAt, pM->pszRcvdAt3164, 0);
}
MsgUnlock(pM);
return(pM->pszRcvdAt3164);
@@ -835,7 +1292,7 @@ char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt)
MsgUnlock(pM);
return "";
}
- datetime.formatTimestampToMySQL(&pM->tRcvdAt, pM->pszRcvdAt_MySQL, 15);
+ datetime.formatTimestampToMySQL(&pM->tRcvdAt, pM->pszRcvdAt_MySQL);
}
MsgUnlock(pM);
return(pM->pszRcvdAt_MySQL);
@@ -846,18 +1303,20 @@ char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt)
MsgUnlock(pM);
return "";
}
- datetime.formatTimestampToPgSQL(&pM->tRcvdAt, pM->pszRcvdAt_PgSQL, 21);
+ datetime.formatTimestampToPgSQL(&pM->tRcvdAt, pM->pszRcvdAt_PgSQL);
}
MsgUnlock(pM);
return(pM->pszRcvdAt_PgSQL);
case tplFmtRFC3164Date:
+ case tplFmtRFC3164BuggyDate:
MsgLock(pM);
if(pM->pszRcvdAt3164 == NULL) {
if((pM->pszRcvdAt3164 = malloc(16)) == NULL) {
MsgUnlock(pM);
return "";
}
- datetime.formatTimestamp3164(&pM->tRcvdAt, pM->pszRcvdAt3164, 16);
+ datetime.formatTimestamp3164(&pM->tRcvdAt, pM->pszRcvdAt3164,
+ (eFmt == tplFmtRFC3164BuggyDate));
}
MsgUnlock(pM);
return(pM->pszRcvdAt3164);
@@ -868,123 +1327,89 @@ char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt)
MsgUnlock(pM);
return "";
}
- datetime.formatTimestamp3339(&pM->tRcvdAt, pM->pszRcvdAt3339, 33);
+ datetime.formatTimestamp3339(&pM->tRcvdAt, pM->pszRcvdAt3339);
}
MsgUnlock(pM);
return(pM->pszRcvdAt3339);
case tplFmtSecFrac:
- MsgLock(pM);
- if(pM->pszRcvdAt_SecFrac == NULL) {
- if((pM->pszRcvdAt_SecFrac = malloc(10)) == NULL) {
- MsgUnlock(pM);
- return ""; /* TODO: check this: can it cause a free() of constant memory?) */
+ if(pM->pszRcvdAt_SecFrac[0] == '\0') {
+ MsgLock(pM);
+ /* re-check, may have changed while we did not hold lock */
+ if(pM->pszRcvdAt_SecFrac[0] == '\0') {
+ datetime.formatTimestampSecFrac(&pM->tRcvdAt, pM->pszRcvdAt_SecFrac);
}
- datetime.formatTimestampSecFrac(&pM->tRcvdAt, pM->pszRcvdAt_SecFrac, 10);
+ MsgUnlock(pM);
}
- MsgUnlock(pM);
return(pM->pszRcvdAt_SecFrac);
}
+ ENDfunc
return "INVALID eFmt OPTION!";
}
-char *getSeverity(msg_t *pM)
+static inline char *getSeverity(msg_t *pM)
{
+ char *name = NULL;
+
if(pM == NULL)
return "";
- MsgLock(pM);
- if(pM->pszSeverity == NULL) {
- /* we use a 2 byte buffer - can only be one digit */
- if((pM->pszSeverity = malloc(2)) == NULL) { MsgUnlock(pM) ; return ""; }
- pM->iLenSeverity =
- snprintf((char*)pM->pszSeverity, 2, "%d", pM->iSeverity);
+ if(pM->iSeverity < 0 || pM->iSeverity > 7) {
+ name = "invld";
+ } else {
+ name = syslog_number_names[pM->iSeverity];
}
- MsgUnlock(pM);
- return((char*)pM->pszSeverity);
+
+ return name;
}
-char *getSeverityStr(msg_t *pM)
+static inline char *getSeverityStr(msg_t *pM)
{
- syslogCODE *c;
- int val;
char *name = NULL;
if(pM == NULL)
return "";
- MsgLock(pM);
- if(pM->pszSeverityStr == NULL) {
- for(c = rs_prioritynames, val = pM->iSeverity; c->c_name; c++)
- if(c->c_val == val) {
- name = c->c_name;
- break;
- }
- if(name == NULL) {
- /* we use a 2 byte buffer - can only be one digit */
- if((pM->pszSeverityStr = malloc(2)) == NULL) { MsgUnlock(pM) ; return ""; }
- pM->iLenSeverityStr =
- snprintf((char*)pM->pszSeverityStr, 2, "%d", pM->iSeverity);
- } else {
- if((pM->pszSeverityStr = (uchar*) strdup(name)) == NULL) { MsgUnlock(pM) ; return ""; }
- pM->iLenSeverityStr = strlen((char*)name);
- }
+ if(pM->iSeverity < 0 || pM->iSeverity > 7) {
+ name = "invld";
+ } else {
+ name = syslog_severity_names[pM->iSeverity];
}
- MsgUnlock(pM);
- return((char*)pM->pszSeverityStr);
+
+ return name;
}
-char *getFacility(msg_t *pM)
+static inline char *getFacility(msg_t *pM)
{
+ char *name = NULL;
+
if(pM == NULL)
return "";
- MsgLock(pM);
- if(pM->pszFacility == NULL) {
- /* we use a 12 byte buffer - as of
- * syslog-protocol, facility can go
- * up to 2^32 -1
- */
- if((pM->pszFacility = malloc(12)) == NULL) { MsgUnlock(pM) ; return ""; }
- pM->iLenFacility =
- snprintf((char*)pM->pszFacility, 12, "%d", pM->iFacility);
+ if(pM->iFacility < 0 || pM->iFacility > 23) {
+ name = "invld";
+ } else {
+ name = syslog_number_names[pM->iFacility];
}
- MsgUnlock(pM);
- return((char*)pM->pszFacility);
+
+ return name;
}
-char *getFacilityStr(msg_t *pM)
+static inline char *getFacilityStr(msg_t *pM)
{
- syslogCODE *c;
- int val;
char *name = NULL;
if(pM == NULL)
return "";
- MsgLock(pM);
- if(pM->pszFacilityStr == NULL) {
- for(c = rs_facilitynames, val = pM->iFacility << 3; c->c_name; c++)
- if(c->c_val == val) {
- name = c->c_name;
- break;
- }
- if(name == NULL) {
- /* we use a 12 byte buffer - as of
- * syslog-protocol, facility can go
- * up to 2^32 -1
- */
- if((pM->pszFacilityStr = malloc(12)) == NULL) { MsgUnlock(pM) ; return ""; }
- pM->iLenFacilityStr =
- snprintf((char*)pM->pszFacilityStr, 12, "%d", val >> 3);
- } else {
- if((pM->pszFacilityStr = (uchar*)strdup(name)) == NULL) { MsgUnlock(pM) ; return ""; }
- pM->iLenFacilityStr = strlen((char*)name);
- }
- }
- MsgUnlock(pM);
- return((char*)pM->pszFacilityStr);
+ if(pM->iFacility < 0 || pM->iFacility > 23) {
+ name = "invld";
+ } else {
+ name = syslog_fac_names[pM->iFacility];
+ }
+
+ return name;
}
@@ -1006,9 +1431,23 @@ MsgSetFlowControlType(msg_t *pMsg, flowControl_t eFlowCtl)
RETiRet;
}
+/* set offset after which PRI in raw msg starts
+ * rgerhards, 2009-06-16
+ */
+rsRetVal
+MsgSetAfterPRIOffs(msg_t *pMsg, short offs)
+{
+ assert(pMsg != NULL);
+ pMsg->offAfterPRI = offs;
+ return RS_RET_OK;
+}
+
/* rgerhards 2004-11-24: set APP-NAME in msg object
- * TODO: revisit msg locking code!
+ * This is not locked, because it either is called during message
+ * construction (where we need no locking) or later as part of a function
+ * which already obtained the lock. So in general, this function here must
+ * only be called when it it safe to do so without it aquiring a lock.
*/
rsRetVal MsgSetAPPNAME(msg_t *pMsg, char* pszAPPNAME)
{
@@ -1017,7 +1456,6 @@ rsRetVal MsgSetAPPNAME(msg_t *pMsg, char* pszAPPNAME)
if(pMsg->pCSAPPNAME == NULL) {
/* we need to obtain the object first */
CHKiRet(rsCStrConstruct(&pMsg->pCSAPPNAME));
- rsCStrSetAllocIncrement(pMsg->pCSAPPNAME, 128);
}
/* if we reach this point, we have the object */
iRet = rsCStrSetSzStr(pMsg->pCSAPPNAME, (uchar*) pszAPPNAME);
@@ -1027,20 +1465,6 @@ finalize_it:
}
-static void tryEmulateAPPNAME(msg_t *pM); /* forward reference */
-/* rgerhards, 2005-11-24
- */
-char *getAPPNAME(msg_t *pM)
-{
- assert(pM != NULL);
- MsgLock(pM);
- if(pM->pCSAPPNAME == NULL)
- tryEmulateAPPNAME(pM);
- MsgUnlock(pM);
- return (pM->pCSAPPNAME == NULL) ? "" : (char*) rsCStrGetSzStrNoNULL(pM->pCSAPPNAME);
-}
-
-
/* rgerhards 2004-11-24: set PROCID in msg object
*/
rsRetVal MsgSetPROCID(msg_t *pMsg, char* pszPROCID)
@@ -1049,42 +1473,54 @@ rsRetVal MsgSetPROCID(msg_t *pMsg, char* pszPROCID)
ISOBJ_TYPE_assert(pMsg, msg);
if(pMsg->pCSPROCID == NULL) {
/* we need to obtain the object first */
- CHKiRet(rsCStrConstruct(&pMsg->pCSPROCID));
- rsCStrSetAllocIncrement(pMsg->pCSPROCID, 128);
+ CHKiRet(cstrConstruct(&pMsg->pCSPROCID));
}
/* if we reach this point, we have the object */
- iRet = rsCStrSetSzStr(pMsg->pCSPROCID, (uchar*) pszPROCID);
+ CHKiRet(rsCStrSetSzStr(pMsg->pCSPROCID, (uchar*) pszPROCID));
+ CHKiRet(cstrFinalize(pMsg->pCSPROCID));
finalize_it:
RETiRet;
}
+
+/* check if we have a procid, and, if not, try to aquire/emulate it.
+ * This must be called WITHOUT the message lock being held.
+ * rgerhards, 2009-06-26
+ */
+static inline void preparePROCID(msg_t *pM, bool bLockMutex)
+{
+ if(pM->pCSPROCID == NULL) {
+ if(bLockMutex == LOCK_MUTEX)
+ MsgLock(pM);
+ /* re-query, things may have changed in the mean time... */
+ if(pM->pCSPROCID == NULL)
+ aquirePROCIDFromTAG(pM);
+ if(bLockMutex == LOCK_MUTEX)
+ MsgUnlock(pM);
+ }
+}
+
+
+#if 0
/* rgerhards, 2005-11-24
*/
-int getPROCIDLen(msg_t *pM)
+static inline int getPROCIDLen(msg_t *pM, bool bLockMutex)
{
assert(pM != NULL);
- MsgLock(pM);
- if(pM->pCSPROCID == NULL)
- aquirePROCIDFromTAG(pM);
- MsgUnlock(pM);
+ preparePROCID(pM, bLockMutex);
return (pM->pCSPROCID == NULL) ? 1 : rsCStrLen(pM->pCSPROCID);
}
+#endif
/* rgerhards, 2005-11-24
*/
-char *getPROCID(msg_t *pM)
+char *getPROCID(msg_t *pM, bool bLockMutex)
{
- char* pszRet;
-
ISOBJ_TYPE_assert(pM, msg);
- MsgLock(pM);
- if(pM->pCSPROCID == NULL)
- aquirePROCIDFromTAG(pM);
- pszRet = (pM->pCSPROCID == NULL) ? "-" : (char*) rsCStrGetSzStrNoNULL(pM->pCSPROCID);
- MsgUnlock(pM);
- return pszRet;
+ preparePROCID(pM, bLockMutex);
+ return (pM->pCSPROCID == NULL) ? "-" : (char*) cstrGetSzStrNoNULL(pM->pCSPROCID);
}
@@ -1097,7 +1533,6 @@ rsRetVal MsgSetMSGID(msg_t *pMsg, char* pszMSGID)
if(pMsg->pCSMSGID == NULL) {
/* we need to obtain the object first */
CHKiRet(rsCStrConstruct(&pMsg->pCSMSGID));
- rsCStrSetAllocIncrement(pMsg->pCSMSGID, 128);
}
/* if we reach this point, we have the object */
iRet = rsCStrSetSzStr(pMsg->pCSMSGID, (uchar*) pszMSGID);
@@ -1106,49 +1541,50 @@ finalize_it:
RETiRet;
}
-/* rgerhards, 2005-11-24
- */
-#if 0 /* This method is currently not called, be we like to preserve it */
-static int getMSGIDLen(msg_t *pM)
-{
- return (pM->pCSMSGID == NULL) ? 1 : rsCStrLen(pM->pCSMSGID);
-}
-#endif
-
/* rgerhards, 2005-11-24
*/
-char *getMSGID(msg_t *pM)
+static inline char *getMSGID(msg_t *pM)
{
return (pM->pCSMSGID == NULL) ? "-" : (char*) rsCStrGetSzStrNoNULL(pM->pCSMSGID);
}
-/* Set the TAG to a caller-provided string. This is thought
- * to be a heap buffer that the caller will no longer use. This
- * function is a performance optimization over MsgSetTAG().
- * rgerhards 2004-11-19
+/* rgerhards 2009-06-12: set associated ruleset
*/
-void MsgAssignTAG(msg_t *pMsg, uchar *pBuf)
+void MsgSetRuleset(msg_t *pMsg, ruleset_t *pRuleset)
{
assert(pMsg != NULL);
- pMsg->iLenTAG = (pBuf == NULL) ? 0 : strlen((char*)pBuf);
- pMsg->pszTAG = (uchar*) pBuf;
+ pMsg->pRuleset = pRuleset;
}
-/* rgerhards 2004-11-16: set TAG in msg object
+/* set TAG in msg object
+ * (rewritten 2009-06-18 rgerhards)
*/
-void MsgSetTAG(msg_t *pMsg, char* pszTAG)
+void MsgSetTAG(msg_t *pMsg, uchar* pszBuf, size_t lenBuf)
{
+ uchar *pBuf;
assert(pMsg != NULL);
- if(pMsg->pszTAG != NULL)
- free(pMsg->pszTAG);
- pMsg->iLenTAG = strlen(pszTAG);
- if((pMsg->pszTAG = malloc(pMsg->iLenTAG + 1)) != NULL)
- memcpy(pMsg->pszTAG, pszTAG, pMsg->iLenTAG + 1);
- else
- dbgprintf("Could not allocate memory in MsgSetTAG()\n");
+
+ freeTAG(pMsg);
+
+ pMsg->iLenTAG = lenBuf;
+ if(pMsg->iLenTAG < CONF_TAG_BUFSIZE) {
+ /* small enough: use fixed buffer (faster!) */
+ pBuf = pMsg->TAG.szBuf;
+ } else {
+ if((pBuf = (uchar*) malloc(pMsg->iLenTAG + 1)) == NULL) {
+ /* truncate message, better than completely loosing it... */
+ pBuf = pMsg->TAG.szBuf;
+ pMsg->iLenTAG = CONF_TAG_BUFSIZE - 1;
+ } else {
+ pMsg->TAG.pszTAG = pBuf;
+ }
+ }
+
+ memcpy(pBuf, pszBuf, pMsg->iLenTAG);
+ pBuf[pMsg->iLenTAG] = '\0'; /* this also works with truncation! */
}
@@ -1159,63 +1595,51 @@ void MsgSetTAG(msg_t *pMsg, char* pszTAG)
* if there is a TAG and, if not, if it can emulate it.
* rgerhards, 2005-11-24
*/
-static void tryEmulateTAG(msg_t *pM)
+static inline void tryEmulateTAG(msg_t *pM, bool bLockMutex)
{
- int iTAGLen;
- uchar *pBuf;
+ size_t lenTAG;
+ uchar bufTAG[CONF_TAG_MAXSIZE];
assert(pM != NULL);
- if(pM->pszTAG != NULL)
+ if(bLockMutex == LOCK_MUTEX)
+ MsgLock(pM);
+ if(pM->iLenTAG > 0)
return; /* done, no need to emulate */
if(getProtocolVersion(pM) == 1) {
- if(!strcmp(getPROCID(pM), "-")) {
+ if(!strcmp(getPROCID(pM, MUTEX_ALREADY_LOCKED), "-")) {
/* no process ID, use APP-NAME only */
- MsgSetTAG(pM, getAPPNAME(pM));
+ MsgSetTAG(pM, (uchar*) getAPPNAME(pM, MUTEX_ALREADY_LOCKED), getAPPNAMELen(pM, MUTEX_ALREADY_LOCKED));
} else {
/* now we can try to emulate */
- iTAGLen = getAPPNAMELen(pM) + getPROCIDLen(pM) + 3;
- if((pBuf = malloc(iTAGLen * sizeof(char))) == NULL)
- return; /* nothing we can do */
- snprintf((char*)pBuf, iTAGLen, "%s[%s]", getAPPNAME(pM), getPROCID(pM));
- MsgAssignTAG(pM, pBuf);
+ lenTAG = snprintf((char*)bufTAG, CONF_TAG_MAXSIZE, "%s[%s]",
+ getAPPNAME(pM, MUTEX_ALREADY_LOCKED), getPROCID(pM, MUTEX_ALREADY_LOCKED));
+ bufTAG[32] = '\0'; /* just to make sure... */
+ MsgSetTAG(pM, bufTAG, lenTAG);
}
}
+ if(bLockMutex == LOCK_MUTEX)
+ MsgUnlock(pM);
}
-#if 0 /* This method is currently not called, be we like to preserve it */
-static int getTAGLen(msg_t *pM)
-{
- if(pM == NULL)
- return 0;
- else {
- tryEmulateTAG(pM);
- if(pM->pszTAG == NULL)
- return 0;
- else
- return pM->iLenTAG;
- }
-}
-#endif
-
-
-char *getTAG(msg_t *pM)
+static inline void
+getTAG(msg_t *pM, uchar **ppBuf, int *piLen)
{
- char *ret;
-
- if(pM == NULL)
- ret = "";
- else {
- MsgLock(pM);
- tryEmulateTAG(pM);
- if(pM->pszTAG == NULL)
- ret = "";
- else
- ret = (char*) pM->pszTAG;
- MsgUnlock(pM);
+ if(pM == NULL) {
+ *ppBuf = UCHAR_CONSTANT("");
+ *piLen = 0;
+ } else {
+ if(pM->iLenTAG == 0)
+ tryEmulateTAG(pM, LOCK_MUTEX);
+ if(pM->iLenTAG == 0) {
+ *ppBuf = UCHAR_CONSTANT("");
+ *piLen = 0;
+ } else {
+ *ppBuf = (pM->iLenTAG < CONF_TAG_BUFSIZE) ? pM->TAG.szBuf : pM->TAG.pszTAG;
+ *piLen = pM->iLenTAG;
+ }
}
- return(ret);
}
@@ -1225,7 +1649,10 @@ int getHOSTNAMELen(msg_t *pM)
return 0;
else
if(pM->pszHOSTNAME == NULL)
- return 0;
+ if(pM->pRcvFrom == NULL)
+ return 0;
+ else
+ return prop.GetStringLen(pM->pRcvFrom);
else
return pM->iLenHOSTNAME;
}
@@ -1236,48 +1663,39 @@ char *getHOSTNAME(msg_t *pM)
if(pM == NULL)
return "";
else
- if(pM->pszHOSTNAME == NULL)
- return "";
- else
+ if(pM->pszHOSTNAME == NULL) {
+ if(pM->pRcvFrom == NULL) {
+ return "";
+ } else {
+ uchar *psz;
+ int len;
+ prop.GetString(pM->pRcvFrom, &psz, &len);
+ return (char*) psz;
+ }
+ } else {
return (char*) pM->pszHOSTNAME;
+ }
}
-uchar *getInputName(msg_t *pM)
-{
- if(pM == NULL)
- return (uchar*) "";
- else
- if(pM->pszInputName == NULL)
- return (uchar*) "";
- else
- return pM->pszInputName;
-}
-
-
-char *getRcvFrom(msg_t *pM)
+uchar *getRcvFrom(msg_t *pM)
{
- if(pM == NULL)
- return "";
- else
- if(pM->pszRcvFrom == NULL)
- return "";
+ uchar *psz;
+ int len;
+ BEGINfunc
+ if(pM == NULL) {
+ psz = UCHAR_CONSTANT("");
+ } else {
+ if(pM->pRcvFrom == NULL)
+ psz = UCHAR_CONSTANT("");
else
- return (char*) pM->pszRcvFrom;
+ prop.GetString(pM->pRcvFrom, &psz, &len);
+ }
+ ENDfunc
+ return psz;
}
-uchar *getRcvFromIP(msg_t *pM)
-{
- if(pM == NULL)
- return (uchar*) "";
- else
- if(pM->pszRcvFromIP == NULL)
- return (uchar*) "";
- else
- return pM->pszRcvFromIP;
-}
-
/* rgerhards 2004-11-24: set STRUCTURED DATA in msg object
*/
rsRetVal MsgSetStructuredData(msg_t *pMsg, char* pszStrucData)
@@ -1287,7 +1705,6 @@ rsRetVal MsgSetStructuredData(msg_t *pMsg, char* pszStrucData)
if(pMsg->pCSStrucData == NULL) {
/* we need to obtain the object first */
CHKiRet(rsCStrConstruct(&pMsg->pCSStrucData));
- rsCStrSetAllocIncrement(pMsg->pCSStrucData, 128);
}
/* if we reach this point, we have the object */
iRet = rsCStrSetSzStr(pMsg->pCSStrucData, (uchar*) pszStrucData);
@@ -1310,107 +1727,56 @@ static int getStructuredDataLen(msg_t *pM)
/* get the "STRUCTURED-DATA" as sz string
* rgerhards, 2005-11-24
*/
-char *getStructuredData(msg_t *pM)
+static inline char *getStructuredData(msg_t *pM)
{
return (pM->pCSStrucData == NULL) ? "-" : (char*) rsCStrGetSzStrNoNULL(pM->pCSStrucData);
}
-
-/* get the length of the "programname" sz string
- * rgerhards, 2005-10-19
+/* check if we have a ProgramName, and, if not, try to aquire/emulate it.
+ * rgerhards, 2009-06-26
*/
-int getProgramNameLen(msg_t *pM)
+static inline void prepareProgramName(msg_t *pM, bool bLockMutex)
{
- int iRet;
+ if(pM->pCSProgName == NULL) {
+ if(bLockMutex == LOCK_MUTEX)
+ MsgLock(pM);
- assert(pM != NULL);
- MsgLock(pM);
- if((iRet = aquireProgramName(pM)) != RS_RET_OK) {
- dbgprintf("error %d returned by aquireProgramName() in getProgramNameLen()\n", iRet);
- MsgUnlock(pM);
- return 0; /* best we can do (consistent wiht what getProgramName() returns) */
- }
- MsgUnlock(pM);
+ /* re-query as things might have changed during locking */
+ if(pM->pCSProgName == NULL)
+ aquireProgramName(pM);
- return (pM->pCSProgName == NULL) ? 0 : rsCStrLen(pM->pCSProgName);
+ if(bLockMutex == LOCK_MUTEX)
+ MsgUnlock(pM);
+ }
}
-/* get the "programname" as sz string
+/* get the length of the "programname" sz string
* rgerhards, 2005-10-19
*/
-char *getProgramName(msg_t *pM) /* this is the non-locking version for internal use */
+int getProgramNameLen(msg_t *pM, bool bLockMutex)
{
- int iRet;
- char *pszRet;
-
assert(pM != NULL);
- MsgLock(pM);
- if((iRet = aquireProgramName(pM)) != RS_RET_OK) {
- dbgprintf("error %d returned by aquireProgramName() in getProgramName()\n", iRet);
- pszRet = ""; /* best we can do */
- } else {
- pszRet = (pM->pCSProgName == NULL) ? "" : (char*) rsCStrGetSzStrNoNULL(pM->pCSProgName);
- }
-
- MsgUnlock(pM);
- return pszRet;
+ prepareProgramName(pM, bLockMutex);
+ return (pM->pCSProgName == NULL) ? 0 : rsCStrLen(pM->pCSProgName);
}
-/* The code below was an approach without PTHREAD_MUTEX_RECURSIVE
- * However, it turned out to be quite complex. So far, we use recursive
- * locking, which is OK from a performance point of view, especially as
- * we do not anticipate that multithreading msg objects is used often.
- * However, we may re-think about using non-recursive locking and I leave this
- * code in here to conserve the idea. -- rgerhards, 2008-01-05
- */
-#if 0
-static char *getProgramNameNoLock(msg_t *pM) /* this is the non-locking version for internal use */
-{
- int iRet;
- assert(pM != NULL);
- if((iRet = aquireProgramName(pM)) != RS_RET_OK) {
- dbgprintf("error %d returned by aquireProgramName() in getProgramName()\n", iRet);
- return ""; /* best we can do */
- }
- return (pM->pCSProgName == NULL) ? "" : (char*) rsCStrGetSzStrNoNULL(pM->pCSProgName);
-}
-char *getProgramName(msg_t *pM) /* this is the external callable version */
+/* get the "programname" as sz string
+ * rgerhards, 2005-10-19
+ */
+char *getProgramName(msg_t *pM, bool bLockMutex)
{
- char *pszRet;
-
- MsgLock(pM);
- pszRet = getProgramNameNoLock(pM);
- MsgUnlock(pM);
- return pszRet;
+ prepareProgramName(pM, bLockMutex);
+ return (pM->pCSProgName == NULL) ? "" : (char*) rsCStrGetSzStrNoNULL(pM->pCSProgName);
}
-/* an alternative approach has been: */
-/* The macro below is used to generate external function definitions
- * for such functions that may also be called internally (and thus have
- * both a locking and non-locking implementation. Over time, we could
- * reconsider how we handle that. -- rgerhards, 2008-01-05
- */
-#define EXT_LOCKED_FUNC(fName, ret) \
-ret fName(msg_t *pM) \
-{ \
- ret valRet; \
- MsgLock(pM); \
- valRet = fName##NoLock(pM); \
- MsgUnlock(pM); \
- return(valRet); \
-}
-EXT_LOCKED_FUNC(getProgramName, char*)
-/* in this approach, the external function is provided by the macro and
- * needs not to be writen.
- */
-#endif /* #if 0 -- saved code */
/* This function tries to emulate APPNAME if it is not present. Its
* main use is when we have received a log record via legacy syslog and
* now would like to send out the same one via syslog-protocol.
+ * MUST be called with the Msg Lock locked!
*/
static void tryEmulateAPPNAME(msg_t *pM)
{
@@ -1420,81 +1786,136 @@ static void tryEmulateAPPNAME(msg_t *pM)
if(getProtocolVersion(pM) == 0) {
/* only then it makes sense to emulate */
- MsgSetAPPNAME(pM, getProgramName(pM));
+ MsgSetAPPNAME(pM, getProgramName(pM, MUTEX_ALREADY_LOCKED));
+ }
+}
+
+
+
+/* check if we have a APPNAME, and, if not, try to aquire/emulate it.
+ * This must be called WITHOUT the message lock being held.
+ * rgerhards, 2009-06-26
+ */
+static inline void prepareAPPNAME(msg_t *pM, bool bLockMutex)
+{
+ if(pM->pCSAPPNAME == NULL) {
+ if(bLockMutex == LOCK_MUTEX)
+ MsgLock(pM);
+
+ /* re-query as things might have changed during locking */
+ if(pM->pCSAPPNAME == NULL)
+ tryEmulateAPPNAME(pM);
+
+ if(bLockMutex == LOCK_MUTEX)
+ MsgUnlock(pM);
}
}
+/* rgerhards, 2005-11-24
+ */
+char *getAPPNAME(msg_t *pM, bool bLockMutex)
+{
+ assert(pM != NULL);
+ prepareAPPNAME(pM, bLockMutex);
+ return (pM->pCSAPPNAME == NULL) ? "" : (char*) rsCStrGetSzStrNoNULL(pM->pCSAPPNAME);
+}
/* rgerhards, 2005-11-24
*/
-static int getAPPNAMELen(msg_t *pM)
+static int getAPPNAMELen(msg_t *pM, bool bLockMutex)
{
assert(pM != NULL);
- if(pM->pCSAPPNAME == NULL)
- tryEmulateAPPNAME(pM);
+ prepareAPPNAME(pM, bLockMutex);
return (pM->pCSAPPNAME == NULL) ? 0 : rsCStrLen(pM->pCSAPPNAME);
}
-/* rgerhards 2008-09-10: set pszInputName in msg object
+/* rgerhards 2008-09-10: set pszInputName in msg object. This calls AddRef()
+ * on the property, because this must be done in all current cases and there
+ * is no case expected where this may not be necessary.
+ * rgerhards, 2009-06-16
*/
-void MsgSetInputName(msg_t *pMsg, char* pszInputName)
+void MsgSetInputName(msg_t *pThis, prop_t *inputName)
{
- assert(pMsg != NULL);
- if(pMsg->pszInputName != NULL)
- free(pMsg->pszInputName);
+ assert(pThis != NULL);
- pMsg->iLenInputName = strlen(pszInputName);
- if((pMsg->pszInputName = malloc(pMsg->iLenInputName + 1)) != NULL) {
- memcpy(pMsg->pszInputName, pszInputName, pMsg->iLenInputName + 1);
- }
+ prop.AddRef(inputName);
+ if(pThis->pInputName != NULL)
+ prop.Destruct(&pThis->pInputName);
+ pThis->pInputName = inputName;
}
-/* rgerhards 2004-11-16: set pszRcvFrom in msg object
+
+/* rgerhards 2008-09-10: set RcvFrom name in msg object. This calls AddRef()
+ * on the property, because this must be done in all current cases and there
+ * is no case expected where this may not be necessary.
+ * rgerhards, 2009-06-30
*/
-void MsgSetRcvFrom(msg_t *pMsg, char* pszRcvFrom)
+void MsgSetRcvFrom(msg_t *pThis, prop_t *new)
{
- assert(pMsg != NULL);
- if(pMsg->pszRcvFrom != NULL)
- free(pMsg->pszRcvFrom);
+ assert(pThis != NULL);
- pMsg->iLenRcvFrom = strlen(pszRcvFrom);
- if((pMsg->pszRcvFrom = malloc(pMsg->iLenRcvFrom + 1)) != NULL) {
- memcpy(pMsg->pszRcvFrom, pszRcvFrom, pMsg->iLenRcvFrom + 1);
- }
+ prop.AddRef(new);
+ if(pThis->pRcvFrom != NULL)
+ prop.Destruct(&pThis->pRcvFrom);
+ pThis->pRcvFrom = new;
}
-/* rgerhards 2005-05-16: set pszRcvFromIP in msg object */
-rsRetVal
-MsgSetRcvFromIP(msg_t *pMsg, uchar* pszRcvFromIP)
+/* This is used to set the property via a string. This function should not be
+ * called if there is a reliable way for a caller to make sure that the
+ * same name can be used across multiple messages. However, if it can not
+ * ensure that, calling this function is the second best thing, because it
+ * will re-use the previously created property if it contained the same
+ * name (but it works only for the immediate previous).
+ * rgerhards, 2009-06-31
+ */
+void MsgSetRcvFromStr(msg_t *pThis, uchar *psz, int len, prop_t **ppProp)
{
- DEFiRet;
- assert(pMsg != NULL);
- if(pMsg->pszRcvFromIP != NULL) {
- free(pMsg->pszRcvFromIP);
- pMsg->iLenRcvFromIP = 0;
- }
+ assert(pThis != NULL);
+ assert(ppProp != NULL);
- CHKmalloc(pMsg->pszRcvFromIP = (uchar*)strdup((char*)pszRcvFromIP));
- pMsg->iLenRcvFromIP = strlen((char*)pszRcvFromIP);
-finalize_it:
- RETiRet;
+ prop.CreateOrReuseStringProp(ppProp, psz, len);
+ MsgSetRcvFrom(pThis, *ppProp);
}
-/* Set the HOSTNAME to a caller-provided string. This is thought
- * to be a heap buffer that the caller will no longer use. This
- * function is a performance optimization over MsgSetHOSTNAME().
- * rgerhards 2004-11-19
+/* set RcvFromIP name in msg object. This calls AddRef()
+ * on the property, because this must be done in all current cases and there
+ * is no case expected where this may not be necessary.
+ * rgerhards, 2009-06-30
*/
-void MsgAssignHOSTNAME(msg_t *pMsg, char *pBuf)
+rsRetVal MsgSetRcvFromIP(msg_t *pThis, prop_t *new)
{
- assert(pMsg != NULL);
- assert(pBuf != NULL);
- if(pMsg->pszHOSTNAME != NULL)
- free(pMsg->pszHOSTNAME);
- pMsg->iLenHOSTNAME = strlen(pBuf);
- pMsg->pszHOSTNAME = (uchar*) pBuf;
+ assert(pThis != NULL);
+
+ BEGINfunc
+ prop.AddRef(new);
+ if(pThis->pRcvFromIP != NULL)
+ prop.Destruct(&pThis->pRcvFromIP);
+ pThis->pRcvFromIP = new;
+ ENDfunc
+ return RS_RET_OK;
+}
+
+
+/* This is used to set the property via a string. This function should not be
+ * called if there is a reliable way for a caller to make sure that the
+ * same name can be used across multiple messages. However, if it can not
+ * ensure that, calling this function is the second best thing, because it
+ * will re-use the previously created property if it contained the same
+ * name (but it works only for the immediate previous).
+ * rgerhards, 2009-06-31
+ */
+rsRetVal MsgSetRcvFromIPStr(msg_t *pThis, uchar *psz, int len, prop_t **ppProp)
+{
+ DEFiRet;
+ assert(pThis != NULL);
+
+ CHKiRet(prop.CreateOrReuseStringProp(ppProp, psz, len));
+ MsgSetRcvFromIP(pThis, *ppProp);
+
+finalize_it:
+ RETiRet;
}
@@ -1508,84 +1929,120 @@ void MsgAssignHOSTNAME(msg_t *pMsg, char *pBuf)
* we need it. The rest of the code already knows how to handle an
* unset HOSTNAME.
*/
-void MsgSetHOSTNAME(msg_t *pMsg, char* pszHOSTNAME)
+void MsgSetHOSTNAME(msg_t *pThis, uchar* pszHOSTNAME, int lenHOSTNAME)
{
- assert(pMsg != NULL);
- if(pMsg->pszHOSTNAME != NULL)
- free(pMsg->pszHOSTNAME);
+ assert(pThis != NULL);
- pMsg->iLenHOSTNAME = strlen(pszHOSTNAME);
- if((pMsg->pszHOSTNAME = malloc(pMsg->iLenHOSTNAME + 1)) != NULL)
- memcpy(pMsg->pszHOSTNAME, pszHOSTNAME, pMsg->iLenHOSTNAME + 1);
- else
- dbgprintf("Could not allocate memory in MsgSetHOSTNAME()\n");
+ freeHOSTNAME(pThis);
+
+ pThis->iLenHOSTNAME = lenHOSTNAME;
+ if(pThis->iLenHOSTNAME < CONF_HOSTNAME_BUFSIZE) {
+ /* small enough: use fixed buffer (faster!) */
+ pThis->pszHOSTNAME = pThis->szHOSTNAME;
+ } else if((pThis->pszHOSTNAME = (uchar*) malloc(pThis->iLenHOSTNAME + 1)) == NULL) {
+ /* truncate message, better than completely loosing it... */
+ pThis->pszHOSTNAME = pThis->szHOSTNAME;
+ pThis->iLenHOSTNAME = CONF_HOSTNAME_BUFSIZE - 1;
+ }
+
+ memcpy(pThis->pszHOSTNAME, pszHOSTNAME, pThis->iLenHOSTNAME);
+ pThis->pszHOSTNAME[pThis->iLenHOSTNAME] = '\0'; /* this also works with truncation! */
}
-/* Set the UxTradMsg to a caller-provided string. This is thought
- * to be a heap buffer that the caller will no longer use. This
- * function is a performance optimization over MsgSetUxTradMsg().
- * rgerhards 2004-11-19
+/* set the offset of the MSG part into the raw msg buffer
+ * Note that the offset may be higher than the length of the raw message
+ * (exactly by one). This can happen if we have a message that does not
+ * contain any MSG part.
*/
-#if 0 /* This method is currently not called, be we like to preserve it */
-static void MsgAssignUxTradMsg(msg_t *pMsg, char *pBuf)
+void MsgSetMSGoffs(msg_t *pMsg, short offs)
{
- assert(pMsg != NULL);
- assert(pBuf != NULL);
- pMsg->iLenUxTradMsg = strlen(pBuf);
- pMsg->pszUxTradMsg = pBuf;
+ ISOBJ_TYPE_assert(pMsg, msg);
+ pMsg->offMSG = offs;
+ if(offs > pMsg->iLenRawMsg) {
+ assert(offs - 1 == pMsg->iLenRawMsg);
+ pMsg->iLenMSG = 0;
+ } else {
+ pMsg->iLenMSG = pMsg->iLenRawMsg - offs;
+ }
}
-#endif
-/* rgerhards 2004-11-17: set the traditional Unix message in msg object
+/* replace the MSG part of a message. The update actually takes place inside
+ * rawmsg.
+ * There are two cases: either the new message will be larger than the new msg
+ * or it will be less than or equal. If it is less than or equal, we can utilize
+ * the previous message buffer. If it is larger, we can utilize the msg_t-included
+ * message buffer if it fits in there. If this is not the case, we need to alloc
+ * a new, larger, chunk and copy over the data to it. Note that this function is
+ * (hopefully) relatively seldom being called, so some performance impact is
+ * uncritical. In any case, pszMSG is copied, so if it was dynamically allocated,
+ * the caller is responsible for freeing it.
+ * rgerhards, 2009-06-23
*/
-int MsgSetUxTradMsg(msg_t *pMsg, char* pszUxTradMsg)
+rsRetVal MsgReplaceMSG(msg_t *pThis, uchar* pszMSG, int lenMSG)
{
- assert(pMsg != NULL);
- assert(pszUxTradMsg != NULL);
- pMsg->iLenUxTradMsg = strlen(pszUxTradMsg);
- if(pMsg->pszUxTradMsg != NULL)
- free(pMsg->pszUxTradMsg);
- if((pMsg->pszUxTradMsg = malloc(pMsg->iLenUxTradMsg + 1)) != NULL)
- memcpy(pMsg->pszUxTradMsg, pszUxTradMsg, pMsg->iLenUxTradMsg + 1);
- else
- dbgprintf("Could not allocate memory for pszUxTradMsg buffer.");
+ int lenNew;
+ uchar *bufNew;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, msg);
+ assert(pszMSG != NULL);
- return(0);
+ lenNew = pThis->iLenRawMsg + lenMSG - pThis->iLenMSG;
+ if(lenMSG > pThis->iLenMSG && lenNew >= CONF_RAWMSG_BUFSIZE) {
+ /* we have lost our "bet" and need to alloc a new buffer ;) */
+ CHKmalloc(bufNew = malloc(lenNew + 1));
+ memcpy(bufNew, pThis->pszRawMsg, pThis->offMSG);
+ if(pThis->pszRawMsg != pThis->szRawMsg)
+ free(pThis->pszRawMsg);
+ pThis->pszRawMsg = bufNew;
+ }
+
+ if(lenMSG > 0)
+ memcpy(pThis->pszRawMsg + pThis->offMSG, pszMSG, lenMSG);
+ pThis->pszRawMsg[lenNew] = '\0'; /* this also works with truncation! */
+ pThis->iLenRawMsg = lenNew;
+ pThis->iLenMSG = lenMSG;
+
+finalize_it:
+ RETiRet;
}
-/* rgerhards 2004-11-09: set MSG in msg object
+/* set raw message in message object. Size of message is provided.
+ * The function makes sure that the stored rawmsg is properly
+ * terminated by '\0'.
+ * rgerhards, 2009-06-16
*/
-void MsgSetMSG(msg_t *pMsg, char* pszMSG)
+void MsgSetRawMsg(msg_t *pThis, char* pszRawMsg, size_t lenMsg)
{
- assert(pMsg != NULL);
- assert(pszMSG != NULL);
-
- if(pMsg->pszMSG != NULL)
- free(pMsg->pszMSG);
+ assert(pThis != NULL);
+ if(pThis->pszRawMsg != pThis->szRawMsg)
+ free(pThis->pszRawMsg);
+
+ pThis->iLenRawMsg = lenMsg;
+ if(pThis->iLenRawMsg < CONF_RAWMSG_BUFSIZE) {
+ /* small enough: use fixed buffer (faster!) */
+ pThis->pszRawMsg = pThis->szRawMsg;
+ } else if((pThis->pszRawMsg = (uchar*) malloc(pThis->iLenRawMsg + 1)) == NULL) {
+ /* truncate message, better than completely loosing it... */
+ pThis->pszRawMsg = pThis->szRawMsg;
+ pThis->iLenRawMsg = CONF_RAWMSG_BUFSIZE - 1;
+ }
- pMsg->iLenMSG = strlen(pszMSG);
- if((pMsg->pszMSG = (uchar*) malloc(pMsg->iLenMSG + 1)) != NULL)
- memcpy(pMsg->pszMSG, pszMSG, pMsg->iLenMSG + 1);
- else
- dbgprintf("MsgSetMSG could not allocate memory for pszMSG buffer.");
+ memcpy(pThis->pszRawMsg, pszRawMsg, pThis->iLenRawMsg);
+ pThis->pszRawMsg[pThis->iLenRawMsg] = '\0'; /* this also works with truncation! */
}
-/* rgerhards 2004-11-11: set RawMsg in msg object
+
+/* set raw message in message object. Size of message is not provided. This
+ * function should only be used when it is unavoidable (and over time we should
+ * try to remove it altogether).
+ * rgerhards, 2009-06-16
*/
-void MsgSetRawMsg(msg_t *pMsg, char* pszRawMsg)
+void MsgSetRawMsgWOSize(msg_t *pMsg, char* pszRawMsg)
{
- assert(pMsg != NULL);
- if(pMsg->pszRawMsg != NULL)
- free(pMsg->pszRawMsg);
-
- pMsg->iLenRawMsg = strlen(pszRawMsg);
- if((pMsg->pszRawMsg = (uchar*) malloc(pMsg->iLenRawMsg + 1)) != NULL)
- memcpy(pMsg->pszRawMsg, pszRawMsg, pMsg->iLenRawMsg + 1);
- else
- dbgprintf("Could not allocate memory for pszRawMsg buffer.");
+ MsgSetRawMsg(pMsg, pszRawMsg, strlen(pszRawMsg));
}
@@ -1599,15 +2056,11 @@ void MsgSetRawMsg(msg_t *pMsg, char* pszRawMsg)
*/
char *textpri(char *pRes, size_t pResLen, int pri)
{
- syslogCODE *c_pri, *c_fac;
-
assert(pRes != NULL);
assert(pResLen > 0);
- for (c_fac = rs_facilitynames; c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri)<<3); c_fac++);
- for (c_pri = rs_prioritynames; c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
-
- snprintf (pRes, pResLen, "%s.%s<%d>", c_fac->c_name, c_pri->c_name, pri);
+ snprintf(pRes, pResLen, "%s.%s<%d>", syslog_fac_names[LOG_FAC(pri)],
+ syslog_severity_names[LOG_PRI(pri)], pri);
return pRes;
}
@@ -1631,7 +2084,7 @@ static uchar *getNOW(eNOWType eNow)
return NULL;
}
- datetime.getCurrTime(&t);
+ datetime.getCurrTime(&t, NULL);
switch(eNow) {
case NOW_NOW:
snprintf((char*) pBuf, tmpBUFSIZE, "%4.4d-%2.2d-%2.2d", t.year, t.month, t.day);
@@ -1701,138 +2154,172 @@ static uchar *getNOW(eNOWType eNow)
* be used in selector line processing.
* rgerhards 2005-09-15
*/
-char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
- cstr_t *pCSPropName, unsigned short *pbMustBeFreed)
-{
- uchar *pName;
- char *pRes; /* result pointer */
- char *pBufStart;
- char *pBuf;
+/* a quick helper to save some writing: */
+#define RET_OUT_OF_MEMORY { *pbMustBeFreed = 0;\
+ *pPropLen = sizeof("**OUT OF MEMORY**") - 1; \
+ return(UCHAR_CONSTANT("**OUT OF MEMORY**"));}
+uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
+ propid_t propID, size_t *pPropLen,
+ unsigned short *pbMustBeFreed)
+{
+ uchar *pRes; /* result pointer */
+ int bufLen = -1; /* length of string or -1, if not known */
+ uchar *pBufStart;
+ uchar *pBuf;
int iLen;
short iOffs;
+ BEGINfunc
+ assert(pMsg != NULL);
+ assert(pbMustBeFreed != NULL);
+
#ifdef FEATURE_REGEXP
/* Variables necessary for regular expression matching */
size_t nmatch = 10;
regmatch_t pmatch[10];
#endif
- assert(pMsg != NULL);
- assert(pbMustBeFreed != NULL);
-
- if(pCSPropName == NULL) {
- assert(pTpe != NULL);
- pName = pTpe->data.field.pPropRepl;
- } else {
- pName = rsCStrGetSzStrNoNULL(pCSPropName);
- }
*pbMustBeFreed = 0;
- /* sometimes there are aliases to the original MonitoWare
- * property names. These come after || in the ifs below. */
- if(!strcmp((char*) pName, "msg")) {
- pRes = getMSG(pMsg);
- } else if(!strcmp((char*) pName, "rawmsg")) {
- pRes = getRawMsg(pMsg);
- } else if(!strcmp((char*) pName, "uxtradmsg")) {
- pRes = getUxTradMsg(pMsg);
- } else if(!strcmp((char*) pName, "inputname")) {
- pRes = (char*) getInputName(pMsg);
- } else if(!strcmp((char*) pName, "fromhost")) {
- pRes = getRcvFrom(pMsg);
- } else if(!strcmp((char*) pName, "fromhost-ip")) {
- pRes = (char*) getRcvFromIP(pMsg);
- } else if(!strcmp((char*) pName, "source") || !strcmp((char*) pName, "hostname")) {
- pRes = getHOSTNAME(pMsg);
- } else if(!strcmp((char*) pName, "syslogtag")) {
- pRes = getTAG(pMsg);
- } else if(!strcmp((char*) pName, "pri")) {
- pRes = getPRI(pMsg);
- } else if(!strcmp((char*) pName, "pri-text")) {
- pBuf = malloc(20 * sizeof(char));
- if(pBuf == NULL) {
+ switch(propID) {
+ case PROP_MSG:
+ pRes = getMSG(pMsg);
+ bufLen = getMSGLen(pMsg);
+ break;
+ case PROP_TIMESTAMP:
+ pRes = (uchar*)getTimeReported(pMsg, pTpe->data.field.eDateFormat);
+ break;
+ case PROP_HOSTNAME:
+ pRes = (uchar*)getHOSTNAME(pMsg);
+ break;
+ case PROP_SYSLOGTAG:
+ getTAG(pMsg, &pRes, &bufLen);
+ break;
+ case PROP_RAWMSG:
+ pRes = (uchar*)getRawMsg(pMsg);
+ break;
+ /* enable this, if someone actually uses UxTradMsg, delete after some time has
+ * passed and nobody complained -- rgerhards, 2009-06-16
+ case PROP_UXTRADMSG:
+ pRes = getUxTradMsg(pMsg);
+ break;
+ */
+ case PROP_INPUTNAME:
+ getInputName(pMsg, &pRes, &bufLen);
+ break;
+ case PROP_FROMHOST:
+ pRes = getRcvFrom(pMsg);
+ break;
+ case PROP_FROMHOST_IP:
+ pRes = getRcvFromIP(pMsg);
+ break;
+ case PROP_PRI:
+ pRes = (uchar*)getPRI(pMsg);
+ break;
+ case PROP_PRI_TEXT:
+ pBuf = malloc(20 * sizeof(uchar));
+ if(pBuf == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else {
+ *pbMustBeFreed = 1;
+ pRes = (uchar*)textpri((char*)pBuf, 20, getPRIi(pMsg));
+ }
+ break;
+ case PROP_IUT:
+ pRes = UCHAR_CONSTANT("1"); /* always 1 for syslog messages (a MonitorWare thing;)) */
+ break;
+ case PROP_SYSLOGFACILITY:
+ pRes = (uchar*)getFacility(pMsg);
+ break;
+ case PROP_SYSLOGFACILITY_TEXT:
+ pRes = (uchar*)getFacilityStr(pMsg);
+ break;
+ case PROP_SYSLOGSEVERITY:
+ pRes = (uchar*)getSeverity(pMsg);
+ break;
+ case PROP_SYSLOGSEVERITY_TEXT:
+ pRes = (uchar*)getSeverityStr(pMsg);
+ break;
+ case PROP_TIMEGENERATED:
+ pRes = (uchar*)getTimeGenerated(pMsg, pTpe->data.field.eDateFormat);
+ break;
+ case PROP_PROGRAMNAME:
+ pRes = (uchar*)getProgramName(pMsg, LOCK_MUTEX);
+ break;
+ case PROP_PROTOCOL_VERSION:
+ pRes = (uchar*)getProtocolVersionString(pMsg);
+ break;
+ case PROP_STRUCTURED_DATA:
+ pRes = (uchar*)getStructuredData(pMsg);
+ break;
+ case PROP_APP_NAME:
+ pRes = (uchar*)getAPPNAME(pMsg, LOCK_MUTEX);
+ break;
+ case PROP_PROCID:
+ pRes = (uchar*)getPROCID(pMsg, LOCK_MUTEX);
+ break;
+ case PROP_MSGID:
+ pRes = (uchar*)getMSGID(pMsg);
+ break;
+ case PROP_SYS_NOW:
+ if((pRes = getNOW(NOW_NOW)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else
+ *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
+ break;
+ case PROP_SYS_YEAR:
+ if((pRes = getNOW(NOW_YEAR)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else
+ *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
+ break;
+ case PROP_SYS_MONTH:
+ if((pRes = getNOW(NOW_MONTH)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else
+ *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
+ break;
+ case PROP_SYS_DAY:
+ if((pRes = getNOW(NOW_DAY)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else
+ *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
+ break;
+ case PROP_SYS_HOUR:
+ if((pRes = getNOW(NOW_HOUR)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else
+ *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
+ break;
+ case PROP_SYS_HHOUR:
+ if((pRes = getNOW(NOW_HHOUR)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else
+ *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
+ break;
+ case PROP_SYS_QHOUR:
+ if((pRes = getNOW(NOW_QHOUR)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else
+ *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
+ break;
+ case PROP_SYS_MINUTE:
+ if((pRes = getNOW(NOW_MINUTE)) == NULL) {
+ RET_OUT_OF_MEMORY;
+ } else
+ *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
+ break;
+ case PROP_SYS_MYHOSTNAME:
+ pRes = glbl.GetLocalHostName();
+ break;
+ default:
+ /* there is no point in continuing, we may even otherwise render the
+ * error message unreadable. rgerhards, 2007-07-10
+ */
+ dbgprintf("invalid property id: '%d'\n", propID);
*pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
- } else {
- *pbMustBeFreed = 1;
- pRes = textpri(pBuf, 20, getPRIi(pMsg));
- }
- } else if(!strcmp((char*) pName, "iut")) {
- pRes = "1"; /* always 1 for syslog messages (a MonitorWare thing;)) */
- } else if(!strcmp((char*) pName, "syslogfacility")) {
- pRes = getFacility(pMsg);
- } else if(!strcmp((char*) pName, "syslogfacility-text")) {
- pRes = getFacilityStr(pMsg);
- } else if(!strcmp((char*) pName, "syslogseverity") || !strcmp((char*) pName, "syslogpriority")) {
- pRes = getSeverity(pMsg);
- } else if(!strcmp((char*) pName, "syslogseverity-text") || !strcmp((char*) pName, "syslogpriority-text")) {
- pRes = getSeverityStr(pMsg);
- } else if(!strcmp((char*) pName, "timegenerated")) {
- pRes = getTimeGenerated(pMsg, pTpe->data.field.eDateFormat);
- } else if(!strcmp((char*) pName, "timereported")
- || !strcmp((char*) pName, "timestamp")) {
- pRes = getTimeReported(pMsg, pTpe->data.field.eDateFormat);
- } else if(!strcmp((char*) pName, "programname")) {
- pRes = getProgramName(pMsg);
- } else if(!strcmp((char*) pName, "protocol-version")) {
- pRes = getProtocolVersionString(pMsg);
- } else if(!strcmp((char*) pName, "structured-data")) {
- pRes = getStructuredData(pMsg);
- } else if(!strcmp((char*) pName, "app-name")) {
- pRes = getAPPNAME(pMsg);
- } else if(!strcmp((char*) pName, "procid")) {
- pRes = getPROCID(pMsg);
- } else if(!strcmp((char*) pName, "msgid")) {
- pRes = getMSGID(pMsg);
- /* here start system properties (those, that do not relate to the message itself */
- } else if(!strcmp((char*) pName, "$now")) {
- if((pRes = (char*) getNOW(NOW_NOW)) == NULL) {
- return "***OUT OF MEMORY***";
- } else
- *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
- } else if(!strcmp((char*) pName, "$year")) {
- if((pRes = (char*) getNOW(NOW_YEAR)) == NULL) {
- return "***OUT OF MEMORY***";
- } else
- *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
- } else if(!strcmp((char*) pName, "$month")) {
- if((pRes = (char*) getNOW(NOW_MONTH)) == NULL) {
- return "***OUT OF MEMORY***";
- } else
- *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
- } else if(!strcmp((char*) pName, "$day")) {
- if((pRes = (char*) getNOW(NOW_DAY)) == NULL) {
- return "***OUT OF MEMORY***";
- } else
- *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
- } else if(!strcmp((char*) pName, "$hour")) {
- if((pRes = (char*) getNOW(NOW_HOUR)) == NULL) {
- return "***OUT OF MEMORY***";
- } else
- *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
- } else if(!strcmp((char*) pName, "$hhour")) {
- if((pRes = (char*) getNOW(NOW_HHOUR)) == NULL) {
- return "***OUT OF MEMORY***";
- } else
- *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
- } else if(!strcmp((char*) pName, "$qhour")) {
- if((pRes = (char*) getNOW(NOW_QHOUR)) == NULL) {
- return "***OUT OF MEMORY***";
- } else
- *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
- } else if(!strcmp((char*) pName, "$minute")) {
- if((pRes = (char*) getNOW(NOW_MINUTE)) == NULL) {
- return "***OUT OF MEMORY***";
- } else
- *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
- } else if(!strcmp((char*) pName, "$myhostname")) {
- pRes = (char*) glbl.GetLocalHostName();
- } else {
- /* there is no point in continuing, we may even otherwise render the
- * error message unreadable. rgerhards, 2007-07-10
- */
- dbgprintf("invalid property name: '%s'\n", pName);
- return "**INVALID PROPERTY NAME**";
+ *pPropLen = sizeof("**INVALID PROPERTY NAME**") - 1;
+ return UCHAR_CONSTANT("**INVALID PROPERTY NAME**");
}
/* If we did not receive a template pointer, we are already done... */
@@ -1851,8 +2338,8 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
*/
if(pTpe->data.field.has_fields == 1) {
size_t iCurrFld;
- char *pFld;
- char *pFldEnd;
+ uchar *pFld;
+ uchar *pFldEnd;
/* first, skip to the field in question. The field separator
* is always one character and is stored in the template entry.
*/
@@ -1889,11 +2376,11 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
if(pBuf == NULL) {
if(*pbMustBeFreed == 1)
free(pRes);
- *pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
+ RET_OUT_OF_MEMORY;
}
/* now copy */
memcpy(pBuf, pFld, iLen);
+ bufLen = iLen;
pBuf[iLen] = '\0'; /* terminate it */
if(*pbMustBeFreed == 1)
free(pRes);
@@ -1906,12 +2393,13 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
if(*pbMustBeFreed == 1)
free(pRes);
*pbMustBeFreed = 0;
- return "**FIELD NOT FOUND**";
+ *pPropLen = sizeof("**FIELD NOT FOUND**") - 1;
+ return UCHAR_CONSTANT("**FIELD NOT FOUND**");
}
} else if(pTpe->data.field.iFromPos != 0 || pTpe->data.field.iToPos != 0) {
/* we need to obtain a private copy */
int iFrom, iTo;
- char *pSb;
+ uchar *pSb;
iFrom = pTpe->data.field.iFromPos;
iTo = pTpe->data.field.iToPos;
/* need to zero-base to and from (they are 1-based!) */
@@ -1919,42 +2407,60 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
--iFrom;
if(iTo > 0)
--iTo;
- iLen = iTo - iFrom + 1; /* the +1 is for an actual char, NOT \0! */
- pBufStart = pBuf = malloc((iLen + 1) * sizeof(char));
- if(pBuf == NULL) {
- if(*pbMustBeFreed == 1)
- free(pRes);
- *pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
- }
- pSb = pRes;
- if(iFrom) {
- /* skip to the start of the substring (can't do pointer arithmetic
- * because the whole string might be smaller!!)
- */
- while(*pSb && iFrom) {
- --iFrom;
+ if(bufLen == -1)
+ bufLen = ustrlen(pRes);
+ if(iFrom == 0 && iTo >= bufLen) {
+ /* in this case, the requested string is a superset of what we already have,
+ * so there is no need to do any processing. This is a frequent case for size-limited
+ * fields like TAG in the default forwarding template (so it is a useful optimization
+ * to check for this condition ;)). -- rgerhards, 2009-07-09
+ */
+ ; /*DO NOTHING*/
+ } else {
+ iLen = iTo - iFrom + 1; /* the +1 is for an actual char, NOT \0! */
+ pBufStart = pBuf = malloc((iLen + 1) * sizeof(char));
+ if(pBuf == NULL) {
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ RET_OUT_OF_MEMORY;
+ }
+ pSb = pRes;
+ if(iFrom) {
+ /* skip to the start of the substring (can't do pointer arithmetic
+ * because the whole string might be smaller!!)
+ */
+ while(*pSb && iFrom) {
+ --iFrom;
+ ++pSb;
+ }
+ }
+ /* OK, we are at the begin - now let's copy... */
+ bufLen = iLen;
+ while(*pSb && iLen) {
+ *pBuf++ = *pSb;
++pSb;
+ --iLen;
}
+ *pBuf = '\0';
+ bufLen -= iLen; /* subtract remaining length if the string was smaller! */
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ pRes = pBufStart;
+ *pbMustBeFreed = 1;
}
- /* OK, we are at the begin - now let's copy... */
- while(*pSb && iLen) {
- *pBuf++ = *pSb;
- ++pSb;
- --iLen;
- }
- *pBuf = '\0';
- if(*pbMustBeFreed == 1)
- free(pRes);
- pRes = pBufStart;
- *pbMustBeFreed = 1;
#ifdef FEATURE_REGEXP
} else {
/* Check for regular expressions */
if (pTpe->data.field.has_regex != 0) {
- if (pTpe->data.field.has_regex == 2)
+ if (pTpe->data.field.has_regex == 2) {
/* Could not compile regex before! */
- return "**NO MATCH** **BAD REGULAR EXPRESSION**";
+ if (*pbMustBeFreed == 1) {
+ free(pRes);
+ *pbMustBeFreed = 0;
+ }
+ *pPropLen = sizeof("**NO MATCH** **BAD REGULAR EXPRESSION**") - 1;
+ return UCHAR_CONSTANT("**NO MATCH** **BAD REGULAR EXPRESSION**");
+ }
dbgprintf("string to match for regex is: %s\n", pRes);
@@ -1967,7 +2473,7 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
*/
while(!bFound) {
int iREstat;
- iREstat = regexp.regexec(&pTpe->data.field.re, pRes + iOffs, nmatch, pmatch, 0);
+ iREstat = regexp.regexec(&pTpe->data.field.re, (char*)(pRes + iOffs), nmatch, pmatch, 0);
dbgprintf("regexec return is %d\n", iREstat);
if(iREstat == 0) {
if(pmatch[0].rm_so == -1) {
@@ -1978,7 +2484,7 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
bFound = 1;
} else {
dbgprintf("regex found at offset %d, new offset %d, tries %d\n",
- iOffs, iOffs + pmatch[0].rm_eo, iTry);
+ iOffs, (int) (iOffs + pmatch[0].rm_eo), iTry);
iOffs += pmatch[0].rm_eo;
++iTry;
}
@@ -1994,12 +2500,16 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
free(pRes);
*pbMustBeFreed = 0;
}
- if(pTpe->data.field.nomatchAction == TPL_REGEX_NOMATCH_USE_DFLTSTR)
- return "**NO MATCH**";
- else if(pTpe->data.field.nomatchAction == TPL_REGEX_NOMATCH_USE_ZERO)
- return "0";
- else
- return "";
+ if(pTpe->data.field.nomatchAction == TPL_REGEX_NOMATCH_USE_DFLTSTR) {
+ bufLen = sizeof("**NO MATCH**") - 1;
+ pRes = UCHAR_CONSTANT("**NO MATCH**");
+ } else if(pTpe->data.field.nomatchAction == TPL_REGEX_NOMATCH_USE_ZERO) {
+ bufLen = 1;
+ pRes = UCHAR_CONSTANT("0");
+ } else {
+ bufLen = 0;
+ pRes = UCHAR_CONSTANT("");
+ }
}
} else {
/* Match- but did it match the one we wanted? */
@@ -2010,29 +2520,35 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
free(pRes);
*pbMustBeFreed = 0;
}
- if(pTpe->data.field.nomatchAction == TPL_REGEX_NOMATCH_USE_DFLTSTR)
- return "**NO MATCH**";
- else
- return "";
+ if(pTpe->data.field.nomatchAction == TPL_REGEX_NOMATCH_USE_DFLTSTR) {
+ bufLen = sizeof("**NO MATCH**") - 1;
+ pRes = UCHAR_CONSTANT("**NO MATCH**");
+ } else if(pTpe->data.field.nomatchAction == TPL_REGEX_NOMATCH_USE_ZERO) {
+ bufLen = 1;
+ pRes = UCHAR_CONSTANT("0");
+ } else {
+ bufLen = 0;
+ pRes = UCHAR_CONSTANT("");
+ }
}
}
/* OK, we have a usable match - we now need to malloc pB */
int iLenBuf;
- char *pB;
+ uchar *pB;
iLenBuf = pmatch[pTpe->data.field.iSubMatchToUse].rm_eo
- pmatch[pTpe->data.field.iSubMatchToUse].rm_so;
- pB = (char *) malloc((iLenBuf + 1) * sizeof(char));
+ pB = malloc((iLenBuf + 1) * sizeof(uchar));
if (pB == NULL) {
if (*pbMustBeFreed == 1)
free(pRes);
- *pbMustBeFreed = 0;
- return "**OUT OF MEMORY ALLOCATING pBuf**";
+ RET_OUT_OF_MEMORY;
}
/* Lets copy the matched substring to the buffer */
memcpy(pB, pRes + iOffs + pmatch[pTpe->data.field.iSubMatchToUse].rm_so, iLenBuf);
+ bufLen = iLenBuf;
pB[iLenBuf] = '\0';/* terminate string, did not happen before */
if (*pbMustBeFreed == 1)
@@ -2050,7 +2566,8 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
free(pRes);
*pbMustBeFreed = 0;
}
- return "***REGEXP NOT AVAILABLE***";
+ *pPropLen = sizeof("***REGEXP NOT AVAILABLE***") - 1;
+ return UCHAR_CONSTANT("***REGEXP NOT AVAILABLE***");
}
}
#endif /* #ifdef FEATURE_REGEXP */
@@ -2058,28 +2575,13 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
/* now check if we need to do our "SP if first char is non-space" hack logic */
if(*pRes && pTpe->data.field.options.bSPIffNo1stSP) {
- char *pB;
- uchar cFirst = *pRes;
-
/* here, we always destruct the buffer and return a new one */
- pB = (char *) malloc(2 * sizeof(char));
- if(pB == NULL) {
- if(*pbMustBeFreed == 1)
- free(pRes);
- *pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
- }
- pRes = pB;
- *pbMustBeFreed = 1;
-
- if(cFirst == ' ') {
- /* if we have a SP, we must return an empty string */
- *pRes = '\0'; /* empty */
- } else {
- /* if it is no SP, we need to return one */
- *pRes = ' ';
- *(pRes+1) = '\0';
- }
+ uchar cFirst = *pRes; /* save first char */
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ pRes = (cFirst == ' ') ? UCHAR_CONSTANT("") : UCHAR_CONSTANT(" ");
+ bufLen = (cFirst == ' ') ? 0 : 1;
+ *pbMustBeFreed = 0;
}
if(*pRes) {
@@ -2088,21 +2590,21 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
*/
if(pTpe->data.field.eCaseConv != tplCaseConvNo) {
/* we need to obtain a private copy */
- int iBufLen = strlen(pRes);
- char *pBStart;
- char *pB;
- char *pSrc;
- pBStart = pB = malloc((iBufLen + 1) * sizeof(char));
+ if(bufLen == -1)
+ bufLen = ustrlen(pRes);
+ uchar *pBStart;
+ uchar *pB;
+ uchar *pSrc;
+ pBStart = pB = malloc((bufLen + 1) * sizeof(char));
if(pB == NULL) {
if(*pbMustBeFreed == 1)
free(pRes);
- *pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
+ RET_OUT_OF_MEMORY;
}
pSrc = pRes;
while(*pSrc) {
*pB++ = (pTpe->data.field.eCaseConv == tplCaseConvUpper) ?
- (char)toupper((int)*pSrc) : (char)tolower((int)*pSrc);
+ (uchar)toupper((int)*pSrc) : (uchar)tolower((int)*pSrc);
/* currently only these two exist */
++pSrc;
}
@@ -2126,10 +2628,10 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
*/
if(pTpe->data.field.options.bDropCC) {
int iLenBuf = 0;
- char *pSrc = pRes;
- char *pDstStart;
- char *pDst;
- char bDropped = 0;
+ uchar *pSrc = pRes;
+ uchar *pDstStart;
+ uchar *pDst;
+ uchar bDropped = 0;
while(*pSrc) {
if(!iscntrl((int) *pSrc++))
@@ -2143,8 +2645,7 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
if(pDst == NULL) {
if(*pbMustBeFreed == 1)
free(pRes);
- *pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
+ RET_OUT_OF_MEMORY;
}
for(pSrc = pRes; *pSrc; pSrc++) {
if(!iscntrl((int) *pSrc))
@@ -2154,12 +2655,13 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
if(*pbMustBeFreed == 1)
free(pRes);
pRes = pDstStart;
+ bufLen = iLenBuf;
*pbMustBeFreed = 1;
}
} else if(pTpe->data.field.options.bSpaceCC) {
- char *pSrc;
- char *pDstStart;
- char *pDst;
+ uchar *pSrc;
+ uchar *pDstStart;
+ uchar *pDst;
if(*pbMustBeFreed == 1) {
/* in this case, we already work on dynamic
@@ -2172,12 +2674,13 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
*pDst = ' ';
}
} else {
- pDst = pDstStart = malloc(strlen(pRes) + 1);
+ if(bufLen == -1)
+ bufLen = ustrlen(pRes);
+ pDst = pDstStart = malloc(bufLen + 1);
if(pDst == NULL) {
if(*pbMustBeFreed == 1)
free(pRes);
- *pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
+ RET_OUT_OF_MEMORY;
}
for(pSrc = pRes; *pSrc; pSrc++) {
if(iscntrl((int) *pSrc))
@@ -2197,7 +2700,7 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
*/
int iNumCC = 0;
int iLenBuf = 0;
- char *pB;
+ uchar *pB;
for(pB = pRes ; *pB ; ++pB) {
++iLenBuf;
@@ -2207,21 +2710,20 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
if(iNumCC > 0) { /* if 0, there is nothing to escape, so we are done */
/* OK, let's do the escaping... */
- char *pBStart;
- char szCCEsc[8]; /* buffer for escape sequence */
+ uchar *pBStart;
+ uchar szCCEsc[8]; /* buffer for escape sequence */
int i;
iLenBuf += iNumCC * 4;
- pBStart = pB = malloc((iLenBuf + 1) * sizeof(char));
+ pBStart = pB = malloc((iLenBuf + 1) * sizeof(uchar));
if(pB == NULL) {
if(*pbMustBeFreed == 1)
free(pRes);
- *pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
+ RET_OUT_OF_MEMORY;
}
while(*pRes) {
if(iscntrl((int) *pRes)) {
- snprintf(szCCEsc, sizeof(szCCEsc), "#%3.3d", *pRes);
+ snprintf((char*)szCCEsc, sizeof(szCCEsc), "#%3.3d", *pRes);
for(i = 0 ; i < 4 ; ++i)
*pB++ = szCCEsc[i];
} else {
@@ -2233,6 +2735,7 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
if(*pbMustBeFreed == 1)
free(pRes);
pRes = pBStart;
+ bufLen = -1;
*pbMustBeFreed = 1;
}
}
@@ -2244,10 +2747,10 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
if(pTpe->data.field.options.bSecPathDrop || pTpe->data.field.options.bSecPathReplace) {
if(pTpe->data.field.options.bSecPathDrop) {
int iLenBuf = 0;
- char *pSrc = pRes;
- char *pDstStart;
- char *pDst;
- char bDropped = 0;
+ uchar *pSrc = pRes;
+ uchar *pDstStart;
+ uchar *pDst;
+ uchar bDropped = 0;
while(*pSrc) {
if(*pSrc++ != '/')
@@ -2261,8 +2764,7 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
if(pDst == NULL) {
if(*pbMustBeFreed == 1)
free(pRes);
- *pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
+ RET_OUT_OF_MEMORY;
}
for(pSrc = pRes; *pSrc; pSrc++) {
if(*pSrc != '/')
@@ -2272,12 +2774,13 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
if(*pbMustBeFreed == 1)
free(pRes);
pRes = pDstStart;
+ bufLen = -1; /* TODO: can we do better? */
*pbMustBeFreed = 1;
}
} else {
- char *pSrc;
- char *pDstStart;
- char *pDst;
+ uchar *pSrc;
+ uchar *pDstStart;
+ uchar *pDst;
if(*pbMustBeFreed == 1) {
/* here, again, we can modify the string as we already obtained
@@ -2290,12 +2793,13 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
*pDst++ = '_';
}
} else {
- pDst = pDstStart = malloc(strlen(pRes) + 1);
+ if(bufLen == -1)
+ bufLen = ustrlen(pRes);
+ pDst = pDstStart = malloc(bufLen + 1);
if(pDst == NULL) {
if(*pbMustBeFreed == 1)
free(pRes);
- *pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
+ RET_OUT_OF_MEMORY;
}
for(pSrc = pRes; *pSrc; pSrc++) {
if(*pSrc == '/')
@@ -2315,48 +2819,93 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
/* check for "." and ".." (note the parenthesis in the if condition!) */
if((*pRes == '.') && (*(pRes + 1) == '\0' || (*(pRes + 1) == '.' && *(pRes + 2) == '\0'))) {
- char *pTmp = pRes;
+ uchar *pTmp = pRes;
if(*(pRes + 1) == '\0')
- pRes = "_";
+ pRes = UCHAR_CONSTANT("_");
else
- pRes = "_.";;
+ pRes = UCHAR_CONSTANT("_.");;
if(*pbMustBeFreed == 1)
free(pTmp);
*pbMustBeFreed = 0;
} else if(*pRes == '\0') {
if(*pbMustBeFreed == 1)
free(pRes);
- pRes = "_";
+ pRes = UCHAR_CONSTANT("_");
+ bufLen = 1;
*pbMustBeFreed = 0;
}
}
/* Now drop last LF if present (pls note that this must not be done
- * if bEscapeCC was set!
+ * if bEscapeCC was set)!
*/
if(pTpe->data.field.options.bDropLastLF && !pTpe->data.field.options.bEscapeCC) {
- int iLn = strlen(pRes);
- char *pB;
+ int iLn;
+ uchar *pB;
+ if(bufLen == -1)
+ bufLen = ustrlen(pRes);
+ iLn = bufLen;
if(iLn > 0 && *(pRes + iLn - 1) == '\n') {
/* we have a LF! */
/* check if we need to obtain a private copy */
if(*pbMustBeFreed == 0) {
/* ok, original copy, need a private one */
- pB = malloc((iLn + 1) * sizeof(char));
+ pB = malloc((iLn + 1) * sizeof(uchar));
if(pB == NULL) {
- *pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
+ RET_OUT_OF_MEMORY;
}
memcpy(pB, pRes, iLn - 1);
pRes = pB;
*pbMustBeFreed = 1;
}
*(pRes + iLn - 1) = '\0'; /* drop LF ;) */
+ --bufLen;
}
}
- /*dbgprintf("MsgGetProp(\"%s\"): \"%s\"\n", pName, pRes); only for verbose debug logging */
+ /* finally, we need to check if the property should be formatted in CSV
+ * format (we use RFC 4180, and always use double quotes). As of this writing,
+ * this should be the last action carried out on the property, but in the
+ * future there may be reasons to change that. -- rgerhards, 2009-04-02
+ */
+ if(pTpe->data.field.options.bCSV) {
+ /* we need to obtain a private copy, as we need to at least add the double quotes */
+ int iBufLen;
+ uchar *pBStart;
+ uchar *pDst;
+ uchar *pSrc;
+ if(bufLen == -1)
+ bufLen = ustrlen(pRes);
+ iBufLen = bufLen;
+ /* the malloc may be optimized, we currently use the worst case... */
+ pBStart = pDst = malloc((2 * iBufLen + 3) * sizeof(uchar));
+ if(pDst == NULL) {
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ RET_OUT_OF_MEMORY;
+ }
+ pSrc = pRes;
+ *pDst++ = '"'; /* starting quote */
+ while(*pSrc) {
+ if(*pSrc == '"')
+ *pDst++ = '"'; /* need to add double double quote (see RFC4180) */
+ *pDst++ = *pSrc++;
+ }
+ *pDst++ = '"'; /* ending quote */
+ *pDst = '\0';
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ pRes = pBStart;
+ bufLen = -1;
+ *pbMustBeFreed = 1;
+ }
+
+ if(bufLen == -1)
+ bufLen = ustrlen(pRes);
+ *pPropLen = bufLen;
+
+ ENDfunc
return(pRes);
}
@@ -2371,8 +2920,10 @@ msgGetMsgVar(msg_t *pThis, cstr_t *pstrPropName, var_t **ppVar)
{
DEFiRet;
var_t *pVar;
+ size_t propLen;
uchar *pszProp = NULL;
cstr_t *pstrProp;
+ propid_t propid;
unsigned short bMustBeFreed = 0;
ISOBJ_TYPE_assert(pThis, msg);
@@ -2384,7 +2935,9 @@ msgGetMsgVar(msg_t *pThis, cstr_t *pstrPropName, var_t **ppVar)
CHKiRet(var.ConstructFinalize(pVar));
/* always call MsgGetProp() without a template specifier */
- pszProp = (uchar*) MsgGetProp(pThis, NULL, pstrPropName, &bMustBeFreed);
+ /* TODO: optimize propNameToID() call -- rgerhards, 2009-06-26 */
+ propNameToID(pstrPropName, &propid);
+ pszProp = (uchar*) MsgGetProp(pThis, NULL, propid, &propLen, &bMustBeFreed);
/* now create a string object out of it and hand that over to the var */
CHKiRet(rsCStrConstructFromszStr(&pstrProp, pszProp));
@@ -2399,8 +2952,6 @@ finalize_it:
RETiRet;
}
-
-
/* This function can be used as a generic way to set properties.
* We have to handle a lot of legacy, so our return value is not always
* 100% correct (called functions do not always provide one, should
@@ -2410,6 +2961,9 @@ finalize_it:
#define isProp(name) !rsCStrSzStrCmp(pProp->pcsName, (uchar*) name, sizeof(name) - 1)
rsRetVal MsgSetProperty(msg_t *pThis, var_t *pProp)
{
+ prop_t *myProp;
+ prop_t *propRcvFrom = NULL;
+ prop_t *propRcvFromIP = NULL;
DEFiRet;
ISOBJ_TYPE_assert(pThis, msg);
@@ -2423,22 +2977,34 @@ rsRetVal MsgSetProperty(msg_t *pThis, var_t *pProp)
pThis->iFacility = pProp->val.num;
} else if(isProp("msgFlags")) {
pThis->msgFlags = pProp->val.num;
+ } else if(isProp("offMSG")) {
+ MsgSetMSGoffs(pThis, pProp->val.num);
} else if(isProp("pszRawMsg")) {
- MsgSetRawMsg(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
- } else if(isProp("pszMSG")) {
- MsgSetMSG(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ MsgSetRawMsg(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr), cstrLen(pProp->val.pStr));
+ /* enable this, if someone actually uses UxTradMsg, delete after some time has
+ * passed and nobody complained -- rgerhards, 2009-06-16
+ } else if(isProp("offAfterPRI")) {
+ pThis->offAfterPRI = pProp->val.num;
+ */
} else if(isProp("pszUxTradMsg")) {
- MsgSetUxTradMsg(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ /*IGNORE*/; /* this *was* a property, but does no longer exist */
} else if(isProp("pszTAG")) {
- MsgSetTAG(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ MsgSetTAG(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr), cstrLen(pProp->val.pStr));
} else if(isProp("pszInputName")) {
- MsgSetInputName(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ /* we need to create a property */
+ CHKiRet(prop.Construct(&myProp));
+ CHKiRet(prop.SetString(myProp, rsCStrGetSzStrNoNULL(pProp->val.pStr), rsCStrLen(pProp->val.pStr)));
+ CHKiRet(prop.ConstructFinalize(myProp));
+ MsgSetInputName(pThis, myProp);
+ prop.Destruct(&myProp);
} else if(isProp("pszRcvFromIP")) {
- MsgSetRcvFromIP(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ MsgSetRcvFromIPStr(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr), rsCStrLen(pProp->val.pStr), &propRcvFromIP);
+ prop.Destruct(&propRcvFromIP);
} else if(isProp("pszRcvFrom")) {
- MsgSetRcvFrom(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ MsgSetRcvFromStr(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr), rsCStrLen(pProp->val.pStr), &propRcvFrom);
+ prop.Destruct(&propRcvFrom);
} else if(isProp("pszHOSTNAME")) {
- MsgSetHOSTNAME(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ MsgSetHOSTNAME(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr), rsCStrLen(pProp->val.pStr));
} else if(isProp("pCSStrucData")) {
MsgSetStructuredData(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
} else if(isProp("pCSAPPNAME")) {
@@ -2447,12 +3013,17 @@ rsRetVal MsgSetProperty(msg_t *pThis, var_t *pProp)
MsgSetPROCID(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
} else if(isProp("pCSMSGID")) {
MsgSetMSGID(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ } else if(isProp("ttGenTime")) {
+ pThis->ttGenTime = pProp->val.num;
} else if(isProp("tRcvdAt")) {
memcpy(&pThis->tRcvdAt, &pProp->val.vSyslogTime, sizeof(struct syslogTime));
} else if(isProp("tTIMESTAMP")) {
memcpy(&pThis->tTIMESTAMP, &pProp->val.vSyslogTime, sizeof(struct syslogTime));
+ } else if(isProp("pszMSG")) {
+ dbgprintf("no longer supported property pszMSG silently ignored\n");
}
+finalize_it:
RETiRet;
}
#undef isProp
@@ -2496,6 +3067,7 @@ BEGINObjClassInit(msg, 1, OBJ_IS_CORE_MODULE)
CHKiRet(objUse(var, CORE_COMPONENT));
CHKiRet(objUse(datetime, CORE_COMPONENT));
CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(prop, CORE_COMPONENT));
/* set our own handlers */
OBJSetMethodHandler(objMethod_SERIALIZE, MsgSerialize);
@@ -2508,7 +3080,5 @@ BEGINObjClassInit(msg, 1, OBJ_IS_CORE_MODULE)
funcDeleteMutex = MsgLockingDummy;
funcMsgPrepareEnqueue = MsgLockingDummy;
ENDObjClassInit(msg)
-
-/*
- * vi:set ai:
+/* vim:set ai:
*/
diff --git a/runtime/msg.h b/runtime/msg.h
index 1bad9c66..3a02365b 100644
--- a/runtime/msg.h
+++ b/runtime/msg.h
@@ -3,7 +3,7 @@
*
* File begun on 2007-07-13 by RGerhards (extracted from syslogd.c)
*
- * Copyright 2007 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007-2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of the rsyslog runtime library.
*
@@ -33,6 +33,7 @@
#include "syslogd-types.h"
#include "template.h"
+
/* rgerhards 2004-11-08: The following structure represents a
* syslog message.
*
@@ -47,129 +48,140 @@
* will be decremented. If it is 1, however, the object is actually
* destroyed. To make this work, it is vital that MsgAddRef() is
* called each time a "copy" is stored somewhere.
+ *
+ * WARNING: this structure is not calloc()ed, so be careful when
+ * adding new fields. You need to initialize them in
+ * msgBaseConstruct(). That function header comment also describes
+ * why this is the case.
*/
struct msg {
BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
- pthread_mutexattr_t mutAttr;
-short bDoLock; /* use the mutex? */
+ flowControl_t flowCtlType; /**< type of flow control we can apply, for enqueueing, needs not to be persisted because
+ once data has entered the queue, this property is no longer needed. */
pthread_mutex_t mut;
- int iRefCount; /* reference counter (0 = unused) */
- short bParseHOSTNAME; /* should the hostname be parsed from the message? */
+ bool bDoLock; /* use the mutex? */
+ bool bParseHOSTNAME; /* should the hostname be parsed from the message? */
+ short iRefCount; /* reference counter (0 = unused) */
/* background: the hostname is not present on "regular" messages
* received via UNIX domain sockets from the same machine. However,
* it is available when we have a forwarder (e.g. rfc3195d) using local
* sockets. All in all, the parser would need parse templates, that would
* resolve all these issues... rgerhards, 2005-10-06
*/
- flowControl_t flowCtlType; /**< type of flow control we can apply, for enqueueing, needs not to be persisted because
- once data has entered the queue, this property is no longer needed. */
short iSeverity; /* the severity 0..7 */
- uchar *pszSeverity; /* severity as string... */
- int iLenSeverity; /* ... and its length. */
- uchar *pszSeverityStr; /* severity name... */
- int iLenSeverityStr; /* ... and its length. */
short iFacility; /* Facility code 0 .. 23*/
- uchar *pszFacility; /* Facility as string... */
- int iLenFacility; /* ... and its length. */
- uchar *pszFacilityStr; /* facility name... */
- int iLenFacilityStr; /* ... and its length. */
- uchar *pszPRI; /* the PRI as a string */
- int iLenPRI; /* and its length */
- uchar *pszRawMsg; /* message as it was received on the
- * wire. This is important in case we
- * need to preserve cryptographic verifiers.
- */
+ short offAfterPRI; /* offset, at which raw message WITHOUT PRI part starts in pszRawMsg */
+ short offMSG; /* offset at which the MSG part starts in pszRawMsg */
+ short iProtocolVersion;/* protocol version of message received 0 - legacy, 1 syslog-protocol) */
+ int msgFlags; /* flags associated with this message */
int iLenRawMsg; /* length of raw message */
- uchar *pszMSG; /* the MSG part itself */
int iLenMSG; /* Length of the MSG part */
- uchar *pszUxTradMsg; /* the traditional UNIX message */
- int iLenUxTradMsg;/* Length of the traditional UNIX message */
- uchar *pszTAG; /* pointer to tag value */
int iLenTAG; /* Length of the TAG part */
- uchar *pszHOSTNAME; /* HOSTNAME from syslog message */
int iLenHOSTNAME; /* Length of HOSTNAME */
- uchar *pszRcvFrom; /* System message was received from */
- int iLenRcvFrom; /* Length of pszRcvFrom */
- uchar *pszRcvFromIP; /* IP of system message was received from */
- int iLenRcvFromIP; /* Length of pszRcvFromIP */
- uchar *pszInputName; /* name of the input module that submitted this message */
- int iLenInputName; /* Length of pszInputName */
- short iProtocolVersion;/* protocol version of message received 0 - legacy, 1 syslog-protocol) */
- cstr_t *pCSProgName; /* the (BSD) program name */
- cstr_t *pCSStrucData; /* STRUCTURED-DATA */
- cstr_t *pCSAPPNAME; /* APP-NAME */
- cstr_t *pCSPROCID; /* PROCID */
- cstr_t *pCSMSGID; /* MSGID */
- struct syslogTime tRcvdAt;/* time the message entered this program */
+ uchar *pszRawMsg; /* message as it was received on the wire. This is important in case we
+ * need to preserve cryptographic verifiers. */
+ uchar *pszHOSTNAME; /* HOSTNAME from syslog message */
char *pszRcvdAt3164; /* time as RFC3164 formatted string (always 15 charcters) */
char *pszRcvdAt3339; /* time as RFC3164 formatted string (32 charcters at most) */
- char *pszRcvdAt_SecFrac;/* time just as fractional seconds (6 charcters) */
char *pszRcvdAt_MySQL; /* rcvdAt as MySQL formatted string (always 14 charcters) */
char *pszRcvdAt_PgSQL; /* rcvdAt as PgSQL formatted string (always 21 characters) */
- struct syslogTime tTIMESTAMP;/* (parsed) value of the timestamp */
char *pszTIMESTAMP3164; /* TIMESTAMP as RFC3164 formatted string (always 15 charcters) */
char *pszTIMESTAMP3339; /* TIMESTAMP as RFC3339 formatted string (32 charcters at most) */
char *pszTIMESTAMP_MySQL;/* TIMESTAMP as MySQL formatted string (always 14 charcters) */
char *pszTIMESTAMP_PgSQL;/* TIMESTAMP as PgSQL formatted string (always 21 characters) */
- char *pszTIMESTAMP_SecFrac;/* TIMESTAMP fractional seconds (always 6 characters) */
- int msgFlags; /* flags associated with this message */
+ cstr_t *pCSProgName; /* the (BSD) program name */
+ cstr_t *pCSStrucData; /* STRUCTURED-DATA */
+ cstr_t *pCSAPPNAME; /* APP-NAME */
+ cstr_t *pCSPROCID; /* PROCID */
+ cstr_t *pCSMSGID; /* MSGID */
+ prop_t *pInputName; /* input name property */
+ prop_t *pRcvFrom; /* name of system message was received from */
+ prop_t *pRcvFromIP; /* IP of system message was received from */
+ ruleset_t *pRuleset; /* ruleset to be used for processing this message */
+ time_t ttGenTime; /* time msg object was generated, same as tRcvdAt, but a Unix timestamp.
+ While this field looks redundant, it is required because a Unix timestamp
+ is used at later processing stages (namely in the output arena). Thanks to
+ the subleties of how time is defined, there is no reliable way to reconstruct
+ the Unix timestamp from the syslogTime fields (in practice, we may be close
+ enough to reliable, but I prefer to leave the subtle things to the OS, where
+ it obviously is solved in way or another...). */
+ struct syslogTime tRcvdAt;/* time the message entered this program */
+ struct syslogTime tTIMESTAMP;/* (parsed) value of the timestamp */
+ /* some fixed-size buffers to save malloc()/free() for frequently used fields (from the default templates) */
+ uchar szRawMsg[CONF_RAWMSG_BUFSIZE]; /* most messages are small, and these are stored here (without malloc/free!) */
+ uchar szHOSTNAME[CONF_HOSTNAME_BUFSIZE];
+ union {
+ uchar *pszTAG; /* pointer to tag value */
+ uchar szBuf[CONF_TAG_BUFSIZE];
+ } TAG;
+ char pszTimestamp3164[16];
+ char pszTimestamp3339[33];
+ char pszTIMESTAMP_SecFrac[7]; /* Note: a pointer is 64 bits/8 char, so this is actually fewer than a pointer! */
+ char pszRcvdAt_SecFrac[7]; /* same as above. Both are fractional seconds for their respective timestamp */
};
+
+/* message flags (msgFlags), not an enum for historical reasons
+ */
+#define NOFLAG 0x000 /* no flag is set (to be used when a flag must be specified and none is required) */
+#define INTERNAL_MSG 0x001 /* msg generated by logmsgInternal() --> special handling */
+/* 0x002 not used because it was previously a known value - rgerhards, 2008-10-09 */
+#define IGNDATE 0x004 /* ignore, if given, date in message and use date of reception as msg date */
+#define MARK 0x008 /* this message is a mark */
+#define NEEDS_PARSING 0x010 /* raw message, must be parsed before processing can be done */
+#define PARSE_HOSTNAME 0x020 /* parse the hostname during message parsing */
+
+
/* function prototypes
*/
PROTOTYPEObjClassInit(msg);
-char* getProgramName(msg_t*);
rsRetVal msgConstruct(msg_t **ppThis);
+rsRetVal msgConstructWithTime(msg_t **ppThis, struct syslogTime *stTime, time_t ttGenTime);
rsRetVal msgDestruct(msg_t **ppM);
msg_t* MsgDup(msg_t* pOld);
msg_t *MsgAddRef(msg_t *pM);
void setProtocolVersion(msg_t *pM, int iNewVersion);
-int getProtocolVersion(msg_t *pM);
-char *getProtocolVersionString(msg_t *pM);
-int getMSGLen(msg_t *pM);
-char *getRawMsg(msg_t *pM);
-char *getUxTradMsg(msg_t *pM);
-char *getMSG(msg_t *pM);
-char *getPRI(msg_t *pM);
-int getPRIi(msg_t *pM);
-char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt);
-char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt);
-char *getSeverity(msg_t *pM);
-char *getSeverityStr(msg_t *pM);
-char *getFacility(msg_t *pM);
-char *getFacilityStr(msg_t *pM);
-void MsgSetInputName(msg_t *pMsg, char*);
+void MsgSetInputName(msg_t *pMsg, prop_t*);
rsRetVal MsgSetAPPNAME(msg_t *pMsg, char* pszAPPNAME);
-char *getAPPNAME(msg_t *pM);
rsRetVal MsgSetPROCID(msg_t *pMsg, char* pszPROCID);
-int getPROCIDLen(msg_t *pM);
-char *getPROCID(msg_t *pM);
rsRetVal MsgSetMSGID(msg_t *pMsg, char* pszMSGID);
-void MsgAssignTAG(msg_t *pMsg, uchar *pBuf);
-void MsgSetTAG(msg_t *pMsg, char* pszTAG);
+void MsgSetTAG(msg_t *pMsg, uchar* pszBuf, size_t lenBuf);
+void MsgSetRuleset(msg_t *pMsg, ruleset_t*);
rsRetVal MsgSetFlowControlType(msg_t *pMsg, flowControl_t eFlowCtl);
-char *getTAG(msg_t *pM);
-int getHOSTNAMELen(msg_t *pM);
-char *getHOSTNAME(msg_t *pM);
-char *getRcvFrom(msg_t *pM);
rsRetVal MsgSetStructuredData(msg_t *pMsg, char* pszStrucData);
-char *getStructuredData(msg_t *pM);
-int getProgramNameLen(msg_t *pM);
-char *getProgramName(msg_t *pM);
-void MsgSetRcvFrom(msg_t *pMsg, char* pszRcvFrom);
-rsRetVal MsgSetRcvFromIP(msg_t *pMsg, uchar* pszRcvFromIP);
-void MsgAssignHOSTNAME(msg_t *pMsg, char *pBuf);
-void MsgSetHOSTNAME(msg_t *pMsg, char* pszHOSTNAME);
-int MsgSetUxTradMsg(msg_t *pMsg, char* pszUxTradMsg);
-void MsgSetMSG(msg_t *pMsg, char* pszMSG);
-void MsgSetRawMsg(msg_t *pMsg, char* pszRawMsg);
-void moveHOSTNAMEtoTAG(msg_t *pM);
-char *getMSGID(msg_t *pM);
-char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
- cstr_t *pCSPropName, unsigned short *pbMustBeFreed);
+void MsgSetRcvFrom(msg_t *pMsg, prop_t*);
+void MsgSetRcvFromStr(msg_t *pMsg, uchar* pszRcvFrom, int, prop_t **);
+rsRetVal MsgSetRcvFromIP(msg_t *pMsg, prop_t*);
+rsRetVal MsgSetRcvFromIPStr(msg_t *pThis, uchar *psz, int len, prop_t **ppProp);
+void MsgSetHOSTNAME(msg_t *pMsg, uchar* pszHOSTNAME, int lenHOSTNAME);
+rsRetVal MsgSetAfterPRIOffs(msg_t *pMsg, short offs);
+void MsgSetMSGoffs(msg_t *pMsg, short offs);
+void MsgSetRawMsgWOSize(msg_t *pMsg, char* pszRawMsg);
+void MsgSetRawMsg(msg_t *pMsg, char* pszRawMsg, size_t lenMsg);
+rsRetVal MsgReplaceMSG(msg_t *pThis, uchar* pszMSG, int lenMSG);
+uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
+ propid_t propID, size_t *pPropLen, unsigned short *pbMustBeFreed);
char *textpri(char *pRes, size_t pResLen, int pri);
rsRetVal msgGetMsgVar(msg_t *pThis, cstr_t *pstrPropName, var_t **ppVar);
rsRetVal MsgEnableThreadSafety(void);
+uchar *getRcvFrom(msg_t *pM);
+
+
+/* TODO: remove these five (so far used in action.c) */
+uchar *getMSG(msg_t *pM);
+char *getHOSTNAME(msg_t *pM);
+char *getPROCID(msg_t *pM, bool bLockMutex);
+char *getAPPNAME(msg_t *pM, bool bLockMutex);
+int getMSGLen(msg_t *pM);
+
+char *getHOSTNAME(msg_t *pM);
+int getHOSTNAMELen(msg_t *pM);
+char *getProgramName(msg_t *pM, bool bLockMutex);
+int getProgramNameLen(msg_t *pM, bool bLockMutex);
+uchar *getRcvFrom(msg_t *pM);
+rsRetVal propNameToID(cstr_t *pCSPropName, propid_t *pPropID);
+uchar *propIDToName(propid_t propID);
+
/* The MsgPrepareEnqueue() function is a macro for performance reasons.
* It needs one global variable to work. This is acceptable, as it gains
@@ -180,7 +192,23 @@ rsRetVal MsgEnableThreadSafety(void);
extern void (*funcMsgPrepareEnqueue)(msg_t *pMsg);
#define MsgPrepareEnqueue(pMsg) funcMsgPrepareEnqueue(pMsg)
+
+/* ------------------------------ some inline functions ------------------------------ */
+
+/* set raw message size. This is needed in some cases where a trunctation is necessary
+ * but the raw message must not be newly set. The most important (and currently only)
+ * use case is if we remove trailing LF or NUL characters. Note that the size can NOT
+ * be extended, only shrunk!
+ * rgerhards, 2009-08-26
+ */
+static inline void
+MsgSetRawMsgSize(msg_t *pMsg, size_t newLen)
+{
+ assert(newLen <= (size_t) pMsg->iLenRawMsg);
+ pMsg->iLenRawMsg = newLen;
+}
+
+
#endif /* #ifndef MSG_H_INCLUDED */
-/*
- * vi:set ai:
+/* vim:set ai:
*/
diff --git a/runtime/net.c b/runtime/net.c
index 29f7062b..fe6eef5b 100644
--- a/runtime/net.c
+++ b/runtime/net.c
@@ -63,6 +63,11 @@
#include "errmsg.h"
#include "net.h"
+#ifdef OS_SOLARIS
+# define s6_addr32 _S6_un._S6_u32
+ typedef unsigned int u_int32_t;
+#endif
+
MODULE_TYPE_LIB
/* static data */
@@ -1005,8 +1010,8 @@ static int
should_use_so_bsdcompat(void)
{
#ifndef OS_BSD
- static int init_done;
- static int so_bsdcompat_is_obsolete;
+ static int init_done = 0;
+ static int so_bsdcompat_is_obsolete = 0;
if (!init_done) {
struct utsname myutsname;
@@ -1233,7 +1238,9 @@ rsRetVal cvthname(struct sockaddr_storage *f, uchar *pszHost, uchar *pszHostFQDN
* make this in option in the long term. (rgerhards, 2007-09-11)
*/
strcpy((char*)pszHost, (char*)pszHostFQDN);
- if ((p = (uchar*) strchr((char*)pszHost, '.'))) { /* find start of domain name "machine.example.com" */
+ if( (glbl.GetPreserveFQDN() == 0)
+ && (p = (uchar*) strchr((char*)pszHost, '.'))) { /* find start of domain name "machine.example.com" */
+ strcmp((char*)(p + 1), (char*)glbl.GetLocalDomain());
if(strcmp((char*)(p + 1), (char*)glbl.GetLocalDomain()) == 0) {
*p = '\0'; /* simply terminate the string */
} else {
@@ -1489,6 +1496,49 @@ int *create_udp_socket(uchar *hostname, uchar *pszPort, int bIsServer)
}
+/* check if two provided socket addresses point to the same host. Note that the
+ * length of the sockets must be provided as third parameter. This is necessary to
+ * compare non IPv4/v6 hosts, in which case we do a simple memory compare of the
+ * address structure (in that case, the same host may not reliably be detected).
+ * Note that we need to do the comparison not on the full structure, because it contains things
+ * like the port, which we do not need to look at when thinking about hostnames. So we look
+ * at the relevant fields, what means a somewhat more complicated processing.
+ * Also note that we use a non-standard calling interface, as this is much more natural and
+ * it looks extremely unlikely that we get an exception of any kind here. What we
+ * return is mimiced after memcmp(), and as such useful for building binary trees
+ * (the order relation may be a bit arbritrary, but at least it is consistent).
+ * rgerhards, 2009-09-03
+ */
+static int CmpHost(struct sockaddr_storage *s1, struct sockaddr_storage* s2, size_t socklen)
+{
+ int ret;
+
+ if(((struct sockaddr*) s1)->sa_family != ((struct sockaddr*) s2)->sa_family) {
+ ret = memcmp(s1, s2, socklen);
+ goto finalize_it;
+ }
+
+ if(((struct sockaddr*) s1)->sa_family == AF_INET) {
+ if(((struct sockaddr_in *) s1)->sin_addr.s_addr == ((struct sockaddr_in*)s2)->sin_addr.s_addr) {
+ ret = 0;
+ } else if(((struct sockaddr_in *) s1)->sin_addr.s_addr < ((struct sockaddr_in*)s2)->sin_addr.s_addr) {
+ ret = -1;
+ } else {
+ ret = 1;
+ }
+ } else if(((struct sockaddr*) s1)->sa_family == AF_INET6) {
+ /* IPv6 addresses are always 16 octets long */
+ ret = memcmp(((struct sockaddr_in6 *)s1)->sin6_addr.s6_addr, ((struct sockaddr_in6*)s2)->sin6_addr.s6_addr, 16);
+ } else {
+ ret = memcmp(s1, s2, socklen);
+ }
+
+dbgprintf("CmpHost returns %d\n", ret);
+finalize_it:
+ return ret;
+}
+
+
/* queryInterface function
* rgerhards, 2008-03-05
*/
@@ -1517,6 +1567,7 @@ CODESTARTobjQueryInterface(net)
pIf->AddPermittedPeer = AddPermittedPeer;
pIf->DestructPermittedPeers = DestructPermittedPeers;
pIf->PermittedPeerWildcardMatch = PermittedPeerWildcardMatch;
+ pIf->CmpHost = CmpHost;
/* data members */
pIf->pACLAddHostnameOnFail = &ACLAddHostnameOnFail;
pIf->pACLDontResolve = &ACLDontResolve;
diff --git a/runtime/net.h b/runtime/net.h
index 092c3116..ec364b1c 100644
--- a/runtime/net.h
+++ b/runtime/net.h
@@ -146,11 +146,13 @@ BEGINinterface(net) /* name must also be changed in ENDinterface macro! */
rsRetVal (*AddPermittedPeer)(permittedPeers_t **ppRootPeer, uchar *pszID);
rsRetVal (*DestructPermittedPeers)(permittedPeers_t **ppRootPeer);
rsRetVal (*PermittedPeerWildcardMatch)(permittedPeers_t *pPeer, uchar *pszNameToMatch, int *pbIsMatching);
+ /* v5 interface additions */
+ int (*CmpHost)(struct sockaddr_storage *, struct sockaddr_storage*, size_t);
/* data members - these should go away over time... TODO */
int *pACLAddHostnameOnFail; /* add hostname to acl when DNS resolving has failed */
int *pACLDontResolve; /* add hostname to acl instead of resolving it to IP(s) */
ENDinterface(net)
-#define netCURR_IF_VERSION 4 /* increment whenever you change the interface structure! */
+#define netCURR_IF_VERSION 5 /* increment whenever you change the interface structure! */
/* prototypes */
PROTOTYPEObj(net);
diff --git a/runtime/netstrm.c b/runtime/netstrm.c
index ffa1c578..3658006f 100644
--- a/runtime/netstrm.c
+++ b/runtime/netstrm.c
@@ -17,7 +17,7 @@
* Rainer Gerhards and Adiscon GmbH have agreed to permit using the code
* under the terms of the GNU Lesser General Public License.
*
- * Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007-2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of the rsyslog runtime library.
*
@@ -233,6 +233,19 @@ Send(netstrm_t *pThis, uchar *pBuf, ssize_t *pLenBuf)
RETiRet;
}
+/* Enable Keep-Alive handling for those drivers that support it.
+ * rgerhards, 2009-06-02
+ */
+static rsRetVal
+EnableKeepAlive(netstrm_t *pThis)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, netstrm);
+ iRet = pThis->Drvr.EnableKeepAlive(pThis->pDrvrData);
+ RETiRet;
+}
+
+
/* check connection - slim wrapper for NSD driver function */
static void
@@ -337,6 +350,7 @@ CODESTARTobjQueryInterface(netstrm)
pIf->SetDrvrPermPeers = SetDrvrPermPeers;
pIf->CheckConnection = CheckConnection;
pIf->GetSock = GetSock;
+ pIf->EnableKeepAlive = EnableKeepAlive;
finalize_it:
ENDobjQueryInterface(netstrm)
diff --git a/runtime/netstrm.h b/runtime/netstrm.h
index 3ab790e8..f6931104 100644
--- a/runtime/netstrm.h
+++ b/runtime/netstrm.h
@@ -31,6 +31,7 @@ struct netstrm_s {
BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
nsd_t *pDrvrData; /**< the driver's data elements (at most other places, this is called pNsd) */
nsd_if_t Drvr; /**< our stream driver */
+ void *pUsr; /**< pointer to user-provided data structure */
netstrms_t *pNS; /**< pointer to our netstream subsystem object */
};
@@ -68,9 +69,13 @@ BEGINinterface(netstrm) /* name must also be changed in ENDinterface macro! */
* sockets - not really desirable, but not the end of the world... TODO: should be
* reconsidered when a new ACL system is build. -- rgerhards, 2008-12-01
*/
+ /* v4 */
+ rsRetVal (*EnableKeepAlive)(netstrm_t *pThis);
ENDinterface(netstrm)
-#define netstrmCURR_IF_VERSION 3 /* increment whenever you change the interface structure! */
-/* interface version 3 added GetRemAddr() */
+#define netstrmCURR_IF_VERSION 4 /* increment whenever you change the interface structure! */
+/* interface version 3 added GetRemAddr()
+ * interface version 4 added EnableKeepAlive() -- rgerhards, 2009-06-02
+ * */
/* prototypes */
PROTOTYPEObj(netstrm);
diff --git a/runtime/nsd.h b/runtime/nsd.h
index f0c9b9b6..8668c934 100644
--- a/runtime/nsd.h
+++ b/runtime/nsd.h
@@ -69,9 +69,13 @@ BEGINinterface(nsd) /* name must also be changed in ENDinterface macro! */
* sockets - not really desirable, but not the end of the world... TODO: should be
* reconsidered when a new ACL system is build. -- rgerhards, 2008-12-01
*/
+ /* v5 */
+ rsRetVal (*EnableKeepAlive)(nsd_t *pThis);
ENDinterface(nsd)
-#define nsdCURR_IF_VERSION 4 /* increment whenever you change the interface structure! */
-/* interface version 4 added GetRemAddr() */
+#define nsdCURR_IF_VERSION 5 /* increment whenever you change the interface structure! */
+/* interface version 4 added GetRemAddr()
+ * interface version 5 added EnableKeepAlive() -- rgerhards, 2009-06-02
+ */
/* interface for the select call */
BEGINinterface(nsdsel) /* name must also be changed in ENDinterface macro! */
diff --git a/runtime/nsd_gtls.c b/runtime/nsd_gtls.c
index 3a79a015..e1dcf870 100644
--- a/runtime/nsd_gtls.c
+++ b/runtime/nsd_gtls.c
@@ -82,6 +82,7 @@ static gnutls_certificate_credentials xcred;
static gnutls_dh_params dh_params;
#ifdef DEBUG
+#if 0 /* uncomment, if needed some time again -- DEV Debug only */
/* This defines a log function to be provided to GnuTLS. It hopefully
* helps us track down hard to find problems.
* rgerhards, 2008-06-20
@@ -90,6 +91,7 @@ static void logFunction(int level, const char *msg)
{
dbgprintf("GnuTLS log msg, level %d: %s\n", level, msg);
}
+#endif
#endif /* #ifdef DEBUG */
@@ -333,7 +335,7 @@ gtlsGetCertInfo(nsd_gtls_t *pThis, cstr_t **ppStr)
gnutls_x509_crt_deinit(cert);
}
- CHKiRet(rsCStrFinish(pStr));
+ CHKiRet(cstrFinalize(pStr));
*ppStr = pStr;
finalize_it:
@@ -453,7 +455,7 @@ GenFingerprintStr(uchar *pFingerprint, size_t sizeFingerprint, cstr_t **ppStr)
snprintf((char*)buf, sizeof(buf), ":%2.2X", pFingerprint[i]);
CHKiRet(rsCStrAppendStrWithLen(pStr, buf, 3));
}
- CHKiRet(rsCStrFinish(pStr));
+ CHKiRet(cstrFinalize(pStr));
*ppStr = pStr;
@@ -708,20 +710,20 @@ gtlsGetCN(nsd_gtls_t *pThis, gnutls_x509_crt *pCert, cstr_t **ppstrCN)
}
/* we found a common name, now extract it */
- CHKiRet(rsCStrConstruct(&pstrCN));
+ CHKiRet(cstrConstruct(&pstrCN));
while(szDN[i] != '\0' && szDN[i] != ',') {
if(szDN[i] == '\\') {
/* hex escapes are not implemented */
++i; /* escape char processed */
if(szDN[i] == '\0')
ABORT_FINALIZE(RS_RET_CERT_INVALID_DN);
- CHKiRet(rsCStrAppendChar(pstrCN, szDN[i]));
+ CHKiRet(cstrAppendChar(pstrCN, szDN[i]));
} else {
- CHKiRet(rsCStrAppendChar(pstrCN, szDN[i]));
+ CHKiRet(cstrAppendChar(pstrCN, szDN[i]));
}
++i; /* char processed */
}
- CHKiRet(rsCStrFinish(pstrCN));
+ CHKiRet(cstrFinalize(pstrCN));
/* we got it - we ignore the rest of the DN string (if any). So we may
* not detect if it contains more than one CN
@@ -732,7 +734,7 @@ gtlsGetCN(nsd_gtls_t *pThis, gnutls_x509_crt *pCert, cstr_t **ppstrCN)
finalize_it:
if(iRet != RS_RET_OK) {
if(pstrCN != NULL)
- rsCStrDestruct(&pstrCN);
+ cstrDestruct(&pstrCN);
}
RETiRet;
@@ -759,7 +761,7 @@ gtlsChkPeerFingerprint(nsd_gtls_t *pThis, gnutls_x509_crt *pCert)
size = sizeof(fingerprint);
CHKgnutls(gnutls_x509_crt_get_fingerprint(*pCert, GNUTLS_DIG_SHA1, fingerprint, &size));
CHKiRet(GenFingerprintStr(fingerprint, size, &pstrFingerprint));
- dbgprintf("peer's certificate SHA1 fingerprint: %s\n", rsCStrGetSzStr(pstrFingerprint));
+ dbgprintf("peer's certificate SHA1 fingerprint: %s\n", cstrGetSzStr(pstrFingerprint));
/* now search through the permitted peers to see if we can find a permitted one */
bFoundPositiveMatch = 0;
@@ -777,7 +779,7 @@ gtlsChkPeerFingerprint(nsd_gtls_t *pThis, gnutls_x509_crt *pCert)
if(pThis->bReportAuthErr == 1) {
errno = 0;
errmsg.LogError(0, RS_RET_INVALID_FINGERPRINT, "error: peer fingerprint '%s' unknown - we are "
- "not permitted to talk to it", rsCStrGetSzStr(pstrFingerprint));
+ "not permitted to talk to it", cstrGetSzStr(pstrFingerprint));
pThis->bReportAuthErr = 0;
}
ABORT_FINALIZE(RS_RET_INVALID_FINGERPRINT);
@@ -785,7 +787,7 @@ gtlsChkPeerFingerprint(nsd_gtls_t *pThis, gnutls_x509_crt *pCert)
finalize_it:
if(pstrFingerprint != NULL)
- rsCStrDestruct(&pstrFingerprint);
+ cstrDestruct(&pstrFingerprint);
RETiRet;
}
@@ -872,21 +874,21 @@ gtlsChkPeerName(nsd_gtls_t *pThis, gnutls_x509_crt *pCert)
/* if we did not succeed so far, we try the CN part of the DN... */
CHKiRet(gtlsGetCN(pThis, pCert, &pstrCN));
if(pstrCN != NULL) { /* NULL if there was no CN present */
- dbgprintf("gtls now checking auth for CN '%s'\n", rsCStrGetSzStr(pstrCN));
- snprintf((char*)lnBuf, sizeof(lnBuf), "CN: %s; ", rsCStrGetSzStr(pstrCN));
+ dbgprintf("gtls now checking auth for CN '%s'\n", cstrGetSzStr(pstrCN));
+ snprintf((char*)lnBuf, sizeof(lnBuf), "CN: %s; ", cstrGetSzStr(pstrCN));
CHKiRet(rsCStrAppendStr(pStr, lnBuf));
- CHKiRet(gtlsChkOnePeerName(pThis, rsCStrGetSzStr(pstrCN), &bFoundPositiveMatch));
+ CHKiRet(gtlsChkOnePeerName(pThis, cstrGetSzStr(pstrCN), &bFoundPositiveMatch));
}
}
if(!bFoundPositiveMatch) {
dbgprintf("invalid peer name, not permitted to talk to it\n");
if(pThis->bReportAuthErr == 1) {
- CHKiRet(rsCStrFinish(pStr));
+ CHKiRet(cstrFinalize(pStr));
errno = 0;
errmsg.LogError(0, RS_RET_INVALID_FINGERPRINT, "error: peer name not authorized - "
"not permitted to talk to it. Names: %s",
- rsCStrGetSzStr(pStr));
+ cstrGetSzStr(pStr));
pThis->bReportAuthErr = 0;
}
ABORT_FINALIZE(RS_RET_INVALID_FINGERPRINT);
@@ -1008,8 +1010,8 @@ gtlsChkPeerCertValidity(nsd_gtls_t *pThis)
errmsg.LogError(0, NO_ERRCODE, "not permitted to talk to peer, certificate invalid: %s",
pszErrCause);
gtlsGetCertInfo(pThis, &pStr);
- errmsg.LogError(0, NO_ERRCODE, "invalid cert info: %s", rsCStrGetSzStr(pStr));
- rsCStrDestruct(&pStr);
+ errmsg.LogError(0, NO_ERRCODE, "invalid cert info: %s", cstrGetSzStr(pStr));
+ cstrDestruct(&pStr);
ABORT_FINALIZE(RS_RET_CERT_INVALID);
}
@@ -1030,8 +1032,8 @@ gtlsChkPeerCertValidity(nsd_gtls_t *pThis)
else if(ttCert > ttNow) {
errmsg.LogError(0, RS_RET_CERT_NOT_YET_ACTIVE, "not permitted to talk to peer: certificate %d not yet active", i);
gtlsGetCertInfo(pThis, &pStr);
- errmsg.LogError(0, RS_RET_CERT_NOT_YET_ACTIVE, "invalid cert info: %s", rsCStrGetSzStr(pStr));
- rsCStrDestruct(&pStr);
+ errmsg.LogError(0, RS_RET_CERT_NOT_YET_ACTIVE, "invalid cert info: %s", cstrGetSzStr(pStr));
+ cstrDestruct(&pStr);
ABORT_FINALIZE(RS_RET_CERT_NOT_YET_ACTIVE);
}
@@ -1041,8 +1043,8 @@ gtlsChkPeerCertValidity(nsd_gtls_t *pThis)
else if(ttCert < ttNow) {
errmsg.LogError(0, RS_RET_CERT_EXPIRED, "not permitted to talk to peer: certificate %d expired", i);
gtlsGetCertInfo(pThis, &pStr);
- errmsg.LogError(0, RS_RET_CERT_EXPIRED, "invalid cert info: %s", rsCStrGetSzStr(pStr));
- rsCStrDestruct(&pStr);
+ errmsg.LogError(0, RS_RET_CERT_EXPIRED, "invalid cert info: %s", cstrGetSzStr(pStr));
+ cstrDestruct(&pStr);
ABORT_FINALIZE(RS_RET_CERT_EXPIRED);
}
gnutls_x509_crt_deinit(cert);
@@ -1116,6 +1118,7 @@ gtlsEndSess(nsd_gtls_t *pThis)
}
}
gnutls_deinit(pThis->sess);
+ pThis->bHaveSess = 0;
}
RETiRet;
}
@@ -1169,6 +1172,8 @@ CODESTARTobjDestruct(nsd_gtls)
gnutls_x509_crt_deinit(pThis->ourCert);
if(pThis->bOurKeyIsInit)
gnutls_x509_privkey_deinit(pThis->ourKey);
+ if(pThis->bHaveSess)
+ gnutls_deinit(pThis->sess);
ENDobjDestruct(nsd_gtls)
@@ -1323,7 +1328,10 @@ finalize_it:
static void
CheckConnection(nsd_t __attribute__((unused)) *pNsd)
{
- /* dummy, do nothing */
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+
+ nsd_ptcp.CheckConnection(pThis->pTcp);
}
@@ -1558,6 +1566,16 @@ finalize_it:
RETiRet;
}
+/* Enable KEEPALIVE handling on the socket.
+ * rgerhards, 2009-06-02
+ */
+static rsRetVal
+EnableKeepAlive(nsd_t *pNsd)
+{
+ return nsd_ptcp.EnableKeepAlive(pNsd);
+}
+
+
/* open a connection to a remote host (server). With GnuTLS, we always
* open a plain tcp socket and then, if in TLS mode, do a handshake on it.
@@ -1667,6 +1685,7 @@ CODESTARTobjQueryInterface(nsd_gtls)
pIf->GetRemoteHName = GetRemoteHName;
pIf->GetRemoteIP = GetRemoteIP;
pIf->GetRemAddr = GetRemAddr;
+ pIf->EnableKeepAlive = EnableKeepAlive;
finalize_it:
ENDobjQueryInterface(nsd_gtls)
diff --git a/runtime/nsd_ptcp.c b/runtime/nsd_ptcp.c
index cc531ca0..54ee0666 100644
--- a/runtime/nsd_ptcp.c
+++ b/runtime/nsd_ptcp.c
@@ -614,6 +614,34 @@ finalize_it:
}
+/* Enable KEEPALIVE handling on the socket.
+ * rgerhards, 2009-06-02
+ */
+static rsRetVal
+EnableKeepAlive(nsd_t *pNsd)
+{
+ nsd_ptcp_t *pThis = (nsd_ptcp_t*) pNsd;
+ int ret;
+ int optval;
+ socklen_t optlen;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, nsd_ptcp);
+
+ optval = 1;
+ optlen = sizeof(optval);
+ ret = setsockopt(pThis->sock, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen);
+ if(ret < 0) {
+ dbgprintf("EnableKeepAlive socket call returns error %d\n", ret);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+ dbgprintf("KEEPALIVE enabled for nsd %p\n", pThis);
+
+finalize_it:
+ RETiRet;
+}
+
+
/* open a connection to a remote host (server).
* rgerhards, 2008-03-19
*/
@@ -754,6 +782,7 @@ CODESTARTobjQueryInterface(nsd_ptcp)
pIf->GetRemoteHName = GetRemoteHName;
pIf->GetRemoteIP = GetRemoteIP;
pIf->CheckConnection = CheckConnection;
+ pIf->EnableKeepAlive = EnableKeepAlive;
finalize_it:
ENDobjQueryInterface(nsd_ptcp)
diff --git a/runtime/obj-types.h b/runtime/obj-types.h
index 914c2f2c..e1b54d4f 100644
--- a/runtime/obj-types.h
+++ b/runtime/obj-types.h
@@ -105,12 +105,13 @@ struct obj_s { /* the dummy struct that each derived class can be casted to */
# define ISOBJ_TYPE_assert(pObj, objType) \
do { \
ASSERT(pObj != NULL); \
- ASSERT((unsigned) ((obj_t*) (pObj))->iObjCooCKiE == (unsigned) 0xBADEFEE); \
if(strcmp((char*)(((obj_t*)pObj)->pObjInfo->pszID), #objType)) { \
dbgprintf("%s:%d ISOBJ assert failure: invalid object type, expected '%s' " \
- "actual '%s'\n", __FILE__, __LINE__, #objType, (((obj_t*)pObj)->pObjInfo->pszID)); \
+ "actual '%s', cookie: %X\n", __FILE__, __LINE__, #objType, \
+ (((obj_t*)pObj)->pObjInfo->pszID), ((obj_t*)(pObj))->iObjCooCKiE); \
assert(0); /* trigger assertion, messge we already have */ \
} \
+ ASSERT((unsigned) ((obj_t*)(pObj))->iObjCooCKiE == (unsigned) 0xBADEFEE); \
} while(0)
#else /* non-debug mode, no checks but much faster */
# define BEGINobjInstance obj_t objData
@@ -280,7 +281,7 @@ rsRetVal objName##ClassExit(void) \
* rgerhards, 2008-01-30
*/
#define BEGINobjDestruct(OBJ) \
- rsRetVal OBJ##Destruct(OBJ##_t **ppThis) \
+ rsRetVal OBJ##Destruct(OBJ##_t __attribute__((unused)) **ppThis) \
{ \
DEFiRet; \
int iCancelStateSave; \
@@ -292,6 +293,15 @@ rsRetVal objName##ClassExit(void) \
ISOBJ_TYPE_assert(pThis, OBJ); \
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave);
+/* note: there was a long-time bug in the macro below that lead to *ppThis = NULL
+ * only when the object was actually destructed. I discovered this issue during
+ * introduction of the pRcvFrom property in msg_t, but it potentially had other
+ * effects, too. I am not sure if some experienced instability resulted from this
+ * bug OR if its fix will cause harm to so-far "correctly" running code. The later
+ * may very well be. Thus I will change it only for the current branch and also
+ * the beta, but not in all old builds. Let's see how things evolve.
+ * rgerhards, 2009-06-30
+ */
#define ENDobjDestruct(OBJ) \
goto finalize_it; /* prevent compiler warning ;) */ \
/* no more code here! */ \
@@ -299,8 +309,8 @@ rsRetVal objName##ClassExit(void) \
if(pThis != NULL) { \
obj.DestructObjSelf((obj_t*) pThis); \
free(pThis); \
- *ppThis = NULL; \
} \
+ *ppThis = NULL; \
pthread_setcancelstate(iCancelStateSave, NULL); \
RETiRet; \
}
@@ -314,7 +324,7 @@ rsRetVal objName##ClassExit(void) \
#define PROTOTYPEObjDebugPrint(obj) rsRetVal obj##DebugPrint(obj##_t *pThis)
#define INTERFACEObjDebugPrint(obj) rsRetVal (*DebugPrint)(obj##_t *pThis)
#define BEGINobjDebugPrint(obj) \
- rsRetVal obj##DebugPrint(obj##_t *pThis) \
+ rsRetVal obj##DebugPrint(obj##_t __attribute__((unused)) *pThis) \
{ \
DEFiRet; \
diff --git a/runtime/obj.c b/runtime/obj.c
index 2a9df9ed..aebea332 100644
--- a/runtime/obj.c
+++ b/runtime/obj.c
@@ -75,6 +75,7 @@
#include <string.h>
#include <ctype.h>
#include <assert.h>
+#include <pthread.h>
/* how many objects are supported by rsyslogd? */
#define OBJ_NUM_IDS 100 /* TODO change to a linked list? info: 16 were currently in use 2008-02-29 */
@@ -87,13 +88,17 @@
#include "modules.h"
#include "errmsg.h"
#include "cfsysline.h"
+#include "unicode-helper.h"
+#include "apc.h"
/* static data */
DEFobjCurrIf(obj) /* we define our own interface, as this is expected by some macros! */
DEFobjCurrIf(var)
DEFobjCurrIf(module)
DEFobjCurrIf(errmsg)
+DEFobjCurrIf(strm)
static objInfo_t *arrObjInfo[OBJ_NUM_IDS]; /* array with object information pointers */
+static pthread_mutex_t mutObjGlobalOp; /* mutex to guard global operations of the object system */
/* cookies for serialized lines */
@@ -144,8 +149,8 @@ InfoConstruct(objInfo_t **ppThis, uchar *pszID, int iObjVers,
ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
pThis->pszID = pszID;
- pThis->lenID = strlen((char*)pszID);
- pThis->pszName = (uchar*)strdup((char*)pszID); /* it's OK if we have NULL ptr, GetName() will deal with that! */
+ pThis->lenID = ustrlen(pszID);
+ pThis->pszName = ustrdup(pszID); /* it's OK if we have NULL ptr, GetName() will deal with that! */
pThis->iObjVers = iObjVers;
pThis->QueryIF = pQueryIF;
pThis->pModInfo = pModInfo;
@@ -176,8 +181,7 @@ InfoDestruct(objInfo_t **ppThis)
pThis = *ppThis;
assert(pThis != NULL);
- if(pThis->pszName != NULL)
- free(pThis->pszName);
+ free(pThis->pszName);
free(pThis);
*ppThis = NULL;
@@ -205,9 +209,7 @@ DestructObjSelf(obj_t *pThis)
DEFiRet;
ISOBJ_assert(pThis);
- if(pThis->pszName != NULL) {
- free(pThis->pszName);
- }
+ free(pThis->pszName);
RETiRet;
}
@@ -228,20 +230,20 @@ static rsRetVal objSerializeHeader(strm_t *pStrm, obj_t *pObj, uchar *pszRecType
assert(!strcmp((char*) pszRecType, "Obj") || !strcmp((char*) pszRecType, "OPB"));
/* object cookie and serializer version (so far always 1) */
- CHKiRet(strmWriteChar(pStrm, COOKIE_OBJLINE));
- CHKiRet(strmWrite(pStrm, (uchar*) pszRecType, 3)); /* record types are always 3 octets */
- CHKiRet(strmWriteChar(pStrm, ':'));
- CHKiRet(strmWriteChar(pStrm, '1'));
+ CHKiRet(strm.WriteChar(pStrm, COOKIE_OBJLINE));
+ CHKiRet(strm.Write(pStrm, (uchar*) pszRecType, 3)); /* record types are always 3 octets */
+ CHKiRet(strm.WriteChar(pStrm, ':'));
+ CHKiRet(strm.WriteChar(pStrm, '1'));
/* object type, version and string length */
- CHKiRet(strmWriteChar(pStrm, ':'));
- CHKiRet(strmWrite(pStrm, pObj->pObjInfo->pszID, pObj->pObjInfo->lenID));
- CHKiRet(strmWriteChar(pStrm, ':'));
- CHKiRet(strmWriteLong(pStrm, objGetVersion(pObj)));
+ CHKiRet(strm.WriteChar(pStrm, ':'));
+ CHKiRet(strm.Write(pStrm, pObj->pObjInfo->pszID, pObj->pObjInfo->lenID));
+ CHKiRet(strm.WriteChar(pStrm, ':'));
+ CHKiRet(strm.WriteLong(pStrm, objGetVersion(pObj)));
/* record trailer */
- CHKiRet(strmWriteChar(pStrm, ':'));
- CHKiRet(strmWriteChar(pStrm, '\n'));
+ CHKiRet(strm.WriteChar(pStrm, ':'));
+ CHKiRet(strm.WriteChar(pStrm, '\n'));
finalize_it:
RETiRet;
@@ -259,7 +261,7 @@ BeginSerialize(strm_t *pStrm, obj_t *pObj)
ISOBJ_TYPE_assert(pStrm, strm);
ISOBJ_assert(pObj);
- CHKiRet(strmRecordBegin(pStrm));
+ CHKiRet(strm.RecordBegin(pStrm));
CHKiRet(objSerializeHeader(pStrm, pObj, (uchar*) "Obj"));
finalize_it:
@@ -284,7 +286,7 @@ BeginSerializePropBag(strm_t *pStrm, obj_t *pObj)
ISOBJ_TYPE_assert(pStrm, strm);
ISOBJ_assert(pObj);
- CHKiRet(strmRecordBegin(pStrm));
+ CHKiRet(strm.RecordBegin(pStrm));
CHKiRet(objSerializeHeader(pStrm, pObj, (uchar*) "OPB"));
finalize_it:
@@ -320,31 +322,31 @@ SerializeProp(strm_t *pStrm, uchar *pszPropName, propType_t propType, void *pUsr
switch(propType) {
case PROPTYPE_PSZ:
pszBuf = (uchar*) pUsr;
- lenBuf = strlen((char*) pszBuf);
+ lenBuf = ustrlen(pszBuf);
vType = VARTYPE_STR;
break;
case PROPTYPE_SHORT:
CHKiRet(srUtilItoA((char*) szBuf, sizeof(szBuf), (long) *((short*) pUsr)));
pszBuf = szBuf;
- lenBuf = strlen((char*) szBuf);
+ lenBuf = ustrlen(szBuf);
vType = VARTYPE_NUMBER;
break;
case PROPTYPE_INT:
CHKiRet(srUtilItoA((char*) szBuf, sizeof(szBuf), (long) *((int*) pUsr)));
pszBuf = szBuf;
- lenBuf = strlen((char*) szBuf);
+ lenBuf = ustrlen(szBuf);
vType = VARTYPE_NUMBER;
break;
case PROPTYPE_LONG:
CHKiRet(srUtilItoA((char*) szBuf, sizeof(szBuf), *((long*) pUsr)));
pszBuf = szBuf;
- lenBuf = strlen((char*) szBuf);
+ lenBuf = ustrlen(szBuf);
vType = VARTYPE_NUMBER;
break;
case PROPTYPE_INT64:
CHKiRet(srUtilItoA((char*) szBuf, sizeof(szBuf), *((int64*) pUsr)));
pszBuf = szBuf;
- lenBuf = strlen((char*) szBuf);
+ lenBuf = ustrlen(szBuf);
vType = VARTYPE_NUMBER;
break;
case PROPTYPE_CSTR:
@@ -377,23 +379,23 @@ SerializeProp(strm_t *pStrm, uchar *pszPropName, propType_t propType, void *pUsr
}
/* cookie */
- CHKiRet(strmWriteChar(pStrm, COOKIE_PROPLINE));
+ CHKiRet(strm.WriteChar(pStrm, COOKIE_PROPLINE));
/* name */
- CHKiRet(strmWrite(pStrm, pszPropName, strlen((char*)pszPropName)));
- CHKiRet(strmWriteChar(pStrm, ':'));
+ CHKiRet(strm.Write(pStrm, pszPropName, ustrlen(pszPropName)));
+ CHKiRet(strm.WriteChar(pStrm, ':'));
/* type */
- CHKiRet(strmWriteLong(pStrm, (int) vType));
- CHKiRet(strmWriteChar(pStrm, ':'));
+ CHKiRet(strm.WriteLong(pStrm, (int) vType));
+ CHKiRet(strm.WriteChar(pStrm, ':'));
/* length */
- CHKiRet(strmWriteLong(pStrm, lenBuf));
- CHKiRet(strmWriteChar(pStrm, ':'));
+ CHKiRet(strm.WriteLong(pStrm, lenBuf));
+ CHKiRet(strm.WriteChar(pStrm, ':'));
/* data */
- CHKiRet(strmWrite(pStrm, (uchar*) pszBuf, lenBuf));
+ CHKiRet(strm.Write(pStrm, (uchar*) pszBuf, lenBuf));
/* trailer */
- CHKiRet(strmWriteChar(pStrm, ':'));
- CHKiRet(strmWriteChar(pStrm, '\n'));
+ CHKiRet(strm.WriteChar(pStrm, ':'));
+ CHKiRet(strm.WriteChar(pStrm, '\n'));
finalize_it:
RETiRet;
@@ -410,12 +412,12 @@ EndSerialize(strm_t *pStrm)
assert(pStrm != NULL);
- CHKiRet(strmWriteChar(pStrm, COOKIE_ENDLINE));
- CHKiRet(strmWrite(pStrm, (uchar*) "End\n", sizeof("END\n") - 1));
- CHKiRet(strmWriteChar(pStrm, COOKIE_BLANKLINE));
- CHKiRet(strmWriteChar(pStrm, '\n'));
+ CHKiRet(strm.WriteChar(pStrm, COOKIE_ENDLINE));
+ CHKiRet(strm.Write(pStrm, (uchar*) "End\n", sizeof("END\n") - 1));
+ CHKiRet(strm.WriteChar(pStrm, COOKIE_BLANKLINE));
+ CHKiRet(strm.WriteChar(pStrm, '\n'));
- CHKiRet(strmRecordEnd(pStrm));
+ CHKiRet(strm.RecordEnd(pStrm));
finalize_it:
RETiRet;
@@ -423,7 +425,7 @@ finalize_it:
/* define a helper to make code below a bit cleaner (and quicker to write) */
-#define NEXTC CHKiRet(strmReadChar(pStrm, &c))/*;dbgprintf("c: %c\n", c)*/
+#define NEXTC CHKiRet(strm.ReadChar(pStrm, &c))/*;dbgprintf("c: %c\n", c)*/
/* de-serialize an embedded, non-octect-counted string. This is useful
@@ -440,20 +442,20 @@ objDeserializeEmbedStr(cstr_t **ppStr, strm_t *pStrm)
assert(ppStr != NULL);
- CHKiRet(rsCStrConstruct(&pStr));
+ CHKiRet(cstrConstruct(&pStr));
NEXTC;
while(c != ':') {
- CHKiRet(rsCStrAppendChar(pStr, c));
+ CHKiRet(cstrAppendChar(pStr, c));
NEXTC;
}
- CHKiRet(rsCStrFinish(pStr));
+ CHKiRet(cstrFinalize(pStr));
*ppStr = pStr;
finalize_it:
if(iRet != RS_RET_OK && pStr != NULL)
- rsCStrDestruct(&pStr);
+ cstrDestruct(&pStr);
RETiRet;
}
@@ -508,14 +510,14 @@ static rsRetVal objDeserializeStr(cstr_t **ppCStr, int iLen, strm_t *pStrm)
assert(ppCStr != NULL);
assert(iLen >= 0);
- CHKiRet(rsCStrConstruct(&pCStr));
+ CHKiRet(cstrConstruct(&pCStr));
NEXTC;
for(i = 0 ; i < iLen ; ++i) {
- CHKiRet(rsCStrAppendChar(pCStr, c));
+ CHKiRet(cstrAppendChar(pCStr, c));
NEXTC;
}
- CHKiRet(rsCStrFinish(pCStr));
+ CHKiRet(cstrFinalize(pCStr));
/* check terminator */
if(c != ':') ABORT_FINALIZE(RS_RET_INVALID_DELIMITER);
@@ -524,7 +526,7 @@ static rsRetVal objDeserializeStr(cstr_t **ppCStr, int iLen, strm_t *pStrm)
finalize_it:
if(iRet != RS_RET_OK && pCStr != NULL)
- rsCStrDestruct(&pCStr);
+ cstrDestruct(&pCStr);
RETiRet;
}
@@ -617,19 +619,19 @@ static rsRetVal objDeserializeProperty(var_t *pProp, strm_t *pStrm)
NEXTC;
if(c != COOKIE_PROPLINE) {
/* oops, we've read one char that does not belong to use - unget it first */
- CHKiRet(strmUnreadChar(pStrm, c));
+ CHKiRet(strm.UnreadChar(pStrm, c));
ABORT_FINALIZE(RS_RET_NO_PROPLINE);
}
/* get the property name first */
- CHKiRet(rsCStrConstruct(&pProp->pcsName));
+ CHKiRet(cstrConstruct(&pProp->pcsName));
NEXTC;
while(c != ':') {
- CHKiRet(rsCStrAppendChar(pProp->pcsName, c));
+ CHKiRet(cstrAppendChar(pProp->pcsName, c));
NEXTC;
}
- CHKiRet(rsCStrFinish(pProp->pcsName));
+ CHKiRet(cstrFinalize(pProp->pcsName));
/* property type */
CHKiRet(objDeserializeNumber(&i, pStrm));
@@ -718,7 +720,7 @@ static rsRetVal objDeserializeTryRecover(strm_t *pStrm)
}
}
- CHKiRet(strmUnreadChar(pStrm, c));
+ CHKiRet(strm.UnreadChar(pStrm, c));
finalize_it:
dbgprintf("deserializer has possibly been able to re-sync and recover, state %d\n", iRet);
@@ -803,7 +805,7 @@ Deserialize(void *ppObj, uchar *pszTypeExpected, strm_t *pStrm, rsRetVal (*fFixu
}
} while(iRetLocal != RS_RET_OK);
- if(rsCStrSzStrCmp(pstrID, pszTypeExpected, strlen((char*)pszTypeExpected))) /* TODO: optimize strlen() - caller shall provide */
+ if(rsCStrSzStrCmp(pstrID, pszTypeExpected, ustrlen(pszTypeExpected))) /* TODO: optimize strlen() - caller shall provide */
ABORT_FINALIZE(RS_RET_INVALID_OID);
CHKiRet(FindObjInfo(pstrID, &pObjInfo));
@@ -948,13 +950,8 @@ SetName(obj_t *pThis, uchar *pszName)
{
DEFiRet;
- if(pThis->pszName != NULL)
- free(pThis->pszName);
-
- pThis->pszName = (uchar*) strdup((char*) pszName);
-
- if(pThis->pszName == NULL)
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ free(pThis->pszName);
+ CHKmalloc(pThis->pszName = ustrdup(pszName));
finalize_it:
RETiRet;
@@ -1057,7 +1054,7 @@ RegisterObj(uchar *pszObjName, objInfo_t *pInfo)
i = 0;
while(!bFound && i < OBJ_NUM_IDS && arrObjInfo[i] != NULL) {
if( arrObjInfo[i] != NULL
- && !strcmp((char*)arrObjInfo[i]->pszID, (char*)pszObjName)) {
+ && !ustrcmp(arrObjInfo[i]->pszID, pszObjName)) {
bFound = 1;
break;
}
@@ -1096,7 +1093,7 @@ UnregisterObj(uchar *pszObjName)
i = 0;
while(!bFound && i < OBJ_NUM_IDS) {
if( arrObjInfo[i] != NULL
- && !strcmp((char*)arrObjInfo[i]->pszID, (char*)pszObjName)) {
+ && !ustrcmp(arrObjInfo[i]->pszID, pszObjName)) {
bFound = 1;
break;
}
@@ -1132,6 +1129,7 @@ UseObj(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf)
/* DEV debug only: dbgprintf("source file %s requests object '%s', ifIsLoaded %d\n", srcFile, pObjName, pIf->ifIsLoaded); */
+ d_pthread_mutex_lock(&mutObjGlobalOp);
if(pIf->ifIsLoaded == 1) {
ABORT_FINALIZE(RS_RET_OK); /* we are already set */
@@ -1172,6 +1170,8 @@ UseObj(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf)
pIf->ifIsLoaded = 1; /* we are happy */
finalize_it:
+ d_pthread_mutex_unlock(&mutObjGlobalOp);
+
if(pStr != NULL)
rsCStrDestruct(&pStr);
@@ -1193,15 +1193,16 @@ ReleaseObj(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf)
/* dev debug only dbgprintf("source file %s releasing object '%s', ifIsLoaded %d\n", srcFile, pObjName, pIf->ifIsLoaded); */
+ d_pthread_mutex_lock(&mutObjGlobalOp);
if(pObjFile == NULL)
FINALIZE; /* if it is not a lodable module, we do not need to do anything... */
if(pIf->ifIsLoaded == 0) {
- ABORT_FINALIZE(RS_RET_OK); /* we are not loaded - this is perfectly OK... */
+ FINALIZE; /* we are not loaded - this is perfectly OK... */
} else if(pIf->ifIsLoaded == 2) {
pIf->ifIsLoaded = 0; /* clean up */
- ABORT_FINALIZE(RS_RET_OK); /* we had a load error and can not continue */
+ FINALIZE; /* we had a load error and can not/must not continue */
}
CHKiRet(rsCStrConstructFromszStr(&pStr, pObjName));
@@ -1213,6 +1214,8 @@ ReleaseObj(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf)
pIf->ifIsLoaded = 0; /* indicated "no longer valid" */
finalize_it:
+ d_pthread_mutex_unlock(&mutObjGlobalOp);
+
if(pStr != NULL)
rsCStrDestruct(&pStr);
@@ -1278,14 +1281,15 @@ objClassExit(void)
{
DEFiRet;
/* release objects we no longer need */
+ objRelease(strm, CORE_COMPONENT);
objRelease(var, CORE_COMPONENT);
objRelease(module, CORE_COMPONENT);
objRelease(errmsg, CORE_COMPONENT);
/* TODO: implement the class exits! */
#if 0
- cfsyslineInit(pModInfo);
- varClassInit(pModInfo);
+ cfsyslineExit(pModInfo);
+ varClassExit(pModInfo);
#endif
errmsgClassExit();
moduleClassExit();
@@ -1304,8 +1308,9 @@ objClassExit(void)
rsRetVal
objClassInit(modInfo_t *pModInfo)
{
- DEFiRet;
+ pthread_mutexattr_t mutAttr;
int i;
+ DEFiRet;
/* first, initialize the object system itself. This must be done
* before any other object is created.
@@ -1314,17 +1319,27 @@ objClassInit(modInfo_t *pModInfo)
arrObjInfo[i] = NULL;
}
+ /* the mutex must be recursive, because objects may call into other
+ * object identifieres recursively.
+ */
+ pthread_mutexattr_init(&mutAttr);
+ pthread_mutexattr_settype(&mutAttr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&mutObjGlobalOp, &mutAttr);
+
/* request objects we use */
CHKiRet(objGetObjInterface(&obj)); /* get ourselves ;) */
/* init classes we use (limit to as few as possible!) */
+ CHKiRet(apcClassInit(pModInfo));
CHKiRet(errmsgClassInit(pModInfo));
CHKiRet(cfsyslineInit());
CHKiRet(varClassInit(pModInfo));
CHKiRet(moduleClassInit(pModInfo));
+ CHKiRet(strmClassInit(pModInfo));
CHKiRet(objUse(var, CORE_COMPONENT));
CHKiRet(objUse(module, CORE_COMPONENT));
CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(strm, CORE_COMPONENT));
finalize_it:
RETiRet;
diff --git a/runtime/obj.h b/runtime/obj.h
index dc04203b..419d29cc 100644
--- a/runtime/obj.h
+++ b/runtime/obj.h
@@ -68,7 +68,7 @@
#define objSerializePTR(strm, propName, propType) \
CHKiRet(obj.SerializeProp(strm, (uchar*) #propName, PROPTYPE_##propType, (void*) pThis->propName));
#define DEFobjStaticHelpers \
- static objInfo_t *pObjInfoOBJ = NULL; \
+ static objInfo_t __attribute__((unused)) *pObjInfoOBJ = NULL; \
DEFobjCurrIf(obj)
@@ -77,11 +77,13 @@
/* the next macro MUST be called in Constructors: */
#ifndef NDEBUG /* this means if debug... */
# define objConstructSetObjInfo(pThis) \
- ASSERT(((obj_t*) (pThis))->pObjInfo == NULL); \
((obj_t*) (pThis))->pObjInfo = pObjInfoOBJ; \
+ ((obj_t*) (pThis))->pszName = NULL; \
((obj_t*) (pThis))->iObjCooCKiE = 0xBADEFEE
#else
-# define objConstructSetObjInfo(pThis) ((obj_t*) (pThis))->pObjInfo = pObjInfoOBJ
+# define objConstructSetObjInfo(pThis) \
+ ((obj_t*) (pThis))->pObjInfo = pObjInfoOBJ; \
+ ((obj_t*) (pThis))->pszName = NULL
#endif
#define objDestruct(pThis) (((obj_t*) (pThis))->pObjInfo->objMethods[objMethod_DESTRUCT])(&pThis)
#define objSerialize(pThis) (((obj_t*) (pThis))->pObjInfo->objMethods[objMethod_SERIALIZE])
diff --git a/runtime/objomsr.c b/runtime/objomsr.c
index 21d284f3..8dddc4b8 100644
--- a/runtime/objomsr.c
+++ b/runtime/objomsr.c
@@ -141,5 +141,23 @@ int OMSRgetEntry(omodStringRequest_t *pThis, int iEntry, uchar **ppTplName, int
return RS_RET_OK;
}
+
+
+/* return the full set of template options that are supported by this version of
+ * OMSR. They are returned in an unsigned long value. The caller can mask that
+ * value to check on the option he is interested in.
+ * Note that this interface was added in 4.1.6, so a plugin must obtain a pointer
+ * to this interface via queryHostEtryPt().
+ * rgerhards, 2009-04-03
+ */
+rsRetVal
+OMSRgetSupportedTplOpts(unsigned long *pOpts)
+{
+ DEFiRet;
+ assert(pOpts != NULL);
+ *pOpts = OMSR_RQD_TPL_OPT_SQL | OMSR_TPL_AS_ARRAY;
+ RETiRet;
+}
+
/* vim:set ai:
*/
diff --git a/runtime/objomsr.h b/runtime/objomsr.h
index 2255e4f3..75ad0fb8 100644
--- a/runtime/objomsr.h
+++ b/runtime/objomsr.h
@@ -1,6 +1,6 @@
/* Definition of the omsr (omodStringRequest) object.
*
- * Copyright 2007 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007, 2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of the rsyslog runtime library.
*
@@ -27,7 +27,8 @@
/* define flags for required template options */
#define OMSR_NO_RQD_TPL_OPTS 0
#define OMSR_RQD_TPL_OPT_SQL 1
-/* next option is 2, 4, 8, ... */
+#define OMSR_TPL_AS_ARRAY 2 /* introduced in 4.1.6, 2009-04-03 */
+/* next option is 4, 8, 16, ... */
struct omodStringRequest_s { /* strings requested by output module for doAction() */
int iNumEntries; /* number of array entries for data elements below */
@@ -40,6 +41,7 @@ typedef struct omodStringRequest_s omodStringRequest_t;
rsRetVal OMSRdestruct(omodStringRequest_t *pThis);
rsRetVal OMSRconstruct(omodStringRequest_t **ppThis, int iNumEntries);
rsRetVal OMSRsetEntry(omodStringRequest_t *pThis, int iEntry, uchar *pTplName, int iTplOpts);
+rsRetVal OMSRgetSupportedTplOpts(unsigned long *pOpts);
int OMSRgetEntryCount(omodStringRequest_t *pThis);
int OMSRgetEntry(omodStringRequest_t *pThis, int iEntry, uchar **ppTplName, int *piTplOpts);
diff --git a/runtime/parser.c b/runtime/parser.c
new file mode 100644
index 00000000..d27f3e38
--- /dev/null
+++ b/runtime/parser.c
@@ -0,0 +1,332 @@
+/* parser.c
+ * This module contains functions for message parsers. It still needs to be
+ * converted into an object (and much extended).
+ *
+ * Module begun 2008-10-09 by Rainer Gerhards (based on previous code from syslogd.c)
+ *
+ * Copyright 2008 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 <ctype.h>
+#include <string.h>
+#include <assert.h>
+#ifdef USE_NETZIP
+#include <zlib.h>
+#endif
+
+#include "rsyslog.h"
+#include "dirty.h"
+#include "msg.h"
+#include "obj.h"
+#include "errmsg.h"
+
+/* some defines */
+#define DEFUPRI (LOG_USER|LOG_NOTICE)
+
+/* definitions for objects we access */
+DEFobjStaticHelpers
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(errmsg)
+
+/* static data */
+
+
+/* this is a dummy class init
+ */
+rsRetVal parserClassInit(void)
+{
+ DEFiRet;
+
+ /* request objects we use */
+ CHKiRet(objGetObjInterface(&obj)); /* this provides the root pointer for all other queries */
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+// TODO: free components! see action.c
+finalize_it:
+ RETiRet;
+}
+
+
+/* uncompress a received message if it is compressed.
+ * pMsg->pszRawMsg buffer is updated.
+ * rgerhards, 2008-10-09
+ */
+static inline rsRetVal uncompressMessage(msg_t *pMsg)
+{
+ DEFiRet;
+# ifdef USE_NETZIP
+ uchar *deflateBuf = NULL;
+ uLongf iLenDefBuf;
+ uchar *pszMsg;
+ size_t lenMsg;
+
+ assert(pMsg != NULL);
+ pszMsg = pMsg->pszRawMsg;
+ lenMsg = pMsg->iLenRawMsg;
+
+ /* we first need to check if we have a compressed record. If so,
+ * we must decompress it.
+ */
+ if(lenMsg > 0 && *pszMsg == 'z') { /* compressed data present? (do NOT change order if conditions!) */
+ /* we have compressed data, so let's deflate it. We support a maximum
+ * message size of iMaxLine. If it is larger, an error message is logged
+ * and the message is dropped. We do NOT try to decompress larger messages
+ * as such might be used for denial of service. It might happen to later
+ * builds that such functionality be added as an optional, operator-configurable
+ * feature.
+ */
+ int ret;
+ iLenDefBuf = glbl.GetMaxLine();
+ CHKmalloc(deflateBuf = malloc(sizeof(uchar) * (iLenDefBuf + 1)));
+ ret = uncompress((uchar *) deflateBuf, &iLenDefBuf, (uchar *) pszMsg+1, lenMsg-1);
+ DBGPRINTF("Compressed message uncompressed with status %d, length: new %ld, old %d.\n",
+ ret, (long) iLenDefBuf, (int) (lenMsg-1));
+ /* Now check if the uncompression worked. If not, there is not much we can do. In
+ * that case, we log an error message but ignore the message itself. Storing the
+ * compressed text is dangerous, as it contains control characters. So we do
+ * not do this. If someone would like to have a copy, this code here could be
+ * modified to do a hex-dump of the buffer in question. We do not include
+ * this functionality right now.
+ * rgerhards, 2006-12-07
+ */
+ if(ret != Z_OK) {
+ errmsg.LogError(0, NO_ERRCODE, "Uncompression of a message failed with return code %d "
+ "- enable debug logging if you need further information. "
+ "Message ignored.", ret);
+ FINALIZE; /* unconditional exit, nothing left to do... */
+ }
+ MsgSetRawMsg(pMsg, (char*)deflateBuf, iLenDefBuf);
+ }
+finalize_it:
+ if(deflateBuf != NULL)
+ free(deflateBuf);
+
+# else /* ifdef USE_NETZIP */
+
+ /* in this case, we still need to check if the message is compressed. If so, we must
+ * tell the user we can not accept it.
+ */
+ if(pMsg->iLenRawMsg > 0 && *pMsg->pszRawMsg == 'z') {
+ errmsg.LogError(0, NO_ERRCODE, "Received a compressed message, but rsyslogd does not have compression "
+ "support enabled. The message will be ignored.");
+ ABORT_FINALIZE(RS_RET_NO_ZIP);
+ }
+
+finalize_it:
+# endif /* ifdef USE_NETZIP */
+
+ RETiRet;
+}
+
+
+/* sanitize a received message
+ * if a message gets to large during sanitization, it is truncated. This is
+ * as specified in the upcoming syslog RFC series.
+ * rgerhards, 2008-10-09
+ * We check if we have a NUL character at the very end of the
+ * message. This seems to be a frequent problem with a number of senders.
+ * So I have now decided to drop these NULs. However, if they are intentional,
+ * that may cause us some problems, e.g. with syslog-sign. On the other hand,
+ * current code always has problems with intentional NULs (as it needs to escape
+ * them to prevent problems with the C string libraries), so that does not
+ * really matter. Just to be on the save side, we'll log destruction of such
+ * NULs in the debug log.
+ * rgerhards, 2007-09-14
+ */
+static inline rsRetVal
+sanitizeMessage(msg_t *pMsg)
+{
+ DEFiRet;
+ uchar *pszMsg;
+ uchar *pDst; /* destination for copy job */
+ size_t lenMsg;
+ size_t iSrc;
+ size_t iDst;
+ size_t iMaxLine;
+ size_t maxDest;
+ bool bUpdatedLen = FALSE;
+ uchar szSanBuf[32*1024]; /* buffer used for sanitizing a string */
+
+ assert(pMsg != NULL);
+ assert(pMsg->iLenRawMsg > 0);
+
+# ifdef USE_NETZIP
+ CHKiRet(uncompressMessage(pMsg));
+# endif
+
+ pszMsg = pMsg->pszRawMsg;
+ lenMsg = pMsg->iLenRawMsg;
+
+ /* remove NUL character at end of message (see comment in function header)
+ * Note that we do not need to add a NUL character in this case, because it
+ * is already present ;)
+ */
+ if(pszMsg[lenMsg-1] == '\0') {
+ DBGPRINTF("dropped NUL at very end of message\n");
+ bUpdatedLen = TRUE;
+ lenMsg--;
+ }
+
+ /* then we check if we need to drop trailing LFs, which often make
+ * their way into syslog messages unintentionally. In order to remain
+ * compatible to recent IETF developments, we allow the user to
+ * turn on/off this handling. rgerhards, 2007-07-23
+ */
+ if(bDropTrailingLF && pszMsg[lenMsg-1] == '\n') {
+ DBGPRINTF("dropped LF at very end of message (DropTrailingLF is set)\n");
+ lenMsg--;
+ pszMsg[lenMsg] = '\0';
+ bUpdatedLen = TRUE;
+ }
+
+ /* it is much quicker to sweep over the message and see if it actually
+ * needs sanitation than to do the sanitation in any case. So we first do
+ * this and terminate when it is not needed - which is expectedly the case
+ * for the vast majority of messages. -- rgerhards, 2009-06-15
+ */
+ int bNeedSanitize = 0;
+ for(iSrc = 0 ; iSrc < lenMsg ; iSrc++) {
+ if(iscntrl(pszMsg[iSrc])) {
+ if(pszMsg[iSrc] == '\0' || bEscapeCCOnRcv) {
+ bNeedSanitize = 1;
+ break;
+ }
+ }
+ }
+
+ if(!bNeedSanitize) {
+ if(bUpdatedLen == TRUE)
+ MsgSetRawMsgSize(pMsg, lenMsg);
+ FINALIZE;
+ }
+
+ /* now copy over the message and sanitize it */
+ iMaxLine = glbl.GetMaxLine();
+ maxDest = lenMsg * 4; /* message can grow at most four-fold */
+ if(maxDest > iMaxLine)
+ maxDest = iMaxLine; /* but not more than the max size! */
+ if(maxDest < sizeof(szSanBuf))
+ pDst = szSanBuf;
+ else
+ CHKmalloc(pDst = malloc(sizeof(uchar) * (iMaxLine + 1)));
+ iSrc = iDst = 0;
+ while(iSrc < lenMsg && iDst < maxDest - 3) { /* leave some space if last char must be escaped */
+ if(iscntrl((int) pszMsg[iSrc])) {
+ /* note: \0 must always be escaped, the rest of the code currently
+ * can not handle it! -- rgerhards, 2009-08-26
+ */
+ if(pszMsg[iSrc] == '\0' || bEscapeCCOnRcv) {
+ /* we are configured to escape control characters. Please note
+ * that this most probably break non-western character sets like
+ * Japanese, Korean or Chinese. rgerhards, 2007-07-17
+ */
+ pDst[iDst++] = cCCEscapeChar;
+ pDst[iDst++] = '0' + ((pszMsg[iSrc] & 0300) >> 6);
+ pDst[iDst++] = '0' + ((pszMsg[iSrc] & 0070) >> 3);
+ pDst[iDst++] = '0' + ((pszMsg[iSrc] & 0007));
+ }
+ } else {
+ pDst[iDst++] = pszMsg[iSrc];
+ }
+ ++iSrc;
+ }
+ pDst[iDst] = '\0';
+
+ MsgSetRawMsg(pMsg, (char*)pDst, iDst); /* save sanitized string */
+
+ if(pDst != szSanBuf)
+ free(pDst);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Parse a received message. The object's rawmsg property is taken and
+ * parsed according to the relevant standards. This can later be
+ * extended to support configured parsers.
+ * rgerhards, 2008-10-09
+ */
+rsRetVal parseMsg(msg_t *pMsg)
+{
+ DEFiRet;
+ uchar *msg;
+ int pri;
+ int lenMsg;
+
+ if(pMsg->iLenRawMsg == 0)
+ ABORT_FINALIZE(RS_RET_EMPTY_MSG);
+
+ CHKiRet(sanitizeMessage(pMsg));
+
+ /* we needed to sanitize first, because we otherwise do not have a C-string we can print... */
+ DBGPRINTF("msg parser: flags %x, from '%s', msg '%s'\n", pMsg->msgFlags, getRcvFrom(pMsg), pMsg->pszRawMsg);
+
+ /* pull PRI */
+ lenMsg = pMsg->iLenRawMsg;
+ msg = pMsg->pszRawMsg;
+ pri = DEFUPRI;
+ if(*msg == '<') {
+ /* while we process the PRI, we also fill the PRI textual representation
+ * inside the msg object. This may not be ideal from an OOP point of view,
+ * but it offers us performance...
+ */
+ pri = 0;
+ while(--lenMsg > 0 && isdigit((int) *++msg)) {
+ pri = 10 * pri + (*msg - '0');
+ }
+ if(*msg == '>')
+ ++msg;
+ if(pri & ~(LOG_FACMASK|LOG_PRIMASK))
+ pri = DEFUPRI;
+ }
+ pMsg->iFacility = LOG_FAC(pri);
+ pMsg->iSeverity = LOG_PRI(pri);
+ MsgSetAfterPRIOffs(pMsg, msg - pMsg->pszRawMsg);
+
+ /* rger 2005-11-24 (happy thanksgiving!): we now need to check if we have
+ * a traditional syslog message or one formatted according to syslog-protocol.
+ * We need to apply different parsers depending on that. We use the
+ * -protocol VERSION field for the detection.
+ */
+ if(msg[0] == '1' && msg[1] == ' ') {
+ dbgprintf("Message has syslog-protocol format.\n");
+ setProtocolVersion(pMsg, 1);
+ if(parseRFCSyslogMsg(pMsg, pMsg->msgFlags) == 1) {
+ msgDestruct(&pMsg);
+ ABORT_FINALIZE(RS_RET_ERR); // TODO: we need to handle these cases!
+ }
+ } else { /* we have legacy syslog */
+ dbgprintf("Message has legacy syslog format.\n");
+ setProtocolVersion(pMsg, 0);
+ if(parseLegacySyslogMsg(pMsg, pMsg->msgFlags) == 1) {
+ msgDestruct(&pMsg);
+ ABORT_FINALIZE(RS_RET_ERR); // TODO: we need to handle these cases!
+ }
+ }
+
+ /* finalize message object */
+ pMsg->msgFlags &= ~NEEDS_PARSING; /* this message is now parsed */
+
+finalize_it:
+ RETiRet;
+}
diff --git a/runtime/parser.h b/runtime/parser.h
new file mode 100644
index 00000000..cec9c083
--- /dev/null
+++ b/runtime/parser.h
@@ -0,0 +1,30 @@
+/* header for parser.c
+ * This is not yet an object, but contains all those code necessary to
+ * parse syslog messages.
+ *
+ * Copyright 2008 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 LGPL can be found in the file "COPYING.LESSER" in this distribution.
+ */
+#ifndef INCLUDED_PARSE_H
+#define INCLUDED_PARSE_H
+
+extern rsRetVal parserClassInit(void);
+extern rsRetVal parseMsg(msg_t*);
+
+#endif /* #ifndef INCLUDED_PARSE_H */
diff --git a/runtime/prop.c b/runtime/prop.c
new file mode 100644
index 00000000..d188b2ed
--- /dev/null
+++ b/runtime/prop.c
@@ -0,0 +1,247 @@
+/* prop.c - rsyslog's prop object
+ *
+ * This object is meant to support message properties that are stored
+ * seperately from the message. The main intent is to support properties
+ * that are "constant" during a period of time, so that many messages may
+ * contain a reference to the same property. It is important, though, that
+ * properties are destroyed when they are no longer needed.
+ *
+ * Please note that this is a performance-critical part of the software and
+ * as such we may use some methods in here which do not look elegant, but
+ * which are fast...
+ *
+ * Module begun 2009-06-17 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 <assert.h>
+#include <string.h>
+
+#include "rsyslog.h"
+#include "obj.h"
+#include "obj-types.h"
+#include "unicode-helper.h"
+#include "atomic.h"
+#include "prop.h"
+
+/* static data */
+DEFobjStaticHelpers
+
+
+/* Standard-Constructor
+ */
+BEGINobjConstruct(prop) /* be sure to specify the object type also in END macro! */
+ pThis->iRefCount = 1;
+ENDobjConstruct(prop)
+
+
+/* destructor for the prop object */
+BEGINobjDestruct(prop) /* be sure to specify the object type also in END and CODESTART macros! */
+ int currRefCount;
+CODESTARTobjDestruct(prop)
+ currRefCount = ATOMIC_DEC_AND_FETCH(pThis->iRefCount);
+ if(currRefCount == 0) {
+ /* (only) in this case we need to actually destruct the object */
+ if(pThis->len >= CONF_PROP_BUFSIZE)
+ free(pThis->szVal.psz);
+ } else {
+ pThis = NULL; /* tell framework NOT to destructing the object! */
+ }
+ENDobjDestruct(prop)
+
+/* set string, we make our own private copy! This MUST only be called BEFORE
+ * ConstructFinalize()!
+ */
+static rsRetVal SetString(prop_t *pThis, uchar *psz, int len)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, prop);
+ if(pThis->len >= CONF_PROP_BUFSIZE)
+ free(pThis->szVal.psz);
+ pThis->len = len;
+ if(len < CONF_PROP_BUFSIZE) {
+ memcpy(pThis->szVal.sz, psz, len + 1);
+ } else {
+ CHKmalloc(pThis->szVal.psz = malloc(len + 1));
+ memcpy(pThis->szVal.psz, psz, len + 1);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* get string length */
+static int GetStringLen(prop_t *pThis)
+{
+ return pThis->len;
+}
+
+
+/* get string */
+static rsRetVal GetString(prop_t *pThis, uchar **ppsz, int *plen)
+{
+ BEGINfunc
+ ISOBJ_TYPE_assert(pThis, prop);
+ if(pThis->len < CONF_PROP_BUFSIZE) {
+ *ppsz = pThis->szVal.sz;
+ } else {
+ *ppsz = pThis->szVal.psz;
+ }
+ *plen = pThis->len;
+ ENDfunc
+ return RS_RET_OK;
+}
+
+
+/* ConstructionFinalizer
+ * rgerhards, 2008-01-09
+ */
+static rsRetVal
+propConstructFinalize(prop_t __attribute__((unused)) *pThis)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, prop);
+ RETiRet;
+}
+
+
+/* add a new reference. It is VERY IMPORTANT to call this function whenever
+ * the property is handed over to some entitiy that later call Destruct() on it.
+ */
+static rsRetVal AddRef(prop_t *pThis)
+{
+ ATOMIC_INC(pThis->iRefCount);
+ return RS_RET_OK;
+}
+
+
+/* this is a "do it all in one shot" function that creates a new property,
+ * assigns the provided string to it and finalizes the property. Among the
+ * convenience, it is alos (very, very) slightly faster.
+ * rgerhards, 2009-07-01
+ */
+static rsRetVal CreateStringProp(prop_t **ppThis, uchar* psz, int len)
+{
+ DEFiRet;
+ propConstruct(ppThis);
+ SetString(*ppThis, psz, len);
+ propConstructFinalize(*ppThis);
+ RETiRet;
+}
+
+/* another one-stop function, quite useful: it takes a property pointer and
+ * a string. If the string is already contained in the property, nothing happens.
+ * If the string is different (or the pointer NULL), the current property
+ * is destructed and a new one created. This can be used to get a specific
+ * name in those cases where there is a good chance that the property
+ * immediatly previously processed already contained the value we need - in
+ * which case we save us all the creation overhead by just reusing the already
+ * existing property).
+ * rgerhards, 2009-07-01
+ */
+rsRetVal CreateOrReuseStringProp(prop_t **ppThis, uchar *psz, int len)
+{
+ uchar *pszPrev;
+ int lenPrev;
+ DEFiRet;
+ assert(ppThis != NULL);
+
+ if(*ppThis == NULL) {
+ /* we need to create a property */
+ CHKiRet(CreateStringProp(ppThis, psz, len));
+ } else {
+ /* already exists, check if we can re-use it */
+ GetString(*ppThis, &pszPrev, &lenPrev);
+ if(len != lenPrev || ustrcmp(psz, pszPrev)) {
+ /* different, need to discard old & create new one */
+ propDestruct(ppThis);
+ CHKiRet(CreateStringProp(ppThis, psz, len));
+ } /* else we can re-use the existing one! */
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* debugprint for the prop object */
+BEGINobjDebugPrint(prop) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDebugPrint(prop)
+ dbgprintf("prop object %p - no further debug info implemented\n", pThis);
+ENDobjDebugPrint(prop)
+
+
+/* queryInterface function
+ * rgerhards, 2008-02-21
+ */
+BEGINobjQueryInterface(prop)
+CODESTARTobjQueryInterface(prop)
+ if(pIf->ifVersion != propCURR_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 = propConstruct;
+ pIf->ConstructFinalize = propConstructFinalize;
+ pIf->Destruct = propDestruct;
+ pIf->DebugPrint = propDebugPrint;
+ pIf->SetString = SetString;
+ pIf->GetString = GetString;
+ pIf->GetStringLen = GetStringLen;
+ pIf->AddRef = AddRef;
+ pIf->CreateStringProp = CreateStringProp;
+ pIf->CreateOrReuseStringProp = CreateOrReuseStringProp;
+
+finalize_it:
+ENDobjQueryInterface(prop)
+
+
+/* Exit the prop class.
+ * rgerhards, 2009-04-06
+ */
+BEGINObjClassExit(prop, OBJ_IS_CORE_MODULE) /* class, version */
+// objRelease(errmsg, CORE_COMPONENT);
+ENDObjClassExit(prop)
+
+
+/* Initialize the prop class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-02-19
+ */
+BEGINObjClassInit(prop, 1, OBJ_IS_CORE_MODULE) /* class, version */
+ /* request objects we use */
+// CHKiRet(objUse(errmsg, CORE_COMPONENT));
+
+ /* set our own handlers */
+ OBJSetMethodHandler(objMethod_DEBUGPRINT, propDebugPrint);
+ OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, propConstructFinalize);
+ENDObjClassInit(prop)
+
+/* vi:set ai:
+ */
diff --git a/runtime/prop.h b/runtime/prop.h
new file mode 100644
index 00000000..e3519664
--- /dev/null
+++ b/runtime/prop.h
@@ -0,0 +1,58 @@
+/* The prop object.
+ *
+ * This implements props within rsyslog.
+ *
+ * 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_PROP_H
+#define INCLUDED_PROP_H
+
+/* the prop object */
+struct prop_s {
+ BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
+ int iRefCount; /* reference counter */
+ union {
+ uchar *psz; /* stored string */
+ uchar sz[CONF_PROP_BUFSIZE];
+ } szVal;
+ int len; /* we use int intentionally, otherwise we may get some troubles... */
+};
+
+/* interfaces */
+BEGINinterface(prop) /* name must also be changed in ENDinterface macro! */
+ INTERFACEObjDebugPrint(prop);
+ rsRetVal (*Construct)(prop_t **ppThis);
+ rsRetVal (*ConstructFinalize)(prop_t *pThis);
+ rsRetVal (*Destruct)(prop_t **ppThis);
+ rsRetVal (*SetString)(prop_t *pThis, uchar* psz, int len);
+ rsRetVal (*GetString)(prop_t *pThis, uchar** ppsz, int *plen);
+ int (*GetStringLen)(prop_t *pThis);
+ rsRetVal (*AddRef)(prop_t *pThis);
+ rsRetVal (*CreateStringProp)(prop_t **ppThis, uchar* psz, int len);
+ rsRetVal (*CreateOrReuseStringProp)(prop_t **ppThis, uchar *psz, int len);
+ENDinterface(prop)
+#define propCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+
+
+/* prototypes */
+PROTOTYPEObj(prop);
+
+#endif /* #ifndef INCLUDED_PROP_H */
diff --git a/runtime/queue.c b/runtime/queue.c
index e58b1686..4d94941a 100644
--- a/runtime/queue.c
+++ b/runtime/queue.c
@@ -49,20 +49,28 @@
#include "obj.h"
#include "wtp.h"
#include "wti.h"
+#include "msg.h"
+#include "atomic.h"
+
+#ifdef OS_SOLARIS
+# include <sched.h>
+# define pthread_yield() sched_yield()
+#endif
/* static data */
DEFobjStaticHelpers
DEFobjCurrIf(glbl)
+DEFobjCurrIf(strm)
/* forward-definitions */
-rsRetVal queueChkPersist(queue_t *pThis);
-static rsRetVal queueSetEnqOnly(queue_t *pThis, int bEnqOnly, int bLockMutex);
-static rsRetVal queueRateLimiter(queue_t *pThis);
-static int queueChkStopWrkrDA(queue_t *pThis);
-static int queueIsIdleDA(queue_t *pThis);
-static rsRetVal queueConsumerDA(queue_t *pThis, wti_t *pWti, int iCancelStateSave);
-static rsRetVal queueConsumerCancelCleanup(void *arg1, void *arg2);
-static rsRetVal queueUngetObj(queue_t *pThis, obj_t *pUsr, int bLockMutex);
+static rsRetVal qqueueChkPersist(qqueue_t *pThis);
+static rsRetVal qqueueSetEnqOnly(qqueue_t *pThis, int bEnqOnly, int bLockMutex);
+static rsRetVal qqueueRateLimiter(qqueue_t *pThis);
+static int qqueueChkStopWrkrDA(qqueue_t *pThis);
+static int qqueueIsIdleDA(qqueue_t *pThis);
+static rsRetVal qqueueConsumerDA(qqueue_t *pThis, wti_t *pWti, int iCancelStateSave);
+static rsRetVal qqueueConsumerCancelCleanup(void *arg1, void *arg2);
+static rsRetVal qqueueUngetObj(qqueue_t *pThis, obj_t *pUsr, int bLockMutex);
/* some constants for queuePersist () */
#define QUEUE_CHECKPOINT 1
@@ -76,7 +84,7 @@ static rsRetVal queueUngetObj(queue_t *pThis, obj_t *pUsr, int bLockMutex);
* rgerhards, 2008-01-29
*/
static inline int
-queueGetOverallQueueSize(queue_t *pThis)
+qqueueGetOverallQueueSize(qqueue_t *pThis)
{
#if 0 /* leave a bit in for debugging -- rgerhards, 2008-01-30 */
BEGINfunc
@@ -95,14 +103,14 @@ ENDfunc
* This function returns void, as it makes no sense to communicate an error back, even if
* it happens.
*/
-static inline void queueDrain(queue_t *pThis)
+static inline void queueDrain(qqueue_t *pThis)
{
void *pUsr;
ASSERT(pThis != NULL);
/* iQueueSize is not decremented by qDel(), so we need to do it ourselves */
- while(pThis->iQueueSize-- > 0) {
+ while(ATOMIC_DEC_AND_FETCH(pThis->iQueueSize) > 0) {
pThis->qDel(pThis, &pUsr);
if(pUsr != NULL) {
objDestruct(pUsr);
@@ -118,26 +126,26 @@ static inline void queueDrain(queue_t *pThis)
* this point in time. The mutex must be locked when
* ths function is called. -- rgerhards, 2008-01-25
*/
-static inline rsRetVal queueAdviseMaxWorkers(queue_t *pThis)
+static inline rsRetVal qqueueAdviseMaxWorkers(qqueue_t *pThis)
{
DEFiRet;
int iMaxWorkers;
- ISOBJ_TYPE_assert(pThis, queue);
+ ISOBJ_TYPE_assert(pThis, qqueue);
if(!pThis->bEnqOnly) {
if(pThis->bRunsDA) {
/* if we have not yet reached the high water mark, there is no need to start a
* worker. -- rgerhards, 2008-01-26
*/
- if(queueGetOverallQueueSize(pThis) >= pThis->iHighWtrMrk || pThis->bQueueStarted == 0) {
+ if(qqueueGetOverallQueueSize(pThis) >= pThis->iHighWtrMrk || pThis->bQueueStarted == 0) {
wtpAdviseMaxWorkers(pThis->pWtpDA, 1); /* disk queues have always one worker */
}
} else {
if(pThis->qType == QUEUETYPE_DISK || pThis->iMinMsgsPerWrkr == 0) {
iMaxWorkers = 1;
} else {
- iMaxWorkers = queueGetOverallQueueSize(pThis) / pThis->iMinMsgsPerWrkr + 1;
+ iMaxWorkers = qqueueGetOverallQueueSize(pThis) / pThis->iMinMsgsPerWrkr + 1;
}
wtpAdviseMaxWorkers(pThis->pWtpReg, iMaxWorkers); /* disk queues have always one worker */
}
@@ -152,11 +160,11 @@ static inline rsRetVal queueAdviseMaxWorkers(queue_t *pThis)
* rgerhards, 2008-02-27
*/
static rsRetVal
-queueWaitDAModeInitialized(queue_t *pThis)
+qqueueWaitDAModeInitialized(qqueue_t *pThis)
{
DEFiRet;
- ISOBJ_TYPE_assert(pThis, queue);
+ ISOBJ_TYPE_assert(pThis, qqueue);
ASSERT(pThis->bRunsDA);
while(pThis->bRunsDA != 2) {
@@ -178,17 +186,17 @@ queueWaitDAModeInitialized(queue_t *pThis)
* rgerhards, 2008-01-15
*/
static rsRetVal
-queueTurnOffDAMode(queue_t *pThis)
+qqueueTurnOffDAMode(qqueue_t *pThis)
{
DEFiRet;
- ISOBJ_TYPE_assert(pThis, queue);
+ ISOBJ_TYPE_assert(pThis, qqueue);
ASSERT(pThis->bRunsDA);
/* at this point, we need a fully initialized DA queue. So if it isn't, we finally need
* to wait for its startup... -- rgerhards, 2008-01-25
*/
- queueWaitDAModeInitialized(pThis);
+ qqueueWaitDAModeInitialized(pThis);
/* if we need to pull any data that we still need from the (child) disk queue,
* now would be the time to do so. At present, we do not need this, but I'd like to
@@ -207,15 +215,15 @@ queueTurnOffDAMode(queue_t *pThis)
/* we destruct the queue object, which will also shutdown the queue worker. As the queue is empty,
* this will be quick.
*/
- queueDestruct(&pThis->pqDA); /* and now we are ready to destruct the DA queue */
+ qqueueDestruct(&pThis->pqDA); /* and now we are ready to destruct the DA queue */
dbgoprint((obj_t*) pThis, "disk-assistance has been turned off, disk queue was empty (iRet %d)\n",
iRet);
/* now we need to check if the regular queue has some messages. This may be the case
* when it is waiting that the high water mark is reached again. If so, we need to start up
* a regular worker. -- rgerhards, 2008-01-26
*/
- if(queueGetOverallQueueSize(pThis) > 0) {
- queueAdviseMaxWorkers(pThis);
+ if(qqueueGetOverallQueueSize(pThis) > 0) {
+ qqueueAdviseMaxWorkers(pThis);
}
}
@@ -231,11 +239,11 @@ queueTurnOffDAMode(queue_t *pThis)
* rgerhards, 2008-01-14
*/
static rsRetVal
-queueChkIsDA(queue_t *pThis)
+qqueueChkIsDA(qqueue_t *pThis)
{
DEFiRet;
- ISOBJ_TYPE_assert(pThis, queue);
+ ISOBJ_TYPE_assert(pThis, qqueue);
if(pThis->pszFilePrefix != NULL) {
pThis->bIsDA = 1;
dbgoprint((obj_t*) pThis, "is disk-assisted, disk will be used on demand\n");
@@ -259,18 +267,18 @@ queueChkIsDA(queue_t *pThis)
* rgerhards, 2008-01-15
*/
static rsRetVal
-queueStartDA(queue_t *pThis)
+qqueueStartDA(qqueue_t *pThis)
{
DEFiRet;
uchar pszDAQName[128];
- ISOBJ_TYPE_assert(pThis, queue);
+ ISOBJ_TYPE_assert(pThis, qqueue);
if(pThis->bRunsDA == 2) /* check if already in (fully initialized) DA mode... */
FINALIZE; /* ... then we are already done! */
/* create message queue */
- CHKiRet(queueConstruct(&pThis->pqDA, QUEUETYPE_DISK , 1, 0, pThis->pConsumer));
+ CHKiRet(qqueueConstruct(&pThis->pqDA, QUEUETYPE_DISK , 1, 0, pThis->pConsumer));
/* give it a name */
snprintf((char*) pszDAQName, sizeof(pszDAQName)/sizeof(uchar), "%s[DA]", obj.GetName((obj_t*) pThis));
@@ -281,30 +289,31 @@ queueStartDA(queue_t *pThis)
*/
pThis->pqDA->pqParent = pThis;
- CHKiRet(queueSetpUsr(pThis->pqDA, pThis->pUsr));
- CHKiRet(queueSetsizeOnDiskMax(pThis->pqDA, pThis->sizeOnDiskMax));
- CHKiRet(queueSetiDeqSlowdown(pThis->pqDA, pThis->iDeqSlowdown));
- CHKiRet(queueSetMaxFileSize(pThis->pqDA, pThis->iMaxFileSize));
- CHKiRet(queueSetFilePrefix(pThis->pqDA, pThis->pszFilePrefix, pThis->lenFilePrefix));
- CHKiRet(queueSetiPersistUpdCnt(pThis->pqDA, pThis->iPersistUpdCnt));
- CHKiRet(queueSettoActShutdown(pThis->pqDA, pThis->toActShutdown));
- CHKiRet(queueSettoEnq(pThis->pqDA, pThis->toEnq));
- CHKiRet(queueSetEnqOnly(pThis->pqDA, pThis->bDAEnqOnly, MUTEX_ALREADY_LOCKED));
- CHKiRet(queueSetiDeqtWinFromHr(pThis->pqDA, pThis->iDeqtWinFromHr));
- CHKiRet(queueSetiDeqtWinToHr(pThis->pqDA, pThis->iDeqtWinToHr));
- CHKiRet(queueSetiHighWtrMrk(pThis->pqDA, 0));
- CHKiRet(queueSetiDiscardMrk(pThis->pqDA, 0));
+ CHKiRet(qqueueSetpUsr(pThis->pqDA, pThis->pUsr));
+ CHKiRet(qqueueSetsizeOnDiskMax(pThis->pqDA, pThis->sizeOnDiskMax));
+ CHKiRet(qqueueSetiDeqSlowdown(pThis->pqDA, pThis->iDeqSlowdown));
+ CHKiRet(qqueueSetMaxFileSize(pThis->pqDA, pThis->iMaxFileSize));
+ CHKiRet(qqueueSetFilePrefix(pThis->pqDA, pThis->pszFilePrefix, pThis->lenFilePrefix));
+ CHKiRet(qqueueSetiPersistUpdCnt(pThis->pqDA, pThis->iPersistUpdCnt));
+ CHKiRet(qqueueSetbSyncQueueFiles(pThis->pqDA, pThis->bSyncQueueFiles));
+ CHKiRet(qqueueSettoActShutdown(pThis->pqDA, pThis->toActShutdown));
+ CHKiRet(qqueueSettoEnq(pThis->pqDA, pThis->toEnq));
+ CHKiRet(qqueueSetEnqOnly(pThis->pqDA, pThis->bDAEnqOnly, MUTEX_ALREADY_LOCKED));
+ CHKiRet(qqueueSetiDeqtWinFromHr(pThis->pqDA, pThis->iDeqtWinFromHr));
+ CHKiRet(qqueueSetiDeqtWinToHr(pThis->pqDA, pThis->iDeqtWinToHr));
+ CHKiRet(qqueueSetiHighWtrMrk(pThis->pqDA, 0));
+ CHKiRet(qqueueSetiDiscardMrk(pThis->pqDA, 0));
if(pThis->toQShutdown == 0) {
- CHKiRet(queueSettoQShutdown(pThis->pqDA, 0)); /* if the user really wants... */
+ CHKiRet(qqueueSettoQShutdown(pThis->pqDA, 0)); /* if the user really wants... */
} else {
/* we use the shortest possible shutdown (0 is endless!) because when we run on disk AND
* have an obviously large backlog, we can't finish it in any case. So there is no point
* in holding shutdown longer than necessary. -- rgerhards, 2008-01-15
*/
- CHKiRet(queueSettoQShutdown(pThis->pqDA, 1));
+ CHKiRet(qqueueSettoQShutdown(pThis->pqDA, 1));
}
- iRet = queueStart(pThis->pqDA);
+ iRet = qqueueStart(pThis->pqDA);
/* file not found is expected, that means it is no previous QIF available */
if(iRet != RS_RET_OK && iRet != RS_RET_FILE_NOT_FOUND)
FINALIZE; /* something is wrong */
@@ -322,12 +331,12 @@ queueStartDA(queue_t *pThis)
pthread_cond_broadcast(&pThis->condDAReady); /* signal we are now initialized and ready to go ;) */
dbgoprint((obj_t*) pThis, "is now running in disk assisted mode, disk queue 0x%lx\n",
- queueGetID(pThis->pqDA));
+ qqueueGetID(pThis->pqDA));
finalize_it:
if(iRet != RS_RET_OK) {
if(pThis->pqDA != NULL) {
- queueDestruct(&pThis->pqDA);
+ qqueueDestruct(&pThis->pqDA);
}
dbgoprint((obj_t*) pThis, "error %d creating disk queue - giving up.\n", iRet);
pThis->bIsDA = 0;
@@ -344,7 +353,7 @@ finalize_it:
* rgerhards, 2008-01-16
*/
static inline rsRetVal
-queueInitDA(queue_t *pThis, int bEnqOnly, int bLockMutex)
+qqueueInitDA(qqueue_t *pThis, int bEnqOnly, int bLockMutex)
{
DEFiRet;
DEFVARS_mutexProtection;
@@ -362,12 +371,12 @@ queueInitDA(queue_t *pThis, int bEnqOnly, int bLockMutex)
lenBuf = snprintf((char*)pszBuf, sizeof(pszBuf), "%s:DA", obj.GetName((obj_t*) pThis));
CHKiRet(wtpConstruct (&pThis->pWtpDA));
CHKiRet(wtpSetDbgHdr (pThis->pWtpDA, pszBuf, lenBuf));
- CHKiRet(wtpSetpfChkStopWrkr (pThis->pWtpDA, (rsRetVal (*)(void *pUsr, int)) queueChkStopWrkrDA));
- CHKiRet(wtpSetpfIsIdle (pThis->pWtpDA, (rsRetVal (*)(void *pUsr, int)) queueIsIdleDA));
- CHKiRet(wtpSetpfDoWork (pThis->pWtpDA, (rsRetVal (*)(void *pUsr, void *pWti, int)) queueConsumerDA));
- CHKiRet(wtpSetpfOnWorkerCancel (pThis->pWtpDA, (rsRetVal (*)(void *pUsr, void*pWti)) queueConsumerCancelCleanup));
- CHKiRet(wtpSetpfOnWorkerStartup (pThis->pWtpDA, (rsRetVal (*)(void *pUsr)) queueStartDA));
- CHKiRet(wtpSetpfOnWorkerShutdown(pThis->pWtpDA, (rsRetVal (*)(void *pUsr)) queueTurnOffDAMode));
+ CHKiRet(wtpSetpfChkStopWrkr (pThis->pWtpDA, (rsRetVal (*)(void *pUsr, int)) qqueueChkStopWrkrDA));
+ CHKiRet(wtpSetpfIsIdle (pThis->pWtpDA, (rsRetVal (*)(void *pUsr, int)) qqueueIsIdleDA));
+ CHKiRet(wtpSetpfDoWork (pThis->pWtpDA, (rsRetVal (*)(void *pUsr, void *pWti, int)) qqueueConsumerDA));
+ CHKiRet(wtpSetpfOnWorkerCancel (pThis->pWtpDA, (rsRetVal (*)(void *pUsr, void*pWti)) qqueueConsumerCancelCleanup));
+ CHKiRet(wtpSetpfOnWorkerStartup (pThis->pWtpDA, (rsRetVal (*)(void *pUsr)) qqueueStartDA));
+ CHKiRet(wtpSetpfOnWorkerShutdown(pThis->pWtpDA, (rsRetVal (*)(void *pUsr)) qqueueTurnOffDAMode));
CHKiRet(wtpSetpmutUsr (pThis->pWtpDA, pThis->mut));
CHKiRet(wtpSetpcondBusy (pThis->pWtpDA, &pThis->notEmpty));
CHKiRet(wtpSetiNumWorkerThreads (pThis->pWtpDA, 1));
@@ -400,14 +409,14 @@ finalize_it:
* rgerhards, 2008-01-14
*/
static inline rsRetVal
-queueChkStrtDA(queue_t *pThis)
+qqueueChkStrtDA(qqueue_t *pThis)
{
DEFiRet;
- ISOBJ_TYPE_assert(pThis, queue);
+ ISOBJ_TYPE_assert(pThis, qqueue);
/* if we do not hit the high water mark, we have nothing to do */
- if(queueGetOverallQueueSize(pThis) != pThis->iHighWtrMrk)
+ if(qqueueGetOverallQueueSize(pThis) != pThis->iHighWtrMrk)
ABORT_FINALIZE(RS_RET_OK);
if(pThis->bRunsDA) {
@@ -421,15 +430,15 @@ queueChkStrtDA(queue_t *pThis)
* we need at least one).
*/
dbgoprint((obj_t*) pThis, "%d entries - passed high water mark in DA mode, send notify\n",
- queueGetOverallQueueSize(pThis));
- queueAdviseMaxWorkers(pThis);
+ qqueueGetOverallQueueSize(pThis));
+ qqueueAdviseMaxWorkers(pThis);
} else {
/* this is the case when we are currently not running in DA mode. So it is time
* to turn it back on.
*/
dbgoprint((obj_t*) pThis, "%d entries - passed high water mark for disk-assisted mode, initiating...\n",
- queueGetOverallQueueSize(pThis));
- queueInitDA(pThis, QUEUE_MODE_ENQDEQ, MUTEX_ALREADY_LOCKED); /* initiate DA mode */
+ qqueueGetOverallQueueSize(pThis));
+ qqueueInitDA(pThis, QUEUE_MODE_ENQDEQ, MUTEX_ALREADY_LOCKED); /* initiate DA mode */
}
finalize_it:
@@ -447,7 +456,7 @@ finalize_it:
*/
/* -------------------- fixed array -------------------- */
-static rsRetVal qConstructFixedArray(queue_t *pThis)
+static rsRetVal qConstructFixedArray(qqueue_t *pThis)
{
DEFiRet;
@@ -463,14 +472,14 @@ static rsRetVal qConstructFixedArray(queue_t *pThis)
pThis->tVars.farray.head = 0;
pThis->tVars.farray.tail = 0;
- queueChkIsDA(pThis);
+ qqueueChkIsDA(pThis);
finalize_it:
RETiRet;
}
-static rsRetVal qDestructFixedArray(queue_t *pThis)
+static rsRetVal qDestructFixedArray(qqueue_t *pThis)
{
DEFiRet;
@@ -485,7 +494,7 @@ static rsRetVal qDestructFixedArray(queue_t *pThis)
}
-static rsRetVal qAddFixedArray(queue_t *pThis, void* in)
+static rsRetVal qAddFixedArray(qqueue_t *pThis, void* in)
{
DEFiRet;
@@ -498,7 +507,7 @@ static rsRetVal qAddFixedArray(queue_t *pThis, void* in)
RETiRet;
}
-static rsRetVal qDelFixedArray(queue_t *pThis, void **out)
+static rsRetVal qDelFixedArray(qqueue_t *pThis, void **out)
{
DEFiRet;
@@ -517,7 +526,7 @@ static rsRetVal qDelFixedArray(queue_t *pThis, void **out)
/* first some generic functions which are also used for the unget linked list */
-static inline rsRetVal queueAddLinkedList(qLinkedList_t **ppRoot, qLinkedList_t **ppLast, void* pUsr)
+static inline rsRetVal qqueueAddLinkedList(qLinkedList_t **ppRoot, qLinkedList_t **ppLast, void* pUsr)
{
DEFiRet;
qLinkedList_t *pEntry;
@@ -543,7 +552,7 @@ finalize_it:
RETiRet;
}
-static inline rsRetVal queueDelLinkedList(qLinkedList_t **ppRoot, qLinkedList_t **ppLast, obj_t **ppUsr)
+static inline rsRetVal qqueueDelLinkedList(qLinkedList_t **ppRoot, qLinkedList_t **ppLast, obj_t **ppUsr)
{
DEFiRet;
qLinkedList_t *pEntry;
@@ -570,7 +579,7 @@ static inline rsRetVal queueDelLinkedList(qLinkedList_t **ppRoot, qLinkedList_t
/* end generic functions which are also used for the unget linked list */
-static rsRetVal qConstructLinkedList(queue_t *pThis)
+static rsRetVal qConstructLinkedList(qqueue_t *pThis)
{
DEFiRet;
@@ -579,13 +588,13 @@ static rsRetVal qConstructLinkedList(queue_t *pThis)
pThis->tVars.linklist.pRoot = 0;
pThis->tVars.linklist.pLast = 0;
- queueChkIsDA(pThis);
+ qqueueChkIsDA(pThis);
RETiRet;
}
-static rsRetVal qDestructLinkedList(queue_t __attribute__((unused)) *pThis)
+static rsRetVal qDestructLinkedList(qqueue_t __attribute__((unused)) *pThis)
{
DEFiRet;
@@ -598,11 +607,11 @@ static rsRetVal qDestructLinkedList(queue_t __attribute__((unused)) *pThis)
RETiRet;
}
-static rsRetVal qAddLinkedList(queue_t *pThis, void* pUsr)
+static rsRetVal qAddLinkedList(qqueue_t *pThis, void* pUsr)
{
DEFiRet;
- iRet = queueAddLinkedList(&pThis->tVars.linklist.pRoot, &pThis->tVars.linklist.pLast, pUsr);
+ iRet = qqueueAddLinkedList(&pThis->tVars.linklist.pRoot, &pThis->tVars.linklist.pLast, pUsr);
#if 0
qLinkedList_t *pEntry;
@@ -626,10 +635,10 @@ finalize_it:
RETiRet;
}
-static rsRetVal qDelLinkedList(queue_t *pThis, obj_t **ppUsr)
+static rsRetVal qDelLinkedList(qqueue_t *pThis, obj_t **ppUsr)
{
DEFiRet;
- iRet = queueDelLinkedList(&pThis->tVars.linklist.pRoot, &pThis->tVars.linklist.pLast, ppUsr);
+ iRet = qqueueDelLinkedList(&pThis->tVars.linklist.pRoot, &pThis->tVars.linklist.pLast, ppUsr);
#if 0
qLinkedList_t *pEntry;
@@ -656,12 +665,12 @@ static rsRetVal qDelLinkedList(queue_t *pThis, obj_t **ppUsr)
static rsRetVal
-queueLoadPersStrmInfoFixup(strm_t *pStrm, queue_t __attribute__((unused)) *pThis)
+qqueueLoadPersStrmInfoFixup(strm_t *pStrm, qqueue_t __attribute__((unused)) *pThis)
{
DEFiRet;
ISOBJ_TYPE_assert(pStrm, strm);
- ISOBJ_TYPE_assert(pThis, queue);
- CHKiRet(strmSetDir(pStrm, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir())));
+ ISOBJ_TYPE_assert(pThis, qqueue);
+ CHKiRet(strm.SetDir(pStrm, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir())));
finalize_it:
RETiRet;
}
@@ -672,21 +681,20 @@ finalize_it:
* rgerhards, 2008-01-15
*/
static rsRetVal
-queueHaveQIF(queue_t *pThis)
+qqueueHaveQIF(qqueue_t *pThis)
{
DEFiRet;
uchar pszQIFNam[MAXFNAME];
- size_t lenQIFNam;
struct stat stat_buf;
- ISOBJ_TYPE_assert(pThis, queue);
+ ISOBJ_TYPE_assert(pThis, qqueue);
if(pThis->pszFilePrefix == NULL)
ABORT_FINALIZE(RS_RET_NO_FILEPREFIX);
/* Construct file name */
- lenQIFNam = snprintf((char*)pszQIFNam, sizeof(pszQIFNam) / sizeof(uchar), "%s/%s.qi",
- (char*) glbl.GetWorkDir(), (char*)pThis->pszFilePrefix);
+ snprintf((char*)pszQIFNam, sizeof(pszQIFNam) / sizeof(uchar), "%s/%s.qi",
+ (char*) glbl.GetWorkDir(), (char*)pThis->pszFilePrefix);
/* check if the file exists */
if(stat((char*) pszQIFNam, &stat_buf) == -1) {
@@ -709,7 +717,7 @@ finalize_it:
* rgerhards, 2008-01-11
*/
static rsRetVal
-queueTryLoadPersistedInfo(queue_t *pThis)
+qqueueTryLoadPersistedInfo(qqueue_t *pThis)
{
DEFiRet;
strm_t *psQIF = NULL;
@@ -719,7 +727,7 @@ queueTryLoadPersistedInfo(queue_t *pThis)
int iUngottenObjs;
obj_t *pUsr;
- ISOBJ_TYPE_assert(pThis, queue);
+ ISOBJ_TYPE_assert(pThis, qqueue);
/* Construct file name */
lenQIFNam = snprintf((char*)pszQIFNam, sizeof(pszQIFNam) / sizeof(uchar), "%s/%s.qi",
@@ -738,11 +746,11 @@ queueTryLoadPersistedInfo(queue_t *pThis)
/* If we reach this point, we have a .qi file */
- CHKiRet(strmConstruct(&psQIF));
- CHKiRet(strmSettOperationsMode(psQIF, STREAMMODE_READ));
- CHKiRet(strmSetsType(psQIF, STREAMTYPE_FILE_SINGLE));
- CHKiRet(strmSetFName(psQIF, pszQIFNam, lenQIFNam));
- CHKiRet(strmConstructFinalize(psQIF));
+ CHKiRet(strm.Construct(&psQIF));
+ CHKiRet(strm.SettOperationsMode(psQIF, STREAMMODE_READ));
+ CHKiRet(strm.SetsType(psQIF, STREAMTYPE_FILE_SINGLE));
+ CHKiRet(strm.SetFName(psQIF, pszQIFNam, lenQIFNam));
+ CHKiRet(strm.ConstructFinalize(psQIF));
/* first, we try to read the property bag for ourselfs */
CHKiRet(obj.DeserializePropBag((obj_t*) pThis, psQIF));
@@ -754,18 +762,18 @@ queueTryLoadPersistedInfo(queue_t *pThis)
while(iUngottenObjs > 0) {
/* fill the queue from disk */
CHKiRet(obj.Deserialize((void*) &pUsr, (uchar*)"msg", psQIF, NULL, NULL));
- queueUngetObj(pThis, pUsr, MUTEX_ALREADY_LOCKED);
+ qqueueUngetObj(pThis, pUsr, MUTEX_ALREADY_LOCKED);
--iUngottenObjs; /* one less */
}
/* and now the stream objects (some order as when persisted!) */
CHKiRet(obj.Deserialize(&pThis->tVars.disk.pWrite, (uchar*) "strm", psQIF,
- (rsRetVal(*)(obj_t*,void*))queueLoadPersStrmInfoFixup, pThis));
+ (rsRetVal(*)(obj_t*,void*))qqueueLoadPersStrmInfoFixup, pThis));
CHKiRet(obj.Deserialize(&pThis->tVars.disk.pRead, (uchar*) "strm", psQIF,
- (rsRetVal(*)(obj_t*,void*))queueLoadPersStrmInfoFixup, pThis));
+ (rsRetVal(*)(obj_t*,void*))qqueueLoadPersStrmInfoFixup, pThis));
- CHKiRet(strmSeekCurrOffs(pThis->tVars.disk.pWrite));
- CHKiRet(strmSeekCurrOffs(pThis->tVars.disk.pRead));
+ CHKiRet(strm.SeekCurrOffs(pThis->tVars.disk.pWrite));
+ CHKiRet(strm.SeekCurrOffs(pThis->tVars.disk.pRead));
/* OK, we could successfully read the file, so we now can request that it be
* deleted when we are done with the persisted information.
@@ -774,7 +782,7 @@ queueTryLoadPersistedInfo(queue_t *pThis)
finalize_it:
if(psQIF != NULL)
- strmDestruct(&psQIF);
+ strm.Destruct(&psQIF);
if(iRet != RS_RET_OK) {
dbgoprint((obj_t*) pThis, "error %d reading .qi file - can not read persisted info (if any)\n",
@@ -792,7 +800,7 @@ finalize_it:
* allowed file size at this point - that should be a config setting...
* rgerhards, 2008-01-10
*/
-static rsRetVal qConstructDisk(queue_t *pThis)
+static rsRetVal qConstructDisk(qqueue_t *pThis)
{
DEFiRet;
int bRestarted = 0;
@@ -800,7 +808,7 @@ static rsRetVal qConstructDisk(queue_t *pThis)
ASSERT(pThis != NULL);
/* and now check if there is some persistent information that needs to be read in */
- iRet = queueTryLoadPersistedInfo(pThis);
+ iRet = qqueueTryLoadPersistedInfo(pThis);
if(iRet == RS_RET_OK)
bRestarted = 1;
else if(iRet != RS_RET_FILE_NOT_FOUND)
@@ -809,24 +817,26 @@ static rsRetVal qConstructDisk(queue_t *pThis)
if(bRestarted == 1) {
;
} else {
- CHKiRet(strmConstruct(&pThis->tVars.disk.pWrite));
- CHKiRet(strmSetDir(pThis->tVars.disk.pWrite, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir())));
- CHKiRet(strmSetiMaxFiles(pThis->tVars.disk.pWrite, 10000000));
- CHKiRet(strmSettOperationsMode(pThis->tVars.disk.pWrite, STREAMMODE_WRITE));
- CHKiRet(strmSetsType(pThis->tVars.disk.pWrite, STREAMTYPE_FILE_CIRCULAR));
- CHKiRet(strmConstructFinalize(pThis->tVars.disk.pWrite));
+ CHKiRet(strm.Construct(&pThis->tVars.disk.pWrite));
+ CHKiRet(strm.SetbSync(pThis->tVars.disk.pWrite, pThis->bSyncQueueFiles));
+ CHKiRet(strm.SetDir(pThis->tVars.disk.pWrite, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir())));
+ CHKiRet(strm.SetiMaxFiles(pThis->tVars.disk.pWrite, 10000000));
+ CHKiRet(strm.SettOperationsMode(pThis->tVars.disk.pWrite, STREAMMODE_WRITE));
+ CHKiRet(strm.SetsType(pThis->tVars.disk.pWrite, STREAMTYPE_FILE_CIRCULAR));
+ CHKiRet(strm.ConstructFinalize(pThis->tVars.disk.pWrite));
- CHKiRet(strmConstruct(&pThis->tVars.disk.pRead));
- CHKiRet(strmSetbDeleteOnClose(pThis->tVars.disk.pRead, 1));
- CHKiRet(strmSetDir(pThis->tVars.disk.pRead, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir())));
- CHKiRet(strmSetiMaxFiles(pThis->tVars.disk.pRead, 10000000));
- CHKiRet(strmSettOperationsMode(pThis->tVars.disk.pRead, STREAMMODE_READ));
- CHKiRet(strmSetsType(pThis->tVars.disk.pRead, STREAMTYPE_FILE_CIRCULAR));
- CHKiRet(strmConstructFinalize(pThis->tVars.disk.pRead));
+ CHKiRet(strm.Construct(&pThis->tVars.disk.pRead));
+ CHKiRet(strm.SetbSync(pThis->tVars.disk.pRead, pThis->bSyncQueueFiles));
+ CHKiRet(strm.SetbDeleteOnClose(pThis->tVars.disk.pRead, 1));
+ CHKiRet(strm.SetDir(pThis->tVars.disk.pRead, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir())));
+ CHKiRet(strm.SetiMaxFiles(pThis->tVars.disk.pRead, 10000000));
+ CHKiRet(strm.SettOperationsMode(pThis->tVars.disk.pRead, STREAMMODE_READ));
+ CHKiRet(strm.SetsType(pThis->tVars.disk.pRead, STREAMTYPE_FILE_CIRCULAR));
+ CHKiRet(strm.ConstructFinalize(pThis->tVars.disk.pRead));
- CHKiRet(strmSetFName(pThis->tVars.disk.pWrite, pThis->pszFilePrefix, pThis->lenFilePrefix));
- CHKiRet(strmSetFName(pThis->tVars.disk.pRead, pThis->pszFilePrefix, pThis->lenFilePrefix));
+ CHKiRet(strm.SetFName(pThis->tVars.disk.pWrite, pThis->pszFilePrefix, pThis->lenFilePrefix));
+ CHKiRet(strm.SetFName(pThis->tVars.disk.pRead, pThis->pszFilePrefix, pThis->lenFilePrefix));
}
/* now we set (and overwrite in case of a persisted restart) some parameters which
@@ -834,37 +844,39 @@ static rsRetVal qConstructDisk(queue_t *pThis)
* for example file name generation must not be changed as that would break the
* ability to read existing queue files. -- rgerhards, 2008-01-12
*/
- CHKiRet(strmSetiMaxFileSize(pThis->tVars.disk.pWrite, pThis->iMaxFileSize));
- CHKiRet(strmSetiMaxFileSize(pThis->tVars.disk.pRead, pThis->iMaxFileSize));
+ CHKiRet(strm.SetiMaxFileSize(pThis->tVars.disk.pWrite, pThis->iMaxFileSize));
+ CHKiRet(strm.SetiMaxFileSize(pThis->tVars.disk.pRead, pThis->iMaxFileSize));
finalize_it:
RETiRet;
}
-static rsRetVal qDestructDisk(queue_t *pThis)
+static rsRetVal qDestructDisk(qqueue_t *pThis)
{
DEFiRet;
ASSERT(pThis != NULL);
-
- strmDestruct(&pThis->tVars.disk.pWrite);
- strmDestruct(&pThis->tVars.disk.pRead);
+
+ if (pThis->tVars.disk.pWrite != NULL)
+ strm.Destruct(&pThis->tVars.disk.pWrite);
+ if (pThis->tVars.disk.pRead != NULL)
+ strm.Destruct(&pThis->tVars.disk.pRead);
RETiRet;
}
-static rsRetVal qAddDisk(queue_t *pThis, void* pUsr)
+static rsRetVal qAddDisk(qqueue_t *pThis, void* pUsr)
{
DEFiRet;
number_t nWriteCount;
ASSERT(pThis != NULL);
- CHKiRet(strmSetWCntr(pThis->tVars.disk.pWrite, &nWriteCount));
+ CHKiRet(strm.SetWCntr(pThis->tVars.disk.pWrite, &nWriteCount));
CHKiRet((objSerialize(pUsr))(pUsr, pThis->tVars.disk.pWrite));
- CHKiRet(strmFlush(pThis->tVars.disk.pWrite));
- CHKiRet(strmSetWCntr(pThis->tVars.disk.pWrite, NULL)); /* no more counting for now... */
+ CHKiRet(strm.Flush(pThis->tVars.disk.pWrite));
+ CHKiRet(strm.SetWCntr(pThis->tVars.disk.pWrite, NULL)); /* no more counting for now... */
pThis->tVars.disk.sizeOnDisk += nWriteCount;
@@ -881,16 +893,16 @@ finalize_it:
RETiRet;
}
-static rsRetVal qDelDisk(queue_t *pThis, void **ppUsr)
+static rsRetVal qDelDisk(qqueue_t *pThis, void **ppUsr)
{
DEFiRet;
int64 offsIn;
int64 offsOut;
- CHKiRet(strmGetCurrOffset(pThis->tVars.disk.pRead, &offsIn));
+ CHKiRet(strm.GetCurrOffset(pThis->tVars.disk.pRead, &offsIn));
CHKiRet(obj.Deserialize(ppUsr, (uchar*) "msg", pThis->tVars.disk.pRead, NULL, NULL));
- CHKiRet(strmGetCurrOffset(pThis->tVars.disk.pRead, &offsOut));
+ CHKiRet(strm.GetCurrOffset(pThis->tVars.disk.pRead, &offsOut));
/* This time it is a bit tricky: we free disk space only upon file deletion. So we need
* to keep track of what we have read until we get an out-offset that is lower than the
@@ -912,18 +924,18 @@ finalize_it:
}
/* -------------------- direct (no queueing) -------------------- */
-static rsRetVal qConstructDirect(queue_t __attribute__((unused)) *pThis)
+static rsRetVal qConstructDirect(qqueue_t __attribute__((unused)) *pThis)
{
return RS_RET_OK;
}
-static rsRetVal qDestructDirect(queue_t __attribute__((unused)) *pThis)
+static rsRetVal qDestructDirect(qqueue_t __attribute__((unused)) *pThis)
{
return RS_RET_OK;
}
-static rsRetVal qAddDirect(queue_t *pThis, void* pUsr)
+static rsRetVal qAddDirect(qqueue_t *pThis, void* pUsr)
{
DEFiRet;
@@ -940,7 +952,7 @@ static rsRetVal qAddDirect(queue_t *pThis, void* pUsr)
RETiRet;
}
-static rsRetVal qDelDirect(queue_t __attribute__((unused)) *pThis, __attribute__((unused)) void **out)
+static rsRetVal qDelDirect(qqueue_t __attribute__((unused)) *pThis, __attribute__((unused)) void **out)
{
return RS_RET_OK;
}
@@ -955,12 +967,12 @@ static rsRetVal qDelDirect(queue_t __attribute__((unused)) *pThis, __attribute__
* rgerhards, 2008-01-20
*/
static rsRetVal
-queueUngetObj(queue_t *pThis, obj_t *pUsr, int bLockMutex)
+qqueueUngetObj(qqueue_t *pThis, obj_t *pUsr, int bLockMutex)
{
DEFiRet;
DEFVARS_mutexProtection;
- ISOBJ_TYPE_assert(pThis, queue);
+ ISOBJ_TYPE_assert(pThis, qqueue);
ISOBJ_assert(pUsr); /* TODO: we aborted right at this place at least 3 times -- race? 2008-02-28, -03-10, -03-15
The second time I noticed it the queue was in destruction with NO worker threads
running. The pUsr ptr was totally off and provided no clue what it may be pointing
@@ -969,7 +981,7 @@ queueUngetObj(queue_t *pThis, obj_t *pUsr, int bLockMutex)
dbgoprint((obj_t*) pThis, "ungetting user object %s\n", obj.GetName(pUsr));
BEGIN_MTX_PROTECTED_OPERATIONS(pThis->mut, bLockMutex);
- iRet = queueAddLinkedList(&pThis->pUngetRoot, &pThis->pUngetLast, pUsr);
+ iRet = qqueueAddLinkedList(&pThis->pUngetRoot, &pThis->pUngetLast, pUsr);
++pThis->iUngottenObjs; /* indicate one more */
END_MTX_PROTECTED_OPERATIONS(pThis->mut);
@@ -985,14 +997,14 @@ queueUngetObj(queue_t *pThis, obj_t *pUsr, int bLockMutex)
* rgerhards, 2008-01-29
*/
static rsRetVal
-queueGetUngottenObj(queue_t *pThis, obj_t **ppUsr)
+qqueueGetUngottenObj(qqueue_t *pThis, obj_t **ppUsr)
{
DEFiRet;
- ISOBJ_TYPE_assert(pThis, queue);
+ ISOBJ_TYPE_assert(pThis, qqueue);
ASSERT(ppUsr != NULL);
- iRet = queueDelLinkedList(&pThis->pUngetRoot, &pThis->pUngetLast, ppUsr);
+ iRet = qqueueDelLinkedList(&pThis->pUngetRoot, &pThis->pUngetLast, ppUsr);
--pThis->iUngottenObjs; /* indicate one less */
dbgoprint((obj_t*) pThis, "dequeued ungotten user object %s\n", obj.GetName(*ppUsr));
@@ -1006,7 +1018,7 @@ queueGetUngottenObj(queue_t *pThis, obj_t **ppUsr)
* things truely different. -- rgerhards, 2008-02-12
*/
static rsRetVal
-queueAdd(queue_t *pThis, void *pUsr)
+qqueueAdd(qqueue_t *pThis, void *pUsr)
{
DEFiRet;
@@ -1015,7 +1027,7 @@ queueAdd(queue_t *pThis, void *pUsr)
CHKiRet(pThis->qAdd(pThis, pUsr));
if(pThis->qType != QUEUETYPE_DIRECT) {
- ++pThis->iQueueSize;
+ ATOMIC_INC(pThis->iQueueSize);
dbgoprint((obj_t*) pThis, "entry added, size now %d entries\n", pThis->iQueueSize);
}
@@ -1029,7 +1041,7 @@ finalize_it:
* ungotten list and, if so, dequeue it first.
*/
static rsRetVal
-queueDel(queue_t *pThis, void *pUsr)
+qqueueDel(qqueue_t *pThis, void *pUsr)
{
DEFiRet;
@@ -1041,10 +1053,10 @@ queueDel(queue_t *pThis, void *pUsr)
* losing the whole process because it loops... -- rgerhards, 2008-01-03
*/
if(pThis->iUngottenObjs > 0) {
- iRet = queueGetUngottenObj(pThis, (obj_t**) pUsr);
+ iRet = qqueueGetUngottenObj(pThis, (obj_t**) pUsr);
} else {
iRet = pThis->qDel(pThis, pUsr);
- --pThis->iQueueSize;
+ ATOMIC_DEC(pThis->iQueueSize);
}
dbgoprint((obj_t*) pThis, "entry deleted, state %d, size now %d entries\n",
@@ -1065,14 +1077,14 @@ queueDel(queue_t *pThis, void *pUsr)
* complex) if each would have its own shutdown. The function does not self check
* this condition - the caller must make sure it is not called with a parent.
*/
-static rsRetVal queueShutdownWorkers(queue_t *pThis)
+static rsRetVal qqueueShutdownWorkers(qqueue_t *pThis)
{
DEFiRet;
DEFVARS_mutexProtection;
struct timespec tTimeout;
rsRetVal iRetLocal;
- ISOBJ_TYPE_assert(pThis, queue);
+ ISOBJ_TYPE_assert(pThis, qqueue);
ASSERT(pThis->pqParent == NULL); /* detect invalid calling sequence */
dbgoprint((obj_t*) pThis, "initiating worker thread shutdown sequence\n");
@@ -1086,7 +1098,7 @@ static rsRetVal queueShutdownWorkers(queue_t *pThis)
/* first try to shutdown the queue within the regular shutdown period */
BEGIN_MTX_PROTECTED_OPERATIONS(pThis->mut, LOCK_MUTEX); /* some workers may be running in parallel! */
- if(queueGetOverallQueueSize(pThis) > 0) {
+ if(qqueueGetOverallQueueSize(pThis) > 0) {
if(pThis->bRunsDA) {
/* We may have waited on the low water mark. As it may have changed, we
* see if we reactivate the worker.
@@ -1124,7 +1136,7 @@ static rsRetVal queueShutdownWorkers(queue_t *pThis)
if(pThis->bRunsDA) {
END_MTX_PROTECTED_OPERATIONS(pThis->mut);
dbgoprint((obj_t*) pThis, "we have a DA queue (0x%lx), requesting its shutdown.\n",
- queueGetID(pThis->pqDA));
+ qqueueGetID(pThis->pqDA));
/* we use the same absolute timeout as above, so we do not use more than the configured
* timeout interval!
*/
@@ -1153,19 +1165,19 @@ static rsRetVal queueShutdownWorkers(queue_t *pThis)
/* at this stage, we need to have the DA worker properly initialized and running (if there is one) */
if(pThis->bRunsDA)
- queueWaitDAModeInitialized(pThis);
+ qqueueWaitDAModeInitialized(pThis);
BEGIN_MTX_PROTECTED_OPERATIONS(pThis->mut, LOCK_MUTEX); /* some workers may be running in parallel! */
/* optimize parameters for shutdown of DA-enabled queues */
- if(pThis->bIsDA && queueGetOverallQueueSize(pThis) > 0 && pThis->bSaveOnShutdown) {
+ if(pThis->bIsDA && qqueueGetOverallQueueSize(pThis) > 0 && pThis->bSaveOnShutdown) {
/* switch to enqueue-only mode so that no more actions happen */
if(pThis->bRunsDA == 0) {
- queueInitDA(pThis, QUEUE_MODE_ENQONLY, MUTEX_ALREADY_LOCKED); /* switch to DA mode */
+ qqueueInitDA(pThis, QUEUE_MODE_ENQONLY, MUTEX_ALREADY_LOCKED); /* switch to DA mode */
} else {
/* TODO: RACE: we may reach this point when the DA worker has been initialized (state 1)
* but is not yet running (state 2). In this case, pThis->pqDA is NULL! rgerhards, 2008-02-27
*/
- queueSetEnqOnly(pThis->pqDA, QUEUE_MODE_ENQONLY, MUTEX_ALREADY_LOCKED); /* switch to enqueue-only mode */
+ qqueueSetEnqOnly(pThis->pqDA, QUEUE_MODE_ENQONLY, MUTEX_ALREADY_LOCKED); /* switch to enqueue-only mode */
}
END_MTX_PROTECTED_OPERATIONS(pThis->mut);
/* make sure we do not timeout before we are done */
@@ -1187,7 +1199,7 @@ static rsRetVal queueShutdownWorkers(queue_t *pThis)
* they will automatically terminate as there no longer is any message left to process.
*/
BEGIN_MTX_PROTECTED_OPERATIONS(pThis->mut, LOCK_MUTEX); /* some workers may be running in parallel! */
- if(queueGetOverallQueueSize(pThis) > 0) {
+ if(qqueueGetOverallQueueSize(pThis) > 0) {
timeoutComp(&tTimeout, pThis->toActShutdown);
if(wtpGetCurNumWrkr(pThis->pWtpReg, LOCK_MUTEX) > 0) {
END_MTX_PROTECTED_OPERATIONS(pThis->mut);
@@ -1203,7 +1215,7 @@ static rsRetVal queueShutdownWorkers(queue_t *pThis)
/* we need to re-aquire the mutex for the next check in this case! */
BEGIN_MTX_PROTECTED_OPERATIONS(pThis->mut, LOCK_MUTEX); /* some workers may be running in parallel! */
}
- if(pThis->bIsDA && wtpGetCurNumWrkr(pThis->pWtpDA, LOCK_MUTEX) > 0) {
+ if(pThis->bRunsDA && wtpGetCurNumWrkr(pThis->pWtpDA, LOCK_MUTEX) > 0) {
/* and now the same for the DA queue */
END_MTX_PROTECTED_OPERATIONS(pThis->mut);
dbgoprint((obj_t*) pThis, "trying immediate shutdown of DA workers\n");
@@ -1256,7 +1268,7 @@ static rsRetVal queueShutdownWorkers(queue_t *pThis)
* Well, more precisely, they *are in termination*. Some cancel cleanup handlers
* may still be running.
*/
- dbgoprint((obj_t*) pThis, "worker threads terminated, remaining queue size %d.\n", queueGetOverallQueueSize(pThis));
+ dbgoprint((obj_t*) pThis, "worker threads terminated, remaining queue size %d.\n", qqueueGetOverallQueueSize(pThis));
RETiRet;
}
@@ -1268,17 +1280,17 @@ static rsRetVal queueShutdownWorkers(queue_t *pThis)
* is done by queueStart(). The reason is that we want to give the caller a chance
* to modify some parameters before the queue is actually started.
*/
-rsRetVal queueConstruct(queue_t **ppThis, queueType_t qType, int iWorkerThreads,
+rsRetVal qqueueConstruct(qqueue_t **ppThis, queueType_t qType, int iWorkerThreads,
int iMaxQueueSize, rsRetVal (*pConsumer)(void*,void*))
{
DEFiRet;
- queue_t *pThis;
+ qqueue_t *pThis;
ASSERT(ppThis != NULL);
ASSERT(pConsumer != NULL);
ASSERT(iWorkerThreads >= 0);
- if((pThis = (queue_t *)calloc(1, sizeof(queue_t))) == NULL) {
+ if((pThis = (qqueue_t *)calloc(1, sizeof(qqueue_t))) == NULL) {
ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
}
@@ -1314,7 +1326,7 @@ rsRetVal queueConstruct(queue_t **ppThis, queueType_t qType, int iWorkerThreads,
pThis->qConstruct = qConstructLinkedList;
pThis->qDestruct = qDestructLinkedList;
pThis->qAdd = qAddLinkedList;
- pThis->qDel = (rsRetVal (*)(queue_t*,void**)) qDelLinkedList;
+ pThis->qDel = (rsRetVal (*)(qqueue_t*,void**)) qDelLinkedList;
break;
case QUEUETYPE_DISK:
pThis->qConstruct = qConstructDisk;
@@ -1341,25 +1353,25 @@ finalize_it:
/* cancellation cleanup handler for queueWorker ()
* Updates admin structure and frees ressources.
* Params:
- * arg1 - user pointer (in this case a queue_t)
+ * arg1 - user pointer (in this case a qqueue_t)
* arg2 - user data pointer (in this case a queue data element, any object [queue's pUsr ptr!])
* Note that arg2 may be NULL, in which case no dequeued but unprocessed pUsr exists!
* rgerhards, 2008-01-16
*/
static rsRetVal
-queueConsumerCancelCleanup(void *arg1, void *arg2)
+qqueueConsumerCancelCleanup(void *arg1, void *arg2)
{
DEFiRet;
- queue_t *pThis = (queue_t*) arg1;
+ qqueue_t *pThis = (qqueue_t*) arg1;
obj_t *pUsr = (obj_t*) arg2;
- ISOBJ_TYPE_assert(pThis, queue);
+ ISOBJ_TYPE_assert(pThis, qqueue);
if(pUsr != NULL) {
/* make sure the data element is not lost */
dbgoprint((obj_t*) pThis, "cancelation cleanup handler consumer called, we need to unget one user data element\n");
- CHKiRet(queueUngetObj(pThis, pUsr, LOCK_MUTEX));
+ CHKiRet(qqueueUngetObj(pThis, pUsr, LOCK_MUTEX));
}
finalize_it:
@@ -1381,13 +1393,13 @@ finalize_it:
* the return state!
* rgerhards, 2008-01-24
*/
-static int queueChkDiscardMsg(queue_t *pThis, int iQueueSize, int bRunsDA, void *pUsr)
+static int qqueueChkDiscardMsg(qqueue_t *pThis, int iQueueSize, int bRunsDA, void *pUsr)
{
DEFiRet;
rsRetVal iRetLocal;
int iSeverity;
- ISOBJ_TYPE_assert(pThis, queue);
+ ISOBJ_TYPE_assert(pThis, qqueue);
ISOBJ_assert(pUsr);
if(pThis->iDiscardMrk > 0 && iQueueSize >= pThis->iDiscardMrk && bRunsDA == 0) {
@@ -1412,7 +1424,7 @@ finalize_it:
* rgerhards, 2008-10-21
*/
static rsRetVal
-queueDequeueConsumable(queue_t *pThis, wti_t *pWti, int iCancelStateSave)
+qqueueDequeueConsumable(qqueue_t *pThis, wti_t *pWti, int iCancelStateSave)
{
DEFiRet;
void *pUsr;
@@ -1420,9 +1432,9 @@ queueDequeueConsumable(queue_t *pThis, wti_t *pWti, int iCancelStateSave)
int bRunsDA; /* cache for early mutex release */
/* dequeue element (still protected from mutex) */
- iRet = queueDel(pThis, &pUsr);
- queueChkPersist(pThis);
- iQueueSize = queueGetOverallQueueSize(pThis); /* cache this for after mutex release */
+ iRet = qqueueDel(pThis, &pUsr);
+ qqueueChkPersist(pThis);
+ iQueueSize = qqueueGetOverallQueueSize(pThis); /* cache this for after mutex release */
bRunsDA = pThis->bRunsDA; /* cache this for after mutex release */
/* We now need to save the user pointer for the cancel cleanup handler, BUT ONLY
@@ -1441,11 +1453,11 @@ queueDequeueConsumable(queue_t *pThis, wti_t *pWti, int iCancelStateSave)
* we have someone waiting for the condition (or only when we hit the watermark right
* on the nail [exact value]) -- rgerhards, 2008-03-14
*/
- if(iQueueSize < pThis->iFullDlyMrk) {
+ if(iQueueSize < pThis->iFullDlyMrk / 2) {
pthread_cond_broadcast(&pThis->belowFullDlyWtrMrk);
}
- if(iQueueSize < pThis->iLightDlyMrk) {
+ if(iQueueSize < pThis->iLightDlyMrk / 2) {
pthread_cond_broadcast(&pThis->belowLightDlyWtrMrk);
}
@@ -1473,7 +1485,7 @@ queueDequeueConsumable(queue_t *pThis, wti_t *pWti, int iCancelStateSave)
* provide real-time creation of spool files.
* Note: It is OK to use the cached iQueueSize here, because it does not hurt if it is slightly wrong.
*/
- CHKiRet(queueChkDiscardMsg(pThis, iQueueSize, bRunsDA, pUsr));
+ CHKiRet(qqueueChkDiscardMsg(pThis, iQueueSize, bRunsDA, pUsr));
finalize_it:
if(iRet != RS_RET_OK && iRet != RS_RET_DISCARDMSG) {
@@ -1522,7 +1534,7 @@ finalize_it:
* but you get the idea from the code above.
*/
static rsRetVal
-queueRateLimiter(queue_t *pThis)
+qqueueRateLimiter(qqueue_t *pThis)
{
DEFiRet;
int iDelay;
@@ -1530,9 +1542,7 @@ queueRateLimiter(queue_t *pThis)
time_t tCurr;
struct tm m;
- ISOBJ_TYPE_assert(pThis, queue);
-
- dbgoprint((obj_t*) pThis, "entering rate limiter\n");
+ ISOBJ_TYPE_assert(pThis, qqueue);
iDelay = 0;
if(pThis->iDeqtWinToHr != 25) { /* 25 means disabled */
@@ -1587,14 +1597,14 @@ queueRateLimiter(queue_t *pThis)
* rgerhards, 2008-01-21
*/
static rsRetVal
-queueConsumerReg(queue_t *pThis, wti_t *pWti, int iCancelStateSave)
+qqueueConsumerReg(qqueue_t *pThis, wti_t *pWti, int iCancelStateSave)
{
DEFiRet;
- ISOBJ_TYPE_assert(pThis, queue);
+ ISOBJ_TYPE_assert(pThis, qqueue);
ISOBJ_TYPE_assert(pWti, wti);
- CHKiRet(queueDequeueConsumable(pThis, pWti, iCancelStateSave));
+ CHKiRet(qqueueDequeueConsumable(pThis, pWti, iCancelStateSave));
CHKiRet(pThis->pConsumer(pThis->pUsr, pWti->pUsrp));
/* we now need to check if we should deliberately delay processing a bit
@@ -1621,15 +1631,15 @@ finalize_it:
* rgerhards, 2008-01-14
*/
static rsRetVal
-queueConsumerDA(queue_t *pThis, wti_t *pWti, int iCancelStateSave)
+qqueueConsumerDA(qqueue_t *pThis, wti_t *pWti, int iCancelStateSave)
{
DEFiRet;
- ISOBJ_TYPE_assert(pThis, queue);
+ ISOBJ_TYPE_assert(pThis, qqueue);
ISOBJ_TYPE_assert(pWti, wti);
- CHKiRet(queueDequeueConsumable(pThis, pWti, iCancelStateSave));
- CHKiRet(queueEnqObj(pThis->pqDA, eFLOWCTL_NO_DELAY, pWti->pUsrp));
+ CHKiRet(qqueueDequeueConsumable(pThis, pWti, iCancelStateSave));
+ CHKiRet(qqueueEnqObj(pThis->pqDA, eFLOWCTL_NO_DELAY, pWti->pUsrp));
finalize_it:
dbgoprint((obj_t*) pThis, "DAConsumer returns with iRet %d\n", iRet);
@@ -1645,7 +1655,7 @@ finalize_it:
* the DA queue
*/
static int
-queueChkStopWrkrDA(queue_t *pThis)
+qqueueChkStopWrkrDA(qqueue_t *pThis)
{
/* if our queue is in destruction, we drain to the DA queue and so we shall not terminate
* until we have done so.
@@ -1657,14 +1667,14 @@ queueChkStopWrkrDA(queue_t *pThis)
if(pThis->bEnqOnly) {
bStopWrkr = 1;
} else {
- if(pThis->bRunsDA) {
+ if(pThis->bRunsDA == 2) {
ASSERT(pThis->pqDA != NULL);
if( pThis->pqDA->bEnqOnly
&& pThis->pqDA->sizeOnDiskMax > 0
&& pThis->pqDA->tVars.disk.sizeOnDisk > pThis->pqDA->sizeOnDiskMax) {
/* this queue can never grow, so we can give up... */
bStopWrkr = 1;
- } else if(queueGetOverallQueueSize(pThis) < pThis->iHighWtrMrk && pThis->bQueueStarted == 1) {
+ } else if(qqueueGetOverallQueueSize(pThis) < pThis->iHighWtrMrk && pThis->bQueueStarted == 1) {
bStopWrkr = 1;
} else {
bStopWrkr = 0;
@@ -1687,9 +1697,9 @@ queueChkStopWrkrDA(queue_t *pThis)
* the DA queue
*/
static int
-queueChkStopWrkrReg(queue_t *pThis)
+qqueueChkStopWrkrReg(qqueue_t *pThis)
{
- return pThis->bEnqOnly || pThis->bRunsDA || (pThis->pqParent != NULL && queueGetOverallQueueSize(pThis) == 0);
+ return pThis->bEnqOnly || pThis->bRunsDA || (pThis->pqParent != NULL && qqueueGetOverallQueueSize(pThis) == 0);
}
@@ -1697,26 +1707,26 @@ queueChkStopWrkrReg(queue_t *pThis)
* are not stable! DA queue version
*/
static int
-queueIsIdleDA(queue_t *pThis)
+qqueueIsIdleDA(qqueue_t *pThis)
{
/* remember: iQueueSize is the DA queue size, not the main queue! */
/* TODO: I think we need just a single function for DA and non-DA mode - but I leave it for now as is */
- return(queueGetOverallQueueSize(pThis) == 0 || (pThis->bRunsDA && queueGetOverallQueueSize(pThis) <= pThis->iLowWtrMrk));
+ return(qqueueGetOverallQueueSize(pThis) == 0 || (pThis->bRunsDA && qqueueGetOverallQueueSize(pThis) <= pThis->iLowWtrMrk));
}
/* must only be called when the queue mutex is locked, else results
* are not stable! Regular queue version
*/
static int
-queueIsIdleReg(queue_t *pThis)
+qqueueIsIdleReg(qqueue_t *pThis)
{
#if 0 /* enable for performance testing */
int ret;
- ret = queueGetOverallQueueSize(pThis) == 0 || (pThis->bRunsDA && queueGetOverallQueueSize(pThis) <= pThis->iLowWtrMrk);
+ ret = qqueueGetOverallQueueSize(pThis) == 0 || (pThis->bRunsDA && qqueueGetOverallQueueSize(pThis) <= pThis->iLowWtrMrk);
if(ret) fprintf(stderr, "queue is idle\n");
return ret;
#else
/* regular code! */
- return(queueGetOverallQueueSize(pThis) == 0 || (pThis->bRunsDA && queueGetOverallQueueSize(pThis) <= pThis->iLowWtrMrk));
+ return(qqueueGetOverallQueueSize(pThis) == 0 || (pThis->bRunsDA && qqueueGetOverallQueueSize(pThis) <= pThis->iLowWtrMrk));
#endif
}
@@ -1735,11 +1745,11 @@ queueIsIdleReg(queue_t *pThis)
* I am telling this, because I, too, always get confused by those...
*/
static rsRetVal
-queueRegOnWrkrShutdown(queue_t *pThis)
+qqueueRegOnWrkrShutdown(qqueue_t *pThis)
{
DEFiRet;
- ISOBJ_TYPE_assert(pThis, queue);
+ ISOBJ_TYPE_assert(pThis, qqueue);
if(pThis->pqParent != NULL) {
pThis->pqParent->bChildIsDone = 1; /* indicate we are done */
@@ -1756,11 +1766,11 @@ queueRegOnWrkrShutdown(queue_t *pThis)
* hook to indicate in the parent queue (if we are a child) that we are not done yet.
*/
static rsRetVal
-queueRegOnWrkrStartup(queue_t *pThis)
+qqueueRegOnWrkrStartup(qqueue_t *pThis)
{
DEFiRet;
- ISOBJ_TYPE_assert(pThis, queue);
+ ISOBJ_TYPE_assert(pThis, qqueue);
if(pThis->pqParent != NULL) {
pThis->pqParent->bChildIsDone = 0;
@@ -1773,7 +1783,7 @@ queueRegOnWrkrStartup(queue_t *pThis)
/* start up the queue - it must have been constructed and parameters defined
* before.
*/
-rsRetVal queueStart(queue_t *pThis) /* this is the ConstructionFinalizer */
+rsRetVal qqueueStart(qqueue_t *pThis) /* this is the ConstructionFinalizer */
{
DEFiRet;
rsRetVal iRetLocal;
@@ -1812,7 +1822,7 @@ rsRetVal queueStart(queue_t *pThis) /* this is the ConstructionFinalizer */
dbgoprint((obj_t*) pThis, "type %d, enq-only %d, disk assisted %d, maxFileSz %lld, qsize %d, child %d, "
"full delay %d, light delay %d starting\n",
pThis->qType, pThis->bEnqOnly, pThis->bIsDA, pThis->iMaxFileSize,
- queueGetOverallQueueSize(pThis), pThis->pqParent == NULL ? 0 : 1,
+ qqueueGetOverallQueueSize(pThis), pThis->pqParent == NULL ? 0 : 1,
pThis->iFullDlyMrk, pThis->iLightDlyMrk);
if(pThis->qType == QUEUETYPE_DIRECT)
@@ -1824,13 +1834,13 @@ rsRetVal queueStart(queue_t *pThis) /* this is the ConstructionFinalizer */
lenBuf = snprintf((char*)pszBuf, sizeof(pszBuf), "%s:Reg", obj.GetName((obj_t*) pThis));
CHKiRet(wtpConstruct (&pThis->pWtpReg));
CHKiRet(wtpSetDbgHdr (pThis->pWtpReg, pszBuf, lenBuf));
- CHKiRet(wtpSetpfRateLimiter (pThis->pWtpReg, (rsRetVal (*)(void *pUsr)) queueRateLimiter));
- CHKiRet(wtpSetpfChkStopWrkr (pThis->pWtpReg, (rsRetVal (*)(void *pUsr, int)) queueChkStopWrkrReg));
- CHKiRet(wtpSetpfIsIdle (pThis->pWtpReg, (rsRetVal (*)(void *pUsr, int)) queueIsIdleReg));
- CHKiRet(wtpSetpfDoWork (pThis->pWtpReg, (rsRetVal (*)(void *pUsr, void *pWti, int)) queueConsumerReg));
- CHKiRet(wtpSetpfOnWorkerCancel (pThis->pWtpReg, (rsRetVal (*)(void *pUsr, void*pWti))queueConsumerCancelCleanup));
- CHKiRet(wtpSetpfOnWorkerStartup (pThis->pWtpReg, (rsRetVal (*)(void *pUsr)) queueRegOnWrkrStartup));
- CHKiRet(wtpSetpfOnWorkerShutdown(pThis->pWtpReg, (rsRetVal (*)(void *pUsr)) queueRegOnWrkrShutdown));
+ CHKiRet(wtpSetpfRateLimiter (pThis->pWtpReg, (rsRetVal (*)(void *pUsr)) qqueueRateLimiter));
+ CHKiRet(wtpSetpfChkStopWrkr (pThis->pWtpReg, (rsRetVal (*)(void *pUsr, int)) qqueueChkStopWrkrReg));
+ CHKiRet(wtpSetpfIsIdle (pThis->pWtpReg, (rsRetVal (*)(void *pUsr, int)) qqueueIsIdleReg));
+ CHKiRet(wtpSetpfDoWork (pThis->pWtpReg, (rsRetVal (*)(void *pUsr, void *pWti, int)) qqueueConsumerReg));
+ CHKiRet(wtpSetpfOnWorkerCancel (pThis->pWtpReg, (rsRetVal (*)(void *pUsr, void*pWti))qqueueConsumerCancelCleanup));
+ CHKiRet(wtpSetpfOnWorkerStartup (pThis->pWtpReg, (rsRetVal (*)(void *pUsr)) qqueueRegOnWrkrStartup));
+ CHKiRet(wtpSetpfOnWorkerShutdown(pThis->pWtpReg, (rsRetVal (*)(void *pUsr)) qqueueRegOnWrkrShutdown));
CHKiRet(wtpSetpmutUsr (pThis->pWtpReg, pThis->mut));
CHKiRet(wtpSetpcondBusy (pThis->pWtpReg, &pThis->notEmpty));
CHKiRet(wtpSetiNumWorkerThreads (pThis->pWtpReg, pThis->iNumWorkerThreads));
@@ -1843,10 +1853,10 @@ rsRetVal queueStart(queue_t *pThis) /* this is the ConstructionFinalizer */
/* If we are disk-assisted, we need to check if there is a QIF file
* which we need to load. -- rgerhards, 2008-01-15
*/
- iRetLocal = queueHaveQIF(pThis);
+ iRetLocal = qqueueHaveQIF(pThis);
if(iRetLocal == RS_RET_OK) {
dbgoprint((obj_t*) pThis, "on-disk queue present, needs to be reloaded\n");
- queueInitDA(pThis, QUEUE_MODE_ENQDEQ, LOCK_MUTEX); /* initiate DA mode */
+ qqueueInitDA(pThis, QUEUE_MODE_ENQDEQ, LOCK_MUTEX); /* initiate DA mode */
bInitialized = 1; /* we are done */
} else {
/* TODO: use logerror? -- rgerhards, 2008-01-16 */
@@ -1863,7 +1873,7 @@ rsRetVal queueStart(queue_t *pThis) /* this is the ConstructionFinalizer */
/* if the queue already contains data, we need to start the correct number of worker threads. This can be
* the case when a disk queue has been loaded. If we did not start it here, it would never start.
*/
- queueAdviseMaxWorkers(pThis);
+ qqueueAdviseMaxWorkers(pThis);
pThis->bQueueStarted = 1;
finalize_it:
@@ -1878,7 +1888,7 @@ finalize_it:
* and 0 otherwise.
* rgerhards, 2008-01-10
*/
-static rsRetVal queuePersist(queue_t *pThis, int bIsCheckpoint)
+static rsRetVal qqueuePersist(qqueue_t *pThis, int bIsCheckpoint)
{
DEFiRet;
strm_t *psQIF = NULL; /* Queue Info File */
@@ -1889,7 +1899,7 @@ static rsRetVal queuePersist(queue_t *pThis, int bIsCheckpoint)
ASSERT(pThis != NULL);
if(pThis->qType != QUEUETYPE_DISK) {
- if(queueGetOverallQueueSize(pThis) > 0) {
+ if(qqueueGetOverallQueueSize(pThis) > 0) {
/* This error code is OK, but we will probably not implement this any time
* The reason is that persistence happens via DA queues. But I would like to
* leave the code as is, as we so have a hook in case we need one.
@@ -1900,28 +1910,29 @@ static rsRetVal queuePersist(queue_t *pThis, int bIsCheckpoint)
FINALIZE; /* if the queue is empty, we are happy and done... */
}
- dbgoprint((obj_t*) pThis, "persisting queue to disk, %d entries...\n", queueGetOverallQueueSize(pThis));
+ dbgoprint((obj_t*) pThis, "persisting queue to disk, %d entries...\n", qqueueGetOverallQueueSize(pThis));
/* Construct file name */
lenQIFNam = snprintf((char*)pszQIFNam, sizeof(pszQIFNam) / sizeof(uchar), "%s/%s.qi",
(char*) glbl.GetWorkDir(), (char*)pThis->pszFilePrefix);
- if((bIsCheckpoint != QUEUE_CHECKPOINT) && (queueGetOverallQueueSize(pThis) == 0)) {
+ if((bIsCheckpoint != QUEUE_CHECKPOINT) && (qqueueGetOverallQueueSize(pThis) == 0)) {
if(pThis->bNeedDelQIF) {
unlink((char*)pszQIFNam);
pThis->bNeedDelQIF = 0;
}
/* indicate spool file needs to be deleted */
- CHKiRet(strmSetbDeleteOnClose(pThis->tVars.disk.pRead, 1));
+ if (pThis->tVars.disk.pRead != NULL)
+ CHKiRet(strm.SetbDeleteOnClose(pThis->tVars.disk.pRead, 1));
FINALIZE; /* nothing left to do, so be happy */
}
- CHKiRet(strmConstruct(&psQIF));
- CHKiRet(strmSettOperationsMode(psQIF, STREAMMODE_WRITE));
- CHKiRet(strmSetiAddtlOpenFlags(psQIF, O_TRUNC));
- CHKiRet(strmSetsType(psQIF, STREAMTYPE_FILE_SINGLE));
- CHKiRet(strmSetFName(psQIF, pszQIFNam, lenQIFNam));
- CHKiRet(strmConstructFinalize(psQIF));
+ CHKiRet(strm.Construct(&psQIF));
+ CHKiRet(strm.SettOperationsMode(psQIF, STREAMMODE_WRITE_TRUNC));
+ CHKiRet(strm.SetbSync(psQIF, pThis->bSyncQueueFiles));
+ CHKiRet(strm.SetsType(psQIF, STREAMTYPE_FILE_SINGLE));
+ CHKiRet(strm.SetFName(psQIF, pszQIFNam, lenQIFNam));
+ CHKiRet(strm.ConstructFinalize(psQIF));
/* first, write the property bag for ourselfs
* And, surprisingly enough, we currently need to persist only the size of the
@@ -1940,20 +1951,22 @@ static rsRetVal queuePersist(queue_t *pThis, int bIsCheckpoint)
* to the regular files. -- rgerhards, 2008-01-29
*/
while(pThis->iUngottenObjs > 0) {
- CHKiRet(queueGetUngottenObj(pThis, &pUsr));
+ CHKiRet(qqueueGetUngottenObj(pThis, &pUsr));
CHKiRet((objSerialize(pUsr))(pUsr, psQIF));
objDestruct(pUsr);
}
/* now persist the stream info */
- CHKiRet(strmSerialize(pThis->tVars.disk.pWrite, psQIF));
- CHKiRet(strmSerialize(pThis->tVars.disk.pRead, psQIF));
+ if (pThis->tVars.disk.pWrite != NULL)
+ CHKiRet(strm.Serialize(pThis->tVars.disk.pWrite, psQIF));
+ if (pThis->tVars.disk.pRead != NULL)
+ CHKiRet(strm.Serialize(pThis->tVars.disk.pRead, psQIF));
/* tell the input file object that it must not delete the file on close if the queue
* is non-empty - but only if we are not during a simple checkpoint
*/
- if(bIsCheckpoint != QUEUE_CHECKPOINT) {
- CHKiRet(strmSetbDeleteOnClose(pThis->tVars.disk.pRead, 0));
+ if(bIsCheckpoint != QUEUE_CHECKPOINT && pThis->tVars.disk.pRead != NULL) {
+ CHKiRet(strm.SetbDeleteOnClose(pThis->tVars.disk.pRead, 0));
}
/* we have persisted the queue object. So whenever it comes to an empty queue,
@@ -1963,7 +1976,7 @@ static rsRetVal queuePersist(queue_t *pThis, int bIsCheckpoint)
finalize_it:
if(psQIF != NULL)
- strmDestruct(&psQIF);
+ strm.Destruct(&psQIF);
RETiRet;
}
@@ -1974,24 +1987,22 @@ finalize_it:
* abide to our regular call interface)...
* rgerhards, 2008-01-13
*/
-rsRetVal queueChkPersist(queue_t *pThis)
+static rsRetVal qqueueChkPersist(qqueue_t *pThis)
{
- DEFiRet;
-
- ISOBJ_TYPE_assert(pThis, queue);
+ ISOBJ_TYPE_assert(pThis, qqueue);
if(pThis->iPersistUpdCnt && ++pThis->iUpdsSincePersist >= pThis->iPersistUpdCnt) {
- queuePersist(pThis, QUEUE_CHECKPOINT);
+ qqueuePersist(pThis, QUEUE_CHECKPOINT);
pThis->iUpdsSincePersist = 0;
}
- RETiRet;
+ return RS_RET_OK;
}
/* destructor for the queue object */
-BEGINobjDestruct(queue) /* be sure to specify the object type also in END and CODESTART macros! */
-CODESTARTobjDestruct(queue)
+BEGINobjDestruct(qqueue) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDestruct(qqueue)
pThis->bQueueInDestruction = 1; /* indicate we are in destruction (modifies some behaviour) */
/* shut down all workers (handles *all* of the persistence logic)
@@ -2001,7 +2012,7 @@ CODESTARTobjDestruct(queue)
* with a child! -- rgerhards, 2008-01-28
*/
if(pThis->qType != QUEUETYPE_DIRECT && !pThis->bEnqOnly && pThis->pqParent == NULL)
- queueShutdownWorkers(pThis);
+ qqueueShutdownWorkers(pThis);
/* finally destruct our (regular) worker thread pool
* Note: currently pWtpReg is never NULL, but if we optimize our logic, this may happen,
@@ -2026,7 +2037,7 @@ CODESTARTobjDestruct(queue)
wtpDestruct(&pThis->pWtpDA);
}
if(pThis->pqDA != NULL) {
- queueDestruct(&pThis->pqDA);
+ qqueueDestruct(&pThis->pqDA);
}
/* persist the queue (we always do that - queuePersits() does cleanup if the queue is empty)
@@ -2036,7 +2047,7 @@ CODESTARTobjDestruct(queue)
* disk queues and DA mode. Anyhow, it doesn't hurt to know that we could extend it here
* if need arises (what I doubt...) -- rgerhards, 2008-01-25
*/
- CHKiRet_Hdlr(queuePersist(pThis, QUEUE_NO_CHECKPOINT)) {
+ CHKiRet_Hdlr(qqueuePersist(pThis, QUEUE_NO_CHECKPOINT)) {
dbgoprint((obj_t*) pThis, "error %d persisting queue - data lost!\n", iRet);
}
@@ -2061,7 +2072,7 @@ CODESTARTobjDestruct(queue)
if(pThis->pszSpoolDir != NULL)
free(pThis->pszSpoolDir);
-ENDobjDestruct(queue)
+ENDobjDestruct(qqueue)
/* set the queue's file prefix
@@ -2070,7 +2081,7 @@ ENDobjDestruct(queue)
* rgerhards, 2008-01-09
*/
rsRetVal
-queueSetFilePrefix(queue_t *pThis, uchar *pszPrefix, size_t iLenPrefix)
+qqueueSetFilePrefix(qqueue_t *pThis, uchar *pszPrefix, size_t iLenPrefix)
{
DEFiRet;
@@ -2093,11 +2104,11 @@ finalize_it:
* rgerhards, 2008-01-09
*/
rsRetVal
-queueSetMaxFileSize(queue_t *pThis, size_t iMaxFileSize)
+qqueueSetMaxFileSize(qqueue_t *pThis, size_t iMaxFileSize)
{
DEFiRet;
- ISOBJ_TYPE_assert(pThis, queue);
+ ISOBJ_TYPE_assert(pThis, qqueue);
if(iMaxFileSize < 1024) {
ABORT_FINALIZE(RS_RET_VALUE_TOO_LOW);
@@ -2114,13 +2125,22 @@ finalize_it:
* Enqueues the new element and awakes worker thread.
*/
rsRetVal
-queueEnqObj(queue_t *pThis, flowControl_t flowCtlType, void *pUsr)
+qqueueEnqObj(qqueue_t *pThis, flowControl_t flowCtlType, void *pUsr)
{
DEFiRet;
int iCancelStateSave;
struct timespec t;
- ISOBJ_TYPE_assert(pThis, queue);
+ ISOBJ_TYPE_assert(pThis, qqueue);
+
+ /* first check if we need to discard this message (which will cause CHKiRet() to exit)
+ * rgerhards, 2008-10-07: It is OK to do this outside of mutex protection. The iQueueSize
+ * and bRunsDA parameters may not reflect the correct settings here, but they are
+ * "good enough" in the sense that they can be used to drive the decision. Valgrind's
+ * threading tools may point this access to be an error, but this is done
+ * intentional. I do not see this causes problems to us.
+ */
+ CHKiRet(qqueueChkDiscardMsg(pThis, pThis->iQueueSize, pThis->bRunsDA, pUsr));
/* Please note that this function is not cancel-safe and consequently
* sets the calling thread's cancelibility state to PTHREAD_CANCEL_DISABLE
@@ -2133,12 +2153,9 @@ queueEnqObj(queue_t *pThis, flowControl_t flowCtlType, void *pUsr)
d_pthread_mutex_lock(pThis->mut);
}
- /* first check if we need to discard this message (which will cause CHKiRet() to exit) */
- CHKiRet(queueChkDiscardMsg(pThis, pThis->iQueueSize, pThis->bRunsDA, pUsr));
-
/* then check if we need to add an assistance disk queue */
if(pThis->bIsDA)
- CHKiRet(queueChkStrtDA(pThis));
+ CHKiRet(qqueueChkStrtDA(pThis));
/* handle flow control
* There are two different flow control mechanisms: basic and advanced flow control.
@@ -2188,20 +2205,142 @@ queueEnqObj(queue_t *pThis, flowControl_t flowCtlType, void *pUsr)
objDestruct(pUsr);
ABORT_FINALIZE(RS_RET_QUEUE_FULL);
}
+ dbgoprint((obj_t*) pThis, "enqueueMsg: wait solved queue full condition, enqueing\n");
}
/* and finally enqueue the message */
- CHKiRet(queueAdd(pThis, pUsr));
- queueChkPersist(pThis);
+ CHKiRet(qqueueAdd(pThis, pUsr));
+ qqueueChkPersist(pThis);
finalize_it:
if(pThis->qType != QUEUETYPE_DIRECT) {
/* make sure at least one worker is running. */
- queueAdviseMaxWorkers(pThis);
+ qqueueAdviseMaxWorkers(pThis);
+ /* and release the mutex */
+ d_pthread_mutex_unlock(pThis->mut);
+ pthread_setcancelstate(iCancelStateSave, NULL);
dbgoprint((obj_t*) pThis, "EnqueueMsg advised worker start\n");
+ }
+
+ RETiRet;
+}
+
+
+/* enqueue a single data object. This currently is a helper to qqueueMultiEnqObj.
+ * Note that the queue mutex MUST already be locked when this function is called.
+ * rgerhards, 2009-06-16
+ */
+static inline rsRetVal
+doEnqSingleObj(qqueue_t *pThis, flowControl_t flowCtlType, void *pUsr)
+{
+ DEFiRet;
+ struct timespec t;
+
+ /* first check if we need to discard this message (which will cause CHKiRet() to exit)
+ */
+ CHKiRet(qqueueChkDiscardMsg(pThis, pThis->iQueueSize, pThis->bRunsDA, pUsr));
+
+ /* then check if we need to add an assistance disk queue */
+ if(pThis->bIsDA)
+ CHKiRet(qqueueChkStrtDA(pThis));
+
+ /* handle flow control
+ * There are two different flow control mechanisms: basic and advanced flow control.
+ * Basic flow control has always been implemented and protects the queue structures
+ * in that it makes sure no more data is enqueued than the queue is configured to
+ * support. Enhanced flow control is being added today. There are some sources which
+ * can easily be stopped, e.g. a file reader. This is the case because it is unlikely
+ * that blocking those sources will have negative effects (after all, the file is
+ * continued to be written). Other sources can somewhat be blocked (e.g. the kernel
+ * log reader or the local log stream reader): in general, nothing is lost if messages
+ * from these sources are not picked up immediately. HOWEVER, they can not block for
+ * an extended period of time, as this either causes message loss or - even worse - some
+ * other bad effects (e.g. unresponsive system in respect to the main system log socket).
+ * Finally, there are some (few) sources which can not be blocked at all. UDP syslog is
+ * a prime example. If a UDP message is not received, it is simply lost. So we can't
+ * do anything against UDP sockets that come in too fast. The core idea of advanced
+ * flow control is that we take into account the different natures of the sources and
+ * select flow control mechanisms that fit these needs. This also means, in the end
+ * result, that non-blockable sources like UDP syslog receive priority in the system.
+ * It's a side effect, but a good one ;) -- rgerhards, 2008-03-14
+ */
+ if(flowCtlType == eFLOWCTL_FULL_DELAY) {
+ while(pThis->iQueueSize >= pThis->iFullDlyMrk) {
+ dbgoprint((obj_t*) pThis, "enqueueMsg: FullDelay mark reached for full delayable message - blocking.\n");
+ pthread_cond_wait(&pThis->belowFullDlyWtrMrk, pThis->mut); /* TODO error check? But what do then? */
+ }
+ } else if(flowCtlType == eFLOWCTL_LIGHT_DELAY) {
+ if(pThis->iQueueSize >= pThis->iLightDlyMrk) {
+ dbgoprint((obj_t*) pThis, "enqueueMsg: LightDelay mark reached for light delayable message - blocking a bit.\n");
+ timeoutComp(&t, 1000); /* 1000 millisconds = 1 second TODO: make configurable */
+ pthread_cond_timedwait(&pThis->belowLightDlyWtrMrk, pThis->mut, &t); /* TODO error check? But what do then? */
+ }
+ }
+
+ /* from our regular flow control settings, we are now ready to enqueue the object.
+ * However, we now need to do a check if the queue permits to add more data. If that
+ * is not the case, basic flow control enters the field, which means we wait for
+ * the queue to become ready or drop the new message. -- rgerhards, 2008-03-14
+ */
+ while( (pThis->iMaxQueueSize > 0 && pThis->iQueueSize >= pThis->iMaxQueueSize)
+ || (pThis->qType == QUEUETYPE_DISK && pThis->sizeOnDiskMax != 0
+ && pThis->tVars.disk.sizeOnDisk > pThis->sizeOnDiskMax)) {
+ dbgoprint((obj_t*) pThis, "enqueueMsg: queue FULL - waiting to drain.\n");
+ timeoutComp(&t, pThis->toEnq);
+ if(pthread_cond_timedwait(&pThis->notFull, pThis->mut, &t) != 0) {
+ dbgoprint((obj_t*) pThis, "enqueueMsg: cond timeout, dropping message!\n");
+ objDestruct(pUsr);
+ ABORT_FINALIZE(RS_RET_QUEUE_FULL);
+ }
+ dbgoprint((obj_t*) pThis, "enqueueMsg: wait solved queue full condition, enqueing\n");
+ }
+
+ /* and finally enqueue the message */
+ CHKiRet(qqueueAdd(pThis, pUsr));
+ qqueueChkPersist(pThis); // TODO: optimize, do in outer function! (but we need parts from v5?)
+
+finalize_it:
+ RETiRet;
+}
+
+/* enqueue multiple user data elements at once. The aim is to provide a faster interface
+ * for object submission. Uses the multi_submit_t helper object.
+ * Please note that this function is not cancel-safe and consequently
+ * sets the calling thread's cancelibility state to PTHREAD_CANCEL_DISABLE
+ * during its execution. If that is not done, race conditions occur if the
+ * thread is canceled (most important use case is input module termination).
+ * rgerhards, 2009-06-16
+ */
+rsRetVal
+qqueueMultiEnqObj(qqueue_t *pThis, multi_submit_t *pMultiSub)
+{
+ int iCancelStateSave;
+ int i;
+ rsRetVal localRet;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, qqueue);
+ assert(pMultiSub != NULL);
+
+ if(pThis->qType != QUEUETYPE_DIRECT) {
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave);
+ d_pthread_mutex_lock(pThis->mut);
+ }
+
+ for(i = 0 ; i < pMultiSub->nElem ; ++i) {
+ localRet = doEnqSingleObj(pThis, pMultiSub->ppMsgs[i]->flowCtlType, (void*)pMultiSub->ppMsgs[i]);
+ if(localRet != RS_RET_OK && localRet != RS_RET_QUEUE_FULL)
+ ABORT_FINALIZE(localRet);
+ }
+
+finalize_it:
+ if(pThis->qType != QUEUETYPE_DIRECT) {
+ /* make sure at least one worker is running. */
+ qqueueAdviseMaxWorkers(pThis);
/* and release the mutex */
d_pthread_mutex_unlock(pThis->mut);
pthread_setcancelstate(iCancelStateSave, NULL);
+ dbgoprint((obj_t*) pThis, "MultiEnqObj advised worker start\n");
}
RETiRet;
@@ -2217,12 +2356,12 @@ finalize_it:
* rgerhards, 2008-01-16
*/
static rsRetVal
-queueSetEnqOnly(queue_t *pThis, int bEnqOnly, int bLockMutex)
+qqueueSetEnqOnly(qqueue_t *pThis, int bEnqOnly, int bLockMutex)
{
DEFiRet;
DEFVARS_mutexProtection;
- ISOBJ_TYPE_assert(pThis, queue);
+ ISOBJ_TYPE_assert(pThis, qqueue);
/* for simplicity, we do one big mutex lock. This method is extremely seldom
* called, so that doesn't matter... -- rgerhards, 2008-01-16
@@ -2261,24 +2400,25 @@ finalize_it:
/* some simple object access methods */
-DEFpropSetMeth(queue, iPersistUpdCnt, int)
-DEFpropSetMeth(queue, iDeqtWinFromHr, int)
-DEFpropSetMeth(queue, iDeqtWinToHr, int)
-DEFpropSetMeth(queue, toQShutdown, long)
-DEFpropSetMeth(queue, toActShutdown, long)
-DEFpropSetMeth(queue, toWrkShutdown, long)
-DEFpropSetMeth(queue, toEnq, long)
-DEFpropSetMeth(queue, iHighWtrMrk, int)
-DEFpropSetMeth(queue, iLowWtrMrk, int)
-DEFpropSetMeth(queue, iDiscardMrk, int)
-DEFpropSetMeth(queue, iFullDlyMrk, int)
-DEFpropSetMeth(queue, iDiscardSeverity, int)
-DEFpropSetMeth(queue, bIsDA, int)
-DEFpropSetMeth(queue, iMinMsgsPerWrkr, int)
-DEFpropSetMeth(queue, bSaveOnShutdown, int)
-DEFpropSetMeth(queue, pUsr, void*)
-DEFpropSetMeth(queue, iDeqSlowdown, int)
-DEFpropSetMeth(queue, sizeOnDiskMax, int64)
+DEFpropSetMeth(qqueue, bSyncQueueFiles, int)
+DEFpropSetMeth(qqueue, iPersistUpdCnt, int)
+DEFpropSetMeth(qqueue, iDeqtWinFromHr, int)
+DEFpropSetMeth(qqueue, iDeqtWinToHr, int)
+DEFpropSetMeth(qqueue, toQShutdown, long)
+DEFpropSetMeth(qqueue, toActShutdown, long)
+DEFpropSetMeth(qqueue, toWrkShutdown, long)
+DEFpropSetMeth(qqueue, toEnq, long)
+DEFpropSetMeth(qqueue, iHighWtrMrk, int)
+DEFpropSetMeth(qqueue, iLowWtrMrk, int)
+DEFpropSetMeth(qqueue, iDiscardMrk, int)
+DEFpropSetMeth(qqueue, iFullDlyMrk, int)
+DEFpropSetMeth(qqueue, iDiscardSeverity, int)
+DEFpropSetMeth(qqueue, bIsDA, int)
+DEFpropSetMeth(qqueue, iMinMsgsPerWrkr, int)
+DEFpropSetMeth(qqueue, bSaveOnShutdown, int)
+DEFpropSetMeth(qqueue, pUsr, void*)
+DEFpropSetMeth(qqueue, iDeqSlowdown, int)
+DEFpropSetMeth(qqueue, sizeOnDiskMax, int64)
/* This function can be used as a generic way to set properties. Only the subset
@@ -2287,11 +2427,11 @@ DEFpropSetMeth(queue, sizeOnDiskMax, int64)
* rgerhards, 2008-01-11
*/
#define isProp(name) !rsCStrSzStrCmp(pProp->pcsName, (uchar*) name, sizeof(name) - 1)
-static rsRetVal queueSetProperty(queue_t *pThis, var_t *pProp)
+static rsRetVal qqueueSetProperty(qqueue_t *pThis, var_t *pProp)
{
DEFiRet;
- ISOBJ_TYPE_assert(pThis, queue);
+ ISOBJ_TYPE_assert(pThis, qqueue);
ASSERT(pProp != NULL);
if(isProp("iQueueSize")) {
@@ -2313,19 +2453,20 @@ finalize_it:
#undef isProp
/* dummy */
-rsRetVal queueQueryInterface(void) { return RS_RET_NOT_IMPLEMENTED; }
+rsRetVal qqueueQueryInterface(void) { return RS_RET_NOT_IMPLEMENTED; }
/* Initialize the stream class. Must be called as the very first method
* before anything else is called inside this class.
* rgerhards, 2008-01-09
*/
-BEGINObjClassInit(queue, 1, OBJ_IS_CORE_MODULE)
+BEGINObjClassInit(qqueue, 1, OBJ_IS_CORE_MODULE)
/* request objects we use */
CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(strm, CORE_COMPONENT));
/* now set our own handlers */
- OBJSetMethodHandler(objMethod_SETPROPERTY, queueSetProperty);
-ENDObjClassInit(queue)
+ OBJSetMethodHandler(objMethod_SETPROPERTY, qqueueSetProperty);
+ENDObjClassInit(qqueue)
/* vi:set ai:
*/
diff --git a/runtime/queue.h b/runtime/queue.h
index 9e75b31b..1d82d8d9 100644
--- a/runtime/queue.h
+++ b/runtime/queue.h
@@ -58,10 +58,10 @@ typedef struct qWrkThrd_s {
typedef struct queue_s {
BEGINobjInstance;
queueType_t qType;
- int bEnqOnly; /* does queue run in enqueue-only mode (1) or not (0)? */
- int bSaveOnShutdown;/* persists everthing on shutdown (if DA!)? 1-yes, 0-no */
- int bQueueStarted; /* has queueStart() been called on this queue? 1-yes, 0-no */
- int bQueueInDestruction;/* 1 if queue is in destruction process, 0 otherwise */
+ bool bEnqOnly; /* does queue run in enqueue-only mode (1) or not (0)? */
+ bool bSaveOnShutdown;/* persists everthing on shutdown (if DA!)? 1-yes, 0-no */
+ bool bQueueStarted; /* has queueStart() been called on this queue? 1-yes, 0-no */
+ bool bQueueInDestruction;/* 1 if queue is in destruction process, 0 otherwise */
int iQueueSize; /* Current number of elements in the queue */
int iMaxQueueSize; /* how large can the queue grow? */
int iNumWorkerThreads;/* number of worker threads to use */
@@ -72,13 +72,14 @@ typedef struct queue_s {
void *pUsr; /* a global, user-supplied pointer. Is passed back to consumer. */
int iUpdsSincePersist;/* nbr of queue updates since the last persist call */
int iPersistUpdCnt; /* persits queue info after this nbr of updates - 0 -> persist only on shutdown */
+ bool bSyncQueueFiles;/* if working with files, sync them after each write? */
int iHighWtrMrk; /* high water mark for disk-assisted memory queues */
int iLowWtrMrk; /* low water mark for disk-assisted memory queues */
int iDiscardMrk; /* if the queue is above this mark, low-severity messages are discarded */
int iFullDlyMrk; /* if the queue is above this mark, FULL_DELAYable message are put on hold */
int iLightDlyMrk; /* if the queue is above this mark, LIGHT_DELAYable message are put on hold */
int iDiscardSeverity;/* messages of this severity above are discarded on too-full queue */
- int bNeedDelQIF; /* does the QIF file need to be deleted when queue becomes empty? */
+ bool bNeedDelQIF; /* does the QIF file need to be deleted when queue becomes empty? */
int toQShutdown; /* timeout for regular queue shutdown in ms */
int toActShutdown; /* timeout for long-running action shutdown in ms */
int toWrkShutdown; /* timeout for idle workers in ms, -1 means indefinite (0 is immediate) */
@@ -159,7 +160,7 @@ typedef struct queue_s {
strm_t *pRead; /* current file to be read */
} disk;
} tVars;
-} queue_t;
+} qqueue_t;
/* some symbolic constants for easier reference */
#define QUEUE_MODE_ENQDEQ 0
@@ -176,30 +177,32 @@ typedef struct queue_s {
#define QUEUE_TIMEOUT_ETERNAL 24 * 60 * 60 * 1000
/* prototypes */
-rsRetVal queueDestruct(queue_t **ppThis);
-rsRetVal queueEnqObj(queue_t *pThis, flowControl_t flwCtlType, void *pUsr);
-rsRetVal queueStart(queue_t *pThis);
-rsRetVal queueSetMaxFileSize(queue_t *pThis, size_t iMaxFileSize);
-rsRetVal queueSetFilePrefix(queue_t *pThis, uchar *pszPrefix, size_t iLenPrefix);
-rsRetVal queueConstruct(queue_t **ppThis, queueType_t qType, int iWorkerThreads,
+rsRetVal qqueueDestruct(qqueue_t **ppThis);
+rsRetVal qqueueMultiEnqObj(qqueue_t *pThis, multi_submit_t *pMultiSub);
+rsRetVal qqueueEnqObj(qqueue_t *pThis, flowControl_t flwCtlType, void *pUsr);
+rsRetVal qqueueStart(qqueue_t *pThis);
+rsRetVal qqueueSetMaxFileSize(qqueue_t *pThis, size_t iMaxFileSize);
+rsRetVal qqueueSetFilePrefix(qqueue_t *pThis, uchar *pszPrefix, size_t iLenPrefix);
+rsRetVal qqueueConstruct(qqueue_t **ppThis, queueType_t qType, int iWorkerThreads,
int iMaxQueueSize, rsRetVal (*pConsumer)(void*,void*));
-PROTOTYPEObjClassInit(queue);
-PROTOTYPEpropSetMeth(queue, iPersistUpdCnt, int);
-PROTOTYPEpropSetMeth(queue, iDeqtWinFromHr, int);
-PROTOTYPEpropSetMeth(queue, iDeqtWinToHr, int);
-PROTOTYPEpropSetMeth(queue, toQShutdown, long);
-PROTOTYPEpropSetMeth(queue, toActShutdown, long);
-PROTOTYPEpropSetMeth(queue, toWrkShutdown, long);
-PROTOTYPEpropSetMeth(queue, toEnq, long);
-PROTOTYPEpropSetMeth(queue, iHighWtrMrk, int);
-PROTOTYPEpropSetMeth(queue, iLowWtrMrk, int);
-PROTOTYPEpropSetMeth(queue, iDiscardMrk, int);
-PROTOTYPEpropSetMeth(queue, iDiscardSeverity, int);
-PROTOTYPEpropSetMeth(queue, iMinMsgsPerWrkr, int);
-PROTOTYPEpropSetMeth(queue, bSaveOnShutdown, int);
-PROTOTYPEpropSetMeth(queue, pUsr, void*);
-PROTOTYPEpropSetMeth(queue, iDeqSlowdown, int);
-PROTOTYPEpropSetMeth(queue, sizeOnDiskMax, int64);
-#define queueGetID(pThis) ((unsigned long) pThis)
+PROTOTYPEObjClassInit(qqueue);
+PROTOTYPEpropSetMeth(qqueue, iPersistUpdCnt, int);
+PROTOTYPEpropSetMeth(qqueue, bSyncQueueFiles, int);
+PROTOTYPEpropSetMeth(qqueue, iDeqtWinFromHr, int);
+PROTOTYPEpropSetMeth(qqueue, iDeqtWinToHr, int);
+PROTOTYPEpropSetMeth(qqueue, toQShutdown, long);
+PROTOTYPEpropSetMeth(qqueue, toActShutdown, long);
+PROTOTYPEpropSetMeth(qqueue, toWrkShutdown, long);
+PROTOTYPEpropSetMeth(qqueue, toEnq, long);
+PROTOTYPEpropSetMeth(qqueue, iHighWtrMrk, int);
+PROTOTYPEpropSetMeth(qqueue, iLowWtrMrk, int);
+PROTOTYPEpropSetMeth(qqueue, iDiscardMrk, int);
+PROTOTYPEpropSetMeth(qqueue, iDiscardSeverity, int);
+PROTOTYPEpropSetMeth(qqueue, iMinMsgsPerWrkr, int);
+PROTOTYPEpropSetMeth(qqueue, bSaveOnShutdown, int);
+PROTOTYPEpropSetMeth(qqueue, pUsr, void*);
+PROTOTYPEpropSetMeth(qqueue, iDeqSlowdown, int);
+PROTOTYPEpropSetMeth(qqueue, sizeOnDiskMax, int64);
+#define qqueueGetID(pThis) ((unsigned long) pThis)
#endif /* #ifndef QUEUE_H_INCLUDED */
diff --git a/runtime/rsyslog.c b/runtime/rsyslog.c
index 54db12c2..443d0f41 100644
--- a/runtime/rsyslog.c
+++ b/runtime/rsyslog.c
@@ -77,6 +77,9 @@
#include "conf.h"
#include "glbl.h"
#include "errmsg.h"
+#include "prop.h"
+#include "rule.h"
+#include "ruleset.h"
/* forward definitions */
static rsRetVal dfltErrLogger(int, uchar *errMsg);
@@ -144,20 +147,18 @@ rsrtInit(char **ppErrObj, obj_if_t *pObjIF)
* class immediately after it is initialized. And, of course, we load those classes
* first that we use ourselfs... -- rgerhards, 2008-03-07
*/
+ if(ppErrObj != NULL) *ppErrObj = "prop";
+ CHKiRet(propClassInit(NULL));
if(ppErrObj != NULL) *ppErrObj = "glbl";
CHKiRet(glblClassInit(NULL));
if(ppErrObj != NULL) *ppErrObj = "datetime";
CHKiRet(datetimeClassInit(NULL));
if(ppErrObj != NULL) *ppErrObj = "msg";
CHKiRet(msgClassInit(NULL));
- if(ppErrObj != NULL) *ppErrObj = "str,";
- CHKiRet(strmClassInit(NULL));
- if(ppErrObj != NULL) *ppErrObj = "wti";
- CHKiRet(wtiClassInit(NULL));
- if(ppErrObj != NULL) *ppErrObj = "wtp";
- CHKiRet(wtpClassInit(NULL));
- if(ppErrObj != NULL) *ppErrObj = "queue";
- CHKiRet(queueClassInit(NULL));
+ if(ppErrObj != NULL) *ppErrObj = "ctok_token";
+ CHKiRet(ctok_tokenClassInit(NULL));
+ if(ppErrObj != NULL) *ppErrObj = "ctok";
+ CHKiRet(ctokClassInit(NULL));
if(ppErrObj != NULL) *ppErrObj = "vmstk";
CHKiRet(vmstkClassInit(NULL));
if(ppErrObj != NULL) *ppErrObj = "sysvar";
@@ -168,12 +169,18 @@ rsrtInit(char **ppErrObj, obj_if_t *pObjIF)
CHKiRet(vmopClassInit(NULL));
if(ppErrObj != NULL) *ppErrObj = "vmprg";
CHKiRet(vmprgClassInit(NULL));
- if(ppErrObj != NULL) *ppErrObj = "ctok_token";
- CHKiRet(ctok_tokenClassInit(NULL));
- if(ppErrObj != NULL) *ppErrObj = "ctok";
- CHKiRet(ctokClassInit(NULL));
if(ppErrObj != NULL) *ppErrObj = "expr";
CHKiRet(exprClassInit(NULL));
+ if(ppErrObj != NULL) *ppErrObj = "rule";
+ CHKiRet(ruleClassInit(NULL));
+ if(ppErrObj != NULL) *ppErrObj = "ruleset";
+ CHKiRet(rulesetClassInit(NULL));
+ if(ppErrObj != NULL) *ppErrObj = "wti";
+ CHKiRet(wtiClassInit(NULL));
+ if(ppErrObj != NULL) *ppErrObj = "wtp";
+ CHKiRet(wtpClassInit(NULL));
+ if(ppErrObj != NULL) *ppErrObj = "queue";
+ CHKiRet(qqueueClassInit(NULL));
if(ppErrObj != NULL) *ppErrObj = "conf";
CHKiRet(confClassInit(NULL));
@@ -206,6 +213,8 @@ rsrtExit(void)
/* do actual de-init only if we are the last runtime user */
confClassExit();
glblClassExit();
+ rulesetClassExit();
+ ruleClassExit();
objClassExit(); /* *THIS* *MUST/SHOULD?* always be the first class initilizer being called (except debug)! */
}
diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h
index 9ce58210..8979893a 100644
--- a/runtime/rsyslog.h
+++ b/runtime/rsyslog.h
@@ -29,21 +29,23 @@
/* ############################################################# *
* # Config Settings # *
* ############################################################# */
-#define RS_STRINGBUF_ALLOC_INCREMENT 128
+#define RS_STRINGBUF_ALLOC_INCREMENT 128
+/* MAXSIZE are absolute maxima, while BUFSIZE are just values after which
+ * processing is more time-intense. The BUFSIZE params currently add their
+ * value to the fixed size of the message object.
+ */
+#define CONF_TAG_MAXSIZE 512 /* a value that is deemed far too large for any valid TAG */
+#define CONF_HOSTNAME_MAXSIZE 512 /* a value that is deemed far too large for any valid HOSTNAME */
+#define CONF_RAWMSG_BUFSIZE 101
+#define CONF_TAG_BUFSIZE 32
+#define CONF_HOSTNAME_BUFSIZE 32
+#define CONF_PROP_BUFSIZE 16 /* should be close to sizeof(ptr) or lighly above it */
+
/* ############################################################# *
* # End Config Settings # *
* ############################################################# */
-#ifndef NOLARGEFILE
-# undef _LARGEFILE_SOURCE
-# undef _LARGEFILE64_SOURCE
-# undef _FILE_OFFSET_BITS
-# define _LARGEFILE_SOURCE
-# define _LARGEFILE64_SOURCE
-# define _FILE_OFFSET_BITS 64
-#endif
-
/* portability: not all platforms have these defines, so we
* define them here if they are missing. -- rgerhards, 2008-03-04
*/
@@ -62,7 +64,9 @@
typedef unsigned char uchar;/* get rid of the unhandy "unsigned char" */
typedef struct thrdInfo thrdInfo_t;
typedef struct obj_s obj_t;
-typedef struct filed selector_t;/* TODO: this so far resides in syslogd.c, think about modularization */
+typedef struct ruleset_s ruleset_t;
+typedef struct rule_s rule_t;
+//typedef struct filed selector_t;/* TODO: this so far resides in syslogd.c, think about modularization */
typedef struct NetAddr netAddr_t;
typedef struct netstrms_s netstrms_t;
typedef struct netstrm_s netstrm_t;
@@ -77,6 +81,7 @@ typedef struct nsdsel_gtls_s nsdsel_gtls_t;
typedef obj_t nsd_t;
typedef obj_t nsdsel_t;
typedef struct msg msg_t;
+typedef struct prop_s prop_t;
typedef struct interface_s interface_t;
typedef struct objInfo_s objInfo_t;
typedef enum rsRetVal_ rsRetVal; /**< friendly type for global return value */
@@ -84,17 +89,29 @@ typedef rsRetVal (*errLogFunc_t)(uchar*); /* this is a trick to store a function
typedef struct permittedPeers_s permittedPeers_t; /* this should go away in the long term -- rgerhards, 2008-05-19 */
typedef struct permittedPeerWildcard_s permittedPeerWildcard_t; /* this should go away in the long term -- rgerhards, 2008-05-19 */
typedef struct tcpsrv_s tcpsrv_t;
+typedef struct tcps_sess_s tcps_sess_t;
+typedef struct strmsrv_s strmsrv_t;
+typedef struct strms_sess_s strms_sess_t;
+typedef struct vmstk_s vmstk_t;
+typedef rsRetVal (*prsf_t)(struct vmstk_s*, int); /* pointer to a RainerScript function */
+
+typedef struct tcpLstnPortList_s tcpLstnPortList_t; // TODO: rename?
+typedef struct strmLstnPortList_s strmLstnPortList_t; // TODO: rename?
/* some universal 64 bit define... */
typedef long long int64;
typedef long long unsigned uint64;
typedef int64 number_t; /* type to use for numbers - TODO: maybe an autoconf option? */
+typedef char intTiny; /* 0..127! */
+typedef uchar uintTiny; /* 0..255! */
#ifdef __hpux
typedef unsigned int u_int32_t; /* TODO: is this correct? */
typedef int socklen_t;
#endif
+typedef char bool; /* I intentionally use char, to keep it slim so that many fit into the CPU cache! */
+
/* settings for flow control
* TODO: is there a better place for them? -- rgerhards, 2008-03-14
*/
@@ -104,6 +121,72 @@ typedef enum {
eFLOWCTL_FULL_DELAY = 2 /**< delay possible for extended period of time */
} flowControl_t;
+/* filter operations */
+typedef enum {
+ FIOP_NOP = 0, /* do not use - No Operation */
+ FIOP_CONTAINS = 1, /* contains string? */
+ FIOP_ISEQUAL = 2, /* is (exactly) equal? */
+ FIOP_STARTSWITH = 3, /* starts with a string? */
+ FIOP_REGEX = 4, /* matches a (BRE) regular expression? */
+ FIOP_EREREGEX = 5 /* matches a ERE regular expression? */
+} fiop_t;
+
+
+/* multi-submit support.
+ * This is done via a simple data structure, which holds the number of elements
+ * as well as an array of to-be-submitted messages.
+ * rgerhards, 2009-06-16
+ */
+typedef struct multi_submit_s multi_submit_t;
+struct multi_submit_s {
+ short maxElem; /* maximum number of Elements */
+ short nElem; /* current number of Elements, points to the next one FREE */
+ msg_t **ppMsgs;
+};
+
+
+#ifndef _PATH_CONSOLE
+#define _PATH_CONSOLE "/dev/console"
+#endif
+
+/* properties are now encoded as (tiny) integers. I do not use an enum as I would like
+ * to keep the memory footprint small (and thus cache hits high).
+ * rgerhards, 2009-06-26
+ */
+typedef uintTiny propid_t;
+#define PROP_INVALID 0
+#define PROP_MSG 1
+#define PROP_TIMESTAMP 2
+#define PROP_HOSTNAME 3
+#define PROP_SYSLOGTAG 4
+#define PROP_RAWMSG 5
+#define PROP_INPUTNAME 6
+#define PROP_FROMHOST 7
+#define PROP_FROMHOST_IP 8
+#define PROP_PRI 9
+#define PROP_PRI_TEXT 10
+#define PROP_IUT 11
+#define PROP_SYSLOGFACILITY 12
+#define PROP_SYSLOGFACILITY_TEXT 13
+#define PROP_SYSLOGSEVERITY 14
+#define PROP_SYSLOGSEVERITY_TEXT 15
+#define PROP_TIMEGENERATED 16
+#define PROP_PROGRAMNAME 17
+#define PROP_PROTOCOL_VERSION 18
+#define PROP_STRUCTURED_DATA 19
+#define PROP_APP_NAME 20
+#define PROP_PROCID 21
+#define PROP_MSGID 22
+#define PROP_SYS_NOW 150
+#define PROP_SYS_YEAR 151
+#define PROP_SYS_MONTH 152
+#define PROP_SYS_DAY 153
+#define PROP_SYS_HOUR 154
+#define PROP_SYS_HHOUR 155
+#define PROP_SYS_QHOUR 156
+#define PROP_SYS_MINUTE 157
+#define PROP_SYS_MYHOSTNAME 158
+
/* The error codes below are orginally "borrowed" from
* liblogging. As such, we reserve values up to -2999
@@ -252,8 +335,31 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth
RS_RET_QUEUE_FULL = -2105, /**< queue is full, operation could not be completed */
RS_RET_ACCEPT_ERR = -2106, /**< error during accept() system call */
RS_RET_INVLD_TIME = -2107, /**< invalid timestamp (e.g. could not be parsed) */
+ RS_RET_NO_ZIP = -2108, /**< ZIP functionality is not present */
RS_RET_CODE_ERR = -2109, /**< program code (internal) error */
- RS_RET_NONFATAL_CONFIG_ERR = -2123, /**< non-fatal error during config processing */
+ RS_RET_FUNC_NO_LPAREN = -2110, /**< left parenthesis missing after function call (rainerscript) */
+ RS_RET_FUNC_MISSING_EXPR = -2111, /**< no expression after comma in function call (rainerscript) */
+ RS_RET_INVLD_NBR_ARGUMENTS = -2112, /**< invalid number of arguments for function call (rainerscript) */
+ RS_RET_INVLD_FUNC = -2113, /**< invalid function name for function call (rainerscript) */
+ RS_RET_DUP_FUNC_NAME = -2114, /**< duplicate function name (rainerscript) */
+ RS_RET_UNKNW_FUNC = -2115, /**< unkown function name (rainerscript) */
+ RS_RET_ERR_RLIM_NOFILE = -2116, /**< error setting max. nbr open files process limit */
+ RS_RET_ERR_CREAT_PIPE = -2117, /**< error during pipe creation */
+ RS_RET_ERR_FORK = -2118, /**< error during fork() */
+ RS_RET_ERR_WRITE_PIPE = -2119, /**< error writing to pipe */
+ RS_RET_RSCORE_TOO_OLD = -2120, /**< rsyslog core is too old for ... (eg this plugin) */
+ RS_RET_DEFER_COMMIT = -2121, /**< output plugin status: not yet committed (an OK state!) */
+ RS_RET_PREVIOUS_COMMITTED = -2122, /**< output plugin status: previous record was committed (an OK state!) */
+ RS_RET_ACTION_FAILED = -2123, /**< action failed and is now suspended (consider this permanent for the time being) */
+ RS_RET_NONFATAL_CONFIG_ERR = -2124, /**< non-fatal error during config processing */
+ RS_RET_NON_SIZELIMITCMD = -2125, /**< size limit for file defined, but no size limit command given */
+ RS_RET_SIZELIMITCMD_DIDNT_RESOLVE = -2126, /**< size limit command did not resolve situation */
+ RS_RET_STREAM_DISABLED = -2127, /**< a file has been disabled (e.g. by size limit restriction) */
+ RS_RET_FILENAME_INVALID = -2140, /**< filename invalid, not found, no access, ... */
+ RS_RET_ZLIB_ERR = -2141, /**< error during zlib call */
+ RS_RET_VAR_NOT_FOUND = -2142, /**< variable not found */
+ RS_RET_EMPTY_MSG = -2143, /**< provided (raw) MSG is empty */
+ RS_RET_PEER_CLOSED_CONN = -2144, /**< remote peer closed connection (information, no error) */
/* RainerScript error messages (range 1000.. 1999) */
RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */
@@ -337,9 +443,19 @@ typedef enum rsObjectID rsObjID;
# define __attribute__(x) /*NOTHING*/
#endif
+#ifndef O_CLOEXEC
+/* of course, this limits the functionality... */
+# define O_CLOEXEC 0
+#endif
+
+/* some constants */
+#define MUTEX_ALREADY_LOCKED 0
+#define LOCK_MUTEX 1
+
/* The following prototype is convenient, even though it may not be the 100% correct place.. -- rgerhards 2008-01-07 */
void dbgprintf(char *, ...) __attribute__((format(printf, 1, 2)));
+
#include "debug.h"
#include "obj.h"
diff --git a/runtime/rule.c b/runtime/rule.c
new file mode 100644
index 00000000..4c2c9edb
--- /dev/null
+++ b/runtime/rule.c
@@ -0,0 +1,450 @@
+/* rule.c - rsyslog's rule object
+ *
+ * See file comment in rule.c for the overall structure of rule processing.
+ *
+ * Module begun 2009-06-10 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 <ctype.h>
+
+#include "rsyslog.h"
+#include "obj.h"
+#include "action.h"
+#include "rule.h"
+#include "errmsg.h"
+#include "vm.h"
+#include "var.h"
+#include "srUtils.h"
+#include "unicode-helper.h"
+#include "dirty.h" /* for getFIOPName */
+
+/* static data */
+DEFobjStaticHelpers
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(expr)
+DEFobjCurrIf(var)
+DEFobjCurrIf(vm)
+
+/* iterate over all actions, this is often needed, for example when HUP processing
+ * must be done or a shutdown is pending.
+ */
+static rsRetVal
+iterateAllActions(rule_t *pThis, rsRetVal (*pFunc)(void*, void*), void* pParam)
+{
+ return llExecFunc(&pThis->llActList, pFunc, pParam);
+}
+
+
+
+/* helper to processMsg(), used to call the configured actions. It is
+ * executed from within llExecFunc() of the action list.
+ * rgerhards, 2007-08-02
+ */
+typedef struct processMsgDoActions_s {
+ int bPrevWasSuspended; /* was the previous action suspended? */
+ msg_t *pMsg;
+} processMsgDoActions_t;
+DEFFUNC_llExecFunc(processMsgDoActions)
+{
+ DEFiRet;
+ rsRetVal iRetMod; /* return value of module - we do not always pass that back */
+ action_t *pAction = (action_t*) pData;
+ processMsgDoActions_t *pDoActData = (processMsgDoActions_t*) pParam;
+
+ assert(pAction != NULL);
+
+ if((pAction->bExecWhenPrevSusp == 1) && (pDoActData->bPrevWasSuspended == 0)) {
+ dbgprintf("not calling action because the previous one is not suspended\n");
+ ABORT_FINALIZE(RS_RET_OK);
+ }
+
+ iRetMod = actionCallAction(pAction, pDoActData->pMsg);
+ if(iRetMod == RS_RET_DISCARDMSG) {
+ ABORT_FINALIZE(RS_RET_DISCARDMSG);
+ } else if(iRetMod == RS_RET_SUSPENDED) {
+ /* indicate suspension for next module to be called */
+ pDoActData->bPrevWasSuspended = 1;
+ } else {
+ pDoActData->bPrevWasSuspended = 0;
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* This functions looks at the given message and checks if it matches the
+ * provided filter condition.
+ */
+static rsRetVal
+shouldProcessThisMessage(rule_t *pRule, msg_t *pMsg, int *bProcessMsg)
+{
+ DEFiRet;
+ unsigned short pbMustBeFreed;
+ uchar *pszPropVal;
+ int bRet = 0;
+ size_t propLen;
+ vm_t *pVM = NULL;
+ var_t *pResult = NULL;
+
+ ISOBJ_TYPE_assert(pRule, rule);
+ assert(pMsg != NULL);
+
+ /* we first have a look at the global, BSD-style block filters (for tag
+ * and host). Only if they match, we evaluate the actual filter.
+ * rgerhards, 2005-10-18
+ */
+ if(pRule->eHostnameCmpMode == HN_NO_COMP) {
+ /* EMPTY BY INTENSION - we check this value first, because
+ * it is the one most often used, so this saves us time!
+ */
+ } else if(pRule->eHostnameCmpMode == HN_COMP_MATCH) {
+ if(rsCStrSzStrCmp(pRule->pCSHostnameComp, (uchar*) getHOSTNAME(pMsg), getHOSTNAMELen(pMsg))) {
+ /* not equal, so we are already done... */
+ dbgprintf("hostname filter '+%s' does not match '%s'\n",
+ rsCStrGetSzStrNoNULL(pRule->pCSHostnameComp), getHOSTNAME(pMsg));
+ FINALIZE;
+ }
+ } else { /* must be -hostname */
+ if(!rsCStrSzStrCmp(pRule->pCSHostnameComp, (uchar*) getHOSTNAME(pMsg), getHOSTNAMELen(pMsg))) {
+ /* not equal, so we are already done... */
+ dbgprintf("hostname filter '-%s' does not match '%s'\n",
+ rsCStrGetSzStrNoNULL(pRule->pCSHostnameComp), getHOSTNAME(pMsg));
+ FINALIZE;
+ }
+ }
+
+ if(pRule->pCSProgNameComp != NULL) {
+ int bInv = 0, bEqv = 0, offset = 0;
+ if(*(rsCStrGetSzStrNoNULL(pRule->pCSProgNameComp)) == '-') {
+ if(*(rsCStrGetSzStrNoNULL(pRule->pCSProgNameComp) + 1) == '-')
+ offset = 1;
+ else {
+ bInv = 1;
+ offset = 1;
+ }
+ }
+ if(!rsCStrOffsetSzStrCmp(pRule->pCSProgNameComp, offset,
+ (uchar*) getProgramName(pMsg, LOCK_MUTEX), getProgramNameLen(pMsg, LOCK_MUTEX)))
+ bEqv = 1;
+
+ if((!bEqv && !bInv) || (bEqv && bInv)) {
+ /* not equal or inverted selection, so we are already done... */
+ DBGPRINTF("programname filter '%s' does not match '%s'\n",
+ rsCStrGetSzStrNoNULL(pRule->pCSProgNameComp), getProgramName(pMsg, LOCK_MUTEX));
+ FINALIZE;
+ }
+ }
+
+ /* done with the BSD-style block filters */
+
+ if(pRule->f_filter_type == FILTER_PRI) {
+ /* skip messages that are incorrect priority */
+dbgprintf("testing filter, f_pmask %d\n", pRule->f_filterData.f_pmask[pMsg->iFacility]);
+ if ( (pRule->f_filterData.f_pmask[pMsg->iFacility] == TABLE_NOPRI) || \
+ ((pRule->f_filterData.f_pmask[pMsg->iFacility] & (1<<pMsg->iSeverity)) == 0) )
+ bRet = 0;
+ else
+ bRet = 1;
+ } else if(pRule->f_filter_type == FILTER_EXPR) {
+ CHKiRet(vm.Construct(&pVM));
+ CHKiRet(vm.ConstructFinalize(pVM));
+ CHKiRet(vm.SetMsg(pVM, pMsg));
+ CHKiRet(vm.ExecProg(pVM, pRule->f_filterData.f_expr->pVmprg));
+ CHKiRet(vm.PopBoolFromStack(pVM, &pResult));
+ dbgprintf("result of expression evaluation: %lld\n", pResult->val.num);
+ /* VM is destructed on function exit */
+ bRet = (pResult->val.num) ? 1 : 0;
+ } else {
+ assert(pRule->f_filter_type == FILTER_PROP); /* assert() just in case... */
+ pszPropVal = MsgGetProp(pMsg, NULL, pRule->f_filterData.prop.propID, &propLen, &pbMustBeFreed);
+
+ /* Now do the compares (short list currently ;)) */
+ switch(pRule->f_filterData.prop.operation ) {
+ case FIOP_CONTAINS:
+ if(rsCStrLocateInSzStr(pRule->f_filterData.prop.pCSCompValue, (uchar*) pszPropVal) != -1)
+ bRet = 1;
+ break;
+ case FIOP_ISEQUAL:
+ if(rsCStrSzStrCmp(pRule->f_filterData.prop.pCSCompValue,
+ pszPropVal, ustrlen(pszPropVal)) == 0)
+ bRet = 1; /* process message! */
+ break;
+ case FIOP_STARTSWITH:
+ if(rsCStrSzStrStartsWithCStr(pRule->f_filterData.prop.pCSCompValue,
+ pszPropVal, ustrlen(pszPropVal)) == 0)
+ bRet = 1; /* process message! */
+ break;
+ case FIOP_REGEX:
+ if(rsCStrSzStrMatchRegex(pRule->f_filterData.prop.pCSCompValue,
+ (unsigned char*) pszPropVal, 0, &pRule->f_filterData.prop.regex_cache) == RS_RET_OK)
+ bRet = 1;
+ break;
+ case FIOP_EREREGEX:
+ if(rsCStrSzStrMatchRegex(pRule->f_filterData.prop.pCSCompValue,
+ (unsigned char*) pszPropVal, 1, &pRule->f_filterData.prop.regex_cache) == RS_RET_OK)
+ bRet = 1;
+ break;
+ default:
+ /* here, it handles NOP (for performance reasons) */
+ assert(pRule->f_filterData.prop.operation == FIOP_NOP);
+ bRet = 1; /* as good as any other default ;) */
+ break;
+ }
+
+ /* now check if the value must be negated */
+ if(pRule->f_filterData.prop.isNegated)
+ bRet = (bRet == 1) ? 0 : 1;
+
+ if(Debug) {
+ dbgprintf("Filter: check for property '%s' (value '%s') ",
+ propIDToName(pRule->f_filterData.prop.propID), pszPropVal);
+ if(pRule->f_filterData.prop.isNegated)
+ dbgprintf("NOT ");
+ dbgprintf("%s '%s': %s\n",
+ getFIOPName(pRule->f_filterData.prop.operation),
+ rsCStrGetSzStrNoNULL(pRule->f_filterData.prop.pCSCompValue),
+ bRet ? "TRUE" : "FALSE");
+ }
+
+ /* cleanup */
+ if(pbMustBeFreed)
+ free(pszPropVal);
+ }
+
+finalize_it:
+ /* destruct in any case, not just on error, but it makes error handling much easier */
+ if(pVM != NULL)
+ vm.Destruct(&pVM);
+
+ if(pResult != NULL)
+ var.Destruct(&pResult);
+
+ *bProcessMsg = bRet;
+ RETiRet;
+}
+
+
+
+/* Process (consume) a received message. Calls the actions configured.
+ * rgerhards, 2005-10-13
+ */
+static rsRetVal
+processMsg(rule_t *pThis, msg_t *pMsg)
+{
+ int bProcessMsg;
+ processMsgDoActions_t DoActData;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, rule);
+ assert(pMsg != NULL);
+
+ /* first check the filters... */
+ CHKiRet(shouldProcessThisMessage(pThis, pMsg, &bProcessMsg));
+ if(bProcessMsg) {
+ DoActData.pMsg = pMsg;
+ DoActData.bPrevWasSuspended = 0;
+ CHKiRet(llExecFunc(&pThis->llActList, processMsgDoActions, (void*)&DoActData));
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Standard-Constructor
+ */
+BEGINobjConstruct(rule) /* be sure to specify the object type also in END macro! */
+ENDobjConstruct(rule)
+
+
+/* ConstructionFinalizer
+ * rgerhards, 2008-01-09
+ */
+static rsRetVal
+ruleConstructFinalize(rule_t *pThis)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, rule);
+
+ /* note: actionDestruct is from action.c API! */
+ CHKiRet(llInit(&pThis->llActList, actionDestruct, NULL, NULL));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* destructor for the rule object */
+BEGINobjDestruct(rule) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDestruct(rule)
+ if(pThis->pCSHostnameComp != NULL)
+ rsCStrDestruct(&pThis->pCSHostnameComp);
+ if(pThis->pCSProgNameComp != NULL)
+ rsCStrDestruct(&pThis->pCSProgNameComp);
+
+ if(pThis->f_filter_type == FILTER_PROP) {
+ if(pThis->f_filterData.prop.pCSCompValue != NULL)
+ rsCStrDestruct(&pThis->f_filterData.prop.pCSCompValue);
+ if(pThis->f_filterData.prop.regex_cache != NULL)
+ rsCStrRegexDestruct(&pThis->f_filterData.prop.regex_cache);
+ } else if(pThis->f_filter_type == FILTER_EXPR) {
+ if(pThis->f_filterData.f_expr != NULL)
+ expr.Destruct(&pThis->f_filterData.f_expr);
+ }
+
+ llDestroy(&pThis->llActList);
+ENDobjDestruct(rule)
+
+
+/* set the associated ruleset */
+static rsRetVal
+setAssRuleset(rule_t *pThis, ruleset_t *pRuleset)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, rule);
+ ISOBJ_TYPE_assert(pRuleset, ruleset);
+ pThis->pRuleset = pRuleset;
+ RETiRet;
+}
+
+/* get the associated ruleset (may be NULL if not set!) */
+static ruleset_t*
+getAssRuleset(rule_t *pThis)
+{
+ ISOBJ_TYPE_assert(pThis, rule);
+ return pThis->pRuleset;
+}
+
+
+/* helper to DebugPrint, to print out all actions via
+ * the llExecFunc() facility.
+ */
+DEFFUNC_llExecFunc(dbgPrintInitInfoAction)
+{
+ DEFiRet;
+ iRet = actionDbgPrint((action_t*) pData);
+ dbgprintf("\n");
+ RETiRet;
+}
+
+
+/* debugprint for the rule object */
+BEGINobjDebugPrint(rule) /* be sure to specify the object type also in END and CODESTART macros! */
+ int i;
+CODESTARTobjDebugPrint(rule)
+ dbgoprint((obj_t*) pThis, "rsyslog rule:\n");
+ if(pThis->pCSProgNameComp != NULL)
+ dbgprintf("tag: '%s'\n", rsCStrGetSzStrNoNULL(pThis->pCSProgNameComp));
+ if(pThis->eHostnameCmpMode != HN_NO_COMP)
+ dbgprintf("hostname: %s '%s'\n",
+ pThis->eHostnameCmpMode == HN_COMP_MATCH ?
+ "only" : "allbut",
+ rsCStrGetSzStrNoNULL(pThis->pCSHostnameComp));
+ if(pThis->f_filter_type == FILTER_PRI) {
+ for (i = 0; i <= LOG_NFACILITIES; i++)
+ if (pThis->f_filterData.f_pmask[i] == TABLE_NOPRI)
+ dbgprintf(" X ");
+ else
+ dbgprintf("%2X ", pThis->f_filterData.f_pmask[i]);
+ } else if(pThis->f_filter_type == FILTER_EXPR) {
+ dbgprintf("EXPRESSION-BASED Filter: can currently not be displayed");
+ } else {
+ dbgprintf("PROPERTY-BASED Filter:\n");
+ dbgprintf("\tProperty.: '%s'\n", propIDToName(pThis->f_filterData.prop.propID));
+ dbgprintf("\tOperation: ");
+ if(pThis->f_filterData.prop.isNegated)
+ dbgprintf("NOT ");
+ dbgprintf("'%s'\n", getFIOPName(pThis->f_filterData.prop.operation));
+ dbgprintf("\tValue....: '%s'\n",
+ rsCStrGetSzStrNoNULL(pThis->f_filterData.prop.pCSCompValue));
+ dbgprintf("\tAction...: ");
+ }
+
+ dbgprintf("\nActions:\n");
+ llExecFunc(&pThis->llActList, dbgPrintInitInfoAction, NULL); /* actions */
+
+ dbgprintf("\n");
+ENDobjDebugPrint(rule)
+
+
+/* queryInterface function
+ * rgerhards, 2008-02-21
+ */
+BEGINobjQueryInterface(rule)
+CODESTARTobjQueryInterface(rule)
+ if(pIf->ifVersion != ruleCURR_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 = ruleConstruct;
+ pIf->ConstructFinalize = ruleConstructFinalize;
+ pIf->Destruct = ruleDestruct;
+ pIf->DebugPrint = ruleDebugPrint;
+
+ pIf->IterateAllActions = iterateAllActions;
+ pIf->ProcessMsg = processMsg;
+ pIf->SetAssRuleset = setAssRuleset;
+ pIf->GetAssRuleset = getAssRuleset;
+finalize_it:
+ENDobjQueryInterface(rule)
+
+
+/* Exit the rule class.
+ * rgerhards, 2009-04-06
+ */
+BEGINObjClassExit(rule, OBJ_IS_CORE_MODULE) /* class, version */
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(expr, CORE_COMPONENT);
+ objRelease(var, CORE_COMPONENT);
+ objRelease(vm, CORE_COMPONENT);
+ENDObjClassExit(rule)
+
+
+/* Initialize the rule class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-02-19
+ */
+BEGINObjClassInit(rule, 1, OBJ_IS_CORE_MODULE) /* class, version */
+ /* request objects we use */
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(expr, CORE_COMPONENT));
+ CHKiRet(objUse(var, CORE_COMPONENT));
+ CHKiRet(objUse(vm, CORE_COMPONENT));
+
+ /* set our own handlers */
+ OBJSetMethodHandler(objMethod_DEBUGPRINT, ruleDebugPrint);
+ OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, ruleConstructFinalize);
+ENDObjClassInit(rule)
+
+/* vi:set ai:
+ */
diff --git a/runtime/rule.h b/runtime/rule.h
new file mode 100644
index 00000000..99ac44e7
--- /dev/null
+++ b/runtime/rule.h
@@ -0,0 +1,77 @@
+/* The rule object.
+ *
+ * This implements rules within rsyslog.
+ *
+ * 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_RULE_H
+#define INCLUDED_RULE_H
+
+#include "linkedlist.h"
+#include "regexp.h"
+#include "expr.h"
+
+/* the rule object */
+struct rule_s {
+ BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
+ /* filter properties */
+ enum {
+ FILTER_PRI = 0, /* traditional PRI based filer */
+ FILTER_PROP = 1, /* extended filter, property based */
+ FILTER_EXPR = 2 /* extended filter, expression based */
+ } f_filter_type;
+ EHostnameCmpMode eHostnameCmpMode;
+ cstr_t *pCSHostnameComp; /* hostname to check */
+ cstr_t *pCSProgNameComp; /* tag to check or NULL, if not to be checked */
+ union {
+ u_char f_pmask[LOG_NFACILITIES+1]; /* priority mask */
+ struct {
+ fiop_t operation;
+ regex_t *regex_cache; /* cache for compiled REs, if such are used */
+ cstr_t *pCSCompValue; /* value to "compare" against */
+ bool isNegated;
+ propid_t propID; /* ID of the requested property */
+ } prop;
+ expr_t *f_expr; /* expression object */
+ } f_filterData;
+
+ ruleset_t *pRuleset; /* associated ruleset */
+ linkedList_t llActList; /* list of configured actions */
+};
+
+/* interfaces */
+BEGINinterface(rule) /* name must also be changed in ENDinterface macro! */
+ INTERFACEObjDebugPrint(rule);
+ rsRetVal (*Construct)(rule_t **ppThis);
+ rsRetVal (*ConstructFinalize)(rule_t __attribute__((unused)) *pThis);
+ rsRetVal (*Destruct)(rule_t **ppThis);
+ rsRetVal (*IterateAllActions)(rule_t *pThis, rsRetVal (*pFunc)(void*, void*), void *pParam);
+ rsRetVal (*ProcessMsg)(rule_t *pThis, msg_t *pMsg);
+ rsRetVal (*SetAssRuleset)(rule_t *pThis, ruleset_t*);
+ ruleset_t* (*GetAssRuleset)(rule_t *pThis);
+ENDinterface(rule)
+#define ruleCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+
+
+/* prototypes */
+PROTOTYPEObj(rule);
+
+#endif /* #ifndef INCLUDED_RULE_H */
diff --git a/runtime/ruleset.c b/runtime/ruleset.c
new file mode 100644
index 00000000..af61f24f
--- /dev/null
+++ b/runtime/ruleset.c
@@ -0,0 +1,449 @@
+/* ruleset.c - rsyslog's ruleset object
+ *
+ * We have a two-way structure of linked lists: one global linked list
+ * (llAllRulesets) hold alls rule sets that we know. Included in each
+ * list is a list of rules (which contain a list of actions, but that's
+ * a different story).
+ *
+ * Usually, only a single rule set is executed. However, there exist some
+ * situations where all rules must be iterated over, for example on HUP. Thus,
+ * we also provide interfaces to do that.
+ *
+ * Module begun 2009-06-10 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 <ctype.h>
+
+#include "rsyslog.h"
+#include "obj.h"
+#include "msg.h"
+#include "ruleset.h"
+#include "rule.h"
+#include "errmsg.h"
+#include "unicode-helper.h"
+
+static rsRetVal debugPrintAll(void); // TODO: remove!
+
+/* static data */
+DEFobjStaticHelpers
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(rule)
+
+linkedList_t llRulesets; /* this is NOT a pointer - no typo here ;) */
+ruleset_t *pCurrRuleset = NULL; /* currently "active" ruleset */
+ruleset_t *pDfltRuleset = NULL; /* currentl default ruleset, e.g. for binding to actions which have no other */
+
+/* ---------- linked-list key handling functions ---------- */
+
+/* destructor for linked list keys.
+ */
+static rsRetVal keyDestruct(void __attribute__((unused)) *pData)
+{
+ free(pData);
+ return RS_RET_OK;
+}
+
+
+/* ---------- END linked-list key handling functions ---------- */
+
+
+/* driver to iterate over all of this ruleset actions */
+typedef struct iterateAllActions_s {
+ rsRetVal (*pFunc)(void*, void*);
+ void *pParam;
+} iterateAllActions_t;
+DEFFUNC_llExecFunc(doIterateRulesetActions)
+{
+ DEFiRet;
+ rule_t* pRule = (rule_t*) pData;
+ iterateAllActions_t *pMyParam = (iterateAllActions_t*) pParam;
+ iRet = rule.IterateAllActions(pRule, pMyParam->pFunc, pMyParam->pParam);
+ RETiRet;
+}
+/* iterate over all actions of THIS rule set.
+ */
+static rsRetVal
+iterateRulesetAllActions(ruleset_t *pThis, rsRetVal (*pFunc)(void*, void*), void* pParam)
+{
+ iterateAllActions_t params;
+ DEFiRet;
+ assert(pFunc != NULL);
+
+ params.pFunc = pFunc;
+ params.pParam = pParam;
+ CHKiRet(llExecFunc(&(pThis->llRules), doIterateRulesetActions, &params));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* driver to iterate over all actions */
+DEFFUNC_llExecFunc(doIterateAllActions)
+{
+ DEFiRet;
+ ruleset_t* pThis = (ruleset_t*) pData;
+ iterateAllActions_t *pMyParam = (iterateAllActions_t*) pParam;
+ iRet = iterateRulesetAllActions(pThis, pMyParam->pFunc, pMyParam->pParam);
+ RETiRet;
+}
+/* iterate over ALL actions present in the WHOLE system.
+ * this is often needed, for example when HUP processing
+ * must be done or a shutdown is pending.
+ */
+static rsRetVal
+iterateAllActions(rsRetVal (*pFunc)(void*, void*), void* pParam)
+{
+ iterateAllActions_t params;
+ DEFiRet;
+ assert(pFunc != NULL);
+
+ params.pFunc = pFunc;
+ params.pParam = pParam;
+ CHKiRet(llExecFunc(&llRulesets, doIterateAllActions, &params));
+
+finalize_it:
+ RETiRet;
+}
+
+
+
+/* helper to processMsg(), used to call the configured actions. It is
+ * executed from within llExecFunc() of the action list.
+ * rgerhards, 2007-08-02
+ */
+DEFFUNC_llExecFunc(processMsgDoRules)
+{
+ ISOBJ_TYPE_assert(pData, rule);
+ return rule.ProcessMsg((rule_t*) pData, (msg_t*) pParam);
+}
+
+
+/* Process (consume) a received message. Calls the actions configured.
+ * rgerhards, 2005-10-13
+ */
+static rsRetVal
+processMsg(msg_t *pMsg)
+{
+ ruleset_t *pThis;
+ DEFiRet;
+ assert(pMsg != NULL);
+
+ pThis = (pMsg->pRuleset == NULL) ? pDfltRuleset : pMsg->pRuleset;
+ ISOBJ_TYPE_assert(pThis, ruleset);
+
+ CHKiRet(llExecFunc(&pThis->llRules, processMsgDoRules, pMsg));
+
+finalize_it:
+ if(iRet == RS_RET_DISCARDMSG)
+ iRet = RS_RET_OK;
+
+ RETiRet;
+}
+
+/* Add a new rule to the end of the current rule set. We do a number
+ * of checks and ignore the rule if it does not pass them.
+ */
+static rsRetVal
+addRule(ruleset_t *pThis, rule_t **ppRule)
+{
+ int iActionCnt;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, ruleset);
+ ISOBJ_TYPE_assert(*ppRule, rule);
+
+ CHKiRet(llGetNumElts(&(*ppRule)->llActList, &iActionCnt));
+ if(iActionCnt == 0) {
+ errmsg.LogError(0, NO_ERRCODE, "warning: selector line without actions will be discarded");
+ rule.Destruct(ppRule);
+ } else {
+ CHKiRet(llAppend(&pThis->llRules, NULL, *ppRule));
+ dbgprintf("selector line successfully processed\n");
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* set name for ruleset */
+static rsRetVal setName(ruleset_t *pThis, uchar *pszName)
+{
+ DEFiRet;
+ free(pThis->pszName);
+ CHKmalloc(pThis->pszName = ustrdup(pszName));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* get current ruleset
+ * We use a non-standard calling interface, as nothing can go wrong and it
+ * is really much more natural to return the pointer directly.
+ */
+static ruleset_t*
+GetCurrent(void)
+{
+ return pCurrRuleset;
+}
+
+
+/* Find the ruleset with the given name and return a pointer to its object.
+ */
+static rsRetVal
+GetRuleset(ruleset_t **ppRuleset, uchar *pszName)
+{
+ DEFiRet;
+ assert(ppRuleset != NULL);
+ assert(pszName != NULL);
+
+ CHKiRet(llFind(&llRulesets, pszName, (void*) ppRuleset));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Set a new default rule set. If the default can not be found, no change happens.
+ */
+static rsRetVal
+SetDefaultRuleset(uchar *pszName)
+{
+ ruleset_t *pRuleset;
+ DEFiRet;
+ assert(pszName != NULL);
+
+ CHKiRet(GetRuleset(&pRuleset, pszName));
+ pDfltRuleset = pRuleset;
+ dbgprintf("default rule set changed to %p: '%s'\n", pRuleset, pszName);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Set a new current rule set. If the ruleset can not be found, no change happens.
+ */
+static rsRetVal
+SetCurrRuleset(uchar *pszName)
+{
+ ruleset_t *pRuleset;
+ DEFiRet;
+ assert(pszName != NULL);
+
+ CHKiRet(GetRuleset(&pRuleset, pszName));
+ pCurrRuleset = pRuleset;
+ dbgprintf("current rule set changed to %p: '%s'\n", pRuleset, pszName);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* destructor we need to destruct rules inside our linked list contents.
+ */
+static rsRetVal
+doRuleDestruct(void *pData)
+{
+ rule_t *pRule = (rule_t *) pData;
+ DEFiRet;
+ rule.Destruct(&pRule);
+ RETiRet;
+}
+
+
+/* Standard-Constructor
+ */
+BEGINobjConstruct(ruleset) /* be sure to specify the object type also in END macro! */
+ CHKiRet(llInit(&pThis->llRules, doRuleDestruct, NULL, NULL));
+finalize_it:
+ENDobjConstruct(ruleset)
+
+
+/* ConstructionFinalizer
+ * This also adds the rule set to the list of all known rulesets.
+ */
+static rsRetVal
+rulesetConstructFinalize(ruleset_t *pThis)
+{
+ uchar *keyName;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, ruleset);
+
+ /* we must duplicate our name, as the key destructer would also
+ * free it, resulting in a double-free. It's also cleaner to have
+ * two separate copies.
+ */
+ CHKmalloc(keyName = ustrdup(pThis->pszName));
+ CHKiRet(llAppend(&llRulesets, keyName, pThis));
+
+ /* this now also is the new current ruleset */
+ pCurrRuleset = pThis;
+
+ /* and also the default, if so far none has been set */
+ if(pDfltRuleset == NULL)
+ pDfltRuleset = pThis;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* destructor for the ruleset object */
+BEGINobjDestruct(ruleset) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDestruct(ruleset)
+ dbgprintf("destructing ruleset %p, name %p\n", pThis, pThis->pszName);
+ llDestroy(&pThis->llRules);
+ free(pThis->pszName);
+ENDobjDestruct(ruleset)
+
+/* this is a special destructor for the linkedList class. LinkedList does NOT
+ * provide a pointer to the pointer, but rather the raw pointer itself. So we
+ * must map this, otherwise the destructor will abort.
+ */
+static rsRetVal
+rulesetDestructForLinkedList(void *pData)
+{
+ ruleset_t *pThis = (ruleset_t*) pData;
+ return rulesetDestruct(&pThis);
+}
+
+
+/* destruct ALL rule sets that reside in the system. This must
+ * be callable before unloading this module as the module may
+ * not be unloaded before unload of the actions is required. This is
+ * kind of a left-over from previous logic and may be optimized one
+ * everything runs stable again. -- rgerhards, 2009-06-10
+ */
+static rsRetVal
+destructAllActions(void)
+{
+ DEFiRet;
+
+ CHKiRet(llDestroy(&llRulesets));
+ CHKiRet(llInit(&llRulesets, rulesetDestructForLinkedList, keyDestruct, strcasecmp));
+ pDfltRuleset = NULL;
+
+finalize_it:
+ RETiRet;
+}
+
+/* helper for debugPrint(), initiates rule printing */
+DEFFUNC_llExecFunc(doDebugPrintRule)
+{
+ return rule.DebugPrint((rule_t*) pData);
+}
+/* debugprint for the ruleset object */
+BEGINobjDebugPrint(ruleset) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDebugPrint(ruleset)
+ dbgoprint((obj_t*) pThis, "rsyslog ruleset %s:\n", pThis->pszName);
+ llExecFunc(&pThis->llRules, doDebugPrintRule, NULL);
+ENDobjDebugPrint(ruleset)
+
+
+/* helper for debugPrintAll(), prints a single ruleset */
+DEFFUNC_llExecFunc(doDebugPrintAll)
+{
+ return rulesetDebugPrint((ruleset_t*) pData);
+}
+/* debug print all rulesets
+ */
+static rsRetVal
+debugPrintAll(void)
+{
+ DEFiRet;
+ dbgprintf("All Rulesets:\n");
+ llExecFunc(&llRulesets, doDebugPrintAll, NULL);
+ dbgprintf("End of Rulesets.\n");
+ RETiRet;
+}
+
+
+/* queryInterface function
+ * rgerhards, 2008-02-21
+ */
+BEGINobjQueryInterface(ruleset)
+CODESTARTobjQueryInterface(ruleset)
+ if(pIf->ifVersion != rulesetCURR_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 = rulesetConstruct;
+ pIf->ConstructFinalize = rulesetConstructFinalize;
+ pIf->Destruct = rulesetDestruct;
+ pIf->DebugPrint = rulesetDebugPrint;
+
+ pIf->IterateAllActions = iterateAllActions;
+ pIf->DestructAllActions = destructAllActions;
+ pIf->AddRule = addRule;
+ pIf->ProcessMsg = processMsg;
+ pIf->SetName = setName;
+ pIf->DebugPrintAll = debugPrintAll;
+ pIf->GetCurrent = GetCurrent;
+ pIf->GetRuleset = GetRuleset;
+ pIf->SetDefaultRuleset = SetDefaultRuleset;
+ pIf->SetCurrRuleset = SetCurrRuleset;
+finalize_it:
+ENDobjQueryInterface(ruleset)
+
+
+/* Exit the ruleset class.
+ * rgerhards, 2009-04-06
+ */
+BEGINObjClassExit(ruleset, OBJ_IS_CORE_MODULE) /* class, version */
+ llDestroy(&llRulesets);
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(rule, CORE_COMPONENT);
+ENDObjClassExit(ruleset)
+
+
+/* Initialize the ruleset class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-02-19
+ */
+BEGINObjClassInit(ruleset, 1, OBJ_IS_CORE_MODULE) /* class, version */
+ /* request objects we use */
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(rule, CORE_COMPONENT));
+
+ /* set our own handlers */
+ OBJSetMethodHandler(objMethod_DEBUGPRINT, rulesetDebugPrint);
+ OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, rulesetConstructFinalize);
+
+ /* prepare global data */
+ CHKiRet(llInit(&llRulesets, rulesetDestructForLinkedList, keyDestruct, strcasecmp));
+ENDObjClassInit(ruleset)
+
+/* vi:set ai:
+ */
diff --git a/runtime/ruleset.h b/runtime/ruleset.h
new file mode 100644
index 00000000..32571687
--- /dev/null
+++ b/runtime/ruleset.h
@@ -0,0 +1,60 @@
+/* The ruleset object.
+ *
+ * This implements rulesets within rsyslog.
+ *
+ * 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_RULESET_H
+#define INCLUDED_RULESET_H
+
+#include "linkedlist.h"
+
+/* the ruleset object */
+struct ruleset_s {
+ BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
+ linkedList_t llRules; /* this is NOT a pointer - no typo here ;) */
+ uchar *pszName; /* name of our ruleset */
+};
+
+/* interfaces */
+BEGINinterface(ruleset) /* name must also be changed in ENDinterface macro! */
+ INTERFACEObjDebugPrint(ruleset);
+ rsRetVal (*DebugPrintAll)(void);
+ rsRetVal (*Construct)(ruleset_t **ppThis);
+ rsRetVal (*ConstructFinalize)(ruleset_t __attribute__((unused)) *pThis);
+ rsRetVal (*Destruct)(ruleset_t **ppThis);
+ rsRetVal (*IterateAllActions)(rsRetVal (*pFunc)(void*, void*), void* pParam);
+ rsRetVal (*DestructAllActions)(void);
+ rsRetVal (*AddRule)(ruleset_t *pThis, rule_t **ppRule);
+ rsRetVal (*SetName)(ruleset_t *pThis, uchar *pszName);
+ rsRetVal (*ProcessMsg)(msg_t *pMsg);
+ rsRetVal (*GetRuleset)(ruleset_t **ppThis, uchar*);
+ rsRetVal (*SetDefaultRuleset)(uchar*);
+ rsRetVal (*SetCurrRuleset)(uchar*);
+ ruleset_t* (*GetCurrent)(void);
+ENDinterface(ruleset)
+#define rulesetCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+
+
+/* prototypes */
+PROTOTYPEObj(ruleset);
+
+#endif /* #ifndef INCLUDED_RULESET_H */
diff --git a/runtime/srUtils.h b/runtime/srUtils.h
index bfce4cbb..16766312 100644
--- a/runtime/srUtils.h
+++ b/runtime/srUtils.h
@@ -92,6 +92,7 @@ void srSleep(int iSeconds, int iuSeconds);
char *rs_strerror_r(int errnum, char *buf, size_t buflen);
int decodeSyslogName(uchar *name, syslogName_t *codetab);
int getSubString(uchar **ppSrc, char *pDst, size_t DstSize, char cSep);
+rsRetVal getFileSize(uchar *pszName, off_t *pSize);
/* mutex operations */
/* some macros to cancel-safe lock a mutex (it will automatically be released
@@ -108,8 +109,6 @@ int getSubString(uchar **ppSrc, char *pDst, size_t DstSize, char cSep);
#define mutex_cancelsafe_unlock(mut) pthread_cleanup_pop(1)
/* some useful constants */
-#define MUTEX_ALREADY_LOCKED 0
-#define LOCK_MUTEX 1
#define DEFVARS_mutexProtection\
int iCancelStateSave; \
int bLockedOpIsLocked=0
@@ -124,4 +123,17 @@ int getSubString(uchar **ppSrc, char *pDst, size_t DstSize, char cSep);
d_pthread_mutex_unlock(mut); \
pthread_setcancelstate(iCancelStateSave, NULL); \
}
+
+/* The unconditional versions of the macro always lock the mutex. They are preferred in
+ * complex scenarios, where the simple ones might get mixed up by multiple calls.
+ */
+#define DEFVARS_mutexProtection_uncond\
+ int iCancelStateSave
+#define BEGIN_MTX_PROTECTED_OPERATIONS_UNCOND(mut) \
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave); \
+ d_pthread_mutex_lock(mut);
+#define END_MTX_PROTECTED_OPERATIONS_UNCOND(mut) \
+ d_pthread_mutex_unlock(mut); \
+ pthread_setcancelstate(iCancelStateSave, NULL);
+
#endif
diff --git a/runtime/srutils.c b/runtime/srutils.c
index 97cc3252..1452c9b7 100644
--- a/runtime/srutils.c
+++ b/runtime/srutils.c
@@ -166,10 +166,22 @@ uchar *srUtilStrDup(uchar *pOld, size_t len)
/* creates a path recursively
- * Return 0 on success, -1 otherwise. On failure, errno
- * hold the last OS error.
- * Param "mode" holds the mode that all non-existing directories
- * are to be created with.
+ * Return 0 on success, -1 otherwise. On failure, errno * hold the last OS error.
+ * Param "mode" holds the mode that all non-existing directories are to be
+ * created with.
+ * Note that we have a potential race inside that code, a race that even exists
+ * outside of the rsyslog process (if multiple instances run, or other programs
+ * generate directories): If the directory does not exist, a context switch happens,
+ * at that moment another process creates it, then our creation on the context
+ * switch back fails. This actually happened in practice, and depending on the
+ * configuration it is even likely to happen. We can not solve this situation
+ * with a mutex, as that works only within out process space. So the solution
+ * is that we take the optimistic approach, try the creation, and if it fails
+ * with "already exists" we go back and do one retry of the check/create
+ * sequence. That should then succeed. If the directory is still not found but
+ * the creation fails in the similar way, we return an error on that second
+ * try because otherwise we would potentially run into an endless loop.
+ * loop. -- rgerhards, 2010-03-25
*/
int makeFileParentDirs(uchar *szFile, size_t lenFile, mode_t mode,
uid_t uid, gid_t gid, int bFailOnChownFail)
@@ -177,6 +189,8 @@ int makeFileParentDirs(uchar *szFile, size_t lenFile, mode_t mode,
uchar *p;
uchar *pszWork;
size_t len;
+ int err;
+ int iTry = 0;
int bErr = 0;
assert(szFile != NULL);
@@ -190,8 +204,9 @@ int makeFileParentDirs(uchar *szFile, size_t lenFile, mode_t mode,
if(*p == '/') {
/* temporarily terminate string, create dir and go on */
*p = '\0';
+again:
if(access((char*)pszWork, F_OK)) {
- if(mkdir((char*)pszWork, mode) == 0) {
+ if((err = mkdir((char*)pszWork, mode)) == 0) {
if(uid != (uid_t) -1 || gid != (gid_t) -1) {
/* we need to set owner/group */
if(chown((char*)pszWork, uid, gid) != 0)
@@ -201,8 +216,13 @@ int makeFileParentDirs(uchar *szFile, size_t lenFile, mode_t mode,
* to do so.
*/
}
- } else
+ } else {
+ if(err == EEXIST && iTry == 0) {
+ iTry = 1;
+ goto again;
+ }
bErr = 1;
+ }
if(bErr) {
int eSave = errno;
free(pszWork);
@@ -366,19 +386,23 @@ int getNumberDigits(long lNum)
/* compute an absolute time timeout suitable for calls to pthread_cond_timedwait()
+ * iTimeout is in milliseconds
* rgerhards, 2008-01-14
*/
rsRetVal
timeoutComp(struct timespec *pt, long iTimeout)
{
+ BEGINfunc
assert(pt != NULL);
/* compute timeout */
clock_gettime(CLOCK_REALTIME, pt);
+ pt->tv_sec += iTimeout / 1000;
pt->tv_nsec += (iTimeout % 1000) * 1000000; /* think INTEGER arithmetic! */
if(pt->tv_nsec > 999999999) { /* overrun? */
pt->tv_nsec -= 1000000000;
+ ++pt->tv_sec;
}
- pt->tv_sec += iTimeout / 1000;
+ ENDfunc
return RS_RET_OK; /* so far, this is static... */
}
@@ -393,6 +417,7 @@ timeoutVal(struct timespec *pt)
{
struct timespec t;
long iTimeout;
+ BEGINfunc
assert(pt != NULL);
/* compute timeout */
@@ -403,6 +428,7 @@ timeoutVal(struct timespec *pt)
if(iTimeout < 0)
iTimeout = 0;
+ ENDfunc
return iTimeout;
}
@@ -454,7 +480,7 @@ srSleep(int iSeconds, int iuSeconds)
* Added 2008-01-30
*/
char *rs_strerror_r(int errnum, char *buf, size_t buflen) {
-#ifdef __hpux
+#ifndef HAVE_STRERROR_R
char *pszErr;
pszErr = strerror(errnum);
snprintf(buf, buflen, "%s", pszErr);
@@ -549,6 +575,33 @@ int getSubString(uchar **ppSrc, char *pDst, size_t DstSize, char cSep)
}
+/* get the size of a file or return appropriate error code. If an error is returned,
+ * *pSize content is undefined.
+ * rgerhards, 2009-06-12
+ */
+rsRetVal
+getFileSize(uchar *pszName, off_t *pSize)
+{
+ int ret;
+ struct stat statBuf;
+ DEFiRet;
+
+ ret = stat((char*) pszName, &statBuf);
+ if(ret == -1) {
+ switch(errno) {
+ case EACCES: ABORT_FINALIZE(RS_RET_NO_FILE_ACCESS);
+ case ENOTDIR:
+ case ENOENT: ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND);
+ default: ABORT_FINALIZE(RS_RET_FILE_NO_STAT);
+ }
+ }
+
+ *pSize = statBuf.st_size;
+
+finalize_it:
+ RETiRet;
+}
+
/* vim:set ai:
*/
diff --git a/runtime/stream.c b/runtime/stream.c
index f1f69cc8..a12dda41 100644
--- a/runtime/stream.c
+++ b/runtime/stream.c
@@ -1,4 +1,3 @@
-//TODO: O_TRUC mode!
/* The serial stream class.
*
* A serial stream provides serial data access. In theory, serial streams
@@ -7,8 +6,17 @@
* "driver").
*
* File begun on 2008-01-09 by RGerhards
+ * Large modifications in 2009-06 to support using it with omfile, including zip writer.
+ * Note that this file obtains the zlib wrapper object is needed, but it never frees it
+ * again. While this sounds like a leak (and one may argue it actually is), there is no
+ * harm associated with that. The reason is that strm is a core object, so it is terminated
+ * only when rsyslogd exists. As we could only release on termination (or else bear more
+ * overhead for keeping track of how many users we have), not releasing zlibw is OK, because
+ * it will be released when rsyslogd terminates. We may want to revisit this decision if
+ * it turns out to be problematic. Then, we need to quasi-refcount the number of accesses
+ * to the object.
*
- * Copyright 2008 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2008, 2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of the rsyslog runtime library.
*
@@ -39,24 +47,205 @@
#include <unistd.h>
#include <sys/stat.h> /* required for HP UX */
#include <errno.h>
+#include <pthread.h>
#include "rsyslog.h"
#include "stringbuf.h"
#include "srUtils.h"
#include "obj.h"
#include "stream.h"
+#include "unicode-helper.h"
+#include "module-template.h"
+#if HAVE_SYS_PRCTL_H
+# include <sys/prctl.h>
+#endif
+
+/* some platforms do not have large file support :( */
+#ifndef O_LARGEFILE
+# define O_LARGEFILE 0
+#endif
+#ifndef HAVE_LSEEK64
+ typedef off_t off64_t;
+# define lseek64(fd, offset, whence) lseek(fd, offset, whence)
+#endif
/* static data */
DEFobjStaticHelpers
+DEFobjCurrIf(zlibw)
+
+/* forward definitions */
+static rsRetVal strmFlushInternal(strm_t *pThis);
+static rsRetVal strmWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf);
+static rsRetVal strmCloseFile(strm_t *pThis);
+static void *asyncWriterThread(void *pPtr);
+static rsRetVal doZipWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf);
+static rsRetVal strmPhysWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf);
+
/* methods */
-/* first, we define type-specific handlers. The provide a generic functionality,
+/* Try to resolve a size limit situation. This is used to support custom-file size handlers
+ * for omfile. It first runs the command, and then checks if we are still above the size
+ * treshold. Note that this works only with single file names, NOT with circular names.
+ * Note that pszCurrFName can NOT be taken from pThis, because the stream is closed when
+ * we are called (and that destroys pszCurrFName, as there is NO CURRENT file name!). So
+ * we need to receive the name as a parameter.
+ * initially wirtten 2005-06-21, moved to this class & updates 2009-06-01, both rgerhards
+ */
+static rsRetVal
+resolveFileSizeLimit(strm_t *pThis, uchar *pszCurrFName)
+{
+ uchar *pParams;
+ uchar *pCmd;
+ uchar *p;
+ off_t actualFileSize;
+ rsRetVal localRet;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strm);
+ assert(pszCurrFName != NULL);
+
+ if(pThis->pszSizeLimitCmd == NULL) {
+ ABORT_FINALIZE(RS_RET_NON_SIZELIMITCMD); /* nothing we can do in this case... */
+ }
+
+ /* we first check if we have command line parameters. We assume this,
+ * when we have a space in the program name. If we find it, everything after
+ * the space is treated as a single argument.
+ */
+ CHKmalloc(pCmd = ustrdup(pThis->pszSizeLimitCmd));
+
+ for(p = pCmd ; *p && *p != ' ' ; ++p) {
+ /* JUST SKIP */
+ }
+
+ if(*p == ' ') {
+ *p = '\0'; /* pretend string-end */
+ pParams = p+1;
+ } else
+ pParams = NULL;
+
+ /* the execProg() below is probably not great, but at least is is
+ * fairly secure now. Once we change the way file size limits are
+ * handled, we should also revisit how this command is run (and
+ * with which parameters). rgerhards, 2007-07-20
+ */
+ execProg(pCmd, 1, pParams);
+
+ free(pCmd);
+
+ localRet = getFileSize(pszCurrFName, &actualFileSize);
+
+ if(localRet == RS_RET_OK && actualFileSize >= pThis->iSizeLimit) {
+ ABORT_FINALIZE(RS_RET_SIZELIMITCMD_DIDNT_RESOLVE); /* OK, it didn't work out... */
+ } else if(localRet != RS_RET_FILE_NOT_FOUND) {
+ /* file not found is OK, the command may have moved away the file */
+ ABORT_FINALIZE(localRet);
+ }
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(iRet == RS_RET_SIZELIMITCMD_DIDNT_RESOLVE) {
+ DBGPRINTF("file size limit cmd for file '%s' did no resolve situation\n", pszCurrFName);
+ } else {
+ DBGPRINTF("file size limit cmd for file '%s' failed with code %d.\n", pszCurrFName, iRet);
+ }
+ pThis->bDisabled = 1;
+ }
+
+ RETiRet;
+}
+
+
+/* Check if the file has grown beyond the configured omfile iSizeLimit
+ * and, if so, initiate processing.
+ */
+static rsRetVal
+doSizeLimitProcessing(strm_t *pThis)
+{
+ uchar *pszCurrFName = NULL;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, strm);
+ ASSERT(pThis->iSizeLimit != 0);
+ ASSERT(pThis->fd != -1);
+
+ if(pThis->iCurrOffs >= pThis->iSizeLimit) {
+ /* strmCloseFile() destroys the current file name, so we
+ * need to preserve it.
+ */
+ CHKmalloc(pszCurrFName = ustrdup(pThis->pszCurrFName));
+ CHKiRet(strmCloseFile(pThis));
+ CHKiRet(resolveFileSizeLimit(pThis, pszCurrFName));
+ }
+
+finalize_it:
+ free(pszCurrFName);
+ RETiRet;
+}
+
+
+/* now, we define type-specific handlers. The provide a generic functionality,
* but for this specific type of strm. The mapping to these handlers happens during
* strm construction. Later on, handlers are called by pointers present in the
* strm instance object.
*/
+/* do the physical open() call on a file.
+ */
+static rsRetVal
+doPhysOpen(strm_t *pThis)
+{
+ int iFlags = 0;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strm);
+
+ /* compute which flags we need to provide to open */
+ switch(pThis->tOperationsMode) {
+ case STREAMMODE_READ:
+ iFlags = O_CLOEXEC | O_NOCTTY | O_RDONLY;
+ break;
+ case STREAMMODE_WRITE: /* legacy mode used inside queue engine */
+ iFlags = O_CLOEXEC | O_NOCTTY | O_WRONLY | O_CREAT;
+ break;
+ case STREAMMODE_WRITE_TRUNC:
+ iFlags = O_CLOEXEC | O_NOCTTY | O_WRONLY | O_CREAT | O_TRUNC;
+ break;
+ case STREAMMODE_WRITE_APPEND:
+ iFlags = O_CLOEXEC | O_NOCTTY | O_WRONLY | O_CREAT | O_APPEND;
+ break;
+ default:assert(0);
+ break;
+ }
+ if(pThis->sType == STREAMTYPE_NAMED_PIPE) {
+ DBGPRINTF("Note: stream '%s' is a named pipe, open with O_NONBLOCK\n", pThis->pszCurrFName);
+ iFlags |= O_NONBLOCK;
+ }
+
+ pThis->fd = open((char*)pThis->pszCurrFName, iFlags | O_LARGEFILE, pThis->tOpenMode);
+ DBGPRINTF("file '%s' opened as #%d with mode %d\n", pThis->pszCurrFName, pThis->fd, pThis->tOpenMode);
+ if(pThis->fd == -1) {
+ char errStr[1024];
+ int err = errno;
+ rs_strerror_r(err, errStr, sizeof(errStr));
+ DBGOPRINT((obj_t*) pThis, "open error %d, file '%s': %s\n", errno, pThis->pszCurrFName, errStr);
+ if(err == ENOENT)
+ ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND);
+ else
+ ABORT_FINALIZE(RS_RET_IO_ERROR);
+ } else {
+ if(!ustrcmp(pThis->pszCurrFName, UCHAR_CONSTANT(_PATH_CONSOLE)) || isatty(pThis->fd)) {
+ DBGPRINTF("file %d is a tty-type file\n", pThis->fd);
+ pThis->bIsTTY = 1;
+ } else {
+ pThis->bIsTTY = 0;
+ }
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
/* open a strm file
* It is OK to call this function when the stream is already open. In that
* case, it returns immediately with RS_RET_OK
@@ -64,13 +253,12 @@ DEFobjStaticHelpers
static rsRetVal strmOpenFile(strm_t *pThis)
{
DEFiRet;
- int iFlags;
ASSERT(pThis != NULL);
- ASSERT(pThis->tOperationsMode == STREAMMODE_READ || pThis->tOperationsMode == STREAMMODE_WRITE);
if(pThis->fd != -1)
ABORT_FINALIZE(RS_RET_OK);
+ pThis->pszCurrFName = NULL; /* used to prevent mem leak in case of error */
if(pThis->pszFName == NULL)
ABORT_FINALIZE(RS_RET_FILE_PREFIX_MISSING);
@@ -88,54 +276,98 @@ static rsRetVal strmOpenFile(strm_t *pThis)
}
}
- /* compute which flags we need to provide to open */
- if(pThis->tOperationsMode == STREAMMODE_READ)
- iFlags = O_RDONLY;
- else
- iFlags = O_WRONLY | O_CREAT;
-
- iFlags |= pThis->iAddtlOpenFlags;
-
- pThis->fd = open((char*)pThis->pszCurrFName, iFlags, pThis->tOpenMode);
- if(pThis->fd == -1) {
- int ierrnoSave = errno;
- dbgoprint((obj_t*) pThis, "open error %d, file '%s'\n", errno, pThis->pszCurrFName);
- if(ierrnoSave == ENOENT)
- ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND);
- else
- ABORT_FINALIZE(RS_RET_IO_ERROR);
- }
+ CHKiRet(doPhysOpen(pThis));
pThis->iCurrOffs = 0;
+ if(pThis->tOperationsMode == STREAMMODE_WRITE_APPEND) {
+ /* we need to obtain the current offset */
+ off_t offset;
+ CHKiRet(getFileSize(pThis->pszCurrFName, &offset));
+ pThis->iCurrOffs = offset;
+ }
- dbgoprint((obj_t*) pThis, "opened file '%s' for %s (0x%x) as %d\n", pThis->pszCurrFName,
- (pThis->tOperationsMode == STREAMMODE_READ) ? "READ" : "WRITE", iFlags, pThis->fd);
+ DBGOPRINT((obj_t*) pThis, "opened file '%s' for %s as %d\n", pThis->pszCurrFName,
+ (pThis->tOperationsMode == STREAMMODE_READ) ? "READ" : "WRITE", pThis->fd);
finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pThis->pszCurrFName != NULL) {
+ free(pThis->pszCurrFName);
+ pThis->pszCurrFName = NULL; /* just to prevent mis-adressing down the road... */
+ }
+ if(pThis->fd != -1) {
+ close(pThis->fd);
+ pThis->fd = -1;
+ }
+ }
RETiRet;
}
+/* wait for the output writer thread to be done. This must be called before actions
+ * that require data to be persisted. May be called in non-async mode and is a null
+ * operation than. Must be called with the mutex locked.
+ */
+static inline void
+strmWaitAsyncWriterDone(strm_t *pThis)
+{
+ BEGINfunc
+ if(pThis->bAsyncWrite) {
+ /* awake writer thread and make it write out everything */
+ while(pThis->iCnt > 0) {
+ pthread_cond_signal(&pThis->notEmpty);
+ d_pthread_cond_wait(&pThis->isEmpty, &pThis->mut);
+ }
+ }
+ ENDfunc
+}
+
+
/* close a strm file
* Note that the bDeleteOnClose flag is honored. If it is set, the file will be
* deleted after close. This is in support for the qRead thread.
+ * Note: it is valid to call this function when the physical file is closed. If so,
+ * strmCloseFile() will still check if there is any unwritten data inside buffers
+ * (this may be the case) and, if so, will open the file, write the data, and then
+ * close it again (this is done via strmFlushInternal and friends).
*/
static rsRetVal strmCloseFile(strm_t *pThis)
{
DEFiRet;
ASSERT(pThis != NULL);
- ASSERT(pThis->fd != -1);
- dbgoprint((obj_t*) pThis, "file %d closing\n", pThis->fd);
+ DBGOPRINT((obj_t*) pThis, "file %d(%s) closing\n", pThis->fd,
+ (pThis->pszFName == NULL) ? "N/A" : (char*)pThis->pszFName);
- if(pThis->tOperationsMode == STREAMMODE_WRITE)
- strmFlush(pThis);
+ if(pThis->tOperationsMode != STREAMMODE_READ) {
+ strmFlushInternal(pThis);
+ if(pThis->bAsyncWrite) {
+ strmWaitAsyncWriterDone(pThis);
+ }
+ }
- close(pThis->fd); // TODO: error check
- pThis->fd = -1;
+ /* the file may already be closed (or never have opened), so guard
+ * against this. -- rgerhards, 2010-03-19
+ */
+ if(pThis->fd != -1) {
+ close(pThis->fd);
+ pThis->fd = -1;
+ }
+
+ if(pThis->fdDir != -1) {
+ /* close associated directory handle, if it is open */
+ close(pThis->fdDir);
+ pThis->fdDir = -1;
+ }
if(pThis->bDeleteOnClose) {
- unlink((char*) pThis->pszCurrFName); // TODO: check returncode
+ if(unlink((char*) pThis->pszCurrFName) == -1) {
+ char errStr[1024];
+ int err = errno;
+ rs_strerror_r(err, errStr, sizeof(errStr));
+ DBGPRINTF("error %d unlinking '%s' - ignored: %s\n",
+ errno, pThis->pszCurrFName, errStr);
+ }
}
pThis->iCurrOffs = 0; /* we are back at begin of file */
@@ -179,6 +411,12 @@ finalize_it:
* If we are monitoring a file, someone may have rotated it. In this case, we
* also need to close it and reopen it under the same name.
* rgerhards, 2008-02-13
+ * The previous code also did a check for file truncation, in which case the
+ * file was considered rewritten. However, this potential border case turned
+ * out to be a big trouble spot on busy systems. It caused massive message
+ * duplication (I guess stat() can return a too-low number under some
+ * circumstances). So starting as of now, we only check the inode number and
+ * a file change is detected only if the inode changes. -- rgerhards, 2011-01-10
*/
static rsRetVal
strmHandleEOFMonitor(strm_t *pThis)
@@ -188,23 +426,18 @@ strmHandleEOFMonitor(strm_t *pThis)
struct stat statName;
ISOBJ_TYPE_assert(pThis, strm);
- /* find inodes of both current descriptor as well as file now in file
- * system. If they are different, the file has been rotated (or
- * otherwise rewritten). We also check the size, because the inode
- * does not change if the file is truncated (this, BTW, is also a case
- * where we actually loose log lines, because we can not do anything
- * against truncation...). We do NOT rely on the time of last
- * modificaton because that may not be available under all
- * circumstances. -- rgerhards, 2008-02-13
- */
if(fstat(pThis->fd, &statOpen) == -1)
ABORT_FINALIZE(RS_RET_IO_ERROR);
if(stat((char*) pThis->pszCurrFName, &statName) == -1)
ABORT_FINALIZE(RS_RET_IO_ERROR);
- if(statOpen.st_ino == statName.st_ino && pThis->iCurrOffs == statName.st_size) {
+ DBGPRINTF("stream checking for file change on '%s', inode %u/%u",
+ pThis->pszCurrFName, (unsigned) statOpen.st_ino,
+ (unsigned) statName.st_ino);
+ if(statOpen.st_ino == statName.st_ino) {
ABORT_FINALIZE(RS_RET_EOF);
} else {
/* we had a file change! */
+ DBGPRINTF("we had a file change on '%s'\n", pThis->pszCurrFName);
CHKiRet(strmCloseFile(pThis));
CHKiRet(strmOpenFile(pThis));
}
@@ -229,16 +462,13 @@ strmHandleEOF(strm_t *pThis)
ISOBJ_TYPE_assert(pThis, strm);
switch(pThis->sType) {
case STREAMTYPE_FILE_SINGLE:
+ case STREAMTYPE_NAMED_PIPE:
ABORT_FINALIZE(RS_RET_EOF);
break;
case STREAMTYPE_FILE_CIRCULAR:
/* we have multiple files and need to switch to the next one */
/* TODO: think about emulating EOF in this case (not yet needed) */
-#if 0
- if(pThis->iMaxFiles == 0) /* TODO: why do we need this? ;) */
- ABORT_FINALIZE(RS_RET_EOF);
-#endif
- dbgoprint((obj_t*) pThis, "file %d EOF\n", pThis->fd);
+ DBGOPRINT((obj_t*) pThis, "file %d EOF\n", pThis->fd);
CHKiRet(strmNextFile(pThis));
break;
case STREAMTYPE_FILE_MONITOR:
@@ -270,7 +500,7 @@ strmReadBuf(strm_t *pThis)
*/
CHKiRet(strmOpenFile(pThis));
iLenRead = read(pThis->fd, pThis->pIOBuf, pThis->sIOBufSize);
- dbgoprint((obj_t*) pThis, "file %d read %ld bytes\n", pThis->fd, iLenRead);
+ DBGOPRINT((obj_t*) pThis, "file %d read %ld bytes\n", pThis->fd, iLenRead);
if(iLenRead == 0) {
CHKiRet(strmHandleEOF(pThis));
} else if(iLenRead < 0)
@@ -295,14 +525,14 @@ finalize_it:
* NOTE: needs to be enhanced to support sticking with a strm entry (if not
* deleted).
*/
-rsRetVal strmReadChar(strm_t *pThis, uchar *pC)
+static rsRetVal strmReadChar(strm_t *pThis, uchar *pC)
{
DEFiRet;
ASSERT(pThis != NULL);
ASSERT(pC != NULL);
- /* DEV debug only: dbgoprint((obj_t*) pThis, "strmRead index %d, max %d\n", pThis->iBufPtr, pThis->iBufPtrMax); */
+ /* DEV debug only: DBGOPRINT((obj_t*) pThis, "strmRead index %d, max %d\n", pThis->iBufPtr, pThis->iBufPtrMax); */
if(pThis->iUngetC != -1) { /* do we have an "unread" char that we need to provide? */
*pC = pThis->iUngetC;
++pThis->iCurrOffs; /* one more octet read */
@@ -329,7 +559,7 @@ finalize_it:
* character buffering capability.
* rgerhards, 2008-01-07
*/
-rsRetVal strmUnreadChar(strm_t *pThis, uchar c)
+static rsRetVal strmUnreadChar(strm_t *pThis, uchar c)
{
ASSERT(pThis != NULL);
ASSERT(pThis->iUngetC == -1);
@@ -351,7 +581,7 @@ rsRetVal strmUnreadChar(strm_t *pThis, uchar c)
* are pthread_killed() upon termination. So if we use their native pointer, they
* can cleanup (but only then).
*/
-rsRetVal
+static rsRetVal
strmReadLine(strm_t *pThis, cstr_t **ppCStr)
{
DEFiRet;
@@ -360,19 +590,19 @@ strmReadLine(strm_t *pThis, cstr_t **ppCStr)
ASSERT(pThis != NULL);
ASSERT(ppCStr != NULL);
- CHKiRet(rsCStrConstruct(ppCStr));
+ CHKiRet(cstrConstruct(ppCStr));
/* now read the line */
CHKiRet(strmReadChar(pThis, &c));
while(c != '\n') {
- CHKiRet(rsCStrAppendChar(*ppCStr, c));
+ CHKiRet(cstrAppendChar(*ppCStr, c));
CHKiRet(strmReadChar(pThis, &c));
}
- CHKiRet(rsCStrFinish(*ppCStr));
+ CHKiRet(cstrFinalize(*ppCStr));
finalize_it:
if(iRet != RS_RET_OK && *ppCStr != NULL)
- rsCStrDestruct(ppCStr);
+ cstrDestruct(ppCStr);
RETiRet;
}
@@ -383,26 +613,78 @@ finalize_it:
BEGINobjConstruct(strm) /* be sure to specify the object type also in END macro! */
pThis->iCurrFNum = 1;
pThis->fd = -1;
+ pThis->fdDir = -1;
pThis->iUngetC = -1;
pThis->sType = STREAMTYPE_FILE_SINGLE;
pThis->sIOBufSize = glblGetIOBufSize();
- pThis->tOpenMode = 0600; /* TODO: make configurable */
+ pThis->tOpenMode = 0600;
ENDobjConstruct(strm)
/* ConstructionFinalizer
* rgerhards, 2008-01-09
*/
-rsRetVal strmConstructFinalize(strm_t *pThis)
+static rsRetVal strmConstructFinalize(strm_t *pThis)
{
+ rsRetVal localRet;
+ int i;
DEFiRet;
ASSERT(pThis != NULL);
- if(pThis->pIOBuf == NULL) { /* allocate our io buffer in case we have not yet */
- if((pThis->pIOBuf = (uchar*) malloc(sizeof(uchar) * pThis->sIOBufSize)) == NULL)
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- pThis->iBufPtrMax = 0; /* results in immediate read request */
+ pThis->iBufPtrMax = 0; /* results in immediate read request */
+ if(pThis->iZipLevel) { /* do we need a zip buf? */
+ localRet = objUse(zlibw, LM_ZLIBW_FILENAME);
+ if(localRet != RS_RET_OK) {
+ pThis->iZipLevel = 0;
+ DBGPRINTF("stream was requested with zip mode, but zlibw module unavailable (%d) - using "
+ "without zip\n", localRet);
+ } else {
+ /* we use the same size as the original buf, as we would like
+ * to make sure we can write out everything with a SINGLE api call!
+ * We add another 128 bytes to take care of the gzip header and "all eventualities".
+ */
+ CHKmalloc(pThis->pZipBuf = (Bytef*) malloc(sizeof(uchar) * (pThis->sIOBufSize + 128)));
+ }
+ }
+
+ /* if we are set to sync, we must obtain a file handle to the directory for fsync() purposes */
+ if(pThis->bSync && !pThis->bIsTTY) {
+ pThis->fdDir = open((char*)pThis->pszDir, O_RDONLY | O_CLOEXEC | O_NOCTTY);
+ if(pThis->fdDir == -1) {
+ char errStr[1024];
+ int err = errno;
+ rs_strerror_r(err, errStr, sizeof(errStr));
+ DBGPRINTF("error %d opening directory file for fsync() use - fsync for directory disabled: %s\n",
+ errno, errStr);
+ }
+ }
+
+ DBGPRINTF("file stream %s params: flush interval %d, async write %d\n",
+ (pThis->pszFName == NULL) ? "N/A" : (char*)pThis->pszFName,
+ pThis->iFlushInterval, pThis->bAsyncWrite);
+ /* if we have a flush interval, we need to do async writes in any case */
+ if(pThis->iFlushInterval != 0) {
+ pThis->bAsyncWrite = 1;
+ }
+
+ /* if we work asynchronously, we need a couple of synchronization objects */
+ if(pThis->bAsyncWrite) {
+ pthread_mutex_init(&pThis->mut, 0);
+ pthread_cond_init(&pThis->notFull, 0);
+ pthread_cond_init(&pThis->notEmpty, 0);
+ pthread_cond_init(&pThis->isEmpty, 0);
+ pThis->iCnt = pThis->iEnq = pThis->iDeq = 0;
+ for(i = 0 ; i < STREAM_ASYNC_NUMBUFS ; ++i) {
+ CHKmalloc(pThis->asyncBuf[i].pBuf = (uchar*) malloc(sizeof(uchar) * pThis->sIOBufSize));
+ }
+ pThis->pIOBuf = pThis->asyncBuf[0].pBuf;
+ pThis->bStopWriter = 0;
+ if(pthread_create(&pThis->writerThreadID, NULL, asyncWriterThread, pThis) != 0)
+ DBGPRINTF("ERROR: stream %p cold not create writer thread\n", pThis);
+ } else {
+ /* we work synchronously, so we need to alloc a fixed pIOBuf */
+ CHKmalloc(pThis->pIOBuf = (uchar*) malloc(sizeof(uchar) * pThis->sIOBufSize));
}
finalize_it:
@@ -410,24 +692,56 @@ finalize_it:
}
+/* stop the writer thread (we MUST be runnnig asynchronously when this method
+ * is called!). Note that the mutex must be locked! -- rgerhards, 2009-07-06
+ */
+static inline void
+stopWriter(strm_t *pThis)
+{
+ BEGINfunc
+ pThis->bStopWriter = 1;
+ pthread_cond_signal(&pThis->notEmpty);
+ d_pthread_mutex_unlock(&pThis->mut);
+ pthread_join(pThis->writerThreadID, NULL);
+ ENDfunc
+}
+
+
/* destructor for the strm object */
BEGINobjDestruct(strm) /* be sure to specify the object type also in END and CODESTART macros! */
+ int i;
CODESTARTobjDestruct(strm)
- if(pThis->tOperationsMode == STREAMMODE_WRITE)
- strmFlush(pThis);
-
- /* ... then free resources */
- if(pThis->fd != -1)
- strmCloseFile(pThis);
+ if(pThis->bAsyncWrite)
+ /* Note: mutex will be unlocked in stopWriter! */
+ d_pthread_mutex_lock(&pThis->mut);
- if(pThis->pszDir != NULL)
- free(pThis->pszDir);
- if(pThis->pIOBuf != NULL)
+ /* strmClose() will handle read-only files as well as need to open
+ * files that have unwritten buffers. -- rgerhards, 2010-03-09
+ */
+ strmCloseFile(pThis);
+
+ if(pThis->bAsyncWrite) {
+ stopWriter(pThis);
+ pthread_mutex_destroy(&pThis->mut);
+ pthread_cond_destroy(&pThis->notFull);
+ pthread_cond_destroy(&pThis->notEmpty);
+ pthread_cond_destroy(&pThis->isEmpty);
+ for(i = 0 ; i < STREAM_ASYNC_NUMBUFS ; ++i) {
+ free(pThis->asyncBuf[i].pBuf);
+ }
+ } else {
free(pThis->pIOBuf);
- if(pThis->pszCurrFName != NULL)
- free(pThis->pszCurrFName);
- if(pThis->pszFName != NULL)
- free(pThis->pszFName);
+ }
+
+ /* Finally, we can free the resources.
+ * IMPORTANT: we MUST free this only AFTER the ansyncWriter has been stopped, else
+ * we get random errors...
+ */
+ free(pThis->pszDir);
+ free(pThis->pZipBuf);
+ free(pThis->pszCurrFName);
+ free(pThis->pszFName);
+ pThis->bStopWriter = 2; /* RG: use as flag for destruction */
ENDobjDestruct(strm)
@@ -443,8 +757,11 @@ static rsRetVal strmCheckNextOutputFile(strm_t *pThis)
if(pThis->fd == -1)
FINALIZE;
+ /* wait for output to be empty, so that our counts are correct */
+ strmWaitAsyncWriterDone(pThis);
+
if(pThis->iCurrOffs >= pThis->iMaxFileSize) {
- dbgoprint((obj_t*) pThis, "max file size %ld reached for %d, now %ld - starting new file\n",
+ DBGOPRINT((obj_t*) pThis, "max file size %ld reached for %d, now %ld - starting new file\n",
(long) pThis->iMaxFileSize, pThis->fd, (long) pThis->iCurrOffs);
CHKiRet(strmNextFile(pThis));
}
@@ -453,47 +770,387 @@ finalize_it:
RETiRet;
}
+
+/* try to recover a tty after a write error. This may have happend
+ * due to vhangup(), and, if so, we can simply re-open it.
+ */
+#ifdef linux
+# define ERR_TTYHUP EIO
+#else
+# define ERR_TTYHUP EBADF
+#endif
+static rsRetVal
+tryTTYRecover(strm_t *pThis, int err)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strm);
+ if(err == ERR_TTYHUP) {
+ close(pThis->fd);
+ CHKiRet(doPhysOpen(pThis));
+ }
+
+finalize_it:
+ RETiRet;
+}
+#undef ER_TTYHUP
+
+
+/* issue write() api calls until either the buffer is completely
+ * written or an error occured (it may happen that multiple writes
+ * are required, what is perfectly legal. On exit, *pLenBuf contains
+ * the number of bytes actually written.
+ * rgerhards, 2009-06-08
+ */
+static rsRetVal
+doWriteCall(strm_t *pThis, uchar *pBuf, size_t *pLenBuf)
+{
+ ssize_t lenBuf;
+ ssize_t iTotalWritten;
+ ssize_t iWritten;
+ char *pWriteBuf;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strm);
+
+ lenBuf = *pLenBuf;
+ pWriteBuf = (char*) pBuf;
+ iTotalWritten = 0;
+ do {
+ iWritten = write(pThis->fd, pWriteBuf, lenBuf);
+ if(iWritten < 0) {
+ char errStr[1024];
+ int err = errno;
+ iWritten = 0; /* we have written NO bytes! */
+ rs_strerror_r(err, errStr, sizeof(errStr));
+ DBGPRINTF("log file (%d) write error %d: %s\n", pThis->fd, err, errStr);
+ if(err == EINTR) {
+ /*NO ERROR, just continue */;
+ } else {
+ if(pThis->bIsTTY) {
+ CHKiRet(tryTTYRecover(pThis, err));
+ } else {
+ ABORT_FINALIZE(RS_RET_IO_ERROR);
+ /* Would it make sense to cover more error cases? So far, I
+ * do not see good reason to do so.
+ */
+ }
+ }
+ }
+ /* advance buffer to next write position */
+ iTotalWritten += iWritten;
+ lenBuf -= iWritten;
+ pWriteBuf += iWritten;
+ } while(lenBuf > 0); /* Warning: do..while()! */
+
+ DBGOPRINT((obj_t*) pThis, "file %d write wrote %d bytes\n", pThis->fd, (int) iWritten);
+
+finalize_it:
+ *pLenBuf = iTotalWritten;
+ RETiRet;
+}
+
+
+
/* write memory buffer to a stream object.
- * To support direct writes of large objects, this method may be called
- * with a buffer pointing to some region other than the stream buffer itself.
- * However, in that case the stream buffer must be empty (strmFlush() has to
- * be called before), because we would otherwise mess up with the sequence
- * inside the stream. -- rgerhards, 2008-01-10
*/
-static rsRetVal strmWriteInternal(strm_t *pThis, uchar *pBuf, size_t lenBuf)
+static inline rsRetVal
+doWriteInternal(strm_t *pThis, uchar *pBuf, size_t lenBuf)
+{
+ DEFiRet;
+
+ ASSERT(pThis != NULL);
+
+ if(pThis->iZipLevel) {
+ CHKiRet(doZipWrite(pThis, pBuf, lenBuf));
+ } else {
+ /* write without zipping */
+ CHKiRet(strmPhysWrite(pThis, pBuf, lenBuf));
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* This function is called to "do" an async write call, what primarily means that
+ * the data is handed over to the writer thread (which will then do the actual write
+ * in parallel). Note that the stream mutex has already been locked by the
+ * strmWrite...() calls. Also note that we always have only a single producer,
+ * so we can simply serially assign the next free buffer to it and be sure that
+ * the very some producer comes back in sequence to submit the then-filled buffers.
+ * This also enables us to timout on partially written buffers. -- rgerhards, 2009-07-06
+ */
+static inline rsRetVal
+doAsyncWriteInternal(strm_t *pThis, size_t lenBuf)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strm);
+
+ /* the -1 below is important, because we need one buffer for the main thread! */
+ while(pThis->iCnt >= STREAM_ASYNC_NUMBUFS - 1)
+ d_pthread_cond_wait(&pThis->notFull, &pThis->mut);
+
+ pThis->asyncBuf[pThis->iEnq % STREAM_ASYNC_NUMBUFS].lenBuf = lenBuf;
+ pThis->pIOBuf = pThis->asyncBuf[++pThis->iEnq % STREAM_ASYNC_NUMBUFS].pBuf;
+
+ pThis->bDoTimedWait = 0; /* everything written, no need to timeout partial buffer writes */
+ if(++pThis->iCnt == 1)
+ pthread_cond_signal(&pThis->notEmpty);
+
+ RETiRet;
+}
+
+
+/* schedule writing to the stream. Depending on our concurrency settings,
+ * this either directly writes to the stream or schedules writing via
+ * the background thread. -- rgerhards, 2009-07-07
+ */
+static rsRetVal
+strmSchedWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf)
{
DEFiRet;
- int iWritten;
ASSERT(pThis != NULL);
- ASSERT(pBuf == pThis->pIOBuf || pThis->iBufPtr == 0);
+
+ /* we need to reset the buffer pointer BEFORE calling the actual write
+ * function. Otherwise, in circular mode, the write function will
+ * potentially close the file, then close will flush and as the
+ * buffer pointer is nonzero, will re-call into this code here. In
+ * the end result, we than have a problem (and things are screwed
+ * up). So we reset the buffer pointer first, and all this can
+ * not happen. It is safe to do so, because that pointer is NOT
+ * used inside the write functions. -- rgerhads, 2010-03-10
+ */
+ pThis->iBufPtr = 0; /* we are at the begin of a new buffer */
+ if(pThis->bAsyncWrite) {
+ CHKiRet(doAsyncWriteInternal(pThis, lenBuf));
+ } else {
+ CHKiRet(doWriteInternal(pThis, pBuf, lenBuf));
+ }
+
+
+finalize_it:
+ RETiRet;
+}
+
+
+
+/* This is the writer thread for asynchronous mode.
+ * -- rgerhards, 2009-07-06
+ */
+static void*
+asyncWriterThread(void *pPtr)
+{
+ int iDeq;
+ struct timespec t;
+ bool bTimedOut = 0;
+ strm_t *pThis = (strm_t*) pPtr;
+ ISOBJ_TYPE_assert(pThis, strm);
+
+ BEGINfunc
+# if HAVE_PRCTL && defined PR_SET_NAME
+ if(prctl(PR_SET_NAME, "rs:asyn strmwr", 0, 0, 0) != 0) {
+ DBGPRINTF("prctl failed, not setting thread name for '%s'\n", "stream writer");
+ }
+# endif
+
+ while(1) { /* loop broken inside */
+ d_pthread_mutex_lock(&pThis->mut);
+dbgprintf("XXX: asyncWriterThread iterating %s\n", pThis->pszFName);
+ while(pThis->iCnt == 0) {
+ if(pThis->bStopWriter) {
+ pthread_cond_broadcast(&pThis->isEmpty);
+ d_pthread_mutex_unlock(&pThis->mut);
+ goto finalize_it; /* break main loop */
+ }
+ if(bTimedOut && pThis->iBufPtr > 0) {
+ /* if we timed out, we need to flush pending data */
+ strmFlushInternal(pThis);
+ bTimedOut = 0;
+ continue; /* now we should have data */
+ }
+ bTimedOut = 0;
+ timeoutComp(&t, pThis->iFlushInterval * 2000); /* *1000 millisconds */ // TODO: check the 2000?!?
+ if(pThis->bDoTimedWait) {
+dbgprintf("asyncWriter thread going to timeout sleep\n");
+ if(pthread_cond_timedwait(&pThis->notEmpty, &pThis->mut, &t) != 0) {
+ int err = errno;
+ if(err == ETIMEDOUT) {
+ bTimedOut = 1;
+ } else {
+ bTimedOut = 1;
+ char errStr[1024];
+ rs_strerror_r(err, errStr, sizeof(errStr));
+ DBGPRINTF("stream async writer timeout with error (%d): %s - ignoring\n",
+ err, errStr);
+ }
+ }
+ } else {
+dbgprintf("asyncWriter thread going to eternal sleep\n");
+ d_pthread_cond_wait(&pThis->notEmpty, &pThis->mut);
+ }
+dbgprintf("asyncWriter woke up\n");
+ }
+
+ bTimedOut = 0; /* we may have timed out, but there *is* work to do... */
+
+ iDeq = pThis->iDeq++ % STREAM_ASYNC_NUMBUFS;
+dbgprintf("asyncWriter writes data\n");
+ doWriteInternal(pThis, pThis->asyncBuf[iDeq].pBuf, pThis->asyncBuf[iDeq].lenBuf);
+ // TODO: error check????? 2009-07-06
+
+ --pThis->iCnt;
+ if(pThis->iCnt < STREAM_ASYNC_NUMBUFS) {
+ pthread_cond_signal(&pThis->notFull);
+ if(pThis->iCnt == 0)
+ pthread_cond_broadcast(&pThis->isEmpty);
+ }
+ d_pthread_mutex_unlock(&pThis->mut);
+ }
+
+finalize_it:
+ ENDfunc
+ return NULL; /* to keep pthreads happy */
+}
+
+
+/* sync the file to disk, so that any unwritten data is persisted. This
+ * also syncs the directory and thus makes sure that the file survives
+ * fatal failure. Note that we do NOT return an error status if the
+ * sync fails. Doing so would probably cause more trouble than it
+ * is worth (read: data loss may occur where we otherwise might not
+ * have it). -- rgerhards, 2009-06-08
+ */
+#undef SYNCCALL
+#if HAVE_FDATASYNC
+# define SYNCCALL(x) fdatasync(x)
+#else
+# define SYNCCALL(x) fsync(x)
+#endif
+static rsRetVal
+syncFile(strm_t *pThis)
+{
+ int ret;
+ DEFiRet;
+
+ if(pThis->bIsTTY)
+ FINALIZE; /* TTYs can not be synced */
+
+ DBGPRINTF("syncing file %d\n", pThis->fd);
+ ret = SYNCCALL(pThis->fd);
+ if(ret != 0) {
+ char errStr[1024];
+ int err = errno;
+ rs_strerror_r(err, errStr, sizeof(errStr));
+ DBGPRINTF("sync failed for file %d with error (%d): %s - ignoring\n",
+ pThis->fd, err, errStr);
+ }
+
+ if(pThis->fdDir != -1) {
+ ret = fsync(pThis->fdDir);
+ }
+
+finalize_it:
+ RETiRet;
+}
+#undef SYNCCALL
+
+/* physically write to the output file. the provided data is ready for
+ * writing (e.g. zipped if we are requested to do that).
+ * Note that if the write() API fails, we do not reset any pointers, but return
+ * an error code. That means we may redo work in the next iteration.
+ * rgerhards, 2009-06-04
+ */
+static rsRetVal
+strmPhysWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf)
+{
+ size_t iWritten;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strm);
if(pThis->fd == -1)
CHKiRet(strmOpenFile(pThis));
- iWritten = write(pThis->fd, pBuf, lenBuf);
- dbgoprint((obj_t*) pThis, "file %d write wrote %d bytes\n", pThis->fd, iWritten);
- /* TODO: handle error case -- rgerhards, 2008-01-07 */
-
- /* Now indicate buffer empty again. We do this in any case, because there
- * is no way we could react more intelligently to an error during write.
- * This MUST be done BEFORE strCheckNextOutputFile(), otherwise we have an
- * endless loop. We reset the buffer pointer also in finalize_it - this is
- * necessary if we run into problems. Not resetting it would again cause an
- * endless loop. So it is better to loose some data (which also justifies
- * duplicating that code, too...) -- rgerhards, 2008-01-10
- */
- pThis->iBufPtr = 0;
+ iWritten = lenBuf;
+ CHKiRet(doWriteCall(pThis, pBuf, &iWritten));
+
pThis->iCurrOffs += iWritten;
/* update user counter, if provided */
if(pThis->pUsrWCntr != NULL)
*pThis->pUsrWCntr += iWritten;
- if(pThis->sType == STREAMTYPE_FILE_CIRCULAR)
+ if(pThis->bSync) {
+ CHKiRet(syncFile(pThis));
+ }
+
+ if(pThis->sType == STREAMTYPE_FILE_CIRCULAR) {
CHKiRet(strmCheckNextOutputFile(pThis));
+ } else if(pThis->iSizeLimit != 0) {
+ CHKiRet(doSizeLimitProcessing(pThis));
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* write the output buffer in zip mode
+ * This means we compress it first and then do a physical write.
+ * Note that we always do a full deflateInit ... deflate ... deflateEnd
+ * sequence. While this is not optimal, we need to do it because we need
+ * to ensure that the file is readable even when we are aborted. Doing the
+ * full sequence brings us as far towards this goal as possible (and not
+ * doing it would be a total failure). It may be worth considering to
+ * add a config switch so that the user can decide the risk he is ready
+ * to take, but so far this is not yet implemented (not even requested ;)).
+ * rgerhards, 2009-06-04
+ */
+static rsRetVal
+doZipWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf)
+{
+ z_stream zstrm;
+ int zRet; /* zlib return state */
+ bool bzInitDone = FALSE;
+ DEFiRet;
+ assert(pThis != NULL);
+ assert(pBuf != NULL);
+
+ /* allocate deflate state */
+ zstrm.zalloc = Z_NULL;
+ zstrm.zfree = Z_NULL;
+ zstrm.opaque = Z_NULL;
+ zstrm.next_in = (Bytef*) pBuf; /* as of zlib doc, this must be set BEFORE DeflateInit2 */
+ /* see note in file header for the params we use with deflateInit2() */
+ zRet = zlibw.DeflateInit2(&zstrm, pThis->iZipLevel, Z_DEFLATED, 31, 9, Z_DEFAULT_STRATEGY);
+ if(zRet != Z_OK) {
+ DBGPRINTF("error %d returned from zlib/deflateInit2()\n", zRet);
+ ABORT_FINALIZE(RS_RET_ZLIB_ERR);
+ }
+ bzInitDone = TRUE;
+
+ /* now doing the compression */
+ zstrm.next_in = (Bytef*) pBuf; /* as of zlib doc, this must be set BEFORE DeflateInit2 */
+ zstrm.avail_in = lenBuf;
+ /* run deflate() on buffer until everything has been compressed */
+ do {
+ DBGPRINTF("in deflate() loop, avail_in %d, total_in %ld\n", zstrm.avail_in, zstrm.total_in);
+ zstrm.avail_out = pThis->sIOBufSize;
+ zstrm.next_out = pThis->pZipBuf;
+ zRet = zlibw.Deflate(&zstrm, Z_FINISH); /* no bad return value */
+ DBGPRINTF("after deflate, ret %d, avail_out %d\n", zRet, zstrm.avail_out);
+ assert(zRet != Z_STREAM_ERROR); /* state not clobbered */
+ if(zstrm.avail_out == pThis->sIOBufSize)
+ break; /* this is valid, indicates end of compression --> see zlib howto */
+ CHKiRet(strmPhysWrite(pThis, (uchar*)pThis->pZipBuf, pThis->sIOBufSize - zstrm.avail_out));
+ } while (zstrm.avail_out == 0);
+ assert(zstrm.avail_in == 0); /* all input will be used */
finalize_it:
- pThis->iBufPtr = 0; /* see comment above */
+ if(bzInitDone) {
+ zRet = zlibw.DeflateEnd(&zstrm);
+ if(zRet != Z_OK) {
+ DBGPRINTF("error %d returned from zlib/deflateEnd()\n", zRet);
+ }
+ }
RETiRet;
}
@@ -503,26 +1160,54 @@ finalize_it:
* and is automatically called when the output buffer is full.
* rgerhards, 2008-01-10
*/
-rsRetVal strmFlush(strm_t *pThis)
+static rsRetVal
+strmFlushInternal(strm_t *pThis)
{
DEFiRet;
ASSERT(pThis != NULL);
- dbgoprint((obj_t*) pThis, "file %d flush, buflen %ld\n", pThis->fd, (long) pThis->iBufPtr);
+ DBGOPRINT((obj_t*) pThis, "file %d(%s) flush, buflen %ld%s\n", pThis->fd,
+ (pThis->pszFName == NULL) ? "N/A" : (char*)pThis->pszFName,
+ (long) pThis->iBufPtr, (pThis->iBufPtr == 0) ? " (no need to flush)" : "");
- if(pThis->tOperationsMode == STREAMMODE_WRITE && pThis->iBufPtr > 0) {
- iRet = strmWriteInternal(pThis, pThis->pIOBuf, pThis->iBufPtr);
+ if(pThis->tOperationsMode != STREAMMODE_READ && pThis->iBufPtr > 0) {
+ iRet = strmSchedWrite(pThis, pThis->pIOBuf, pThis->iBufPtr);
}
RETiRet;
}
+/* flush stream output buffer to persistent storage. This can be called at any time
+ * and is automatically called when the output buffer is full. This function is for
+ * use by EXTERNAL callers. Do NOT use it internally. It locks the async writer
+ * mutex if ther is need to do so.
+ * rgerhards, 2010-03-18
+ */
+static rsRetVal
+strmFlush(strm_t *pThis)
+{
+ DEFiRet;
+
+ ASSERT(pThis != NULL);
+
+ if(pThis->bAsyncWrite)
+ d_pthread_mutex_lock(&pThis->mut);
+ CHKiRet(strmFlushInternal(pThis));
+
+finalize_it:
+ if(pThis->bAsyncWrite)
+ d_pthread_mutex_unlock(&pThis->mut);
+
+ RETiRet;
+}
+
+
/* seek a stream to a specific location. Pending writes are flushed, read data
* is invalidated.
* rgerhards, 2008-01-12
*/
-static rsRetVal strmSeek(strm_t *pThis, off_t offs)
+static rsRetVal strmSeek(strm_t *pThis, off64_t offs)
{
DEFiRet;
@@ -531,10 +1216,10 @@ static rsRetVal strmSeek(strm_t *pThis, off_t offs)
if(pThis->fd == -1)
strmOpenFile(pThis);
else
- strmFlush(pThis);
- int i;
- dbgoprint((obj_t*) pThis, "file %d seek, pos %ld\n", pThis->fd, (long) offs);
- i = lseek(pThis->fd, offs, SEEK_SET); // TODO: check error!
+ strmFlushInternal(pThis);
+ long long i;
+ DBGOPRINT((obj_t*) pThis, "file %d seek, pos %llu\n", pThis->fd, (long long unsigned) offs);
+ i = lseek64(pThis->fd, offs, SEEK_SET); // TODO: check error!
pThis->iCurrOffs = offs; /* we are now at *this* offset */
pThis->iBufPtr = 0; /* buffer invalidated */
@@ -545,7 +1230,7 @@ static rsRetVal strmSeek(strm_t *pThis, off_t offs)
/* seek to current offset. This is primarily a helper to readjust the OS file
* pointer after a strm object has been deserialized.
*/
-rsRetVal strmSeekCurrOffs(strm_t *pThis)
+static rsRetVal strmSeekCurrOffs(strm_t *pThis)
{
DEFiRet;
@@ -558,27 +1243,40 @@ rsRetVal strmSeekCurrOffs(strm_t *pThis)
/* write a *single* character to a stream object -- rgerhards, 2008-01-10
*/
-rsRetVal strmWriteChar(strm_t *pThis, uchar c)
+static rsRetVal strmWriteChar(strm_t *pThis, uchar c)
{
DEFiRet;
ASSERT(pThis != NULL);
+ if(pThis->bAsyncWrite)
+ d_pthread_mutex_lock(&pThis->mut);
+
+ if(pThis->bDisabled)
+ ABORT_FINALIZE(RS_RET_STREAM_DISABLED);
+
/* if the buffer is full, we need to flush before we can write */
if(pThis->iBufPtr == pThis->sIOBufSize) {
- CHKiRet(strmFlush(pThis));
+ CHKiRet(strmFlushInternal(pThis));
}
/* we now always have space for one character, so we simply copy it */
*(pThis->pIOBuf + pThis->iBufPtr) = c;
pThis->iBufPtr++;
finalize_it:
+ if(pThis->bAsyncWrite)
+ d_pthread_mutex_unlock(&pThis->mut);
+
RETiRet;
}
-/* write an integer value (actually a long) to a stream object */
-rsRetVal strmWriteLong(strm_t *pThis, long i)
+/* write an integer value (actually a long) to a stream object
+ * Note that we do not need to lock the mutex here, because we call
+ * strmWrite(), which does the lock (aka: we must not lock it, else we
+ * would run into a recursive lock, resulting in a deadlock!)
+ */
+static rsRetVal strmWriteLong(strm_t *pThis, long i)
{
DEFiRet;
uchar szBuf[32];
@@ -593,45 +1291,72 @@ finalize_it:
}
-/* write memory buffer to a stream object
+/* write memory buffer to a stream object.
+ * process the data in chunks and copy it over to our buffer. The caller-provided data
+ * may theoritically be larger than our buffer. In that case, we do multiple copies. One
+ * may argue if it were more efficient to write out the caller-provided buffer in that case
+ * and earlier versions of rsyslog did this. However, this introduces a lot of complexity
+ * inside the buffered writer and potential performance bottlenecks when trying to solve
+ * it. Now keep in mind that we actually do (almost?) never have a case where the
+ * caller-provided buffer is larger than our one. So instead of optimizing a case
+ * which normally does not exist, we expect some degradation in its case but make us
+ * perform better in the regular cases. -- rgerhards, 2009-07-07
+ * Note: the pThis->iBufPtr == pThis->sIOBufSize logic below looks a bit like an
+ * on-off error. In fact, it is not, because iBufPtr always points to the next
+ * *free* byte in the buffer. So if it is sIOBufSize - 1, there actually is one
+ * free byte left. This came up during a code walkthrough and was considered
+ * worth nothing. -- rgerhards, 2010-03-10
*/
-rsRetVal strmWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf)
+static rsRetVal
+strmWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf)
{
DEFiRet;
- size_t iPartial;
+ size_t iWrite;
+ size_t iOffset;
ASSERT(pThis != NULL);
ASSERT(pBuf != NULL);
- /* check if the to-be-written data is larger than our buffer size */
- if(lenBuf >= pThis->sIOBufSize) {
- /* it is - so we do a direct write, that is most efficient.
- * TODO: is it really? think about disk block sizes!
- */
- CHKiRet(strmFlush(pThis)); /* we need to flush first!!! */
- CHKiRet(strmWriteInternal(pThis, pBuf, lenBuf));
- } else {
- /* data fits into a buffer - we just need to see if it
- * fits into the current buffer...
- */
- if(pThis->iBufPtr + lenBuf > pThis->sIOBufSize) {
- /* nope, so we must split it */
- iPartial = pThis->sIOBufSize - pThis->iBufPtr; /* this fits in current buf */
- if(iPartial > 0) { /* the buffer was exactly full, can not write anything! */
- memcpy(pThis->pIOBuf + pThis->iBufPtr, pBuf, iPartial);
- pThis->iBufPtr += iPartial;
- }
- CHKiRet(strmFlush(pThis)); /* get a new buffer for rest of data */
- memcpy(pThis->pIOBuf, pBuf + iPartial, lenBuf - iPartial);
- pThis->iBufPtr = lenBuf - iPartial;
- } else {
- /* we have space, so we simply copy over the string */
- memcpy(pThis->pIOBuf + pThis->iBufPtr, pBuf, lenBuf);
- pThis->iBufPtr += lenBuf;
+//DBGPRINTF("strmWrite(%p, '%65.65s', %ld);, disabled %d, sizelim %ld, size %lld\n", pThis, pBuf,lenBuf, pThis->bDisabled, pThis->iSizeLimit, pThis->iCurrOffs);
+ if(pThis->bAsyncWrite)
+ d_pthread_mutex_lock(&pThis->mut);
+
+ if(pThis->bDisabled)
+ ABORT_FINALIZE(RS_RET_STREAM_DISABLED);
+
+ iOffset = 0;
+ do {
+ if(pThis->iBufPtr == pThis->sIOBufSize) {
+ CHKiRet(strmFlushInternal(pThis)); /* get a new buffer for rest of data */
}
+ iWrite = pThis->sIOBufSize - pThis->iBufPtr; /* this fits in current buf */
+ if(iWrite > lenBuf)
+ iWrite = lenBuf;
+ memcpy(pThis->pIOBuf + pThis->iBufPtr, pBuf + iOffset, iWrite);
+ pThis->iBufPtr += iWrite;
+ iOffset += iWrite;
+ lenBuf -= iWrite;
+ } while(lenBuf > 0);
+
+ /* now check if the buffer right at the end of the write is full and, if so,
+ * write it. This seems more natural than waiting (hours?) for the next message...
+ */
+ if(pThis->iBufPtr == pThis->sIOBufSize) {
+ CHKiRet(strmFlushInternal(pThis)); /* get a new buffer for rest of data */
}
finalize_it:
+ if(pThis->bAsyncWrite) {
+ if(pThis->bDoTimedWait == 0) {
+ /* we potentially have a partial buffer, so re-activate the
+ * writer thread that it can set and pick up timeouts.
+ */
+ pThis->bDoTimedWait = 1;
+ pthread_cond_signal(&pThis->notEmpty);
+ }
+ d_pthread_mutex_unlock(&pThis->mut);
+ }
+
RETiRet;
}
@@ -644,34 +1369,27 @@ DEFpropSetMeth(strm, iFileNumDigits, int)
DEFpropSetMeth(strm, tOperationsMode, int)
DEFpropSetMeth(strm, tOpenMode, mode_t)
DEFpropSetMeth(strm, sType, strmType_t)
-
-rsRetVal strmSetiMaxFiles(strm_t *pThis, int iNewVal)
+DEFpropSetMeth(strm, iZipLevel, int)
+DEFpropSetMeth(strm, bSync, int)
+DEFpropSetMeth(strm, sIOBufSize, size_t)
+DEFpropSetMeth(strm, iSizeLimit, off_t)
+DEFpropSetMeth(strm, iFlushInterval, int)
+DEFpropSetMeth(strm, pszSizeLimitCmd, uchar*)
+
+static rsRetVal strmSetiMaxFiles(strm_t *pThis, int iNewVal)
{
pThis->iMaxFiles = iNewVal;
pThis->iFileNumDigits = getNumberDigits(iNewVal);
return RS_RET_OK;
}
-rsRetVal strmSetiAddtlOpenFlags(strm_t *pThis, int iNewVal)
-{
- DEFiRet;
-
- if(iNewVal & O_APPEND)
- ABORT_FINALIZE(RS_RET_PARAM_ERROR);
-
- pThis->iAddtlOpenFlags = iNewVal;
-
-finalize_it:
- RETiRet;
-}
-
/* set the stream's file prefix
* The passed-in string is duplicated. So if the caller does not need
* it any longer, it must free it.
* rgerhards, 2008-01-09
*/
-rsRetVal
+static rsRetVal
strmSetFName(strm_t *pThis, uchar *pszName, size_t iLenName)
{
DEFiRet;
@@ -685,7 +1403,7 @@ strmSetFName(strm_t *pThis, uchar *pszName, size_t iLenName)
if(pThis->pszFName != NULL)
free(pThis->pszFName);
- if((pThis->pszFName = malloc(sizeof(uchar) * iLenName + 1)) == NULL)
+ if((pThis->pszFName = malloc(sizeof(uchar) * (iLenName + 1))) == NULL)
ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
memcpy(pThis->pszFName, pszName, iLenName + 1); /* always think about the \0! */
@@ -701,7 +1419,7 @@ finalize_it:
* it any longer, it must free it.
* rgerhards, 2008-01-09
*/
-rsRetVal
+static rsRetVal
strmSetDir(strm_t *pThis, uchar *pszDir, size_t iLenDir)
{
DEFiRet;
@@ -712,8 +1430,7 @@ strmSetDir(strm_t *pThis, uchar *pszDir, size_t iLenDir)
if(iLenDir < 1)
ABORT_FINALIZE(RS_RET_FILE_PREFIX_MISSING);
- if((pThis->pszDir = malloc(sizeof(uchar) * iLenDir + 1)) == NULL)
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ CHKmalloc(pThis->pszDir = malloc(sizeof(uchar) * iLenDir + 1));
memcpy(pThis->pszDir, pszDir, iLenDir + 1); /* always think about the \0! */
pThis->lenDir = iLenDir;
@@ -745,7 +1462,7 @@ finalize_it:
*
* rgerhards, 2008-01-10
*/
-rsRetVal strmRecordBegin(strm_t *pThis)
+static rsRetVal strmRecordBegin(strm_t *pThis)
{
ASSERT(pThis != NULL);
ASSERT(pThis->bInRecord == 0);
@@ -753,7 +1470,7 @@ rsRetVal strmRecordBegin(strm_t *pThis)
return RS_RET_OK;
}
-rsRetVal strmRecordEnd(strm_t *pThis)
+static rsRetVal strmRecordEnd(strm_t *pThis)
{
DEFiRet;
ASSERT(pThis != NULL);
@@ -775,16 +1492,16 @@ rsRetVal strmRecordEnd(strm_t *pThis)
* We do not serialize the dynamic properties.
* rgerhards, 2008-01-10
*/
-rsRetVal strmSerialize(strm_t *pThis, strm_t *pStrm)
+static rsRetVal strmSerialize(strm_t *pThis, strm_t *pStrm)
{
DEFiRet;
int i;
- long l;
+ int64 l;
ISOBJ_TYPE_assert(pThis, strm);
ISOBJ_TYPE_assert(pStrm, strm);
- strmFlush(pThis);
+ strmFlushInternal(pThis);
CHKiRet(obj.BeginSerialize(pStrm, (obj_t*) pThis));
objSerializeSCALAR(pStrm, iCurrFNum, INT);
@@ -801,8 +1518,8 @@ rsRetVal strmSerialize(strm_t *pThis, strm_t *pStrm)
i = pThis->tOpenMode;
objSerializeSCALAR_VAR(pStrm, tOpenMode, INT, i);
- l = (long) pThis->iCurrOffs;
- objSerializeSCALAR_VAR(pStrm, iCurrOffs, LONG, l);
+ l = pThis->iCurrOffs;
+ objSerializeSCALAR_VAR(pStrm, iCurrOffs, INT64, l);
CHKiRet(obj.EndSerialize(pStrm));
@@ -821,7 +1538,7 @@ finalize_it:
* any new set overwrites the previous one.
* rgerhards, 2008-02-27
*/
-rsRetVal
+static rsRetVal
strmSetWCntr(strm_t *pThis, number_t *pWCnt)
{
DEFiRet;
@@ -841,8 +1558,8 @@ strmSetWCntr(strm_t *pThis, number_t *pWCnt)
/* This function can be used as a generic way to set properties.
* rgerhards, 2008-01-11
*/
-#define isProp(name) !rsCStrSzStrCmp(pProp->pcsName, (uchar*) name, sizeof(name) - 1)
-rsRetVal strmSetProperty(strm_t *pThis, var_t *pProp)
+#define isProp(name) !rsCStrSzStrCmp(pProp->pcsName, UCHAR_CONSTANT(name), sizeof(name) - 1)
+static rsRetVal strmSetProperty(strm_t *pThis, var_t *pProp)
{
DEFiRet;
@@ -881,7 +1598,7 @@ finalize_it:
* reported on the second call may actually be lower than on the first call. This is due to
* file circulation. A caller must deal with that. -- rgerhards, 2008-01-30
*/
-rsRetVal
+static rsRetVal
strmGetCurrOffset(strm_t *pThis, int64 *pOffs)
{
DEFiRet;
@@ -909,8 +1626,38 @@ CODESTARTobjQueryInterface(strm)
* work here (if we can support an older interface version - that,
* of course, also affects the "if" above).
*/
- /*xxxpIf->oID = OBJvm; SAMPLE */
-
+ pIf->Construct = strmConstruct;
+ pIf->ConstructFinalize = strmConstructFinalize;
+ pIf->Destruct = strmDestruct;
+ pIf->ReadChar = strmReadChar;
+ pIf->UnreadChar = strmUnreadChar;
+ pIf->ReadLine = strmReadLine;
+ pIf->SeekCurrOffs = strmSeekCurrOffs;
+ pIf->Write = strmWrite;
+ pIf->WriteChar = strmWriteChar;
+ pIf->WriteLong = strmWriteLong;
+ pIf->SetFName = strmSetFName;
+ pIf->SetDir = strmSetDir;
+ pIf->Flush = strmFlush;
+ pIf->RecordBegin = strmRecordBegin;
+ pIf->RecordEnd = strmRecordEnd;
+ pIf->Serialize = strmSerialize;
+ pIf->GetCurrOffset = strmGetCurrOffset;
+ pIf->SetWCntr = strmSetWCntr;
+ /* set methods */
+ pIf->SetbDeleteOnClose = strmSetbDeleteOnClose;
+ pIf->SetiMaxFileSize = strmSetiMaxFileSize;
+ pIf->SetiMaxFiles = strmSetiMaxFiles;
+ pIf->SetiFileNumDigits = strmSetiFileNumDigits;
+ pIf->SettOperationsMode = strmSettOperationsMode;
+ pIf->SettOpenMode = strmSettOpenMode;
+ pIf->SetsType = strmSetsType;
+ pIf->SetiZipLevel = strmSetiZipLevel;
+ pIf->SetbSync = strmSetbSync;
+ pIf->SetsIOBufSize = strmSetsIOBufSize;
+ pIf->SetiSizeLimit = strmSetiSizeLimit;
+ pIf->SetiFlushInterval = strmSetiFlushInterval;
+ pIf->SetpszSizeLimitCmd = strmSetpszSizeLimitCmd;
finalize_it:
ENDobjQueryInterface(strm)
@@ -927,7 +1674,5 @@ BEGINObjClassInit(strm, 1, OBJ_IS_CORE_MODULE)
OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, strmConstructFinalize);
ENDObjClassInit(strm)
-
-/*
- * vi:set ai:
+/* vi:set ai:
*/
diff --git a/runtime/stream.h b/runtime/stream.h
index 371358ab..369d5a0f 100644
--- a/runtime/stream.h
+++ b/runtime/stream.h
@@ -19,7 +19,29 @@
* can easily be persistet. The bottom line is that it makes much sense to
* use this class whereever possible as its features may grow in the future.
*
- * Copyright 2008 Rainer Gerhards and Adiscon GmbH.
+ * An important note on writing gzip format via zlib (kept anonymous
+ * by request):
+ *
+ * --------------------------------------------------------------------------
+ * We'd like to make sure the output file is in full gzip format
+ * (compatible with gzip -d/zcat etc). There is a flag in how the output
+ * is initialized within zlib to properly add the gzip wrappers to the
+ * output. (gzip is effectively a small metadata wrapper around raw
+ * zstream output.)
+ *
+ * I had written an old bit of code to do this - the documentation on
+ * deflatInit2() was pretty tricky to nail down on this specific feature:
+ *
+ * int deflateInit2 (z_streamp strm, int level, int method, int windowBits,
+ * int memLevel, int strategy);
+ *
+ * I believe "31" would be the value for the "windowBits" field that you'd
+ * want to try:
+ *
+ * deflateInit2(zstrmptr, 6, Z_DEFLATED, 31, 9, Z_DEFAULT_STRATEGY);
+ * --------------------------------------------------------------------------
+ *
+ * Copyright 2008, 2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of the rsyslog runtime library.
*
@@ -47,20 +69,26 @@
#include "obj-types.h"
#include "glbl.h"
#include "stream.h"
+#include "zlibw.h"
+#include "apc.h"
/* stream types */
typedef enum {
STREAMTYPE_FILE_SINGLE = 0, /**< read a single file */
STREAMTYPE_FILE_CIRCULAR = 1, /**< circular files */
- STREAMTYPE_FILE_MONITOR = 2 /**< monitor a (third-party) file */
+ STREAMTYPE_FILE_MONITOR = 2, /**< monitor a (third-party) file */
+ STREAMTYPE_NAMED_PIPE = 3 /**< file is a named pipe (so far, tested for output only) */
} strmType_t;
-typedef enum {
+typedef enum { /* when extending, do NOT change existing modes! */
STREAMMMODE_INVALID = 0,
STREAMMODE_READ = 1,
- STREAMMODE_WRITE = 2
+ STREAMMODE_WRITE = 2,
+ STREAMMODE_WRITE_TRUNC = 3,
+ STREAMMODE_WRITE_APPEND = 4
} strmMode_t;
+#define STREAM_ASYNC_NUMBUFS 2 /* must be a power of 2 -- TODO: make configurable */
/* The strm_t data structure */
typedef struct strm_s {
BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
@@ -71,61 +99,94 @@ typedef struct strm_s {
int lenFName;
strmMode_t tOperationsMode;
mode_t tOpenMode;
- int iAddtlOpenFlags; /* can be used to specifiy additional (compatible!) open flags */
int64 iMaxFileSize;/* maximum size a file may grow to */
int iMaxFiles; /* maximum number of files if a circular mode is in use */
int iFileNumDigits;/* min number of digits to use in file number (only in circular mode) */
- int bDeleteOnClose; /* set to 1 to auto-delete on close -- be careful with that setting! */
+ bool bDeleteOnClose; /* set to 1 to auto-delete on close -- be careful with that setting! */
int64 iCurrOffs;/* current offset */
int64 *pUsrWCntr; /* NULL or a user-provided counter that receives the nbr of bytes written since the last CntrSet() */
/* dynamic properties, valid only during file open, not to be persistet */
- size_t sIOBufSize;/* size of IO buffer */
+ bool bDisabled; /* should file no longer be written to? (currently set only if omfile file size limit fails) */
+ bool bSync; /* sync this file after every write? */
+ size_t sIOBufSize;/* size of IO buffer */
uchar *pszDir; /* Directory */
int lenDir;
int fd; /* the file descriptor, -1 if closed */
+ int fdDir; /* the directory's descriptor, in case bSync is requested (-1 if closed) */
uchar *pszCurrFName; /* name of current file (if open) */
- uchar *pIOBuf; /* io Buffer */
+ uchar *pIOBuf; /* the iobuffer currently in use to gather data */
size_t iBufPtrMax; /* current max Ptr in Buffer (if partial read!) */
size_t iBufPtr; /* pointer into current buffer */
int iUngetC; /* char set via UngetChar() call or -1 if none set */
- int bInRecord; /* if 1, indicates that we are currently writing a not-yet complete record */
+ bool bInRecord; /* if 1, indicates that we are currently writing a not-yet complete record */
+ int iZipLevel; /* zip level (0..9). If 0, zip is completely disabled */
+ Bytef *pZipBuf;
+ /* support for async flush procesing */
+ bool bAsyncWrite; /* do asynchronous writes (always if a flush interval is given) */
+ bool bStopWriter; /* shall writer thread terminate? */
+ bool bDoTimedWait; /* instruct writer thread to do a times wait to support flush timeouts */
+ int iFlushInterval; /* flush in which interval - 0, no flushing */
+ apc_id_t apcID; /* id of current Apc request (used for cancelling) */
+ pthread_mutex_t mut;/* mutex for flush in async mode */
+ pthread_cond_t notFull;
+ pthread_cond_t notEmpty;
+ pthread_cond_t isEmpty;
+ unsigned short iEnq; /* this MUST be unsigned as we use module arithmetic (else invalid indexing happens!) */
+ unsigned short iDeq; /* this MUST be unsigned as we use module arithmetic (else invalid indexing happens!) */
+ short iCnt; /* current nbr of elements in buffer */
+ struct {
+ uchar *pBuf;
+ size_t lenBuf;
+ } asyncBuf[STREAM_ASYNC_NUMBUFS];
+ pthread_t writerThreadID;
+ int apcRequested; /* is an apc Requested? */
+ /* support for omfile size-limiting commands, special counters, NOT persisted! */
+ off_t iSizeLimit; /* file size limit, 0 = no limit */
+ uchar *pszSizeLimitCmd; /* command to carry out when size limit is reached */
+ bool bIsTTY; /* is this a tty file? */
} strm_t;
+
/* interfaces */
BEGINinterface(strm) /* name must also be changed in ENDinterface macro! */
+ rsRetVal (*Construct)(strm_t **ppThis);
+ rsRetVal (*ConstructFinalize)(strm_t *pThis);
+ rsRetVal (*Destruct)(strm_t **ppThis);
+ rsRetVal (*SetMaxFileSize)(strm_t *pThis, int64 iMaxFileSize);
+ rsRetVal (*SetFileName)(strm_t *pThis, uchar *pszName, size_t iLenName);
+ rsRetVal (*ReadChar)(strm_t *pThis, uchar *pC);
+ rsRetVal (*UnreadChar)(strm_t *pThis, uchar c);
+ rsRetVal (*ReadLine)(strm_t *pThis, cstr_t **ppCStr);
+ rsRetVal (*SeekCurrOffs)(strm_t *pThis);
+ rsRetVal (*Write)(strm_t *pThis, uchar *pBuf, size_t lenBuf);
+ rsRetVal (*WriteChar)(strm_t *pThis, uchar c);
+ rsRetVal (*WriteLong)(strm_t *pThis, long i);
+ rsRetVal (*SetFName)(strm_t *pThis, uchar *pszPrefix, size_t iLenPrefix);
+ rsRetVal (*SetDir)(strm_t *pThis, uchar *pszDir, size_t iLenDir);
+ rsRetVal (*Flush)(strm_t *pThis);
+ rsRetVal (*RecordBegin)(strm_t *pThis);
+ rsRetVal (*RecordEnd)(strm_t *pThis);
+ rsRetVal (*Serialize)(strm_t *pThis, strm_t *pStrm);
+ rsRetVal (*GetCurrOffset)(strm_t *pThis, int64 *pOffs);
+ rsRetVal (*SetWCntr)(strm_t *pThis, number_t *pWCnt);
+ INTERFACEpropSetMeth(strm, bDeleteOnClose, int);
+ INTERFACEpropSetMeth(strm, iMaxFileSize, int);
+ INTERFACEpropSetMeth(strm, iMaxFiles, int);
+ INTERFACEpropSetMeth(strm, iFileNumDigits, int);
+ INTERFACEpropSetMeth(strm, tOperationsMode, int);
+ INTERFACEpropSetMeth(strm, tOpenMode, mode_t);
+ INTERFACEpropSetMeth(strm, sType, strmType_t);
+ INTERFACEpropSetMeth(strm, iZipLevel, int);
+ INTERFACEpropSetMeth(strm, bSync, int);
+ INTERFACEpropSetMeth(strm, sIOBufSize, size_t);
+ INTERFACEpropSetMeth(strm, iSizeLimit, off_t);
+ INTERFACEpropSetMeth(strm, iFlushInterval, int);
+ INTERFACEpropSetMeth(strm, pszSizeLimitCmd, uchar*);
ENDinterface(strm)
-#define strmCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+#define strmCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */
/* prototypes */
-rsRetVal strmConstruct(strm_t **ppThis);
-rsRetVal strmConstructFinalize(strm_t __attribute__((unused)) *pThis);
-rsRetVal strmDestruct(strm_t **ppThis);
-rsRetVal strmSetMaxFileSize(strm_t *pThis, int64 iMaxFileSize);
-rsRetVal strmSetFileName(strm_t *pThis, uchar *pszName, size_t iLenName);
-rsRetVal strmReadChar(strm_t *pThis, uchar *pC);
-rsRetVal strmUnreadChar(strm_t *pThis, uchar c);
-rsRetVal strmReadLine(strm_t *pThis, cstr_t **ppCStr);
-rsRetVal strmSeekCurrOffs(strm_t *pThis);
-rsRetVal strmWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf);
-rsRetVal strmWriteChar(strm_t *pThis, uchar c);
-rsRetVal strmWriteLong(strm_t *pThis, long i);
-rsRetVal strmSetFName(strm_t *pThis, uchar *pszPrefix, size_t iLenPrefix);
-rsRetVal strmSetDir(strm_t *pThis, uchar *pszDir, size_t iLenDir);
-rsRetVal strmFlush(strm_t *pThis);
-rsRetVal strmRecordBegin(strm_t *pThis);
-rsRetVal strmRecordEnd(strm_t *pThis);
-rsRetVal strmSerialize(strm_t *pThis, strm_t *pStrm);
-rsRetVal strmSetiAddtlOpenFlags(strm_t *pThis, int iNewVal);
-rsRetVal strmGetCurrOffset(strm_t *pThis, int64 *pOffs);
-rsRetVal strmSetWCntr(strm_t *pThis, number_t *pWCnt);
PROTOTYPEObjClassInit(strm);
-PROTOTYPEpropSetMeth(strm, bDeleteOnClose, int);
-PROTOTYPEpropSetMeth(strm, iMaxFileSize, int);
-PROTOTYPEpropSetMeth(strm, iMaxFiles, int);
-PROTOTYPEpropSetMeth(strm, iFileNumDigits, int);
-PROTOTYPEpropSetMeth(strm, tOperationsMode, int);
-PROTOTYPEpropSetMeth(strm, tOpenMode, mode_t);
-PROTOTYPEpropSetMeth(strm, sType, strmType_t);
#endif /* #ifndef STREAM_H_INCLUDED */
diff --git a/runtime/stringbuf.c b/runtime/stringbuf.c
index 63b42348..8b2fe455 100644
--- a/runtime/stringbuf.c
+++ b/runtime/stringbuf.c
@@ -6,8 +6,9 @@
* Please see syslogd.c for license information.
* All functions in this "class" start with rsCStr (rsyslog Counted String).
* begun 2005-09-07 rgerhards
+ * did some optimization (read: bugs!) rgerhards, 2009-06-16
*
- * Copyright (C) 2007-2008 by Rainer Gerhards and Adiscon GmbH
+ * Copyright (C) 2007-2009 by Rainer Gerhards and Adiscon GmbH
*
* This file is part of the rsyslog runtime library.
*
@@ -40,6 +41,7 @@
#include "regexp.h"
#include "obj.h"
+uchar* rsCStrGetSzStr(cstr_t *pThis);
/* ################################################################# *
* private members *
@@ -54,22 +56,20 @@ DEFobjCurrIf(regexp)
* ################################################################# */
-rsRetVal rsCStrConstruct(cstr_t **ppThis)
+rsRetVal cstrConstruct(cstr_t **ppThis)
{
DEFiRet;
cstr_t *pThis;
ASSERT(ppThis != NULL);
- if((pThis = (cstr_t*) calloc(1, sizeof(cstr_t))) == NULL)
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ CHKmalloc(pThis = (cstr_t*) calloc(1, sizeof(cstr_t)));
rsSETOBJTYPE(pThis, OIDrsCStr);
pThis->pBuf = NULL;
pThis->pszBuf = NULL;
pThis->iBufSize = 0;
pThis->iStrLen = 0;
- pThis->iAllocIncrement = RS_STRINGBUF_ALLOC_INCREMENT;
*ppThis = pThis;
finalize_it:
@@ -89,7 +89,7 @@ rsRetVal rsCStrConstructFromszStr(cstr_t **ppThis, uchar *sz)
CHKiRet(rsCStrConstruct(&pThis));
- pThis->iBufSize = pThis->iStrLen = strlen((char*)(char *) sz);
+ pThis->iBufSize = pThis->iStrLen = strlen((char *) sz);
if((pThis->pBuf = (uchar*) malloc(sizeof(uchar) * pThis->iStrLen)) == NULL) {
RSFREEOBJ(pThis);
ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
@@ -137,24 +137,8 @@ void rsCStrDestruct(cstr_t **ppThis)
{
cstr_t *pThis = *ppThis;
- /* rgerhards 2005-10-19: The free of pBuf was contained in conditional compilation.
- * The code was only compiled if STRINGBUF_TRIM_ALLOCSIZE was set to 1. I honestly
- * do not know why it was so, I think it was an artifact. Anyhow, I have changed this
- * now. Should there any issue occur, this comment hopefully will shed some light
- * on what happened. I re-verified, and this function has never before been called
- * by anyone. So changing it can have no impact for obvious reasons...
- *
- * rgerhards, 2008-02-20: I changed the interface to the new calling conventions, where
- * the destructor receives a pointer to the object, so that it can set it to NULL.
- */
- if(pThis->pBuf != NULL) {
- free(pThis->pBuf);
- }
-
- if(pThis->pszBuf != NULL) {
- free(pThis->pszBuf);
- }
-
+ free(pThis->pBuf);
+ free(pThis->pszBuf);
RSFREEOBJ(pThis);
*ppThis = NULL;
}
@@ -166,36 +150,32 @@ void rsCStrDestruct(cstr_t **ppThis)
* allocated. In practice, a bit more is allocated because we envision that
* some more characters may be added after these.
* rgerhards, 2008-01-07
+ * changed to utilized realloc() -- rgerhards, 2009-06-16
*/
-static rsRetVal rsCStrExtendBuf(cstr_t *pThis, size_t iMinNeeded)
+rsRetVal
+rsCStrExtendBuf(cstr_t *pThis, size_t iMinNeeded)
{
- DEFiRet;
uchar *pNewBuf;
size_t iNewSize;
+ DEFiRet;
/* first compute the new size needed */
- if(iMinNeeded > pThis->iAllocIncrement) {
- /* we allocate "n" iAllocIncrements. Usually, that should
+ if(iMinNeeded > RS_STRINGBUF_ALLOC_INCREMENT) {
+ /* we allocate "n" ALLOC_INCREMENTs. Usually, that should
* leave some room after the absolutely needed one. It also
* reduces memory fragmentation. Note that all of this are
* integer operations (very important to understand what is
* going on)! Parenthesis are for better readibility.
*/
- iNewSize = ((iMinNeeded / pThis->iAllocIncrement) + 1) * pThis->iAllocIncrement;
+ iNewSize = (iMinNeeded / RS_STRINGBUF_ALLOC_INCREMENT + 1) * RS_STRINGBUF_ALLOC_INCREMENT;
} else {
- iNewSize = pThis->iBufSize + pThis->iAllocIncrement;
+ iNewSize = pThis->iBufSize + RS_STRINGBUF_ALLOC_INCREMENT;
}
iNewSize += pThis->iBufSize; /* add current size */
- /* and then allocate and copy over */
/* DEV debugging only: dbgprintf("extending string buffer, old %d, new %d\n", pThis->iBufSize, iNewSize); */
- if((pNewBuf = (uchar*) malloc(iNewSize * sizeof(uchar))) == NULL)
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- memcpy(pNewBuf, pThis->pBuf, pThis->iBufSize);
+ CHKmalloc(pNewBuf = (uchar*) realloc(pThis->pBuf, iNewSize * sizeof(uchar)));
pThis->iBufSize = iNewSize;
- if(pThis->pBuf != NULL) {
- free(pThis->pBuf);
- }
pThis->pBuf = pNewBuf;
finalize_it:
@@ -243,7 +223,7 @@ rsRetVal rsCStrAppendStr(cstr_t *pThis, uchar* psz)
/* append the contents of one cstr_t object to another
* rgerhards, 2008-02-25
*/
-rsRetVal rsCStrAppendCStr(cstr_t *pThis, cstr_t *pstrAppend)
+rsRetVal cstrAppendCStr(cstr_t *pThis, cstr_t *pstrAppend)
{
return rsCStrAppendStrWithLen(pThis, pstrAppend->pBuf, pstrAppend->iStrLen);
}
@@ -264,45 +244,18 @@ finalize_it:
}
-rsRetVal rsCStrAppendChar(cstr_t *pThis, uchar c)
-{
- DEFiRet;
-
- rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
-
- if(pThis->iStrLen >= pThis->iBufSize) {
- CHKiRet(rsCStrExtendBuf(pThis, 1)); /* need more memory! */
- }
-
- /* ok, when we reach this, we have sufficient memory */
- *(pThis->pBuf + pThis->iStrLen++) = c;
-
- /* check if we need to invalidate an sz representation! */
- if(pThis->pszBuf != NULL) {
- free(pThis->pszBuf);
- pThis->pszBuf = NULL;
- }
-
-finalize_it:
- RETiRet;
-}
-
-
/* Sets the string object to the classigal sz-string provided.
* Any previously stored vlaue is discarded. If a NULL pointer
* the the new value (pszNew) is provided, an empty string is
- * created (this is NOT an error!). Property iAllocIncrement is
- * not modified by this function.
+ * created (this is NOT an error!).
* rgerhards, 2005-10-18
*/
rsRetVal rsCStrSetSzStr(cstr_t *pThis, uchar *pszNew)
{
rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
- if(pThis->pBuf != NULL)
- free(pThis->pBuf);
- if(pThis->pszBuf != NULL)
- free(pThis->pszBuf);
+ free(pThis->pBuf);
+ free(pThis->pszBuf);
if(pszNew == NULL) {
pThis->iStrLen = 0;
pThis->iBufSize = 0;
@@ -312,7 +265,6 @@ rsRetVal rsCStrSetSzStr(cstr_t *pThis, uchar *pszNew)
pThis->iStrLen = strlen((char*)pszNew);
pThis->iBufSize = pThis->iStrLen;
pThis->pszBuf = NULL;
- /* iAllocIncrement is NOT modified! */
/* now save the new value */
if((pThis->pBuf = (uchar*) malloc(sizeof(uchar) * pThis->iStrLen)) == NULL) {
@@ -395,30 +347,18 @@ uchar* rsCStrGetSzStr(cstr_t *pThis)
* MUST be freed by the caller. The function might return NULL if
* no memory can be allocated.
*
- * TODO:
- * This function should at some time become special. The base idea is to
- * add one extra byte to the end of the regular buffer, so that we can
- * convert it to an szString without the need to copy. The extra memory
- * footprint is not hefty, but the performance gain is potentially large.
- * To get it done now, I am not doing the optimiziation right now.
- * rgerhards, 2005-09-07
+ * This is the NEW replacement for rsCStrConvSzStrAndDestruct which does
+ * no longer utilize a special buffer but soley works on pBuf (and also
+ * assumes that cstrFinalize had been called).
*
- * rgerhards, 2007-09-04: I have changed the interface of this function. It now
- * returns an rsRetVal, so that we can communicate back if we have an error.
- * Using the standard method is much better than returning NULL. Secondly, NULL
- * was not actually an error - it was in indication if the string was empty.
- * This was needed in some parts of the code, in others not. I have now added
- * a second parameter to specify what the caller needs. I hope these changes
- * will make it less likely that the function is called incorrectly, what
- * previously happend quite often and was the cause of a number of program
- * aborts. So the parameters are now:
+ * Parameters are as follows:
* pointer to the object, pointer to string-pointer to receive string and
* bRetNULL: 0 - must not return NULL on empty string, return "" in that
* case, 1 - return NULL instead of an empty string.
* PLEASE NOTE: the caller must free the memory returned in ppSz in any case
* (except, of course, if it is NULL).
*/
-rsRetVal rsCStrConvSzStrAndDestruct(cstr_t *pThis, uchar **ppSz, int bRetNULL)
+rsRetVal cstrConvSzStrAndDestruct(cstr_t *pThis, uchar **ppSz, int bRetNULL)
{
DEFiRet;
uchar* pRetBuf;
@@ -429,14 +369,13 @@ rsRetVal rsCStrConvSzStrAndDestruct(cstr_t *pThis, uchar **ppSz, int bRetNULL)
if(pThis->pBuf == NULL) {
if(bRetNULL == 0) {
- if((pRetBuf = malloc(sizeof(uchar))) == NULL)
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ CHKmalloc(pRetBuf = malloc(sizeof(uchar)));
*pRetBuf = '\0';
} else {
pRetBuf = NULL;
}
} else
- pRetBuf = rsCStrGetSzStr(pThis);
+ pRetBuf = pThis->pBuf;
*ppSz = pRetBuf;
@@ -445,64 +384,11 @@ finalize_it:
* that we can NOT use the rsCStrDestruct function as it would
* also free the sz String buffer, which we pass on to the user.
*/
- if(pThis->pBuf != NULL)
- free(pThis->pBuf);
RSFREEOBJ(pThis);
-
RETiRet;
}
-#if STRINGBUF_TRIM_ALLOCSIZE == 1
- /* Only in this mode, we need to trim the string. To do
- * so, we must allocate a new buffer of the exact
- * string size, and then copy the old one over.
- */
- /* WARNING
- * STRINGBUF_TRIM_ALLOCSIZE can, in theory, be used to trim
- * memory buffers. This part of the code was inherited from
- * liblogging (where it is used in a different context) but
- * never put to use in rsyslog. The reason is that it is hardly
- * imaginable where the extra performance cost is worth the save
- * in memory alloc. Then Anders Blomdel rightfully pointed out that
- * the code does not work at all - and nobody even know that it
- * probably shouldn't. Rather than removing, I deciced to somewhat
- * fix the code, so that this feature may be enabled if somebody
- * really has a need for it. Be warned, however, that I NEVER
- * tested the fix. So if you intend to use this feature, you must
- * do full testing before you rely on it. -- rgerhards, 2008-02-12
- */
-rsRetVal rsCStrFinish(cstr_t __attribute__((unused)) *pThis)
-{
- DEFiRet;
- uchar* pBuf;
- rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
-
- if((pBuf = malloc((pThis->iStrLen) * sizeof(uchar))) == NULL)
- { /* OK, in this case we use the previous buffer. At least
- * we have it ;)
- */
- }
- else
- { /* got the new buffer, so let's use it */
- memcpy(pBuf, pThis->pBuf, pThis->iStrLen);
- pThis->pBuf = pBuf;
- }
-
- RETiRet;
-}
-#endif /* #if STRINGBUF_TRIM_ALLOCSIZE == 1 */
-
-
-void rsCStrSetAllocIncrement(cstr_t *pThis, int iNewIncrement)
-{
- rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
- assert(iNewIncrement > 0);
-
- pThis->iAllocIncrement = iNewIncrement;
-}
-
-
/* return the length of the current string
* 2005-09-09 rgerhards
* Please note: this is only a function in a debug build.
@@ -510,7 +396,7 @@ void rsCStrSetAllocIncrement(cstr_t *pThis, int iNewIncrement)
* This is due to performance reasons.
*/
#ifndef NDEBUG
-int rsCStrLen(cstr_t *pThis)
+int cstrLen(cstr_t *pThis)
{
rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
return(pThis->iStrLen);
@@ -561,6 +447,27 @@ rsRetVal rsCStrTrimTrailingWhiteSpace(cstr_t *pThis)
return RS_RET_OK;
}
+/* Trim trailing whitespace from a given string
+ */
+rsRetVal cstrTrimTrailingWhiteSpace(cstr_t *pThis)
+{
+ register int i;
+ register uchar *pC;
+ rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
+
+ i = pThis->iStrLen;
+ pC = pThis->pBuf + i - 1;
+ while(i > 0 && isspace((int)*pC)) {
+ --pC;
+ --i;
+ }
+ /* i now is the new string length! */
+ pThis->iStrLen = i;
+ pThis->pBuf[pThis->iStrLen] = '0'; /* we always have this space */
+
+ return RS_RET_OK;
+}
+
/* compare two string objects - works like strcmp(), but operates
* on CStr objects. Please note that this version here is
* faster in the majority of cases, simply because it can
@@ -694,6 +601,7 @@ int rsCStrCaseInsensitveStartsWithSzStr(cstr_t *pCS1, uchar *psz, size_t iLenSz)
return -1; /* pCS1 is less then psz */
}
+
/* check if a CStr object matches a regex.
* msamia@redhat.com 2007-07-12
* @return returns 0 if matched
@@ -701,25 +609,54 @@ int rsCStrCaseInsensitveStartsWithSzStr(cstr_t *pCS1, uchar *psz, size_t iLenSz)
* rgerhards, 2007-07-16: bug is no real bug, because rsyslogd ensures there
* never is a \0 *inside* a property string.
* Note that the function returns -1 if regexp functionality is not available.
- * TODO: change calling interface! -- rgerhards, 2008-03-07
+ * rgerhards: 2009-03-04: ERE support added, via parameter iType: 0 - BRE, 1 - ERE
+ * Arnaud Cornet/rgerhards: 2009-04-02: performance improvement by caching compiled regex
+ * If a caller does not need the cached version, it must still provide memory for it
+ * and must call rsCStrRegexDestruct() afterwards.
*/
-int rsCStrSzStrMatchRegex(cstr_t *pCS1, uchar *psz)
+rsRetVal rsCStrSzStrMatchRegex(cstr_t *pCS1, uchar *psz, int iType, void *rc)
{
- regex_t preq;
+ regex_t **cache = (regex_t**) rc;
int ret;
+ DEFiRet;
- BEGINfunc
+ assert(pCS1 != NULL);
+ assert(psz != NULL);
+ assert(cache != NULL);
if(objUse(regexp, LM_REGEXP_FILENAME) == RS_RET_OK) {
- regexp.regcomp(&preq, (char*) rsCStrGetSzStr(pCS1), 0);
- ret = regexp.regexec(&preq, (char*) psz, 0, NULL, 0);
- regexp.regfree(&preq);
+ if (*cache == NULL) {
+ *cache = calloc(sizeof(regex_t), 1);
+ regexp.regcomp(*cache, (char*) rsCStrGetSzStr(pCS1), (iType == 1 ? REG_EXTENDED : 0) | REG_NOSUB);
+ }
+ ret = regexp.regexec(*cache, (char*) psz, 0, NULL, 0);
+ if(ret != 0)
+ ABORT_FINALIZE(RS_RET_NOT_FOUND);
} else {
- ret = 1; /* simulate "not found" */
+ ABORT_FINALIZE(RS_RET_NOT_FOUND);
}
- ENDfunc
- return ret;
+finalize_it:
+ RETiRet;
+}
+
+
+/* free a cached compiled regex
+ * Caller must provide a pointer to a buffer that was created by
+ * rsCStrSzStrMatchRegexCache()
+ */
+void rsCStrRegexDestruct(void *rc)
+{
+ regex_t **cache = rc;
+
+ assert(cache != NULL);
+ assert(*cache != NULL);
+
+ if(objUse(regexp, LM_REGEXP_FILENAME) == RS_RET_OK) {
+ regexp.regfree(*cache);
+ free(*cache);
+ *cache = NULL;
+ }
}
@@ -997,56 +934,6 @@ int rsCStrCaseInsensitiveLocateInSzStr(cstr_t *pThis, uchar *sz)
}
-#if 0 /* read comment below why this is commented out. In short: for future use! */
-/* locate the first occurence of a standard sz string inside a rsCStr object.
- * Returns the offset (0-bound) of this first occurrence. If not found, -1 is
- * returned.
- * rgerhards 2005-09-19
- * WARNING: I accidently created this function (I later noticed I didn't relly
- * need it... I will not remove the function, as it probably is useful
- * some time later. However, it is not fully tested, so start with testing
- * it before you put it to first use).
- */
-int rsCStrLocateSzStr(cstr_t *pThis, uchar *sz)
-{
- int iLenSz;
- int i;
- int iMax;
- int bFound;
- rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
-
- if(sz == NULL)
- return 0;
-
- iLenSz = strlen((char*)sz);
- if(iLenSz == 0)
- return 0;
-
- /* compute the largest index where a match could occur - after all,
- * the to-be-located string must be able to be present in the
- * searched string (it needs its size ;)).
- */
- iMax = pThis->iStrLen - iLenSz;
-
- bFound = 0;
- i = 0;
- while(i < iMax && !bFound) {
- int iCheck;
- uchar *pComp = pThis->pBuf + i;
- for(iCheck = 0 ; iCheck < iLenSz ; ++iCheck)
- if(*(pComp + iCheck) != *(sz + iCheck))
- break;
- if(iCheck == iLenSz)
- bFound = 1; /* found! - else it wouldn't be equal */
- else
- ++i; /* on to the next try */
- }
-
- return(bFound ? i : -1);
-}
-#endif /* end comment out */
-
-
/* our exit function. TODO: remove once converted to a class
* rgerhards, 2008-03-11
*/
@@ -1070,11 +957,5 @@ finalize_it:
}
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * tab-width: 8
- * End:
- * vi:set ai:
+/* vi:set ai:
*/
diff --git a/runtime/stringbuf.h b/runtime/stringbuf.h
index c1966449..c5130238 100644
--- a/runtime/stringbuf.h
+++ b/runtime/stringbuf.h
@@ -1,5 +1,5 @@
-/*! \file stringbuf.h
- * \brief The counted string object
+/* stringbuf.h
+ * The counted string object
*
* This is the byte-counted string class for rsyslog. It is a replacement
* for classical \0 terminated string functions. We introduce it in
@@ -11,8 +11,7 @@
* \date 2005-09-07
* Initial version begun.
*
- * All functions in this "class" start with rsCStr (rsyslog Counted String).
- * Copyright 2005
+ * Copyright 2005-2009
* Rainer Gerhards and Adiscon GmbH. All Rights Reserved.
*
* This file is part of the rsyslog runtime library.
@@ -36,6 +35,8 @@
#ifndef _STRINGBUF_H_INCLUDED__
#define _STRINGBUF_H_INCLUDED__ 1
+#include <assert.h>
+
/**
* The dynamic string buffer object.
*/
@@ -48,14 +49,14 @@ typedef struct cstr_s
uchar *pszBuf; /**< pointer to the sz version of the string (after it has been created )*/
size_t iBufSize; /**< current maximum size of the string buffer */
size_t iStrLen; /**< length of the string in characters. */
- size_t iAllocIncrement; /**< the amount of bytes the string should be expanded if it needs to */
} cstr_t;
/**
* Construct a rsCStr object.
*/
-rsRetVal rsCStrConstruct(cstr_t **ppThis);
+rsRetVal cstrConstruct(cstr_t **ppThis);
+#define rsCStrConstruct(x) cstrConstruct((x))
rsRetVal rsCStrConstructFromszStr(cstr_t **ppThis, uchar *sz);
rsRetVal rsCStrConstructFromCStr(cstr_t **ppThis, cstr_t *pFrom);
@@ -63,14 +64,88 @@ rsRetVal rsCStrConstructFromCStr(cstr_t **ppThis, cstr_t *pFrom);
* Destruct the string buffer object.
*/
void rsCStrDestruct(cstr_t **ppThis);
+#define cstrDestruct(x) rsCStrDestruct((x))
-/**
- * Append a character to an existing string. If necessary, the
- * method expands the string buffer.
- *
- * \param c Character to append to string.
+
+/* Append a character to the current string object. This may only be done until
+ * cstrFinalize() is called.
+ * rgerhards, 2009-06-16
+ */
+rsRetVal rsCStrExtendBuf(cstr_t *pThis, size_t iMinNeeded); /* our helper, NOT a public interface! */
+static inline rsRetVal cstrAppendChar(cstr_t *pThis, uchar c)
+{
+ rsRetVal iRet = RS_RET_OK;
+
+ if(pThis->iStrLen >= pThis->iBufSize) {
+ CHKiRet(rsCStrExtendBuf(pThis, 1)); /* need more memory! */
+ }
+
+ /* ok, when we reach this, we have sufficient memory */
+ *(pThis->pBuf + pThis->iStrLen++) = c;
+
+finalize_it:
+ return iRet;
+}
+
+
+/* some inline functions for things that are really frequently called... */
+
+/* Finalize the string object. This must be called after all data is added to it
+ * but before that data is used.
+ * rgerhards, 2009-06-16
+ */
+static inline rsRetVal
+cstrFinalize(cstr_t *pThis)
+{
+ rsRetVal iRet = RS_RET_OK;
+
+ if(pThis->iStrLen > 0) {
+ /* terminate string only if one exists */
+ CHKiRet(cstrAppendChar(pThis, '\0'));
+ --pThis->iStrLen; /* do NOT count the \0 byte */
+ }
+
+finalize_it:
+ return iRet;
+}
+
+
+/* Returns the cstr data as a classical C sz string. We use that the
+ * Finalizer did properly terminate our string (but we may stil be NULL).
+ * So it is vital that the finalizer is called BEFORe this function here!
+ * The caller must not free or otherwise manipulate the returned string and must not
+ * destroy the CStr object as long as the ascii string is used.
+ * This function may return NULL, if the string is currently NULL. This
+ * is a feature, not a bug. If you need non-NULL in any case, use
+ * cstrGetSzStrNoNULL() instead.
+ * Note that due to the new single-buffer interface this function almost does nothing!
+ * rgerhards, 2006-09-16
*/
-rsRetVal rsCStrAppendChar(cstr_t *pThis, uchar c);
+static inline uchar* cstrGetSzStr(cstr_t *pThis)
+{
+ rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
+ return(pThis->pBuf);
+}
+
+
+/* Converts the CStr object to a classical sz string and returns that.
+ * Same restrictions as in cstrGetSzStr() applies (see there!). This
+ * function here guarantees that a valid string is returned, even if
+ * the CStr object currently holds a NULL pointer string buffer. If so,
+ * "" is returned.
+ * rgerhards 2005-10-19
+ * WARNING: The returned pointer MUST NOT be freed, as it may be
+ * obtained from that constant memory pool (in case of NULL!)
+ */
+static inline uchar* cstrGetSzStrNoNULL(cstr_t *pThis)
+{
+ rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
+ if(pThis->pBuf == NULL)
+ return (uchar*) "";
+ else
+ return cstrGetSzStr(pThis);
+}
+
/**
* Truncate "n" number of characters from the end of the
@@ -81,6 +156,7 @@ rsRetVal rsCStrAppendChar(cstr_t *pThis, uchar c);
rsRetVal rsCStrTruncate(cstr_t *pThis, size_t nTrunc);
rsRetVal rsCStrTrimTrailingWhiteSpace(cstr_t *pThis);
+rsRetVal cstrTrimTrailingWhiteSpace(cstr_t *pThis);
/**
* Append a string to the buffer. For performance reasons,
@@ -98,22 +174,6 @@ rsRetVal rsCStrAppendStr(cstr_t *pThis, uchar* psz);
*/
rsRetVal rsCStrAppendStrWithLen(cstr_t *pThis, uchar* psz, size_t iStrLen);
-/**
- * Set a new allocation incremet. This will influence
- * the allocation the next time the string will be expanded.
- * It can be set and changed at any time. If done immediately
- * after custructing the StrB object, this will also be
- * the inital allocation.
- *
- * \param iNewIncrement The new increment size
- *
- * \note It is possible to use a very low increment, e.g. 1 byte.
- * This can generate a considerable overhead. We highly
- * advise not to use an increment below 32 bytes, except
- * if you are very well aware why you are doing it ;)
- */
-void rsCStrSetAllocIncrement(cstr_t *pThis, int iNewIncrement);
-#define rsCStrGetAllocIncrement(pThis) ((pThis)->iAllocIncrement)
/**
* Append an integer to the string. No special formatting is
@@ -123,10 +183,9 @@ rsRetVal rsCStrAppendInt(cstr_t *pThis, long i);
rsRetVal strExit(void); /* TODO: remove once we have a real object interface! */
-uchar* rsCStrGetSzStr(cstr_t *pThis);
+uchar* __attribute__((deprecated)) rsCStrGetSzStr(cstr_t *pThis);
uchar* rsCStrGetSzStrNoNULL(cstr_t *pThis);
rsRetVal rsCStrSetSzStr(cstr_t *pThis, uchar *pszNew);
-rsRetVal rsCStrConvSzStrAndDestruct(cstr_t *pThis, uchar **ppSz, int bRetNULL);
int rsCStrCStrCmp(cstr_t *pCS1, cstr_t *pCS2);
int rsCStrSzStrCmp(cstr_t *pCS1, uchar *psz, size_t iLenSz);
int rsCStrOffsetSzStrCmp(cstr_t *pCS1, size_t iOffset, uchar *psz, size_t iLenSz);
@@ -136,30 +195,26 @@ int rsCStrCaseInsensitiveLocateInSzStr(cstr_t *pThis, uchar *sz);
int rsCStrStartsWithSzStr(cstr_t *pCS1, uchar *psz, size_t iLenSz);
int rsCStrCaseInsensitveStartsWithSzStr(cstr_t *pCS1, uchar *psz, size_t iLenSz);
int rsCStrSzStrStartsWithCStr(cstr_t *pCS1, uchar *psz, size_t iLenSz);
-int rsCStrSzStrMatchRegex(cstr_t *pCS1, uchar *psz);
+rsRetVal rsCStrSzStrMatchRegex(cstr_t *pCS1, uchar *psz, int iType, void *cache);
+void rsCStrRegexDestruct(void *rc);
rsRetVal rsCStrConvertToNumber(cstr_t *pStr, number_t *pNumber);
rsRetVal rsCStrConvertToBool(cstr_t *pStr, number_t *pBool);
-rsRetVal rsCStrAppendCStr(cstr_t *pThis, cstr_t *pstrAppend);
+
+/* in migration */
+#define rsCStrAppendCStr(pThis, pstrAppend) cstrAppendCStr(pThis, pstrAppend)
+
+/* new calling interface */
+rsRetVal cstrFinalize(cstr_t *pThis);
+rsRetVal cstrConvSzStrAndDestruct(cstr_t *pThis, uchar **ppSz, int bRetNULL);
+rsRetVal cstrAppendCStr(cstr_t *pThis, cstr_t *pstrAppend);
/* now come inline-like functions */
#ifdef NDEBUG
-# define rsCStrLen(x) ((int)((x)->iStrLen))
-#else
- int rsCStrLen(cstr_t *pThis);
-#endif
-
-#if STRINGBUF_TRIM_ALLOCSIZE != 1
-/* This is the normal case (see comment in rsCStrFinish!). In those cases, the function
- * simply needs to do nothing, so that we can save us the function call.
- * rgerhards, 2008-02-12
- */
-# define rsCStrFinish(pThis) RS_RET_OK
+# define cstrLen(x) ((int)((x)->iStrLen))
#else
- /**
- * Finish the string buffer dynamic allocation.
- */
- rsRetVal rsCStrFinish(cstr_t *pThis);
+ int cstrLen(cstr_t *pThis);
#endif
+#define rsCStrLen(s) cstrLen((s))
#define rsCStrGetBufBeg(x) ((x)->pBuf)
diff --git a/runtime/strms_sess.c b/runtime/strms_sess.c
new file mode 100644
index 00000000..0aeebe03
--- /dev/null
+++ b/runtime/strms_sess.c
@@ -0,0 +1,300 @@
+/* strms_sess.c
+ *
+ * This implements a session of the strmsrv object. For general
+ * comments, see header of strmsrv.c.
+ *
+ * Copyright 2007, 2008, 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 <errno.h>
+#include <ctype.h>
+
+#include "rsyslog.h"
+#include "dirty.h"
+#include "module-template.h"
+#include "net.h"
+#include "strmsrv.h"
+#include "strms_sess.h"
+#include "obj.h"
+#include "errmsg.h"
+#include "netstrm.h"
+#include "msg.h"
+#include "datetime.h"
+
+
+/* static data */
+DEFobjStaticHelpers
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(netstrm)
+DEFobjCurrIf(datetime)
+
+static int iMaxLine; /* maximum size of a single message */
+
+/* forward definitions */
+static rsRetVal Close(strms_sess_t *pThis);
+
+
+/* Standard-Constructor */
+BEGINobjConstruct(strms_sess) /* be sure to specify the object type also in END macro! */
+ENDobjConstruct(strms_sess)
+
+
+/* ConstructionFinalizer
+ */
+static rsRetVal
+strms_sessConstructFinalize(strms_sess_t *pThis)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strms_sess);
+ if(pThis->pSrv->OnSessConstructFinalize != NULL) {
+ CHKiRet(pThis->pSrv->OnSessConstructFinalize(&pThis->pUsr));
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* destructor for the strms_sess object */
+BEGINobjDestruct(strms_sess) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDestruct(strms_sess)
+ if(pThis->pStrm != NULL)
+ netstrm.Destruct(&pThis->pStrm);
+
+ if(pThis->pSrv->pOnSessDestruct != NULL) {
+ pThis->pSrv->pOnSessDestruct(&pThis->pUsr);
+ }
+ /* now destruct our own properties */
+ free(pThis->fromHost);
+ free(pThis->fromHostIP);
+ENDobjDestruct(strms_sess)
+
+
+/* debugprint for the strms_sess object */
+BEGINobjDebugPrint(strms_sess) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDebugPrint(strms_sess)
+ENDobjDebugPrint(strms_sess)
+
+
+/* set property functions */
+/* set the hostname. Note that the caller *hands over* the string. That is,
+ * the caller no longer controls it once SetHost() has received it. Most importantly,
+ * the caller must not free it. -- rgerhards, 2008-04-24
+ */
+static rsRetVal
+SetHost(strms_sess_t *pThis, uchar *pszHost)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strms_sess);
+ free(pThis->fromHost);
+ pThis->fromHost = pszHost;
+ RETiRet;
+}
+
+/* set the remote host's IP. Note that the caller *hands over* the string. That is,
+ * the caller no longer controls it once SetHostIP() has received it. Most importantly,
+ * the caller must not free it. -- rgerhards, 2008-05-16
+ */
+static rsRetVal
+SetHostIP(strms_sess_t *pThis, uchar *pszHostIP)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strms_sess);
+ free(pThis->fromHostIP);
+ pThis->fromHostIP = pszHostIP;
+ RETiRet;
+}
+
+static rsRetVal
+SetStrm(strms_sess_t *pThis, netstrm_t *pStrm)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strms_sess);
+ pThis->pStrm = pStrm;
+ RETiRet;
+}
+
+
+/* set our parent, the strmsrv object */
+static rsRetVal
+SetStrmsrv(strms_sess_t *pThis, strmsrv_t *pSrv)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strms_sess);
+ ISOBJ_TYPE_assert(pSrv, strmsrv);
+ pThis->pSrv = pSrv;
+ RETiRet;
+}
+
+
+/* set our parent listener info*/
+static rsRetVal
+SetLstnInfo(strms_sess_t *pThis, strmLstnPortList_t *pLstnInfo)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strms_sess);
+ assert(pLstnInfo != NULL);
+ pThis->pLstnInfo = pLstnInfo;
+ RETiRet;
+}
+
+
+static rsRetVal
+SetUsrP(strms_sess_t *pThis, void *pUsr)
+{
+ DEFiRet;
+ pThis->pUsr = pUsr;
+ RETiRet;
+}
+
+
+static void *
+GetUsrP(strms_sess_t *pThis)
+{
+ return pThis->pUsr;
+}
+
+
+/* Closes a STRM session
+ * No attention is paid to the return code
+ * of close, so potential-double closes are not detected.
+ */
+static rsRetVal
+Close(strms_sess_t *pThis)
+{
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, strms_sess);
+ netstrm.Destruct(&pThis->pStrm);
+ free(pThis->fromHost);
+ pThis->fromHost = NULL; /* not really needed, but... */
+ free(pThis->fromHostIP);
+ pThis->fromHostIP = NULL; /* not really needed, but... */
+
+ RETiRet;
+}
+
+
+
+/* Processes the data received via a STRM 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!). iSTRMSess must be
+ * the index of the STRM 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
+ */
+static rsRetVal
+DataRcvd(strms_sess_t *pThis, char *pData, size_t iLen)
+{
+ DEFiRet;
+ char *pEnd;
+
+ ISOBJ_TYPE_assert(pThis, strms_sess);
+ assert(pData != NULL);
+ assert(iLen > 0);
+
+ /* We now copy the message to the session buffer. */
+ pEnd = pData + iLen; /* this is one off, which is intensional */
+
+ while(pData < pEnd) {
+ CHKiRet(pThis->pSrv->OnCharRcvd(pThis, (uchar)*pData++));
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* queryInterface function
+ * rgerhards, 2008-02-29
+ */
+BEGINobjQueryInterface(strms_sess)
+CODESTARTobjQueryInterface(strms_sess)
+ if(pIf->ifVersion != strms_sessCURR_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->DebugPrint = strms_sessDebugPrint;
+ pIf->Construct = strms_sessConstruct;
+ pIf->ConstructFinalize = strms_sessConstructFinalize;
+ pIf->Destruct = strms_sessDestruct;
+
+ pIf->Close = Close;
+ pIf->DataRcvd = DataRcvd;
+
+ pIf->SetUsrP = SetUsrP;
+ pIf->GetUsrP = GetUsrP;
+ pIf->SetStrmsrv = SetStrmsrv;
+ pIf->SetLstnInfo = SetLstnInfo;
+ pIf->SetHost = SetHost;
+ pIf->SetHostIP = SetHostIP;
+ pIf->SetStrm = SetStrm;
+finalize_it:
+ENDobjQueryInterface(strms_sess)
+
+
+/* exit our class
+ * rgerhards, 2008-03-10
+ */
+BEGINObjClassExit(strms_sess, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */
+CODESTARTObjClassExit(strms_sess)
+ /* release objects we no longer need */
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(netstrm, LM_NETSTRMS_FILENAME);
+ objRelease(datetime, CORE_COMPONENT);
+ENDObjClassExit(strms_sess)
+
+
+/* Initialize our class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-02-29
+ */
+BEGINObjClassInit(strms_sess, 1, OBJ_IS_CORE_MODULE) /* class, version - CHANGE class also in END MACRO! */
+ /* request objects we use */
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(netstrm, LM_NETSTRMS_FILENAME));
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
+
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ iMaxLine = glbl.GetMaxLine(); /* get maximum size we currently support */
+ objRelease(glbl, CORE_COMPONENT);
+
+ /* set our own handlers */
+ OBJSetMethodHandler(objMethod_DEBUGPRINT, strms_sessDebugPrint);
+ OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, strms_sessConstructFinalize);
+ENDObjClassInit(strms_sess)
+
+/* vim:set ai:
+ */
diff --git a/runtime/strms_sess.h b/runtime/strms_sess.h
new file mode 100644
index 00000000..6483c0c3
--- /dev/null
+++ b/runtime/strms_sess.h
@@ -0,0 +1,75 @@
+/* Definitions for strms_sess class. This implements a session of the
+ * generic stream server.
+ *
+ * Copyright 2008, 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_STRMS_SESS_H
+#define INCLUDED_STRMS_SESS_H
+
+#include "obj.h"
+
+/* a forward-definition, we are somewhat cyclic */
+struct strmsrv_s;
+
+/* the strms_sess object */
+struct strms_sess_s {
+ BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
+ strmsrv_t *pSrv; /* pointer back to my server (e.g. for callbacks) */
+ strmLstnPortList_t *pLstnInfo; /* pointer back to listener info */
+ netstrm_t *pStrm;
+// uchar *pMsg; /* message (fragment) received */
+ uchar *fromHost;
+ uchar *fromHostIP;
+ void *pUsr; /* a user-pointer */
+};
+
+
+/* interfaces */
+BEGINinterface(strms_sess) /* name must also be changed in ENDinterface macro! */
+ INTERFACEObjDebugPrint(strms_sess);
+ rsRetVal (*Construct)(strms_sess_t **ppThis);
+ rsRetVal (*ConstructFinalize)(strms_sess_t __attribute__((unused)) *pThis);
+ rsRetVal (*Destruct)(strms_sess_t **ppThis);
+ rsRetVal (*Close)(strms_sess_t *pThis);
+ rsRetVal (*DataRcvd)(strms_sess_t *pThis, char *pData, size_t iLen);
+ /* set methods */
+ rsRetVal (*SetStrmsrv)(strms_sess_t *pThis, struct strmsrv_s *pSrv);
+ rsRetVal (*SetLstnInfo)(strms_sess_t *pThis, strmLstnPortList_t *pLstnInfo);
+ rsRetVal (*SetUsrP)(strms_sess_t*, void*);
+ void* (*GetUsrP)(strms_sess_t*);
+ rsRetVal (*SetHost)(strms_sess_t *pThis, uchar*);
+ rsRetVal (*SetHostIP)(strms_sess_t *pThis, uchar*);
+ rsRetVal (*SetStrm)(strms_sess_t *pThis, netstrm_t*);
+ rsRetVal (*SetOnMsgReceive)(strms_sess_t *pThis, rsRetVal (*OnMsgReceive)(strms_sess_t*, uchar*, int));
+ENDinterface(strms_sess)
+#define strms_sessCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+/* interface changes
+ * to version v2, rgerhards, 2009-05-22
+ * - Data structures changed
+ * - SetLstnInfo entry point added
+ */
+
+
+/* prototypes */
+PROTOTYPEObj(strms_sess);
+
+
+#endif /* #ifndef INCLUDED_STRMS_SESS_H */
diff --git a/runtime/strmsrv.c b/runtime/strmsrv.c
new file mode 100644
index 00000000..8cebf810
--- /dev/null
+++ b/runtime/strmsrv.c
@@ -0,0 +1,968 @@
+/* strmsrv.c
+ *
+ * This builds a basic stream server. It handles connection creation but
+ * not any protocol. Instead, it calls a "data received" entry point of the
+ * caller with any data received, in which case the caller must react accordingly.
+ * This module works together with the netstream drivers.
+ *
+ * There are actually two classes within the stream server code: one is
+ * the strmsrv itself, the other one is its sessions. This is a helper
+ * class to strmsrv.
+ *
+ * File begun on 2009-06-01 by RGerhards based on strmsrv.c. Note that strmsrv is
+ * placed under LGPL, which is possible because I carefully evaluated and
+ * eliminated all those parts of strmsrv which were not written by me.
+ *
+ * TODO: I would consider it useful to migrate tcpsrv.c/tcps_sess.c to this stream
+ * class here. The requires a little bit redesign, but should not be too hard. The
+ * core idea, already begun here, is that we still support lots of callbacks, but
+ * provide "canned" implementations for standard cases. That way, most upper-layer
+ * modules can be kept rather simple and without any extra overhead. Note that
+ * to support this, tcps_sess.c would need to extract the message reception state
+ * machine to a separate module which then is called via the DoCharRcvd() interface
+ * of this class here. -- rgerhards, 2009-06-01
+ *
+ * Copyright 2007, 2008, 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 <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 <sys/types.h>
+#include <sys/socket.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include "rsyslog.h"
+#include "dirty.h"
+#include "cfsysline.h"
+#include "module-template.h"
+#include "net.h"
+#include "srUtils.h"
+#include "conf.h"
+#include "strmsrv.h"
+#include "obj.h"
+#include "glbl.h"
+#include "netstrms.h"
+#include "netstrm.h"
+#include "nssel.h"
+#include "errmsg.h"
+#include "unicode-helper.h"
+
+MODULE_TYPE_LIB
+
+/* defines */
+#define STRMSESS_MAX_DEFAULT 200 /* default for nbr of strm sessions if no number is given */
+#define STRMLSTN_MAX_DEFAULT 20 /* default for nbr of listeners */
+
+/* static data */
+DEFobjStaticHelpers
+DEFobjCurrIf(conf)
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(strms_sess)
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(net)
+DEFobjCurrIf(netstrms)
+DEFobjCurrIf(netstrm)
+DEFobjCurrIf(nssel)
+
+/* forward definitions */
+static rsRetVal create_strm_socket(strmsrv_t *pThis);
+
+/* standard callbacks, if the caller did not provide us with them (this helps keep us
+ * flexible while at the same time permits very simple upper-layer modules)
+ */
+/* this shall go into a specific ACL module! */
+static int
+isPermittedHost(struct sockaddr __attribute__((unused)) *addr, char __attribute__((unused)) *fromHostFQDN,
+ void __attribute__((unused)) *pUsrSrv, void __attribute__((unused)) *pUsrSess)
+{
+ return 1;
+}
+
+
+static rsRetVal
+doOpenLstnSocks(strmsrv_t *pSrv)
+{
+ ISOBJ_TYPE_assert(pSrv, strmsrv);
+ return create_strm_socket(pSrv);
+}
+
+
+static rsRetVal
+doRcvData(strms_sess_t *pSess, char *buf, size_t lenBuf, ssize_t *piLenRcvd)
+{
+ DEFiRet;
+ assert(pSess != NULL);
+ assert(piLenRcvd != NULL);
+
+ *piLenRcvd = lenBuf;
+ CHKiRet(netstrm.Rcv(pSess->pStrm, (uchar*) buf, piLenRcvd));
+finalize_it:
+ RETiRet;
+}
+
+static rsRetVal
+onRegularClose(strms_sess_t *pSess)
+{
+ DEFiRet;
+ assert(pSess != NULL);
+
+ /* process any incomplete frames left over */
+ //strms_sess.PrepareClose(pSess);
+ /* Session closed */
+ strms_sess.Close(pSess);
+ RETiRet;
+}
+
+
+static rsRetVal
+onErrClose(strms_sess_t *pSess)
+{
+ DEFiRet;
+ assert(pSess != NULL);
+
+ strms_sess.Close(pSess);
+ RETiRet;
+}
+
+/* ------------------------------ end callbacks ------------------------------ */
+
+/* add new listener port to listener port list
+ * rgerhards, 2009-05-21
+ */
+static inline rsRetVal
+addNewLstnPort(strmsrv_t *pThis, uchar *pszPort)
+{
+ strmLstnPortList_t *pEntry;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+
+ /* create entry */
+ CHKmalloc(pEntry = malloc(sizeof(strmLstnPortList_t)));
+ pEntry->pszPort = pszPort;
+ pEntry->pSrv = pThis;
+ CHKmalloc(pEntry->pszInputName = ustrdup(pThis->pszInputName));
+
+ /* and add to list */
+ pEntry->pNext = pThis->pLstnPorts;
+ pThis->pLstnPorts = pEntry;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* configure STRM listener settings.
+ * Note: pszPort is handed over to us - the caller MUST NOT free it!
+ * rgerhards, 2008-03-20
+ */
+static rsRetVal
+configureSTRMListen(strmsrv_t *pThis, uchar *pszPort)
+{
+ int i;
+ uchar *pPort = pszPort;
+ DEFiRet;
+
+ assert(pszPort != NULL);
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+
+ /* extract port */
+ i = 0;
+ while(isdigit((int) *pPort)) {
+ i = i * 10 + *pPort++ - '0';
+ }
+
+ if(i >= 0 && i <= 65535) {
+ CHKiRet(addNewLstnPort(pThis, pszPort));
+ } else {
+ errmsg.LogError(0, NO_ERRCODE, "Invalid STRM listen port %s - ignored.\n", pszPort);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Initialize the session table
+ * returns 0 if OK, somewhat else otherwise
+ */
+static rsRetVal
+STRMSessTblInit(strmsrv_t *pThis)
+{
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+ assert(pThis->pSessions == NULL);
+
+ dbgprintf("Allocating buffer for %d STRM sessions.\n", pThis->iSessMax);
+ if((pThis->pSessions = (strms_sess_t **) calloc(pThis->iSessMax, sizeof(strms_sess_t *))) == NULL) {
+ dbgprintf("Error: STRMSessInit() could not alloc memory for STRM session table.\n");
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* find a free spot in the session table. If the table
+ * is full, -1 is returned, else the index of the free
+ * entry (0 or higher).
+ */
+static int
+STRMSessTblFindFreeSpot(strmsrv_t *pThis)
+{
+ register int i;
+
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+
+ for(i = 0 ; i < pThis->iSessMax ; ++i) {
+ if(pThis->pSessions[i] == NULL)
+ break;
+ }
+
+ return((i < pThis->iSessMax) ? i : -1);
+}
+
+
+/* Get the next session index. Free session tables entries are
+ * skipped. This function is provided the index of the last
+ * session entry, or -1 if no previous entry was obtained. It
+ * returns the index of the next session or -1, if there is no
+ * further entry in the table. Please note that the initial call
+ * might as well return -1, if there is no session at all in the
+ * session table.
+ */
+static int
+STRMSessGetNxtSess(strmsrv_t *pThis, int iCurr)
+{
+ register int i;
+
+ BEGINfunc
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+ assert(pThis->pSessions != NULL);
+ for(i = iCurr + 1 ; i < pThis->iSessMax ; ++i) {
+ if(pThis->pSessions[i] != NULL)
+ break;
+ }
+
+ ENDfunc
+ return((i < pThis->iSessMax) ? i : -1);
+}
+
+
+/* De-Initialize STRM listner sockets.
+ * This function deinitializes everything, including freeing the
+ * session table. No STRM listen receive operations are permitted
+ * unless the subsystem is reinitialized.
+ * rgerhards, 2007-06-21
+ */
+static void deinit_strm_listener(strmsrv_t *pThis)
+{
+ int i;
+ strmLstnPortList_t *pEntry;
+ strmLstnPortList_t *pDel;
+
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+
+ if(pThis->pSessions != NULL) {
+ /* close all STRM connections! */
+ i = STRMSessGetNxtSess(pThis, -1);
+ while(i != -1) {
+ strms_sess.Destruct(&pThis->pSessions[i]);
+ /* now get next... */
+ i = STRMSessGetNxtSess(pThis, i);
+ }
+
+ /* we are done with the session table - so get rid of it... */
+ free(pThis->pSessions);
+ pThis->pSessions = NULL; /* just to make sure... */
+ }
+
+ /* free list of strm listen ports */
+ pEntry = pThis->pLstnPorts;
+ while(pEntry != NULL) {
+ free(pEntry->pszPort);
+ free(pEntry->pszInputName);
+ pDel = pEntry;
+ pEntry = pEntry->pNext;
+ free(pDel);
+ }
+
+ /* finally close our listen streams */
+ for(i = 0 ; i < pThis->iLstnMax ; ++i) {
+ netstrm.Destruct(pThis->ppLstn + i);
+ }
+}
+
+
+/* add a listen socket to our listen socket array. This is a callback
+ * invoked from the netstrm class. -- rgerhards, 2008-04-23
+ */
+static rsRetVal
+addStrmLstn(void *pUsr, netstrm_t *pLstn)
+{
+ strmLstnPortList_t *pPortList = (strmLstnPortList_t *) pUsr;
+ strmsrv_t *pThis = pPortList->pSrv;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+ ISOBJ_TYPE_assert(pLstn, netstrm);
+
+ if(pThis->iLstnMax >= STRMLSTN_MAX_DEFAULT)
+ ABORT_FINALIZE(RS_RET_MAX_LSTN_REACHED);
+
+ pThis->ppLstn[pThis->iLstnMax] = pLstn;
+ pThis->ppLstnPort[pThis->iLstnMax] = pPortList;
+ ++pThis->iLstnMax;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Initialize STRM listener socket for a single port
+ * rgerhards, 2009-05-21
+ */
+static inline rsRetVal
+initSTRMListener(strmsrv_t *pThis, strmLstnPortList_t *pPortEntry)
+{
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+ assert(pPortEntry != NULL);
+
+ /* TODO: add capability to specify local listen address! */
+ CHKiRet(netstrm.LstnInit(pThis->pNS, (void*)pPortEntry, addStrmLstn, pPortEntry->pszPort, NULL, pThis->iSessMax));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Initialize STRM sockets (for listener) and listens on them */
+static rsRetVal
+create_strm_socket(strmsrv_t *pThis)
+{
+ strmLstnPortList_t *pEntry;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+
+ /* init all configured ports */
+ pEntry = pThis->pLstnPorts;
+ while(pEntry != NULL) {
+ CHKiRet(initSTRMListener(pThis, pEntry));
+ pEntry = pEntry->pNext;
+ }
+
+ /* OK, we had success. Now it is also time to
+ * initialize our connections
+ */
+ if(STRMSessTblInit(pThis) != 0) {
+ /* OK, we are in some trouble - we could not initialize the
+ * session table, so we can not continue. We need to free all
+ * we have assigned so far, because we can not really use it...
+ */
+ errmsg.LogError(0, RS_RET_ERR, "Could not initialize STRM session table, suspending STRM message reception.");
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Accept new STRM connection; make entry in session table. If there
+ * is no more space left in the connection table, the new STRM
+ * connection is immediately dropped.
+ * ppSess has a pointer to the newly created session, if it succeeds.
+ * If it does not succeed, no session is created and ppSess is
+ * undefined. If the user has provided an OnSessAccept Callback,
+ * this one is executed immediately after creation of the
+ * session object, so that it can do its own initialization.
+ * rgerhards, 2008-03-02
+ */
+static rsRetVal
+SessAccept(strmsrv_t *pThis, strmLstnPortList_t *pLstnInfo, strms_sess_t **ppSess, netstrm_t *pStrm)
+{
+ DEFiRet;
+ strms_sess_t *pSess = NULL;
+ netstrm_t *pNewStrm = NULL;
+ int iSess = -1;
+ struct sockaddr_storage *addr;
+ uchar *fromHostFQDN = NULL;
+ uchar *fromHostIP = NULL;
+
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+ assert(pLstnInfo != NULL);
+
+ CHKiRet(netstrm.AcceptConnReq(pStrm, &pNewStrm));
+
+ /* Add to session list */
+ iSess = STRMSessTblFindFreeSpot(pThis);
+ if(iSess == -1) {
+ errno = 0;
+ errmsg.LogError(0, RS_RET_MAX_SESS_REACHED, "too many strm sessions - dropping incoming request");
+ ABORT_FINALIZE(RS_RET_MAX_SESS_REACHED);
+ }
+
+ if(pThis->bUseKeepAlive) {
+ CHKiRet(netstrm.EnableKeepAlive(pNewStrm));
+ }
+
+ /* we found a free spot and can construct our session object */
+ CHKiRet(strms_sess.Construct(&pSess));
+ CHKiRet(strms_sess.SetStrmsrv(pSess, pThis));
+ CHKiRet(strms_sess.SetLstnInfo(pSess, pLstnInfo));
+
+ /* get the host name */
+ CHKiRet(netstrm.GetRemoteHName(pNewStrm, &fromHostFQDN));
+ CHKiRet(netstrm.GetRemoteIP(pNewStrm, &fromHostIP));
+ CHKiRet(netstrm.GetRemAddr(pNewStrm, &addr));
+ /* TODO: check if we need to strip the domain name here -- rgerhards, 2008-04-24 */
+
+ /* Here we check if a host is permitted to send us messages. If it isn't, we do not further
+ * process the message but log a warning (if we are configured to do this).
+ * rgerhards, 2005-09-26
+ */
+ if(pThis->pIsPermittedHost != NULL
+ && !pThis->pIsPermittedHost((struct sockaddr*) addr, (char*) fromHostFQDN, pThis->pUsr, pSess->pUsr)) {
+ dbgprintf("%s is not an allowed sender\n", fromHostFQDN);
+ if(glbl.GetOption_DisallowWarning()) {
+ errno = 0;
+ errmsg.LogError(0, RS_RET_HOST_NOT_PERMITTED, "STRM message from disallowed sender %s discarded", fromHostFQDN);
+ }
+ ABORT_FINALIZE(RS_RET_HOST_NOT_PERMITTED);
+ }
+
+ /* OK, we have an allowed sender, so let's continue, what
+ * means we can finally fill in the session object.
+ */
+ CHKiRet(strms_sess.SetHost(pSess, fromHostFQDN));
+ fromHostFQDN = NULL; /* we handed this string over */
+ CHKiRet(strms_sess.SetHostIP(pSess, fromHostIP));
+ fromHostIP = NULL; /* we handed this string over */
+ CHKiRet(strms_sess.SetStrm(pSess, pNewStrm));
+ pNewStrm = NULL; /* prevent it from being freed in error handler, now done in strms_sess! */
+ CHKiRet(strms_sess.ConstructFinalize(pSess));
+
+ /* check if we need to call our callback */
+ if(pThis->pOnSessAccept != NULL) {
+ CHKiRet(pThis->pOnSessAccept(pThis, pSess));
+ }
+
+ *ppSess = pSess;
+ pThis->pSessions[iSess] = pSess;
+ pSess = NULL; /* this is now also handed over */
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pSess != NULL)
+ strms_sess.Destruct(&pSess);
+ if(pNewStrm != NULL)
+ netstrm.Destruct(&pNewStrm);
+ free(fromHostFQDN);
+ free(fromHostIP);
+ }
+
+ RETiRet;
+}
+
+
+static void
+RunCancelCleanup(void *arg)
+{
+ nssel_t **ppSel = (nssel_t**) arg;
+
+ if(*ppSel != NULL)
+ nssel.Destruct(ppSel);
+}
+
+
+/* This function is called to gather input. */
+#pragma GCC diagnostic ignored "-Wempty-body"
+static rsRetVal
+Run(strmsrv_t *pThis)
+{
+ DEFiRet;
+ int nfds;
+ int i;
+ int iSTRMSess;
+ int bIsReady;
+ strms_sess_t *pNewSess;
+ nssel_t *pSel;
+ ssize_t iRcvd;
+ rsRetVal localRet;
+
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+
+ /* this is an endless loop - it is terminated by the framework canelling
+ * this thread. Thus, we also need to instantiate a cancel cleanup handler
+ * to prevent us from leaking anything. -- rgerharsd, 20080-04-24
+ */
+ pthread_cleanup_push(RunCancelCleanup, (void*) &pSel);
+ while(1) {
+ CHKiRet(nssel.Construct(&pSel));
+ // TODO: set driver
+ CHKiRet(nssel.ConstructFinalize(pSel));
+
+ /* Add the STRM listen sockets to the list of read descriptors. */
+ for(i = 0 ; i < pThis->iLstnMax ; ++i) {
+ CHKiRet(nssel.Add(pSel, pThis->ppLstn[i], NSDSEL_RD));
+ }
+
+ /* do the sessions */
+ iSTRMSess = STRMSessGetNxtSess(pThis, -1);
+ while(iSTRMSess != -1) {
+ /* TODO: access to pNsd is NOT really CLEAN, use method... */
+ CHKiRet(nssel.Add(pSel, pThis->pSessions[iSTRMSess]->pStrm, NSDSEL_RD));
+ /* now get next... */
+ iSTRMSess = STRMSessGetNxtSess(pThis, iSTRMSess);
+ }
+
+ /* wait for io to become ready */
+ CHKiRet(nssel.Wait(pSel, &nfds));
+
+ for(i = 0 ; i < pThis->iLstnMax ; ++i) {
+ CHKiRet(nssel.IsReady(pSel, pThis->ppLstn[i], NSDSEL_RD, &bIsReady, &nfds));
+ if(bIsReady) {
+ dbgprintf("New connect on NSD %p.\n", pThis->ppLstn[i]);
+ SessAccept(pThis, pThis->ppLstnPort[i], &pNewSess, pThis->ppLstn[i]);
+ --nfds; /* indicate we have processed one */
+ }
+ }
+
+ /* now check the sessions */
+ iSTRMSess = STRMSessGetNxtSess(pThis, -1);
+ while(nfds && iSTRMSess != -1) {
+ CHKiRet(nssel.IsReady(pSel, pThis->pSessions[iSTRMSess]->pStrm, NSDSEL_RD, &bIsReady, &nfds));
+ if(bIsReady) {
+ char buf[8*1024]; /* reception buffer - may hold a partial or multiple messages */
+ dbgprintf("netstream %p with new data\n", pThis->pSessions[iSTRMSess]->pStrm);
+
+ /* Receive message */
+ iRet = pThis->pRcvData(pThis->pSessions[iSTRMSess], buf, sizeof(buf), &iRcvd);
+ switch(iRet) {
+ case RS_RET_CLOSED:
+ pThis->pOnRegularClose(pThis->pSessions[iSTRMSess]);
+ strms_sess.Destruct(&pThis->pSessions[iSTRMSess]);
+ break;
+ case RS_RET_RETRY:
+ /* we simply ignore retry - this is not an error, but we also have not received anything */
+ break;
+ case RS_RET_OK:
+ /* valid data received, process it! */
+ localRet = strms_sess.DataRcvd(pThis->pSessions[iSTRMSess], buf, iRcvd);
+ if(localRet != RS_RET_OK) {
+ /* in this case, something went awfully wrong.
+ * We are instructed to terminate the session.
+ */
+ errmsg.LogError(0, localRet, "Tearing down STRM Session %d - see "
+ "previous messages for reason(s)\n", iSTRMSess);
+ pThis->pOnErrClose(pThis->pSessions[iSTRMSess]);
+ strms_sess.Destruct(&pThis->pSessions[iSTRMSess]);
+ }
+ break;
+ default:
+ errno = 0;
+ errmsg.LogError(0, iRet, "netstream session %p will be closed due to error\n",
+ pThis->pSessions[iSTRMSess]->pStrm);
+ pThis->pOnErrClose(pThis->pSessions[iSTRMSess]);
+ strms_sess.Destruct(&pThis->pSessions[iSTRMSess]);
+ break;
+ }
+ --nfds; /* indicate we have processed one */
+ }
+ iSTRMSess = STRMSessGetNxtSess(pThis, iSTRMSess);
+ }
+ CHKiRet(nssel.Destruct(&pSel));
+finalize_it: /* this is a very special case - this time only we do not exit the function,
+ * because that would not help us either. So we simply retry it. Let's see
+ * if that actually is a better idea. Exiting the loop wasn't we always
+ * crashed, which made sense (the rest of the engine was not prepared for
+ * that) -- rgerhards, 2008-05-19
+ */
+ /*EMPTY*/;
+ }
+
+ /* note that this point is usually not reached */
+ pthread_cleanup_pop(0); /* remove cleanup handler */
+
+ RETiRet;
+}
+#pragma GCC diagnostic warning "-Wempty-body"
+
+
+/* Standard-Constructor */
+BEGINobjConstruct(strmsrv) /* be sure to specify the object type also in END macro! */
+ pThis->iSessMax = STRMSESS_MAX_DEFAULT; /* TODO: useful default ;) */
+ /* set default callbacks (used if caller does not overwrite them) */
+ pThis->pIsPermittedHost = isPermittedHost;
+ pThis->OpenLstnSocks = doOpenLstnSocks;
+ pThis->pRcvData = doRcvData;
+ pThis->pOnRegularClose = onRegularClose;
+ pThis->pOnErrClose = onErrClose;
+ /* session specific callbacks */
+ //pThis->OnSessConstructFinalize =
+ //pThis->pOnSessDestruct =
+ENDobjConstruct(strmsrv)
+
+
+/* ConstructionFinalizer */
+static rsRetVal
+strmsrvConstructFinalize(strmsrv_t *pThis)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+
+ /* prepare network stream subsystem */
+ CHKiRet(netstrms.Construct(&pThis->pNS));
+ CHKiRet(netstrms.SetDrvrMode(pThis->pNS, pThis->iDrvrMode));
+ if(pThis->pszDrvrAuthMode != NULL)
+ CHKiRet(netstrms.SetDrvrAuthMode(pThis->pNS, pThis->pszDrvrAuthMode));
+ if(pThis->pPermPeers != NULL)
+ CHKiRet(netstrms.SetDrvrPermPeers(pThis->pNS, pThis->pPermPeers));
+ // TODO: set driver!
+ CHKiRet(netstrms.ConstructFinalize(pThis->pNS));
+
+ /* set up listeners */
+ CHKmalloc(pThis->ppLstn = calloc(STRMLSTN_MAX_DEFAULT, sizeof(netstrm_t*)));
+ CHKmalloc(pThis->ppLstnPort = calloc(STRMLSTN_MAX_DEFAULT, sizeof(strmLstnPortList_t*)));
+ iRet = pThis->OpenLstnSocks(pThis);
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pThis->pNS != NULL)
+ netstrms.Destruct(&pThis->pNS);
+ }
+ RETiRet;
+}
+
+
+/* destructor for the strmsrv object */
+BEGINobjDestruct(strmsrv) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDestruct(strmsrv)
+ if(pThis->OnDestruct != NULL)
+ pThis->OnDestruct(pThis->pUsr);
+
+ deinit_strm_listener(pThis);
+
+ if(pThis->pNS != NULL)
+ netstrms.Destruct(&pThis->pNS);
+ free(pThis->pszDrvrAuthMode);
+ free(pThis->ppLstn);
+ free(pThis->ppLstnPort);
+ free(pThis->pszInputName);
+ENDobjDestruct(strmsrv)
+
+
+/* debugprint for the strmsrv object */
+BEGINobjDebugPrint(strmsrv) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDebugPrint(strmsrv)
+ENDobjDebugPrint(strmsrv)
+
+/* set functions */
+static rsRetVal
+SetCBIsPermittedHost(strmsrv_t *pThis, int (*pCB)(struct sockaddr *addr, char *fromHostFQDN, void*, void*))
+{
+ DEFiRet;
+ pThis->pIsPermittedHost = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBOnSessAccept(strmsrv_t *pThis, rsRetVal (*pCB)(strmsrv_t*, strms_sess_t*))
+{
+ DEFiRet;
+ pThis->pOnSessAccept = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBOnDestruct(strmsrv_t *pThis, rsRetVal (*pCB)(void*))
+{
+ DEFiRet;
+ pThis->OnDestruct = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBOnSessConstructFinalize(strmsrv_t *pThis, rsRetVal (*pCB)(void*))
+{
+ DEFiRet;
+ pThis->OnSessConstructFinalize = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBOnSessDestruct(strmsrv_t *pThis, rsRetVal (*pCB)(void*))
+{
+ DEFiRet;
+ pThis->pOnSessDestruct = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBOnRegularClose(strmsrv_t *pThis, rsRetVal (*pCB)(strms_sess_t*))
+{
+ DEFiRet;
+ pThis->pOnRegularClose = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBOnErrClose(strmsrv_t *pThis, rsRetVal (*pCB)(strms_sess_t*))
+{
+ DEFiRet;
+ pThis->pOnErrClose = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBOpenLstnSocks(strmsrv_t *pThis, rsRetVal (*pCB)(strmsrv_t*))
+{
+ DEFiRet;
+ pThis->OpenLstnSocks = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetUsrP(strmsrv_t *pThis, void *pUsr)
+{
+ DEFiRet;
+ pThis->pUsr = pUsr;
+ RETiRet;
+}
+
+static rsRetVal
+SetKeepAlive(strmsrv_t *pThis, int iVal)
+{
+ DEFiRet;
+ dbgprintf("keep-alive set to %d\n", iVal);
+ pThis->bUseKeepAlive = iVal;
+ RETiRet;
+}
+
+static rsRetVal
+SetOnCharRcvd(strmsrv_t *pThis, rsRetVal (*OnCharRcvd)(strms_sess_t*, uchar))
+{
+ DEFiRet;
+ assert(OnCharRcvd != NULL);
+ pThis->OnCharRcvd = OnCharRcvd;
+ RETiRet;
+}
+
+/* Set the input name to use -- rgerhards, 2008-12-10 */
+static rsRetVal
+SetInputName(strmsrv_t *pThis, uchar *name)
+{
+ uchar *pszName;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+ if(name == NULL)
+ pszName = NULL;
+ else
+ CHKmalloc(pszName = ustrdup(name));
+ free(pThis->pszInputName);
+ pThis->pszInputName = pszName;
+finalize_it:
+ RETiRet;
+}
+
+
+/* here follows a number of methods that shuffle authentication settings down
+ * to the drivers. Drivers not supporting these settings may return an error
+ * state.
+ * -------------------------------------------------------------------------- */
+
+/* set the driver mode -- rgerhards, 2008-04-30 */
+static rsRetVal
+SetDrvrMode(strmsrv_t *pThis, int iMode)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+ pThis->iDrvrMode = iMode;
+ RETiRet;
+}
+
+
+/* set the driver authentication mode -- rgerhards, 2008-05-19 */
+static rsRetVal
+SetDrvrAuthMode(strmsrv_t *pThis, uchar *mode)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+ CHKmalloc(pThis->pszDrvrAuthMode = ustrdup(mode));
+finalize_it:
+ RETiRet;
+}
+
+
+/* set the driver's permitted peers -- rgerhards, 2008-05-19 */
+static rsRetVal
+SetDrvrPermPeers(strmsrv_t *pThis, permittedPeers_t *pPermPeers)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+ pThis->pPermPeers = pPermPeers;
+ RETiRet;
+}
+
+
+/* End of methods to shuffle autentication settings to the driver.;
+
+ * -------------------------------------------------------------------------- */
+
+
+/* set max number of sessions
+ * this must be called before ConstructFinalize, or it will have no effect!
+ * rgerhards, 2009-04-09
+ */
+static rsRetVal
+SetSessMax(strmsrv_t *pThis, int iMax)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+ pThis->iSessMax = iMax;
+ RETiRet;
+}
+
+
+/* queryInterface function
+ * rgerhards, 2008-02-29
+ */
+BEGINobjQueryInterface(strmsrv)
+CODESTARTobjQueryInterface(strmsrv)
+ if(pIf->ifVersion != strmsrvCURR_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->DebugPrint = strmsrvDebugPrint;
+ pIf->Construct = strmsrvConstruct;
+ pIf->ConstructFinalize = strmsrvConstructFinalize;
+ pIf->Destruct = strmsrvDestruct;
+
+ pIf->configureSTRMListen = configureSTRMListen;
+ pIf->create_strm_socket = create_strm_socket;
+ pIf->Run = Run;
+
+ pIf->SetKeepAlive = SetKeepAlive;
+ pIf->SetUsrP = SetUsrP;
+ pIf->SetInputName = SetInputName;
+ pIf->SetSessMax = SetSessMax;
+ pIf->SetDrvrMode = SetDrvrMode;
+ pIf->SetDrvrAuthMode = SetDrvrAuthMode;
+ pIf->SetDrvrPermPeers = SetDrvrPermPeers;
+ pIf->SetCBIsPermittedHost = SetCBIsPermittedHost;
+ pIf->SetCBOpenLstnSocks = SetCBOpenLstnSocks;
+ pIf->SetCBOnSessAccept = SetCBOnSessAccept;
+ pIf->SetCBOnSessConstructFinalize = SetCBOnSessConstructFinalize;
+ pIf->SetCBOnSessDestruct = SetCBOnSessDestruct;
+ pIf->SetCBOnDestruct = SetCBOnDestruct;
+ pIf->SetCBOnRegularClose = SetCBOnRegularClose;
+ pIf->SetCBOnErrClose = SetCBOnErrClose;
+ pIf->SetOnCharRcvd = SetOnCharRcvd;
+
+finalize_it:
+ENDobjQueryInterface(strmsrv)
+
+
+/* exit our class
+ * rgerhards, 2008-03-10
+ */
+BEGINObjClassExit(strmsrv, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */
+CODESTARTObjClassExit(strmsrv)
+ /* release objects we no longer need */
+ objRelease(strms_sess, DONT_LOAD_LIB);
+ objRelease(conf, CORE_COMPONENT);
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(netstrms, DONT_LOAD_LIB);
+ objRelease(nssel, DONT_LOAD_LIB);
+ objRelease(netstrm, LM_NETSTRMS_FILENAME);
+ objRelease(net, LM_NET_FILENAME);
+ENDObjClassExit(strmsrv)
+
+
+/* Initialize our class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-02-29
+ */
+BEGINObjClassInit(strmsrv, 1, OBJ_IS_LOADABLE_MODULE) /* class, version - CHANGE class also in END MACRO! */
+ /* request objects we use */
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(net, LM_NET_FILENAME));
+ CHKiRet(objUse(netstrms, LM_NETSTRMS_FILENAME));
+ CHKiRet(objUse(netstrm, DONT_LOAD_LIB));
+ CHKiRet(objUse(nssel, DONT_LOAD_LIB));
+ CHKiRet(objUse(strms_sess, DONT_LOAD_LIB));
+ CHKiRet(objUse(conf, CORE_COMPONENT));
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+
+ /* set our own handlers */
+ OBJSetMethodHandler(objMethod_DEBUGPRINT, strmsrvDebugPrint);
+ OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, strmsrvConstructFinalize);
+ENDObjClassInit(strmsrv)
+
+
+/* --------------- here now comes the plumbing that makes as a library module --------------- */
+
+
+BEGINmodExit
+CODESTARTmodExit
+ /* de-init in reverse order! */
+ strmsrvClassExit();
+ strms_sessClassExit();
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_LIB_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+
+ /* Initialize all classes that are in our module - this includes ourselfs */
+ CHKiRet(strms_sessClassInit(pModInfo));
+ CHKiRet(strmsrvClassInit(pModInfo)); /* must be done after strms_sess, as we use it */
+ENDmodInit
+
+/* vim:set ai:
+ */
diff --git a/runtime/strmsrv.h b/runtime/strmsrv.h
new file mode 100644
index 00000000..86e529c3
--- /dev/null
+++ b/runtime/strmsrv.h
@@ -0,0 +1,112 @@
+/* Definitions for strmsrv class.
+ *
+ * Copyright 2008, 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_STRMSRV_H
+#define INCLUDED_STRMSRV_H
+
+#include "obj.h"
+#include "strms_sess.h"
+
+/* list of strm listen ports */
+struct strmLstnPortList_s {
+ uchar *pszPort; /**< the ports the listener shall listen on */
+ uchar *pszInputName; /**< value to be used as input name */
+ strmsrv_t *pSrv; /**< pointer to higher-level server instance */
+ strmLstnPortList_t *pNext; /**< next port or NULL */
+};
+
+
+/* the strmsrv object */
+struct strmsrv_s {
+ BEGINobjInstance; /**< Data to implement generic object - MUST be the first data element! */
+ int bUseKeepAlive; /**< use socket layer KEEPALIVE handling? */
+ netstrms_t *pNS; /**< pointer to network stream subsystem */
+ int iDrvrMode; /**< mode of the stream driver to use */
+ uchar *pszDrvrAuthMode; /**< auth mode of the stream driver to use */
+ uchar *pszInputName; /**< value to be used as input name */
+ permittedPeers_t *pPermPeers;/**< driver's permitted peers */
+ int iLstnMax; /**< max nbr of listeners currently supported */
+ netstrm_t **ppLstn; /**< our netstream listners */
+ strmLstnPortList_t **ppLstnPort; /**< pointer to relevant listen port description */
+ int iSessMax; /**< max number of sessions supported */
+ strmLstnPortList_t *pLstnPorts; /**< head pointer for listen ports */
+ int addtlFrameDelim; /**< additional frame delimiter for plain STRM syslog framing (e.g. to handle NetScreen) */
+ strms_sess_t **pSessions;/**< array of all of our sessions */
+ void *pUsr; /**< a user-settable pointer (provides extensibility for "derived classes")*/
+ /* callbacks */
+ int (*pIsPermittedHost)(struct sockaddr *addr, char *fromHostFQDN, void*pUsrSrv, void*pUsrSess);
+ rsRetVal (*pRcvData)(strms_sess_t*, char*, size_t, ssize_t *);
+ rsRetVal (*OpenLstnSocks)(struct strmsrv_s*);
+ rsRetVal (*pOnListenDeinit)(void*);
+ rsRetVal (*OnDestruct)(void*);
+ rsRetVal (*pOnRegularClose)(strms_sess_t *pSess);
+ rsRetVal (*pOnErrClose)(strms_sess_t *pSess);
+ /* session specific callbacks */
+ rsRetVal (*pOnSessAccept)(strmsrv_t *, strms_sess_t*);
+ rsRetVal (*OnSessConstructFinalize)(void*);
+ rsRetVal (*pOnSessDestruct)(void*);
+ rsRetVal (*OnCharRcvd)(strms_sess_t*, uchar);
+};
+
+
+/* interfaces */
+BEGINinterface(strmsrv) /* name must also be changed in ENDinterface macro! */
+ INTERFACEObjDebugPrint(strmsrv);
+ rsRetVal (*Construct)(strmsrv_t **ppThis);
+ rsRetVal (*ConstructFinalize)(strmsrv_t __attribute__((unused)) *pThis);
+ rsRetVal (*Destruct)(strmsrv_t **ppThis);
+ rsRetVal (*configureSTRMListen)(strmsrv_t*, uchar *pszPort);
+ //rsRetVal (*SessAccept)(strmsrv_t *pThis, strmLstnPortList_t*, strms_sess_t **ppSess, netstrm_t *pStrm);
+ rsRetVal (*create_strm_socket)(strmsrv_t *pThis);
+ rsRetVal (*Run)(strmsrv_t *pThis);
+ /* set methods */
+ rsRetVal (*SetAddtlFrameDelim)(strmsrv_t*, int);
+ rsRetVal (*SetInputName)(strmsrv_t*, uchar*);
+ rsRetVal (*SetKeepAlive)(strmsrv_t*, int);
+ rsRetVal (*SetUsrP)(strmsrv_t*, void*);
+ rsRetVal (*SetCBIsPermittedHost)(strmsrv_t*, int (*) (struct sockaddr *addr, char*, void*, void*));
+ rsRetVal (*SetCBOpenLstnSocks)(strmsrv_t *, rsRetVal (*)(strmsrv_t*));
+ rsRetVal (*SetCBOnDestruct)(strmsrv_t*, rsRetVal (*) (void*));
+ rsRetVal (*SetCBOnRegularClose)(strmsrv_t*, rsRetVal (*) (strms_sess_t*));
+ rsRetVal (*SetCBOnErrClose)(strmsrv_t*, rsRetVal (*) (strms_sess_t*));
+ rsRetVal (*SetDrvrMode)(strmsrv_t *pThis, int iMode);
+ rsRetVal (*SetDrvrAuthMode)(strmsrv_t *pThis, uchar *pszMode);
+ rsRetVal (*SetDrvrPermPeers)(strmsrv_t *pThis, permittedPeers_t*);
+ /* session specifics */
+ rsRetVal (*SetCBOnSessAccept)(strmsrv_t*, rsRetVal (*) (strmsrv_t*, strms_sess_t*));
+ rsRetVal (*SetCBOnSessDestruct)(strmsrv_t*, rsRetVal (*) (void*));
+ rsRetVal (*SetCBOnSessConstructFinalize)(strmsrv_t*, rsRetVal (*) (void*));
+ rsRetVal (*SetSessMax)(strmsrv_t *pThis, int iMaxSess);
+ rsRetVal (*SetOnCharRcvd)(strmsrv_t *pThis, rsRetVal (*OnMsgCharRcvd)(strms_sess_t*, uchar));
+ENDinterface(strmsrv)
+#define strmsrvCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+/* change for v?:
+ */
+
+
+/* prototypes */
+PROTOTYPEObj(strmsrv);
+
+/* the name of our library binary */
+#define LM_STRMSRV_FILENAME "lmstrmsrv"
+
+#endif /* #ifndef INCLUDED_STRMSRV_H */
diff --git a/runtime/syslogd-types.h b/runtime/syslogd-types.h
index be0dfdd8..4a26f993 100644
--- a/runtime/syslogd-types.h
+++ b/runtime/syslogd-types.h
@@ -76,25 +76,31 @@ enum _EHostnameCmpMode {
};
typedef enum _EHostnameCmpMode EHostnameCmpMode;
+/* time type numerical values for structure below */
+#define TIME_TYPE_UNINIT 0
+#define TIME_TYPE_RFC3164 1
+#define TIME_TYPE_RFC5424 2
/* rgerhards 2004-11-11: the following structure represents
* a time as it is used in syslog.
+ * rgerhards, 2009-06-23: packed structure for better cache performance
+ * (but left ultimate decision about packing to compiler)
*/
struct syslogTime {
- int timeType; /* 0 - unitinialized , 1 - RFC 3164, 2 - syslog-protocol */
- int year;
- int month;
- int day;
- int hour; /* 24 hour clock */
- int minute;
- int second;
- int secfrac; /* fractional seconds (must be 32 bit!) */
- int secfracPrecision;
+ intTiny timeType; /* 0 - unitinialized , 1 - RFC 3164, 2 - syslog-protocol */
+ intTiny month;
+ intTiny day;
+ intTiny hour; /* 24 hour clock */
+ intTiny minute;
+ intTiny second;
+ intTiny secfracPrecision;
+ intTiny OffsetMinute; /* UTC offset in minutes */
+ intTiny OffsetHour; /* UTC offset in hours
+ * full UTC offset minutes = OffsetHours*60 + OffsetMinute. Then use
+ * OffsetMode to know the direction.
+ */
char OffsetMode; /* UTC offset + or - */
- char OffsetHour; /* UTC offset in hours */
- int OffsetMinute; /* UTC offset in minutes */
- /* full UTC offset minutes = OffsetHours*60 + OffsetMinute. Then use
- * OffsetMode to know the direction.
- */
+ short year;
+ int secfrac; /* fractional seconds (must be 32 bit!) */
};
typedef struct syslogTime syslogTime_t;
diff --git a/runtime/sysvar.c b/runtime/sysvar.c
index 5eec8f67..4a6ace19 100644
--- a/runtime/sysvar.c
+++ b/runtime/sysvar.c
@@ -84,7 +84,7 @@ getNOW(eNOWType eNow, cstr_t **ppStr)
uchar szBuf[16];
struct syslogTime t;
- datetime.getCurrTime(&t);
+ datetime.getCurrTime(&t, NULL);
switch(eNow) {
case NOW_NOW:
snprintf((char*) szBuf, sizeof(szBuf)/sizeof(uchar), "%4.4d-%2.2d-%2.2d", t.year, t.month, t.day);
@@ -175,8 +175,6 @@ CODESTARTobjQueryInterface(sysvar)
* work here (if we can support an older interface version - that,
* of course, also affects the "if" above).
*/
- //xxxpIf->oID = "sysvar";//OBJsysvar;
-
pIf->Construct = sysvarConstruct;
pIf->ConstructFinalize = sysvarConstructFinalize;
pIf->Destruct = sysvarDestruct;
diff --git a/runtime/unicode-helper.h b/runtime/unicode-helper.h
new file mode 100644
index 00000000..7a776f68
--- /dev/null
+++ b/runtime/unicode-helper.h
@@ -0,0 +1,69 @@
+/* This is the header file for unicode support.
+ *
+ * Currently, this is a dummy module.
+ * The following functions are wrappers which hopefully enable us to move
+ * from 8-bit chars to unicode with relative ease when we finally attack this
+ *
+ * Note: while we prefer inline functions, this leads to invalid references in
+ * core dumps. So in a debug build, we use macros where appropriate...
+ *
+ * Begun 2009-05-21 RGerhards
+ *
+ * Copyright (C) 2009 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.
+ */
+#ifndef INCLUDED_UNICODE_HELPER_H
+#define INCLUDED_UNICODE_HELPER_H
+
+#include <string.h>
+
+#ifdef DEBUG
+# define ustrncpy(psz1, psz2, len) strncpy((char*)(psz1), (char*)(psz2), (len))
+# define ustrdup(psz) (uchar*)strdup((char*)(psz))
+#else
+ static inline uchar* ustrncpy(uchar *psz1, uchar *psz2, size_t len)
+ {
+ return (uchar*) strncpy((char*) psz1, (char*) psz2, len);
+ }
+
+ static inline uchar* ustrdup(uchar *psz)
+ {
+ return (uchar*) strdup((char*)psz);
+ }
+
+#endif /* #ifdef DEBUG */
+
+static inline int ustrcmp(uchar *psz1, uchar *psz2)
+{
+ return strcmp((char*) psz1, (char*) psz2);
+}
+
+static inline int ustrlen(uchar *psz)
+{
+ return strlen((char*) psz);
+}
+
+
+#define UCHAR_CONSTANT(x) ((uchar*) (x))
+#define CHAR_CONVERT(x) ((char*) (x))
+
+#endif /* multi-include protection */
+/* vim:set ai:
+ */
diff --git a/runtime/vm.c b/runtime/vm.c
index f1fbcc2e..aaf3c879 100644
--- a/runtime/vm.c
+++ b/runtime/vm.c
@@ -27,6 +27,7 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
+#include <ctype.h>
#include "rsyslog.h"
#include "obj.h"
@@ -40,6 +41,146 @@ DEFobjCurrIf(vmstk)
DEFobjCurrIf(var)
DEFobjCurrIf(sysvar)
+/* ------------------------------ function registry code and structures ------------------------------ */
+
+/* we maintain a registry of known functions */
+/* currently, this is a singly-linked list, this shall become a binary
+ * tree when we add the real call interface. So far, entries are added
+ * at the root, only.
+ */
+typedef struct s_rsf_entry {
+ cstr_t *pName; /* function name */
+ prsf_t rsf; /* pointer to function code */
+ struct s_rsf_entry *pNext; /* Pointer to next element or NULL */
+} rsf_entry_t;
+rsf_entry_t *funcRegRoot = NULL;
+
+
+/* add a function to the function registry.
+ * The handed-over cstr_t* object must no longer be used by the caller.
+ * A duplicate function name is an error.
+ * rgerhards, 2009-04-06
+ */
+static rsRetVal
+rsfrAddFunction(uchar *szName, prsf_t rsf)
+{
+ rsf_entry_t *pEntry;
+ size_t lenName;
+ DEFiRet;
+
+ assert(szName != NULL);
+ assert(rsf != NULL);
+
+ /* first check if we have a duplicate name, with the current approach this means
+ * we need to go through the whole list.
+ */
+ lenName = strlen((char*)szName);
+ for(pEntry = funcRegRoot ; pEntry != NULL ; pEntry = pEntry->pNext)
+ if(!rsCStrSzStrCmp(pEntry->pName, szName, lenName))
+ ABORT_FINALIZE(RS_RET_DUP_FUNC_NAME);
+
+ /* unique name, so add to head of list */
+ CHKmalloc(pEntry = calloc(1, sizeof(rsf_entry_t)));
+ CHKiRet(rsCStrConstructFromszStr(&pEntry->pName, szName));
+ CHKiRet(cstrFinalize(pEntry->pName));
+ pEntry->rsf = rsf;
+ pEntry->pNext = funcRegRoot;
+ funcRegRoot = pEntry;
+
+finalize_it:
+ if(iRet != RS_RET_OK && iRet != RS_RET_DUP_FUNC_NAME)
+ free(pEntry);
+
+ RETiRet;
+}
+
+
+/* find a function inside the function registry
+ * The caller provides a cstr_t with the function name and receives
+ * a function pointer back. If no function is found, an RS_RET_UNKNW_FUNC
+ * error is returned. So if the function returns with RS_RET_OK, the caller
+ * can savely assume the function pointer is valid.
+ * rgerhards, 2009-04-06
+ */
+static rsRetVal
+findRSFunction(cstr_t *pcsName, prsf_t *prsf)
+{
+ rsf_entry_t *pEntry;
+ rsf_entry_t *pFound;
+ DEFiRet;
+
+ assert(prsf != NULL);
+
+ /* find function by list walkthrough. */
+ pFound = NULL;
+ for(pEntry = funcRegRoot ; pEntry != NULL && pFound == NULL ; pEntry = pEntry->pNext)
+ if(!rsCStrCStrCmp(pEntry->pName, pcsName))
+ pFound = pEntry;
+
+ if(pFound == NULL)
+ ABORT_FINALIZE(RS_RET_UNKNW_FUNC);
+
+ *prsf = pFound->rsf;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* find the name of a RainerScript function whom's function pointer
+ * is known. This function returns the cstr_t object, which MUST NOT
+ * be modified by the caller.
+ * rgerhards, 2009-04-06
+ */
+static rsRetVal
+findRSFunctionName(prsf_t rsf, cstr_t **ppcsName)
+{
+ rsf_entry_t *pEntry;
+ rsf_entry_t *pFound;
+ DEFiRet;
+
+ assert(rsf != NULL);
+ assert(ppcsName != NULL);
+
+ /* find function by list walkthrough. */
+ pFound = NULL;
+ for(pEntry = funcRegRoot ; pEntry != NULL && pFound == NULL ; pEntry = pEntry->pNext)
+ if(pEntry->rsf == rsf)
+ pFound = pEntry;
+
+ if(pFound == NULL)
+ ABORT_FINALIZE(RS_RET_UNKNW_FUNC);
+
+ *ppcsName = pFound->pName;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* free the whole function registry
+ */
+static void
+rsfrRemoveAll(void)
+{
+ rsf_entry_t *pEntry;
+ rsf_entry_t *pEntryDel;
+
+ BEGINfunc
+ pEntry = funcRegRoot;
+ while(pEntry != NULL) {
+ pEntryDel = pEntry;
+ pEntry = pEntry->pNext;
+ cstrDestruct(&pEntryDel->pName);
+ free(pEntryDel);
+ }
+ funcRegRoot = NULL;
+ ENDfunc
+}
+
+
+/* ------------------------------ end function registry code and structures ------------------------------ */
+
/* ------------------------------ instruction set implementation ------------------------------ *
* The following functions implement the VM's instruction set.
@@ -268,6 +409,7 @@ CODESTARTop(STRADD)
vmstk.PopString(pThis->pStk, &operand1);
CHKiRet(rsCStrAppendCStr(operand1->val.pStr, operand2->val.pStr));
+ CHKiRet(cstrFinalize(operand1->val.pStr));
/* we have a result, so let's push it */
vmstk.Push(pThis->pStk, operand1);
@@ -331,10 +473,109 @@ CODESTARTop(PUSHSYSVAR)
finalize_it:
ENDop(PUSHSYSVAR)
+/* The function call operation is only very roughly implemented. While the plumbing
+ * to reach this instruction is fine, the instruction itself currently supports only
+ * functions with a single argument AND with a name that we know.
+ * TODO: later, we can add here the real logic, that involves looking up function
+ * names, loading them dynamically ... and all that...
+ * implementation begun 2009-03-10 by rgerhards
+ */
+BEGINop(FUNC_CALL) /* remember to set the instruction also in the ENDop macro! */
+ var_t *numOperands;
+CODESTARTop(FUNC_CALL)
+ vmstk.PopNumber(pThis->pStk, &numOperands);
+ CHKiRet((*pOp->operand.rsf)(pThis->pStk, numOperands->val.num));
+ var.Destruct(&numOperands); /* no longer needed */
+finalize_it:
+ENDop(FUNC_CALL)
+
/* ------------------------------ end instruction set implementation ------------------------------ */
+/* ------------------------------ begin built-in function implementation ------------------------------ */
+/* note: this shall probably be moved to a separate module, but for the time being we do it directly
+ * in here. This is on our way to get from a dirty to a clean solution via baby steps that are
+ * a bit less dirty each time...
+ *
+ * The advantage of doing it here is that we do not yet need to think about how to handle the
+ * exit case, where we must not unload function modules which functions are still referenced.
+ *
+ * CALLING INTERFACE:
+ * The function must pop its parameters off the stack and pop its result onto
+ * the stack when it is finished. The number of parameters the function was
+ * called with is provided to it. If the argument count is less then what the function
+ * expected, it may handle the situation with defaults (or return an error). If the
+ * argument count is greater than expected, returnung an error is highly
+ * recommended (use RS_RET_INVLD_NBR_ARGUMENTS for these cases).
+ *
+ * All function names are prefixed with "rsf_" (RainerScript Function) to have
+ * a separate "name space".
+ *
+ * rgerhards, 2009-04-06
+ */
+
+
+/* The strlen function, also probably a prototype of how all functions should be
+ * implemented.
+ * rgerhards, 2009-04-06
+ */
+static rsRetVal
+rsf_strlen(vmstk_t *pStk, int numOperands)
+{
+ DEFiRet;
+ var_t *operand1;
+ int iStrlen;
+
+ if(numOperands != 1)
+ ABORT_FINALIZE(RS_RET_INVLD_NBR_ARGUMENTS);
+
+ /* pop args and do operaton (trivial case here...) */
+ vmstk.PopString(pStk, &operand1);
+ iStrlen = strlen((char*) rsCStrGetSzStr(operand1->val.pStr));
+
+ /* Store result and cleanup */
+ var.SetNumber(operand1, iStrlen);
+ vmstk.Push(pStk, operand1);
+finalize_it:
+ RETiRet;
+}
+
+
+/* The "tolower" function, which converts its sole argument to lower case.
+ * Quite honestly, currently this is primarily a test driver for me...
+ * rgerhards, 2009-04-06
+ */
+static rsRetVal
+rsf_tolower(vmstk_t *pStk, int numOperands)
+{
+ DEFiRet;
+ var_t *operand1;
+ uchar *pSrc;
+ cstr_t *pcstr;
+ int iStrlen;
+
+ if(numOperands != 1)
+ ABORT_FINALIZE(RS_RET_INVLD_NBR_ARGUMENTS);
+
+ /* pop args and do operaton */
+ CHKiRet(cstrConstruct(&pcstr));
+ vmstk.PopString(pStk, &operand1);
+ pSrc = cstrGetSzStr(operand1->val.pStr);
+ iStrlen = strlen((char*)pSrc); // TODO: use count from string!
+ while(iStrlen--) {
+ CHKiRet(cstrAppendChar(pcstr, tolower(*pSrc++)));
+ }
+
+ /* Store result and cleanup */
+ CHKiRet(cstrFinalize(pcstr));
+ var.SetString(operand1, pcstr);
+ vmstk.Push(pStk, operand1);
+finalize_it:
+ RETiRet;
+}
+
+
/* Standard-Constructor
*/
BEGINobjConstruct(vm) /* be sure to specify the object type also in END macro! */
@@ -413,6 +654,7 @@ execProg(vm_t *pThis, vmprg_t *pProg)
doOP(DIV);
doOP(MOD);
doOP(UNARY_MINUS);
+ doOP(FUNC_CALL);
default:
ABORT_FINALIZE(RS_RET_INVALID_VMOP);
dbgoprint((obj_t*) pThis, "invalid instruction %d in vmprg\n", pCurrOp->opcode);
@@ -503,10 +745,23 @@ CODESTARTobjQueryInterface(vm)
pIf->PopBoolFromStack = PopBoolFromStack;
pIf->PopVarFromStack = PopVarFromStack;
pIf->SetMsg = SetMsg;
+ pIf->FindRSFunction = findRSFunction;
+ pIf->FindRSFunctionName = findRSFunctionName;
finalize_it:
ENDobjQueryInterface(vm)
+/* Exit the vm class.
+ * rgerhards, 2009-04-06
+ */
+BEGINObjClassExit(vm, OBJ_IS_CORE_MODULE) /* class, version */
+ rsfrRemoveAll();
+ objRelease(sysvar, CORE_COMPONENT);
+ objRelease(var, CORE_COMPONENT);
+ objRelease(vmstk, CORE_COMPONENT);
+ENDObjClassExit(vm)
+
+
/* Initialize the vm class. Must be called as the very first method
* before anything else is called inside this class.
* rgerhards, 2008-02-19
@@ -520,6 +775,11 @@ BEGINObjClassInit(vm, 1, OBJ_IS_CORE_MODULE) /* class, version */
/* set our own handlers */
OBJSetMethodHandler(objMethod_DEBUGPRINT, vmDebugPrint);
OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, vmConstructFinalize);
+
+ /* register built-in functions // TODO: move to its own module */
+ CHKiRet(rsfrAddFunction((uchar*)"strlen", rsf_strlen));
+ CHKiRet(rsfrAddFunction((uchar*)"tolower", rsf_tolower));
+
ENDObjClassInit(vm)
/* vi:set ai:
diff --git a/runtime/vm.h b/runtime/vm.h
index d2458220..cb3c69d0 100644
--- a/runtime/vm.h
+++ b/runtime/vm.h
@@ -55,8 +55,11 @@ BEGINinterface(vm) /* name must also be changed in ENDinterface macro! */
rsRetVal (*PopBoolFromStack)(vm_t *pThis, var_t **ppVar); /* there are a few cases where we need this... */
rsRetVal (*PopVarFromStack)(vm_t *pThis, var_t **ppVar); /* there are a few cases where we need this... */
rsRetVal (*SetMsg)(vm_t *pThis, msg_t *pMsg); /* there are a few cases where we need this... */
+ /* v2 (4.1.7) */
+ rsRetVal (*FindRSFunction)(cstr_t *pcsName, prsf_t *prsf); /* 2009-06-04 */
+ rsRetVal (*FindRSFunctionName)(prsf_t rsf, cstr_t **ppcsName); /* 2009-06-04 */
ENDinterface(vm)
-#define vmCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+#define vmCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */
/* prototypes */
diff --git a/runtime/vmop.c b/runtime/vmop.c
index fcacb15b..ea627220 100644
--- a/runtime/vmop.c
+++ b/runtime/vmop.c
@@ -32,10 +32,12 @@
#include "rsyslog.h"
#include "obj.h"
#include "vmop.h"
+#include "vm.h"
/* static data */
DEFobjStaticHelpers
DEFobjCurrIf(var)
+DEFobjCurrIf(vm)
/* forward definitions */
@@ -61,9 +63,7 @@ rsRetVal vmopConstructFinalize(vmop_t __attribute__((unused)) *pThis)
/* destructor for the vmop object */
BEGINobjDestruct(vmop) /* be sure to specify the object type also in END and CODESTART macros! */
CODESTARTobjDestruct(vmop)
- if( pThis->opcode == opcode_PUSHSYSVAR
- || pThis->opcode == opcode_PUSHMSGVAR
- || pThis->opcode == opcode_PUSHCONSTANT) {
+ if(pThis->opcode != opcode_FUNC_CALL) {
if(pThis->operand.pVar != NULL)
var.Destruct(&pThis->operand.pVar);
}
@@ -73,12 +73,23 @@ ENDobjDestruct(vmop)
/* DebugPrint support for the vmop object */
BEGINobjDebugPrint(vmop) /* be sure to specify the object type also in END and CODESTART macros! */
uchar *pOpcodeName;
+ cstr_t *pStrVar;
CODESTARTobjDebugPrint(vmop)
vmopOpcode2Str(pThis, &pOpcodeName);
- dbgoprint((obj_t*) pThis, "opcode: %d\t(%s), next %p, var in next line\n", (int) pThis->opcode, pOpcodeName,
- pThis->pNext);
- if(pThis->operand.pVar != NULL)
- var.DebugPrint(pThis->operand.pVar);
+ if(pThis->opcode == opcode_FUNC_CALL) {
+ CHKiRet(vm.FindRSFunctionName(pThis->operand.rsf, &pStrVar));
+ assert(pStrVar != NULL);
+ } else {
+ CHKiRet(rsCStrConstruct(&pStrVar));
+ if(pThis->operand.pVar != NULL) {
+ CHKiRet(var.Obj2Str(pThis->operand.pVar, pStrVar));
+ }
+ }
+ CHKiRet(cstrFinalize(pStrVar));
+ dbgoprint((obj_t*) pThis, "%.12s\t%s\n", pOpcodeName, rsCStrGetSzStrNoNULL(pStrVar));
+ if(pThis->opcode != opcode_FUNC_CALL)
+ rsCStrDestruct(&pStrVar);
+finalize_it:
ENDobjDebugPrint(vmop)
@@ -97,6 +108,7 @@ static rsRetVal
Obj2Str(vmop_t *pThis, cstr_t *pstrPrg)
{
uchar *pOpcodeName;
+ cstr_t *pcsFuncName;
uchar szBuf[2048];
size_t lenBuf;
DEFiRet;
@@ -106,15 +118,37 @@ Obj2Str(vmop_t *pThis, cstr_t *pstrPrg)
vmopOpcode2Str(pThis, &pOpcodeName);
lenBuf = snprintf((char*) szBuf, sizeof(szBuf), "%s\t", pOpcodeName);
CHKiRet(rsCStrAppendStrWithLen(pstrPrg, szBuf, lenBuf));
- if(pThis->operand.pVar != NULL)
- CHKiRet(var.Obj2Str(pThis->operand.pVar, pstrPrg));
- CHKiRet(rsCStrAppendChar(pstrPrg, '\n'));
+ if(pThis->opcode == opcode_FUNC_CALL) {
+ CHKiRet(vm.FindRSFunctionName(pThis->operand.rsf, &pcsFuncName));
+ CHKiRet(rsCStrAppendCStr(pstrPrg, pcsFuncName));
+ } else {
+ if(pThis->operand.pVar != NULL)
+ CHKiRet(var.Obj2Str(pThis->operand.pVar, pstrPrg));
+ }
+ CHKiRet(cstrAppendChar(pstrPrg, '\n'));
finalize_it:
RETiRet;
}
+/* set function
+ * rgerhards, 2009-04-06
+ */
+static rsRetVal
+vmopSetFunc(vmop_t *pThis, cstr_t *pcsFuncName)
+{
+ prsf_t rsf; /* pointer to function */
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, vmop);
+ CHKiRet(vm.FindRSFunction(pcsFuncName, &rsf)); /* check if function exists and obtain pointer to it */
+ assert(rsf != NULL); /* just double-check, would be very hard to find! */
+ pThis->operand.rsf = rsf;
+finalize_it:
+ RETiRet;
+}
+
+
/* set operand (variant case)
* rgerhards, 2008-02-20
*/
@@ -158,37 +192,37 @@ vmopOpcode2Str(vmop_t *pThis, uchar **ppName)
*ppName = (uchar*) "and";
break;
case opcode_PLUS:
- *ppName = (uchar*) "+";
+ *ppName = (uchar*) "add";
break;
case opcode_MINUS:
- *ppName = (uchar*) "-";
+ *ppName = (uchar*) "sub";
break;
case opcode_TIMES:
- *ppName = (uchar*) "*";
+ *ppName = (uchar*) "mul";
break;
case opcode_DIV:
- *ppName = (uchar*) "/";
+ *ppName = (uchar*) "div";
break;
case opcode_MOD:
- *ppName = (uchar*) "%";
+ *ppName = (uchar*) "mod";
break;
case opcode_NOT:
*ppName = (uchar*) "not";
break;
case opcode_CMP_EQ:
- *ppName = (uchar*) "==";
+ *ppName = (uchar*) "cmp_==";
break;
case opcode_CMP_NEQ:
- *ppName = (uchar*) "!=";
+ *ppName = (uchar*) "cmp_!=";
break;
case opcode_CMP_LT:
- *ppName = (uchar*) "<";
+ *ppName = (uchar*) "cmp_<";
break;
case opcode_CMP_GT:
- *ppName = (uchar*) ">";
+ *ppName = (uchar*) "cmp_>";
break;
case opcode_CMP_LTEQ:
- *ppName = (uchar*) "<=";
+ *ppName = (uchar*) "cmp_<=";
break;
case opcode_CMP_CONTAINS:
*ppName = (uchar*) "contains";
@@ -197,28 +231,31 @@ vmopOpcode2Str(vmop_t *pThis, uchar **ppName)
*ppName = (uchar*) "startswith";
break;
case opcode_CMP_GTEQ:
- *ppName = (uchar*) ">=";
+ *ppName = (uchar*) "cmp_>=";
break;
case opcode_PUSHSYSVAR:
- *ppName = (uchar*) "PUSHSYSVAR";
+ *ppName = (uchar*) "push_sysvar";
break;
case opcode_PUSHMSGVAR:
- *ppName = (uchar*) "PUSHMSGVAR";
+ *ppName = (uchar*) "push_msgvar";
break;
case opcode_PUSHCONSTANT:
- *ppName = (uchar*) "PUSHCONSTANT";
+ *ppName = (uchar*) "push_const";
break;
case opcode_POP:
- *ppName = (uchar*) "POP";
+ *ppName = (uchar*) "pop";
break;
case opcode_UNARY_MINUS:
- *ppName = (uchar*) "UNARY_MINUS";
+ *ppName = (uchar*) "unary_minus";
break;
case opcode_STRADD:
- *ppName = (uchar*) "STRADD";
+ *ppName = (uchar*) "strconcat";
+ break;
+ case opcode_FUNC_CALL:
+ *ppName = (uchar*) "func_call";
break;
default:
- *ppName = (uchar*) "INVALID opcode";
+ *ppName = (uchar*) "!invalid_opcode!";
break;
}
@@ -244,6 +281,7 @@ CODESTARTobjQueryInterface(vmop)
pIf->ConstructFinalize = vmopConstructFinalize;
pIf->Destruct = vmopDestruct;
pIf->DebugPrint = vmopDebugPrint;
+ pIf->SetFunc = vmopSetFunc;
pIf->SetOpcode = vmopSetOpcode;
pIf->SetVar = vmopSetVar;
pIf->Opcode2Str = vmopOpcode2Str;
@@ -259,6 +297,7 @@ ENDobjQueryInterface(vmop)
BEGINObjClassInit(vmop, 1, OBJ_IS_CORE_MODULE) /* class, version */
/* request objects we use */
CHKiRet(objUse(var, CORE_COMPONENT));
+ CHKiRet(objUse(vm, CORE_COMPONENT));
OBJSetMethodHandler(objMethod_DEBUGPRINT, vmopDebugPrint);
OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, vmopConstructFinalize);
diff --git a/runtime/vmop.h b/runtime/vmop.h
index c3d5d5f4..67048c26 100644
--- a/runtime/vmop.h
+++ b/runtime/vmop.h
@@ -26,6 +26,7 @@
#define INCLUDED_VMOP_H
#include "ctok_token.h"
+#include "vmstk.h"
#include "stringbuf.h"
/* machine instructions types */
@@ -59,17 +60,45 @@ typedef enum { /* do NOT start at 0 to detect uninitialized types after calloc(
opcode_PUSHMSGVAR = 1002, /* requires var operand */
opcode_PUSHCONSTANT = 1003, /* requires var operand */
opcode_UNARY_MINUS = 1010,
- opcode_END_PROG = 1011
+ opcode_FUNC_CALL = 1012,
+ opcode_END_PROG = 2000
} opcode_t;
+/* Additional doc, operation specific
+
+ FUNC_CALL
+ All parameter passing is via the stack. Parameters are placed onto the stack in reverse order,
+ that means the last parameter is on top of the stack, the first at the bottom location.
+ At the actual top of the stack is the number of parameters. This permits functions to be
+ called with variable number of arguments. The function itself is responsible for poping
+ the right number of parameters of the stack and complaining if the number is incorrect.
+ On exit, a single return value must be pushed onto the stack. The FUNC_CALL operation
+ is generic. Its pVar argument contains the function name string (TODO: very slow, make
+ faster in later releases).
+
+ Sample Function call: sampleFunc(p1, p2, p3) ; returns number 4711 (sample)
+ Stacklayout on entry (order is top to bottom):
+ 3
+ p3
+ p2
+ p1
+ ... other vars ...
+
+ Stack on exit
+ 4711
+ ... other vars ...
+
+ */
+
+
/* the vmop object */
typedef struct vmop_s {
BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
opcode_t opcode;
union {
var_t *pVar;
- /* TODO: add function pointer */
+ prsf_t rsf; /* pointer to function for "call" instruction */
} operand;
struct vmop_s *pNext; /* next operation or NULL, if end of program (logically this belongs to vmprg) */
} vmop_t;
@@ -85,8 +114,13 @@ BEGINinterface(vmop) /* name must also be changed in ENDinterface macro! */
rsRetVal (*SetVar)(vmop_t *pThis, var_t *pVar);
rsRetVal (*Opcode2Str)(vmop_t *pThis, uchar **ppName);
rsRetVal (*Obj2Str)(vmop_t *pThis, cstr_t *pstr);
+ /* v2 */
+ rsRetVal (*SetFunc)(vmop_t *pThis, cstr_t *pcsFuncName);
ENDinterface(vmop)
-#define vmopCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+#define vmopCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */
+/* interface changes, v1 -> v2
+ * added SetFuct after existing function pointers -- rgerhards, 2009-04-06
+ */
/* the remaining prototypes */
PROTOTYPEObj(vmop);
diff --git a/runtime/vmprg.c b/runtime/vmprg.c
index 705e6948..07757b98 100644
--- a/runtime/vmprg.c
+++ b/runtime/vmprg.c
@@ -74,7 +74,7 @@ ENDobjDestruct(vmprg)
BEGINobjDebugPrint(vmprg) /* be sure to specify the object type also in END and CODESTART macros! */
vmop_t *pOp;
CODESTARTobjDebugPrint(vmprg)
- dbgoprint((obj_t*) pThis, "program contents:\n");
+ dbgoprint((obj_t*) pThis, "VM Program:\n");
for(pOp = pThis->vmopRoot ; pOp != NULL ; pOp = pOp->pNext) {
vmop.DebugPrint(pOp);
}
@@ -155,7 +155,6 @@ vmprgAddVarOperation(vmprg_t *pThis, opcode_t opcode, var_t *pVar)
/* construct and fill vmop */
CHKiRet(vmop.Construct(&pOp));
CHKiRet(vmop.ConstructFinalize(pOp));
- CHKiRet(vmop.ConstructFinalize(pOp));
CHKiRet(vmop.SetOpcode(pOp, opcode));
if(pVar != NULL)
CHKiRet(vmop.SetVar(pOp, pVar));
@@ -168,6 +167,32 @@ finalize_it:
}
+/* this is another shortcut for high-level callers. It is similar to vmprgAddVarOperation
+ * but adds a call operation. Among others, this include a check if the function
+ * is known.
+ */
+static rsRetVal
+vmprgAddCallOperation(vmprg_t *pThis, cstr_t *pcsName)
+{
+ DEFiRet;
+ vmop_t *pOp;
+
+ ISOBJ_TYPE_assert(pThis, vmprg);
+
+ /* construct and fill vmop */
+ CHKiRet(vmop.Construct(&pOp));
+ CHKiRet(vmop.ConstructFinalize(pOp));
+ CHKiRet(vmop.SetFunc(pOp, pcsName));
+ CHKiRet(vmop.SetOpcode(pOp, opcode_FUNC_CALL));
+
+ /* and add it to the program */
+ CHKiRet(vmprgAddOperation(pThis, pOp));
+
+finalize_it:
+ RETiRet;
+}
+
+
/* queryInterface function
* rgerhards, 2008-02-21
*/
@@ -189,6 +214,7 @@ CODESTARTobjQueryInterface(vmprg)
pIf->Obj2Str = Obj2Str;
pIf->AddOperation = vmprgAddOperation;
pIf->AddVarOperation = vmprgAddVarOperation;
+ pIf->AddCallOperation = vmprgAddCallOperation;
finalize_it:
ENDobjQueryInterface(vmprg)
diff --git a/runtime/vmprg.h b/runtime/vmprg.h
index c1042f7d..66f03913 100644
--- a/runtime/vmprg.h
+++ b/runtime/vmprg.h
@@ -57,8 +57,10 @@ BEGINinterface(vmprg) /* name must also be changed in ENDinterface macro! */
rsRetVal (*AddOperation)(vmprg_t *pThis, vmop_t *pOp);
rsRetVal (*AddVarOperation)(vmprg_t *pThis, opcode_t opcode, var_t *pVar);
rsRetVal (*Obj2Str)(vmprg_t *pThis, cstr_t *pstr);
+ /* v2 (4.1.7) */
+ rsRetVal (*AddCallOperation)(vmprg_t *pThis, cstr_t *pVar); /* added 2009-04-06 */
ENDinterface(vmprg)
-#define vmprgCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+#define vmprgCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */
/* prototypes */
diff --git a/runtime/vmstk.h b/runtime/vmstk.h
index 2d45ee4d..06657cf4 100644
--- a/runtime/vmstk.h
+++ b/runtime/vmstk.h
@@ -27,11 +27,11 @@
#define VMSTK_SIZE 256
/* the vmstk object */
-typedef struct vmstk_s {
+struct vmstk_s {
BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
var_t *vStk[VMSTK_SIZE];/* the actual stack */
int iStkPtr; /* stack pointer, points to next free location, grows from 0 --> topend */
-} vmstk_t;
+};
/* interfaces */
diff --git a/runtime/wti.c b/runtime/wti.c
index 343a7227..abdf4add 100644
--- a/runtime/wti.c
+++ b/runtime/wti.c
@@ -39,15 +39,22 @@
#include <pthread.h>
#include <errno.h>
+#ifdef OS_SOLARIS
+# include <sched.h>
+#endif
+
#include "rsyslog.h"
#include "stringbuf.h"
#include "srUtils.h"
#include "wtp.h"
#include "wti.h"
#include "obj.h"
+#include "glbl.h"
+#include "atomic.h"
/* static data */
DEFobjStaticHelpers
+DEFobjCurrIf(glbl)
/* forward-definitions */
@@ -99,6 +106,7 @@ rsRetVal
wtiSetState(wti_t *pThis, qWrkCmd_t tCmd, int bActiveOnly, int bLockMutex)
{
DEFiRet;
+ qWrkCmd_t tCurrCmd;
DEFVARS_mutexProtection;
ISOBJ_TYPE_assert(pThis, wti);
@@ -106,13 +114,14 @@ wtiSetState(wti_t *pThis, qWrkCmd_t tCmd, int bActiveOnly, int bLockMutex)
BEGIN_MTX_PROTECTED_OPERATIONS(&pThis->mut, bLockMutex);
+ tCurrCmd = pThis->tCurrCmd;
/* all worker states must be followed sequentially, only termination can be set in any state */
- if( (bActiveOnly && (pThis->tCurrCmd < eWRKTHRD_RUN_CREATED))
- || (pThis->tCurrCmd > tCmd && !(tCmd == eWRKTHRD_TERMINATING || tCmd == eWRKTHRD_STOPPED))) {
- dbgprintf("%s: command %d can not be accepted in current %d processing state - ignored\n",
- wtiGetDbgHdr(pThis), tCmd, pThis->tCurrCmd);
+ if( (bActiveOnly && (tCurrCmd < eWRKTHRD_RUN_CREATED))
+ || (tCurrCmd > tCmd && !(tCmd == eWRKTHRD_TERMINATING || tCmd == eWRKTHRD_STOPPED))) {
+ DBGPRINTF("%s: command %d can not be accepted in current %d processing state - ignored\n",
+ wtiGetDbgHdr(pThis), tCmd, tCurrCmd);
} else {
- dbgprintf("%s: receiving command %d\n", wtiGetDbgHdr(pThis), tCmd);
+ DBGPRINTF("%s: receiving command %d\n", wtiGetDbgHdr(pThis), tCmd);
/* we could replace this with a simple if, but we leave the switch in in case we need
* to add something at a later stage. -- rgerhards, 2008-09-30
*/
@@ -136,7 +145,12 @@ wtiSetState(wti_t *pThis, qWrkCmd_t tCmd, int bActiveOnly, int bLockMutex)
/* DO NOTHING */
break;
}
- pThis->tCurrCmd = tCmd; /* apply the new state */
+ /* apply the new state */
+ unsigned val = ATOMIC_CAS_VAL(pThis->tCurrCmd, tCurrCmd, tCmd);
+ if(val != tCurrCmd) {
+ DBGPRINTF("wtiSetState PROBLEM, tCurrCmd %d overwritten with %d, wanted to set %d\n", tCurrCmd, val, tCmd);
+ }
+
}
END_MTX_PROTECTED_OPERATIONS(&pThis->mut);
@@ -144,7 +158,7 @@ wtiSetState(wti_t *pThis, qWrkCmd_t tCmd, int bActiveOnly, int bLockMutex)
}
-/* Cancel the thread. If the thread is already cancelled or termination,
+/* Cancel the thread. If the thread is already cancelled or terminated,
* we do not again cancel it. But it is save and legal to call wtiCancelThrd() in
* such situations.
* rgerhards, 2008-02-26
@@ -158,11 +172,13 @@ wtiCancelThrd(wti_t *pThis)
d_pthread_mutex_lock(&pThis->mut);
+ wtiProcessThrdChanges(pThis, MUTEX_ALREADY_LOCKED); /* process state change, so that we have current state vars */
+
if(pThis->tCurrCmd >= eWRKTHRD_TERMINATING) {
- dbgoprint((obj_t*) pThis, "canceling worker thread\n");
+ dbgoprint((obj_t*) pThis, "canceling worker thread, curr stat %d\n", pThis->tCurrCmd);
pthread_cancel(pThis->thrdID);
wtiSetState(pThis, eWRKTHRD_TERMINATING, 0, MUTEX_ALREADY_LOCKED);
- pThis->pWtp->bThrdStateChanged = 1; /* indicate change, so harverster will be called */
+ ATOMIC_STORE_1_TO_INT(pThis->pWtp->bThrdStateChanged); /* indicate change, so harverster will be called */
}
d_pthread_mutex_unlock(&pThis->mut);
@@ -194,8 +210,7 @@ CODESTARTobjDestruct(wti)
pthread_cond_destroy(&pThis->condExitDone);
pthread_mutex_destroy(&pThis->mut);
- if(pThis->pszDbgHdr != NULL)
- free(pThis->pszDbgHdr);
+ free(pThis->pszDbgHdr);
ENDobjDestruct(wti)
@@ -303,7 +318,7 @@ wtiWorkerCancelCleanup(void *arg)
pWtp = pThis->pWtp;
ISOBJ_TYPE_assert(pWtp, wtp);
- dbgprintf("%s: cancelation cleanup handler called.\n", wtiGetDbgHdr(pThis));
+ DBGPRINTF("%s: cancelation cleanup handler called.\n", wtiGetDbgHdr(pThis));
/* call user supplied handler (that one e.g. requeues the element) */
pWtp->pfOnWorkerCancel(pThis->pWtp->pUsr, pThis->pUsrp);
@@ -312,7 +327,7 @@ wtiWorkerCancelCleanup(void *arg)
d_pthread_mutex_lock(&pWtp->mut);
wtiSetState(pThis, eWRKTHRD_TERMINATING, 0, MUTEX_ALREADY_LOCKED);
/* TODO: sync access? I currently think it is NOT needed -- rgerhards, 2008-01-28 */
- pWtp->bThrdStateChanged = 1; /* indicate change, so harverster will be called */
+ ATOMIC_STORE_1_TO_INT(pWtp->bThrdStateChanged); /* indicate change, so harverster will be called */
d_pthread_mutex_unlock(&pWtp->mut);
pthread_setcancelstate(iCancelStateSave, NULL);
@@ -321,27 +336,6 @@ wtiWorkerCancelCleanup(void *arg)
/* generic worker thread framework
- *
- * Some special comments below, so that they do not clutter the main function code:
- *
- * On the use of pthread_testcancel():
- * Now make sure we can get canceled - it is not specified if pthread_setcancelstate() is
- * a cancellation point in itself. As we run most of the time without cancel enabled, I fear
- * we may never get cancelled if we do not create a cancellation point ourselfs.
- *
- * On the use of pthread_yield():
- * We yield to give the other threads a chance to obtain the mutex. If we do not
- * do that, this thread may very well aquire the mutex again before another thread
- * has even a chance to run. The reason is that mutex operations are free to be
- * implemented in the quickest possible way (and they typically are!). That is, the
- * mutex lock/unlock most probably just does an atomic memory swap and does not necessarily
- * schedule other threads waiting on the same mutex. That can lead to the same thread
- * aquiring the mutex ever and ever again while all others are starving for it. We
- * have exactly seen this behaviour when we deliberately introduced a long-running
- * test action which basically did a sleep. I understand that with real actions the
- * likelihood of this starvation condition is very low - but it could still happen
- * and would be very hard to debug. The yield() is a sure fix, its performance overhead
- * should be well accepted given the above facts. -- rgerhards, 2008-01-10
*/
#pragma GCC diagnostic ignored "-Wempty-body"
rsRetVal
@@ -369,10 +363,6 @@ wtiWorker(wti_t *pThis)
while(1) { /* loop will be broken below - need to do mutex locks */
/* process any pending thread requests */
wtpProcessThrdChanges(pWtp);
- pthread_testcancel(); /* see big comment in function header */
-# if !defined(__hpux) /* pthread_yield is missing there! */
- pthread_yield(); /* see big comment in function header */
-# endif
/* if we have a rate-limiter set for this worker pool, let's call it. Please
* keep in mind that the rate-limiter may hold us for an extended period
@@ -395,7 +385,7 @@ wtiWorker(wti_t *pThis)
/* if we reach this point, we are still protected by the mutex */
if(pWtp->pfIsIdle(pWtp->pUsr, MUTEX_ALREADY_LOCKED)) {
- dbgprintf("%s: worker IDLE, waiting for work.\n", wtiGetDbgHdr(pThis));
+ DBGPRINTF("%s: worker IDLE, waiting for work.\n", wtiGetDbgHdr(pThis));
pWtp->pfOnIdle(pWtp->pUsr, MUTEX_ALREADY_LOCKED);
if(pWtp->toWrkShutdown == -1) {
@@ -404,7 +394,7 @@ wtiWorker(wti_t *pThis)
} else {
timeoutComp(&t, pWtp->toWrkShutdown);/* get absolute timeout */
if(d_pthread_cond_timedwait(pWtp->pcondBusy, pWtp->pmutUsr, &t) != 0) {
- dbgprintf("%s: inactivity timeout, worker terminating...\n", wtiGetDbgHdr(pThis));
+ DBGPRINTF("%s: inactivity timeout, worker terminating...\n", wtiGetDbgHdr(pThis));
bInactivityTOOccured = 1; /* indicate we had a timeout */
}
}
@@ -424,7 +414,7 @@ wtiWorker(wti_t *pThis)
pWtp->pfOnWorkerShutdown(pWtp->pUsr);
wtiSetState(pThis, eWRKTHRD_TERMINATING, 0, MUTEX_ALREADY_LOCKED);
- pWtp->bThrdStateChanged = 1; /* indicate change, so harverster will be called */
+ ATOMIC_STORE_1_TO_INT(pWtp->bThrdStateChanged); /* indicate change, so harverster will be called */
d_pthread_mutex_unlock(&pThis->mut);
pthread_setcancelstate(iCancelStateSave, NULL);
@@ -470,6 +460,14 @@ finalize_it:
/* dummy */
rsRetVal wtiQueryInterface(void) { return RS_RET_NOT_IMPLEMENTED; }
+/* exit our class
+ */
+BEGINObjClassExit(wti, OBJ_IS_CORE_MODULE) /* CHANGE class also in END MACRO! */
+CODESTARTObjClassExit(nsdsel_gtls)
+ /* release objects we no longer need */
+ objRelease(glbl, CORE_COMPONENT);
+ENDObjClassExit(wti)
+
/* Initialize the wti class. Must be called as the very first method
* before anything else is called inside this class.
@@ -477,6 +475,7 @@ rsRetVal wtiQueryInterface(void) { return RS_RET_NOT_IMPLEMENTED; }
*/
BEGINObjClassInit(wti, 1, OBJ_IS_CORE_MODULE) /* one is the object version (most important for persisting) */
/* request objects we use */
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
ENDObjClassInit(wti)
/*
diff --git a/runtime/wti.h b/runtime/wti.h
index 0cd6744e..72653b15 100644
--- a/runtime/wti.h
+++ b/runtime/wti.h
@@ -37,7 +37,7 @@ typedef struct wti_s {
wtp_t *pWtp; /* my worker thread pool (important if only the work thread instance is passed! */
pthread_cond_t condExitDone; /* signaled when the thread exit is done (once per thread existance) */
pthread_mutex_t mut;
- int bShutdownRqtd; /* shutdown for this thread requested? 0 - no , 1 - yes */
+ bool bShutdownRqtd; /* shutdown for this thread requested? 0 - no , 1 - yes */
uchar *pszDbgHdr; /* header string for debug messages */
} wti_t;
diff --git a/runtime/wtp.c b/runtime/wtp.c
index fcecc4f2..f71d5855 100644
--- a/runtime/wtp.c
+++ b/runtime/wtp.c
@@ -39,6 +39,14 @@
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
+#include <atomic.h>
+#if HAVE_SYS_PRCTL_H
+# include <sys/prctl.h>
+#endif
+
+#ifdef OS_SOLARIS
+# include <sched.h>
+#endif
#include "rsyslog.h"
#include "stringbuf.h"
@@ -46,9 +54,12 @@
#include "wtp.h"
#include "wti.h"
#include "obj.h"
+#include "unicode-helper.h"
+#include "glbl.h"
/* static data */
DEFobjStaticHelpers
+DEFobjCurrIf(glbl)
/* forward-definitions */
@@ -143,8 +154,7 @@ CODESTARTobjDestruct(wtp)
pthread_mutex_destroy(&pThis->mut);
pthread_mutex_destroy(&pThis->mutThrdShutdwn);
- if(pThis->pszDbgHdr != NULL)
- free(pThis->pszDbgHdr);
+ free(pThis->pszDbgHdr);
ENDobjDestruct(wtp)
@@ -206,7 +216,7 @@ wtpProcessThrdChanges(wtp_t *pThis)
*/
do {
/* reset the change marker */
- pThis->bThrdStateChanged = 0;
+ ATOMIC_STORE_0_TO_INT(pThis->bThrdStateChanged);
/* go through all threads */
for(i = 0 ; i < pThis->iNumWorkerThreads ; ++i) {
wtiProcessThrdChanges(pThis->pWrkr[i], LOCK_MUTEX);
@@ -411,6 +421,8 @@ wtpWrkrExecCancelCleanup(void *arg)
static void *
wtpWorker(void *arg) /* the arg is actually a wti object, even though we are in wtp! */
{
+ uchar *pszDbgHdr;
+ uchar thrdName[32] = "rs:";
DEFiRet;
DEFVARS_mutexProtection;
wti_t *pWti = (wti_t*) arg;
@@ -424,6 +436,15 @@ wtpWorker(void *arg) /* the arg is actually a wti object, even though we are in
sigfillset(&sigSet);
pthread_sigmask(SIG_BLOCK, &sigSet, NULL);
+# if HAVE_PRCTL && defined PR_SET_NAME
+ /* set thread name - we ignore if the call fails, has no harsh consequences... */
+ pszDbgHdr = wtpGetDbgHdr(pThis);
+ ustrncpy(thrdName+3, pszDbgHdr, 20);
+ if(prctl(PR_SET_NAME, thrdName, 0, 0, 0) != 0) {
+ DBGPRINTF("prctl failed, not setting thread name for '%s'\n", wtpGetDbgHdr(pThis));
+ }
+# endif
+
BEGIN_MTX_PROTECTED_OPERATIONS(&pThis->mut, LOCK_MUTEX);
/* do some late initialization */
@@ -439,7 +460,7 @@ wtpWorker(void *arg) /* the arg is actually a wti object, even though we are in
do {
END_MTX_PROTECTED_OPERATIONS(&pThis->mut);
- iRet = wtiWorker(pWti); /* just to make sure: this is NOT protected by the mutex! */
+ wtiWorker(pWti); /* just to make sure: this is NOT protected by the mutex! */
BEGIN_MTX_PROTECTED_OPERATIONS(&pThis->mut, LOCK_MUTEX);
} while(pThis->iCurNumWrkThrd == 1 && pThis->bInactivityGuard == 1);
@@ -476,7 +497,7 @@ wtpStartWrkr(wtp_t *pThis, int bLockMutex)
ISOBJ_TYPE_assert(pThis, wtp);
- wtpProcessThrdChanges(pThis);
+ wtpProcessThrdChanges(pThis); // TODO: Performance: this causes a lot of FUTEX calls
BEGIN_MTX_PROTECTED_OPERATIONS(&pThis->mut, bLockMutex);
@@ -500,13 +521,6 @@ wtpStartWrkr(wtp_t *pThis, int bLockMutex)
dbgprintf("%s: started with state %d, num workers now %d\n",
wtpGetDbgHdr(pThis), iState, pThis->iCurNumWrkThrd);
- /* we try to give the starting worker a little boost. It won't help much as we still
- * hold the queue's mutex, but at least it has a chance to start on a single-CPU system.
- */
-# if !defined(__hpux) /* pthread_yield is missing there! */
- pthread_yield();
-# endif
-
/* indicate we just started a worker and would like to see it running */
wtpSetInactivityGuard(pThis, 1, MUTEX_ALREADY_LOCKED);
@@ -637,12 +651,22 @@ finalize_it:
/* dummy */
rsRetVal wtpQueryInterface(void) { return RS_RET_NOT_IMPLEMENTED; }
+/* exit our class
+ */
+BEGINObjClassExit(wtp, OBJ_IS_CORE_MODULE) /* CHANGE class also in END MACRO! */
+CODESTARTObjClassExit(nsdsel_gtls)
+ /* release objects we no longer need */
+ objRelease(glbl, CORE_COMPONENT);
+ENDObjClassExit(wtp)
+
+
/* Initialize the stream class. Must be called as the very first method
* before anything else is called inside this class.
* rgerhards, 2008-01-09
*/
BEGINObjClassInit(wtp, 1, OBJ_IS_CORE_MODULE)
/* request objects we use */
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
ENDObjClassInit(wtp)
/*
diff --git a/runtime/wtp.h b/runtime/wtp.h
index 0f21ac11..1ce171cc 100644
--- a/runtime/wtp.h
+++ b/runtime/wtp.h
@@ -57,7 +57,7 @@ typedef struct wtp_s {
int iCurNumWrkThrd;/* current number of active worker threads */
struct wti_s **pWrkr;/* array with control structure for the worker thread(s) associated with this wtp */
int toWrkShutdown; /* timeout for idle workers in ms, -1 means indefinite (0 is immediate) */
- int bInactivityGuard;/* prevents inactivity due to race condition */
+ bool bInactivityGuard;/* prevents inactivity due to race condition */
rsRetVal (*pConsumer)(void *); /* user-supplied consumer function for dewtpd messages */
/* synchronization variables */
pthread_mutex_t mutThrdShutdwn; /* mutex to guard thread shutdown processing */
diff --git a/runtime/zlibw.c b/runtime/zlibw.c
new file mode 100644
index 00000000..2b386213
--- /dev/null
+++ b/runtime/zlibw.c
@@ -0,0 +1,125 @@
+/* The zlibwrap object.
+ *
+ * This is an rsyslog object wrapper around zlib.
+ *
+ * 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 <string.h>
+#include <assert.h>
+#include <zlib.h>
+
+#include "rsyslog.h"
+#include "module-template.h"
+#include "obj.h"
+#include "zlibw.h"
+
+MODULE_TYPE_LIB
+
+/* static data */
+DEFobjStaticHelpers
+
+
+/* ------------------------------ methods ------------------------------ */
+
+/* zlib make strong use of macros for its interface functions, so we can not simply
+ * pass function pointers to them. Instead, we create very small wrappers which call
+ * the relevant entry points.
+ */
+
+static int myDeflateInit(z_streamp strm, int level)
+{
+ return deflateInit(strm, level);
+}
+
+static int myDeflateInit2(z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy)
+{
+ return deflateInit2(strm, level, method, windowBits, memLevel, strategy);
+}
+
+static int myDeflateEnd(z_streamp strm)
+{
+ return deflateEnd(strm);
+}
+
+static int myDeflate(z_streamp strm, int flush)
+{
+ return deflate(strm, flush);
+}
+
+
+/* queryInterface function
+ * rgerhards, 2008-03-05
+ */
+BEGINobjQueryInterface(zlibw)
+CODESTARTobjQueryInterface(zlibw)
+ if(pIf->ifVersion != zlibwCURR_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->DeflateInit = myDeflateInit;
+ pIf->DeflateInit2 = myDeflateInit2;
+ pIf->Deflate = myDeflate;
+ pIf->DeflateEnd = myDeflateEnd;
+finalize_it:
+ENDobjQueryInterface(zlibw)
+
+
+/* Initialize the zlibw class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-02-19
+ */
+BEGINAbstractObjClassInit(zlibw, 1, OBJ_IS_LOADABLE_MODULE) /* class, version */
+ /* request objects we use */
+
+ /* set our own handlers */
+ENDObjClassInit(zlibw)
+
+
+/* --------------- here now comes the plumbing that makes as a library module --------------- */
+
+
+BEGINmodExit
+CODESTARTmodExit
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_LIB_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+
+ CHKiRet(zlibwClassInit(pModInfo)); /* must be done after tcps_sess, as we use it */
+ /* Initialize all classes that are in our module - this includes ourselfs */
+ENDmodInit
+/* vi:set ai:
+ */
diff --git a/runtime/zlibw.h b/runtime/zlibw.h
new file mode 100644
index 00000000..63d8f386
--- /dev/null
+++ b/runtime/zlibw.h
@@ -0,0 +1,46 @@
+/* The zlibw object. It encapsulates the zlib functionality. The primary
+ * purpose of this wrapper class is to enable rsyslogd core to be build without
+ * zlib libraries.
+ *
+ * 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_ZLIBW_H
+#define INCLUDED_ZLIBW_H
+
+#include <zlib.h>
+
+/* interfaces */
+BEGINinterface(zlibw) /* name must also be changed in ENDinterface macro! */
+ int (*DeflateInit)(z_streamp strm, int);
+ int (*DeflateInit2)(z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy);
+ int (*Deflate)(z_streamp strm, int);
+ int (*DeflateEnd)(z_streamp strm);
+ENDinterface(zlibw)
+#define zlibwCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+
+
+/* prototypes */
+PROTOTYPEObj(zlibw);
+
+/* the name of our library binary */
+#define LM_ZLIBW_FILENAME "lmzlibw"
+
+#endif /* #ifndef INCLUDED_ZLIBW_H */
diff --git a/shave-libtool.in b/shave-libtool.in
new file mode 100644
index 00000000..54ebd690
--- /dev/null
+++ b/shave-libtool.in
@@ -0,0 +1,109 @@
+#!/bin/sh
+#
+# Copyright (c) 2009, Damien Lespiau <damien.lespiau@gmail.com>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following
+# conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+
+# we need sed
+SED=@SED@
+if test -z "$SED" ; then
+SED=sed
+fi
+
+lt_unmangle ()
+{
+ last_result=`echo $1 | $SED -e 's#.libs/##' -e 's#[0-9a-zA-Z_\-\.]*_la-##'`
+}
+
+# the real libtool to use
+LIBTOOL="$1"
+shift
+
+# if 1, don't print anything, the underlaying wrapper will do it
+pass_though=0
+
+# scan the arguments, keep the right ones for libtool, and discover the mode
+preserved_args=
+
+# have we seen the --tag option of libtool in the command line ?
+tag_seen=0
+
+while test "$#" -gt 0; do
+ opt="$1"
+ shift
+
+ case $opt in
+ --mode=*)
+ mode=`echo $opt | $SED -e 's/[-_a-zA-Z0-9]*=//'`
+ preserved_args="$preserved_args $opt"
+ ;;
+ -o)
+ lt_output="$1"
+ preserved_args="$preserved_args $opt"
+ ;;
+ --tag=*)
+ tag_seen=1
+ preserved_args="$preserved_args $opt"
+ ;;
+ *)
+ preserved_args="$preserved_args $opt"
+ ;;
+ esac
+done
+
+case "$mode" in
+compile)
+ # shave will be called and print the actual CC/CXX/LINK line
+ preserved_args="$preserved_args --shave-mode=$mode"
+ pass_though=1
+ ;;
+link)
+ preserved_args="$preserved_args --shave-mode=$mode"
+ Q=" LINK "
+ ;;
+*)
+ # let's u
+ # echo "*** libtool: Unimplemented mode: $mode, fill a bug report"
+ ;;
+esac
+
+lt_unmangle "$lt_output"
+output=$last_result
+
+# automake does not add a --tag switch to its libtool invocation when
+# assembling a .s file and rely on libtool to infer the right action based
+# on the compiler name. As shave is using CC to hook a wrapper, libtool gets
+# confused. Let's detect these cases and add a --tag=CC option.
+tag=""
+if test $tag_seen -eq 0 -a x"$mode" = xcompile; then
+ tag="--tag=CC"
+fi
+
+if test -z $V; then
+ if test $pass_though -eq 0; then
+ echo "$Q$output"
+ fi
+ $LIBTOOL --silent $tag $preserved_args
+else
+ echo $LIBTOOL $tag $preserved_args
+ $LIBTOOL $tag $preserved_args
+fi
diff --git a/shave.in b/shave.in
new file mode 100644
index 00000000..afed42e1
--- /dev/null
+++ b/shave.in
@@ -0,0 +1,102 @@
+#!/bin/sh
+#
+# Copyright (c) 2009, Damien Lespiau <damien.lespiau@gmail.com>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following
+# conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+
+# we need sed
+SED=@SED@
+if test -z "$SED" ; then
+SED=sed
+fi
+
+lt_unmangle ()
+{
+ last_result=`echo $1 | $SED -e 's#.libs/##' -e 's#[0-9a-zA-Z_\-\.]*_la-##'`
+}
+
+# the tool to wrap (cc, cxx, ar, ranlib, ..)
+tool="$1"
+shift
+
+# the reel tool (to call)
+REEL_TOOL="$1"
+shift
+
+pass_through=0
+preserved_args=
+while test "$#" -gt 0; do
+ opt="$1"
+ shift
+
+ case $opt in
+ --shave-mode=*)
+ mode=`echo $opt | $SED -e 's/[-_a-zA-Z0-9]*=//'`
+ ;;
+ -o)
+ lt_output="$1"
+ preserved_args="$preserved_args $opt"
+ ;;
+ *)
+ preserved_args="$preserved_args $opt"
+ ;;
+ esac
+done
+
+# mode=link is handled in the libtool wrapper
+case "$mode,$tool" in
+link,*)
+ pass_through=1
+ ;;
+*,cxx)
+ Q=" CXX "
+ ;;
+*,cc)
+ Q=" CC "
+ ;;
+*,fc)
+ Q=" FC "
+ ;;
+*,f77)
+ Q=" F77 "
+ ;;
+*,objc)
+ Q=" OBJC "
+ ;;
+*,*)
+ # should not happen
+ Q=" CC "
+ ;;
+esac
+
+lt_unmangle "$lt_output"
+output=$last_result
+
+if test -z $V; then
+ if test $pass_through -eq 0; then
+ echo "$Q$output"
+ fi
+ $REEL_TOOL $preserved_args
+else
+ echo $REEL_TOOL $preserved_args
+ $REEL_TOOL $preserved_args
+fi
diff --git a/tcpclt.c b/tcpclt.c
index c53f00f7..617aaef6 100644
--- a/tcpclt.c
+++ b/tcpclt.c
@@ -297,6 +297,12 @@ Send(tcpclt_t *pThis, void *pData, char *msg, size_t len)
CHKiRet(TCPSendBldFrame(pThis, &msg, &len, &bMsgMustBeFreed));
+ if(pThis->iRebindInterval > 0 && ++pThis->iNumMsgs == pThis->iRebindInterval) {
+ /* we need to rebind, and use the retry logic for this*/
+ CHKiRet(pThis->prepRetryFunc(pData)); /* try to recover */
+ pThis->iNumMsgs = 0;
+ }
+
while(!bDone) { /* loop is broken when send succeeds or error occurs */
CHKiRet(pThis->initFunc(pData));
iRet = pThis->sendFunc(pData, msg, len);
@@ -388,6 +394,13 @@ SetFraming(tcpclt_t *pThis, TCPFRAMINGMODE framing)
pThis->tcp_framing = framing;
RETiRet;
}
+static rsRetVal
+SetRebindInterval(tcpclt_t *pThis, int iRebindInterval)
+{
+ DEFiRet;
+ pThis->iRebindInterval = iRebindInterval;
+ RETiRet;
+}
/* Standard-Constructor
@@ -445,6 +458,7 @@ CODESTARTobjQueryInterface(tcpclt)
pIf->SetSendFrame = SetSendFrame;
pIf->SetSendPrepRetry = SetSendPrepRetry;
pIf->SetFraming = SetFraming;
+ pIf->SetRebindInterval = SetRebindInterval;
finalize_it:
ENDobjQueryInterface(tcpclt)
diff --git a/tcpclt.h b/tcpclt.h
index 1d704044..5a8eba75 100644
--- a/tcpclt.h
+++ b/tcpclt.h
@@ -36,6 +36,8 @@ typedef struct tcpclt_s {
short bResendLastOnRecon; /* should the last message be resent on a successful reconnect? */
size_t lenPrevMsg;
/* session specific callbacks */
+ int iRebindInterval; /* how often should the send socket be rebound? */
+ int iNumMsgs; /* number of messages during current "rebind session" */
rsRetVal (*initFunc)(void*);
rsRetVal (*sendFunc)(void*, char*, size_t);
rsRetVal (*prepRetryFunc)(void*);
@@ -55,8 +57,10 @@ BEGINinterface(tcpclt) /* name must also be changed in ENDinterface macro! */
rsRetVal (*SetSendFrame)(tcpclt_t*, rsRetVal (*)(void*, char*, size_t));
rsRetVal (*SetSendPrepRetry)(tcpclt_t*, rsRetVal (*)(void*));
rsRetVal (*SetFraming)(tcpclt_t*, TCPFRAMINGMODE framing);
+ /* v3, 2009-07-14*/
+ rsRetVal (*SetRebindInterval)(tcpclt_t*, int iRebindInterval);
ENDinterface(tcpclt)
-#define tcpcltCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */
+#define tcpcltCURR_IF_VERSION 3 /* increment whenever you change the interface structure! */
/* prototypes */
diff --git a/tcps_sess.c b/tcps_sess.c
index 3024c313..ea9032b3 100644
--- a/tcps_sess.c
+++ b/tcps_sess.c
@@ -36,6 +36,7 @@
#include "rsyslog.h"
#include "dirty.h"
+#include "unicode-helper.h"
#include "module-template.h"
#include "net.h"
#include "tcpsrv.h"
@@ -43,6 +44,9 @@
#include "obj.h"
#include "errmsg.h"
#include "netstrm.h"
+#include "msg.h"
+#include "datetime.h"
+#include "prop.h"
/* static data */
@@ -50,9 +54,12 @@ DEFobjStaticHelpers
DEFobjCurrIf(glbl)
DEFobjCurrIf(errmsg)
DEFobjCurrIf(netstrm)
+DEFobjCurrIf(prop)
+DEFobjCurrIf(datetime)
static int iMaxLine; /* maximum size of a single message */
+
/* forward definitions */
static rsRetVal Close(tcps_sess_t *pThis);
@@ -95,11 +102,10 @@ CODESTARTobjDestruct(tcps_sess)
}
/* now destruct our own properties */
if(pThis->fromHost != NULL)
- free(pThis->fromHost);
+ CHKiRet(prop.Destruct(&pThis->fromHost));
if(pThis->fromHostIP != NULL)
- free(pThis->fromHostIP);
- if(pThis->pMsg != NULL)
- free(pThis->pMsg);
+ CHKiRet(prop.Destruct(&pThis->fromHostIP));
+ free(pThis->pMsg);
ENDobjDestruct(tcps_sess)
@@ -121,12 +127,13 @@ SetHost(tcps_sess_t *pThis, uchar *pszHost)
ISOBJ_TYPE_assert(pThis, tcps_sess);
- if(pThis->fromHost != NULL) {
- free(pThis->fromHost);
- }
-
- pThis->fromHost = pszHost;
+ if(pThis->fromHost == NULL)
+ CHKiRet(prop.Construct(&pThis->fromHost));
+ CHKiRet(prop.SetString(pThis->fromHost, pszHost, ustrlen(pszHost)));
+
+finalize_it:
+ free(pszHost); /* we must free according to our (old) calling conventions */
RETiRet;
}
@@ -141,12 +148,13 @@ SetHostIP(tcps_sess_t *pThis, uchar *pszHostIP)
ISOBJ_TYPE_assert(pThis, tcps_sess);
- if(pThis->fromHostIP != NULL) {
- free(pThis->fromHostIP);
- }
-
- pThis->fromHostIP = pszHostIP;
+ if(pThis->fromHostIP == NULL)
+ CHKiRet(prop.Construct(&pThis->fromHostIP));
+ CHKiRet(prop.SetString(pThis->fromHostIP, pszHostIP, ustrlen(pszHostIP)));
+
+finalize_it:
+ free(pszHostIP);
RETiRet;
}
@@ -182,6 +190,18 @@ SetTcpsrv(tcps_sess_t *pThis, tcpsrv_t *pSrv)
}
+/* set our parent listener info*/
+static rsRetVal
+SetLstnInfo(tcps_sess_t *pThis, tcpLstnPortList_t *pLstnInfo)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, tcps_sess);
+ assert(pLstnInfo != NULL);
+ pThis->pLstnInfo = pLstnInfo;
+ RETiRet;
+}
+
+
static rsRetVal
SetUsrP(tcps_sess_t *pThis, void *pUsr)
{
@@ -191,6 +211,73 @@ SetUsrP(tcps_sess_t *pThis, void *pUsr)
}
+static rsRetVal
+SetOnMsgReceive(tcps_sess_t *pThis, rsRetVal (*OnMsgReceive)(tcps_sess_t*, uchar*, int))
+{
+ DEFiRet;
+ pThis->DoSubmitMessage = OnMsgReceive;
+ 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
+ */
+static rsRetVal
+defaultDoSubmitMessage(tcps_sess_t *pThis, struct syslogTime *stTime, time_t ttGenTime, multi_submit_t *pMultiSub)
+{
+ msg_t *pMsg;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, tcps_sess);
+
+ if(pThis->iMsg == 0) {
+ DBGPRINTF("discarding zero-sized message\n");
+ FINALIZE;
+ }
+
+ if(pThis->DoSubmitMessage != NULL) {
+ pThis->DoSubmitMessage(pThis, pThis->pMsg, pThis->iMsg);
+ 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->pLstnInfo->pInputName);
+ MsgSetFlowControlType(pMsg, eFLOWCTL_LIGHT_DELAY);
+ pMsg->msgFlags = NEEDS_PARSING | PARSE_HOSTNAME;
+ pMsg->bParseHOSTNAME = 1;
+ MsgSetRcvFrom(pMsg, pThis->fromHost);
+ CHKiRet(MsgSetRcvFromIP(pMsg, pThis->fromHostIP));
+ MsgSetRuleset(pMsg, pThis->pLstnInfo->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;
+}
+
+
+
/* This should be called before a normal (non forced) close
* of a TCP session. This function checks if there is any unprocessed
* message left in the TCP stream. Such a message is probably a
@@ -205,6 +292,8 @@ SetUsrP(tcps_sess_t *pThis, void *pUsr)
static rsRetVal
PrepareClose(tcps_sess_t *pThis)
{
+ struct syslogTime stTime;
+ time_t ttGenTime;
DEFiRet;
ISOBJ_TYPE_assert(pThis, tcps_sess);
@@ -230,9 +319,8 @@ PrepareClose(tcps_sess_t *pThis)
* this case.
*/
dbgprintf("Extra data at end of stream in legacy syslog/tcp message - processing\n");
- parseAndSubmitMessage(pThis->fromHost, pThis->fromHostIP, pThis->pMsg, pThis->iMsg, MSG_PARSE_HOSTNAME,
- NOFLAG, eFLOWCTL_LIGHT_DELAY, NULL); /* TODO: add real InputName */
- pThis->bAtStrtOfFram = 1;
+ datetime.getCurrTime(&stTime, &ttGenTime);
+ defaultDoSubmitMessage(pThis, &stTime, ttGenTime, NULL);
}
finalize_it:
@@ -251,10 +339,11 @@ Close(tcps_sess_t *pThis)
ISOBJ_TYPE_assert(pThis, tcps_sess);
netstrm.Destruct(&pThis->pStrm);
- free(pThis->fromHost);
- pThis->fromHost = NULL; /* not really needed, but... */
- free(pThis->fromHostIP);
- pThis->fromHostIP = NULL; /* not really needed, but... */
+ if(pThis->fromHost != NULL) {
+ prop.Destruct(&pThis->fromHost);
+ }
+ if(pThis->fromHostIP != NULL)
+ prop.Destruct(&pThis->fromHostIP);
RETiRet;
}
@@ -267,7 +356,7 @@ Close(tcps_sess_t *pThis)
* rgerhards, 2008-03-14
*/
static rsRetVal
-processDataRcvd(tcps_sess_t *pThis, char c)
+processDataRcvd(tcps_sess_t *pThis, char c, struct syslogTime *stTime, time_t ttGenTime, multi_submit_t *pMultiSub)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, tcps_sess);
@@ -313,9 +402,7 @@ processDataRcvd(tcps_sess_t *pThis, char c)
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");
- parseAndSubmitMessage(pThis->fromHost, pThis->fromHostIP, pThis->pMsg, pThis->iMsg,
- MSG_PARSE_HOSTNAME, NOFLAG, eFLOWCTL_LIGHT_DELAY, NULL); /* TODO: add real InputName */
- pThis->iMsg = 0;
+ defaultDoSubmitMessage(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...
@@ -323,10 +410,10 @@ processDataRcvd(tcps_sess_t *pThis, char c)
*/
}
- if(c == '\n' && pThis->eFraming == TCP_FRAMING_OCTET_STUFFING) { /* record delemiter? */
- parseAndSubmitMessage(pThis->fromHost, pThis->fromHostIP, pThis->pMsg, pThis->iMsg,
- MSG_PARSE_HOSTNAME, NOFLAG, eFLOWCTL_LIGHT_DELAY, NULL); /* TODO: add real InputName */
- pThis->iMsg = 0;
+ if(( (c == '\n')
+ || ((pThis->pSrv->addtlFrameDelim != TCPSRV_NO_ADDTL_DELIMITER) && (c == pThis->pSrv->addtlFrameDelim))
+ ) && pThis->eFraming == TCP_FRAMING_OCTET_STUFFING) { /* record delimiter? */
+ defaultDoSubmitMessage(pThis, stTime, ttGenTime, pMultiSub);
pThis->inputState = eAtStrtFram;
} else {
/* IMPORTANT: here we copy the actual frame content to the message - for BOTH framing modes!
@@ -343,9 +430,7 @@ processDataRcvd(tcps_sess_t *pThis, char c)
pThis->iOctetsRemain--;
if(pThis->iOctetsRemain < 1) {
/* we have end of frame! */
- parseAndSubmitMessage(pThis->fromHost, pThis->fromHostIP, pThis->pMsg, pThis->iMsg,
- MSG_PARSE_HOSTNAME, NOFLAG, eFLOWCTL_LIGHT_DELAY, NULL); /* TODO: add real InputName */
- pThis->iMsg = 0;
+ defaultDoSubmitMessage(pThis, stTime, ttGenTime, pMultiSub);
pThis->inputState = eAtStrtFram;
}
}
@@ -366,27 +451,46 @@ processDataRcvd(tcps_sess_t *pThis, char c)
* 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
*/
+#define NUM_MULTISUB 1024
static rsRetVal
DataRcvd(tcps_sess_t *pThis, char *pData, size_t iLen)
{
- DEFiRet;
+ multi_submit_t multiSub;
+ msg_t *pMsgs[NUM_MULTISUB];
+ struct syslogTime stTime;
+ time_t ttGenTime;
char *pEnd;
+ DEFiRet;
ISOBJ_TYPE_assert(pThis, tcps_sess);
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++));
+ 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
/* queryInterface function
@@ -414,10 +518,12 @@ CODESTARTobjQueryInterface(tcps_sess)
pIf->SetUsrP = SetUsrP;
pIf->SetTcpsrv = SetTcpsrv;
+ pIf->SetLstnInfo = SetLstnInfo;
pIf->SetHost = SetHost;
pIf->SetHostIP = SetHostIP;
pIf->SetStrm = SetStrm;
pIf->SetMsgIdx = SetMsgIdx;
+ pIf->SetOnMsgReceive = SetOnMsgReceive;
finalize_it:
ENDobjQueryInterface(tcps_sess)
@@ -430,6 +536,8 @@ CODESTARTObjClassExit(tcps_sess)
/* release objects we no longer need */
objRelease(errmsg, CORE_COMPONENT);
objRelease(netstrm, LM_NETSTRMS_FILENAME);
+ objRelease(datetime, CORE_COMPONENT);
+ objRelease(prop, CORE_COMPONENT);
ENDObjClassExit(tcps_sess)
@@ -441,6 +549,8 @@ BEGINObjClassInit(tcps_sess, 1, OBJ_IS_CORE_MODULE) /* class, version - CHANGE c
/* request objects we use */
CHKiRet(objUse(errmsg, CORE_COMPONENT));
CHKiRet(objUse(netstrm, LM_NETSTRMS_FILENAME));
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
+ CHKiRet(objUse(prop, CORE_COMPONENT));
CHKiRet(objUse(glbl, CORE_COMPONENT));
iMaxLine = glbl.GetMaxLine(); /* get maximum size we currently support */
@@ -451,7 +561,5 @@ BEGINObjClassInit(tcps_sess, 1, OBJ_IS_CORE_MODULE) /* class, version - CHANGE c
OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, tcps_sessConstructFinalize);
ENDObjClassInit(tcps_sess)
-
-
/* vim:set ai:
*/
diff --git a/tcps_sess.h b/tcps_sess.h
index 576466ff..ec3a6af4 100644
--- a/tcps_sess.h
+++ b/tcps_sess.h
@@ -24,14 +24,16 @@
#define INCLUDED_TCPS_SESS_H
#include "obj.h"
+#include "prop.h"
/* a forward-definition, we are somewhat cyclic */
struct tcpsrv_s;
/* the tcps_sess object */
-typedef struct tcps_sess_s {
+struct tcps_sess_s {
BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
- struct tcpsrv_s *pSrv; /* pointer back to my server (e.g. for callbacks) */
+ tcpsrv_t *pSrv; /* pointer back to my server (e.g. for callbacks) */
+ tcpLstnPortList_t *pLstnInfo; /* pointer back to listener info */
netstrm_t *pStrm;
int iMsg; /* index of next char to store in msg */
int bAtStrtOfFram; /* are we at the very beginning of a new frame? */
@@ -43,10 +45,11 @@ typedef struct tcps_sess_s {
int iOctetsRemain; /* Number of Octets remaining in message */
TCPFRAMINGMODE eFraming;
uchar *pMsg; /* message (fragment) received */
- uchar *fromHost;
- uchar *fromHostIP;
- void *pUsr; /* a user-pointer */
-} tcps_sess_t;
+ prop_t *fromHost; /* host name we received messages from */
+ prop_t *fromHostIP;
+ void *pUsr; /* a user-pointer */
+ rsRetVal (*DoSubmitMessage)(tcps_sess_t*, uchar*, int); /* submit message callback */
+};
/* interfaces */
@@ -60,13 +63,20 @@ BEGINinterface(tcps_sess) /* name must also be changed in ENDinterface macro! */
rsRetVal (*DataRcvd)(tcps_sess_t *pThis, char *pData, size_t iLen);
/* set methods */
rsRetVal (*SetTcpsrv)(tcps_sess_t *pThis, struct tcpsrv_s *pSrv);
+ rsRetVal (*SetLstnInfo)(tcps_sess_t *pThis, tcpLstnPortList_t *pLstnInfo);
rsRetVal (*SetUsrP)(tcps_sess_t*, void*);
rsRetVal (*SetHost)(tcps_sess_t *pThis, uchar*);
rsRetVal (*SetHostIP)(tcps_sess_t *pThis, uchar*);
rsRetVal (*SetStrm)(tcps_sess_t *pThis, netstrm_t*);
rsRetVal (*SetMsgIdx)(tcps_sess_t *pThis, int);
+ rsRetVal (*SetOnMsgReceive)(tcps_sess_t *pThis, rsRetVal (*OnMsgReceive)(tcps_sess_t*, uchar*, int));
ENDinterface(tcps_sess)
-#define tcps_sessCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+#define tcps_sessCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */
+/* interface changes
+ * to version v2, rgerhards, 2009-05-22
+ * - Data structures changed
+ * - SetLstnInfo entry point added
+ */
/* prototypes */
diff --git a/tcpsrv.c b/tcpsrv.c
index 107d03f4..8c992f79 100644
--- a/tcpsrv.c
+++ b/tcpsrv.c
@@ -1,8 +1,11 @@
/* tcpsrv.c
*
- * Common code for plain TCP based servers. This is currently being
+ * Common code for plain TCP syslog based servers. This is currently being
* utilized by imtcp and imgssapi.
*
+ * NOTE: this is *not* a generic TCP server, but one for syslog servers. For
+ * generic stream servers, please use ./runtime/strmsrv.c!
+ *
* There are actually two classes within the tcpserver code: one is
* the tcpsrv itself, the other one is its sessions. This is a helper
* class to tcpsrv.
@@ -17,7 +20,7 @@
*
* File begun on 2007-12-21 by RGerhards (extracted from syslogd.c)
*
- * Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007, 2008, 2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -66,6 +69,8 @@
#include "netstrm.h"
#include "nssel.h"
#include "errmsg.h"
+#include "ruleset.h"
+#include "unicode-helper.h"
MODULE_TYPE_LIB
@@ -77,51 +82,75 @@ MODULE_TYPE_LIB
DEFobjStaticHelpers
DEFobjCurrIf(conf)
DEFobjCurrIf(glbl)
+DEFobjCurrIf(ruleset)
DEFobjCurrIf(tcps_sess)
DEFobjCurrIf(errmsg)
DEFobjCurrIf(net)
DEFobjCurrIf(netstrms)
DEFobjCurrIf(netstrm)
DEFobjCurrIf(nssel)
+DEFobjCurrIf(prop)
-/* configure TCP listener settings. This is called during command
- * line parsing. The argument following -t is supplied as an argument.
- * The format of this argument is
- * "<port-to-use>, <nbr-of-sessions>"
- * Typically, there is no whitespace between port and session number.
- * (but it may be...).
- * NOTE: you can not use dbgprintf() in here - the dbgprintf() system is
- * not yet initilized when this function is called.
- * rgerhards, 2007-06-21
- * The port in cOptarg is handed over to us - the caller MUST NOT free it!
+/* add new listener port to listener port list
+ * rgerhards, 2009-05-21
+ */
+static inline rsRetVal
+addNewLstnPort(tcpsrv_t *pThis, uchar *pszPort)
+{
+ tcpLstnPortList_t *pEntry;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, tcpsrv);
+
+ /* create entry */
+ CHKmalloc(pEntry = malloc(sizeof(tcpLstnPortList_t)));
+ pEntry->pszPort = pszPort;
+ pEntry->pSrv = pThis;
+ pEntry->pRuleset = pThis->pRuleset;
+
+ /* we need to create a property */
+ CHKiRet(prop.Construct(&pEntry->pInputName));
+ CHKiRet(prop.SetString(pEntry->pInputName, pThis->pszInputName, ustrlen(pThis->pszInputName)));
+ CHKiRet(prop.ConstructFinalize(pEntry->pInputName));
+
+ /* and add to list */
+ pEntry->pNext = pThis->pLstnPorts;
+ pThis->pLstnPorts = pEntry;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* configure TCP listener settings.
+ * Note: pszPort is handed over to us - the caller MUST NOT free it!
* rgerhards, 2008-03-20
*/
-static void
-configureTCPListen(tcpsrv_t *pThis, char *cOptarg)
+static rsRetVal
+configureTCPListen(tcpsrv_t *pThis, uchar *pszPort)
{
- register int i;
- register char *pArg = cOptarg;
+ int i;
+ uchar *pPort = pszPort;
+ DEFiRet;
- assert(cOptarg != NULL);
+ assert(pszPort != NULL);
ISOBJ_TYPE_assert(pThis, tcpsrv);
/* extract port */
i = 0;
- while(isdigit((int) *pArg)) {
- i = i * 10 + *pArg++ - '0';
+ while(isdigit((int) *pPort)) {
+ i = i * 10 + *pPort++ - '0';
}
- if(pThis->TCPLstnPort != NULL) {
- free(pThis->TCPLstnPort);
- pThis->TCPLstnPort = NULL;
- }
-
- if( i >= 0 && i <= 65535) {
- pThis->TCPLstnPort = cOptarg;
+ if(i >= 0 && i <= 65535) {
+ CHKiRet(addNewLstnPort(pThis, pszPort));
} else {
- errmsg.LogError(0, NO_ERRCODE, "Invalid TCP listen port %s - changed to 514.\n", cOptarg);
+ errmsg.LogError(0, NO_ERRCODE, "Invalid TCP listen port %s - ignored.\n", pszPort);
}
+
+finalize_it:
+ RETiRet;
}
@@ -202,6 +231,8 @@ TCPSessGetNxtSess(tcpsrv_t *pThis, int iCurr)
static void deinit_tcp_listener(tcpsrv_t *pThis)
{
int i;
+ tcpLstnPortList_t *pEntry;
+ tcpLstnPortList_t *pDel;
ISOBJ_TYPE_assert(pThis, tcpsrv);
@@ -219,11 +250,18 @@ static void deinit_tcp_listener(tcpsrv_t *pThis)
pThis->pSessions = NULL; /* just to make sure... */
}
- if(pThis->TCPLstnPort != NULL)
- free(pThis->TCPLstnPort);
+ /* free list of tcp listen ports */
+ pEntry = pThis->pLstnPorts;
+ while(pEntry != NULL) {
+ free(pEntry->pszPort);
+ prop.Destruct(&pEntry->pInputName);
+ pDel = pEntry;
+ pEntry = pEntry->pNext;
+ free(pDel);
+ }
/* finally close our listen streams */
- for(i = 0 ; i < pThis->iLstnMax ; ++i) {
+ for(i = 0 ; i < pThis->iLstnCurr ; ++i) {
netstrm.Destruct(pThis->ppLstn + i);
}
}
@@ -235,36 +273,39 @@ static void deinit_tcp_listener(tcpsrv_t *pThis)
static rsRetVal
addTcpLstn(void *pUsr, netstrm_t *pLstn)
{
- tcpsrv_t *pThis = (tcpsrv_t*) pUsr;
+ tcpLstnPortList_t *pPortList = (tcpLstnPortList_t *) pUsr;
+ tcpsrv_t *pThis = pPortList->pSrv;
DEFiRet;
ISOBJ_TYPE_assert(pThis, tcpsrv);
ISOBJ_TYPE_assert(pLstn, netstrm);
- if(pThis->iLstnMax >= TCPLSTN_MAX_DEFAULT)
+ if(pThis->iLstnCurr >= pThis->iLstnMax)
ABORT_FINALIZE(RS_RET_MAX_LSTN_REACHED);
- pThis->ppLstn[pThis->iLstnMax] = pLstn;
- ++pThis->iLstnMax;
+ pThis->ppLstn[pThis->iLstnCurr] = pLstn;
+ pThis->ppLstnPort[pThis->iLstnCurr] = pPortList;
+ ++pThis->iLstnCurr;
finalize_it:
RETiRet;
}
-/* Initialize TCP sockets (for listener) and listens on them */
-static rsRetVal
-create_tcp_socket(tcpsrv_t *pThis)
+/* Initialize TCP listener socket for a single port
+ * rgerhards, 2009-05-21
+ */
+static inline rsRetVal
+initTCPListener(tcpsrv_t *pThis, tcpLstnPortList_t *pPortEntry)
{
DEFiRet;
uchar *TCPLstnPort;
ISOBJ_TYPE_assert(pThis, tcpsrv);
+ assert(pPortEntry != NULL);
- if(!strcmp((char*)pThis->TCPLstnPort, "0"))
- TCPLstnPort = (uchar*)"514";
- // TODO: we need to enable the caller to set a port (based on who is
- // using this, 514 may be totally unsuitable... --- rgerhards, 2008-04-22
+ if(!ustrcmp(pPortEntry->pszPort, UCHAR_CONSTANT("0")))
+ TCPLstnPort = UCHAR_CONSTANT("514");
/* use default - we can not do service db update, because there is
* no IANA-assignment for syslog/tcp. In the long term, we might
* re-use RFC 3195 port of 601, but that would probably break to
@@ -272,10 +313,35 @@ create_tcp_socket(tcpsrv_t *pThis)
* rgerhards, 2007-06-28
*/
else
- TCPLstnPort = (uchar*)pThis->TCPLstnPort;
+ TCPLstnPort = pPortEntry->pszPort;
/* TODO: add capability to specify local listen address! */
- CHKiRet(netstrm.LstnInit(pThis->pNS, (void*)pThis, addTcpLstn, TCPLstnPort, NULL, pThis->iSessMax));
+ CHKiRet(netstrm.LstnInit(pThis->pNS, (void*)pPortEntry, addTcpLstn, TCPLstnPort, NULL, pThis->iSessMax));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Initialize TCP sockets (for listener) and listens on them */
+static rsRetVal
+create_tcp_socket(tcpsrv_t *pThis)
+{
+ DEFiRet;
+ rsRetVal localRet;
+ tcpLstnPortList_t *pEntry;
+
+ ISOBJ_TYPE_assert(pThis, tcpsrv);
+
+ /* init all configured ports */
+ pEntry = pThis->pLstnPorts;
+ while(pEntry != NULL) {
+ localRet = initTCPListener(pThis, pEntry);
+ if(localRet != RS_RET_OK) {
+ errmsg.LogError(0, localRet, "Could not create tcp listener, ignoring port %s.", pEntry->pszPort);
+ }
+ pEntry = pEntry->pNext;
+ }
/* OK, we had success. Now it is also time to
* initialize our connections
@@ -297,7 +363,7 @@ finalize_it:
/* Accept new TCP connection; make entry in session table. If there
* is no more space left in the connection table, the new TCP
* connection is immediately dropped.
- * ppSess has a pointer to the newly created session, if it succeds.
+ * ppSess has a pointer to the newly created session, if it succeeds.
* If it does not succeed, no session is created and ppSess is
* undefined. If the user has provided an OnSessAccept Callback,
* this one is executed immediately after creation of the
@@ -305,7 +371,7 @@ finalize_it:
* rgerhards, 2008-03-02
*/
static rsRetVal
-SessAccept(tcpsrv_t *pThis, tcps_sess_t **ppSess, netstrm_t *pStrm)
+SessAccept(tcpsrv_t *pThis, tcpLstnPortList_t *pLstnInfo, tcps_sess_t **ppSess, netstrm_t *pStrm)
{
DEFiRet;
tcps_sess_t *pSess = NULL;
@@ -316,6 +382,7 @@ SessAccept(tcpsrv_t *pThis, tcps_sess_t **ppSess, netstrm_t *pStrm)
uchar *fromHostIP = NULL;
ISOBJ_TYPE_assert(pThis, tcpsrv);
+ assert(pLstnInfo != NULL);
CHKiRet(netstrm.AcceptConnReq(pStrm, &pNewStrm));
@@ -325,13 +392,15 @@ SessAccept(tcpsrv_t *pThis, tcps_sess_t **ppSess, netstrm_t *pStrm)
errno = 0;
errmsg.LogError(0, RS_RET_MAX_SESS_REACHED, "too many tcp sessions - dropping incoming request");
ABORT_FINALIZE(RS_RET_MAX_SESS_REACHED);
- } else {
- /* we found a free spot and can construct our session object */
- CHKiRet(tcps_sess.Construct(&pSess));
- CHKiRet(tcps_sess.SetTcpsrv(pSess, pThis));
}
- /* OK, we have a "good" index... */
+ /* we found a free spot and can construct our session object */
+ CHKiRet(tcps_sess.Construct(&pSess));
+ CHKiRet(tcps_sess.SetTcpsrv(pSess, pThis));
+ CHKiRet(tcps_sess.SetLstnInfo(pSess, pLstnInfo));
+ if(pThis->OnMsgReceive != NULL)
+ CHKiRet(tcps_sess.SetOnMsgReceive(pSess, pThis->OnMsgReceive));
+
/* get the host name */
CHKiRet(netstrm.GetRemoteHName(pNewStrm, &fromHostFQDN));
CHKiRet(netstrm.GetRemoteIP(pNewStrm, &fromHostIP));
@@ -376,12 +445,10 @@ finalize_it:
if(iRet != RS_RET_OK) {
if(pSess != NULL)
tcps_sess.Destruct(&pSess);
- if(fromHostFQDN != NULL)
- free(fromHostFQDN);
- if(fromHostIP != NULL)
- free(fromHostIP);
if(pNewStrm != NULL)
netstrm.Destruct(&pNewStrm);
+ free(fromHostFQDN);
+ free(fromHostIP);
}
RETiRet;
@@ -404,12 +471,13 @@ static rsRetVal
Run(tcpsrv_t *pThis)
{
DEFiRet;
+ rsRetVal localRet;
int nfds;
int i;
int iTCPSess;
int bIsReady;
tcps_sess_t *pNewSess;
- nssel_t *pSel;
+ nssel_t *pSel = NULL;
ssize_t iRcvd;
ISOBJ_TYPE_assert(pThis, tcpsrv);
@@ -425,7 +493,7 @@ Run(tcpsrv_t *pThis)
CHKiRet(nssel.ConstructFinalize(pSel));
/* Add the TCP listen sockets to the list of read descriptors. */
- for(i = 0 ; i < pThis->iLstnMax ; ++i) {
+ for(i = 0 ; i < pThis->iLstnCurr ; ++i) {
CHKiRet(nssel.Add(pSel, pThis->ppLstn[i], NSDSEL_RD));
}
@@ -441,11 +509,11 @@ Run(tcpsrv_t *pThis)
/* wait for io to become ready */
CHKiRet(nssel.Wait(pSel, &nfds));
- for(i = 0 ; i < pThis->iLstnMax ; ++i) {
+ for(i = 0 ; i < pThis->iLstnCurr ; ++i) {
CHKiRet(nssel.IsReady(pSel, pThis->ppLstn[i], NSDSEL_RD, &bIsReady, &nfds));
if(bIsReady) {
dbgprintf("New connect on NSD %p.\n", pThis->ppLstn[i]);
- SessAccept(pThis, &pNewSess, pThis->ppLstn[i]);
+ SessAccept(pThis, pThis->ppLstnPort[i], &pNewSess, pThis->ppLstn[i]);
--nfds; /* indicate we have processed one */
}
}
@@ -453,15 +521,23 @@ Run(tcpsrv_t *pThis)
/* now check the sessions */
iTCPSess = TCPSessGetNxtSess(pThis, -1);
while(nfds && iTCPSess != -1) {
- CHKiRet(nssel.IsReady(pSel, pThis->pSessions[iTCPSess]->pStrm, NSDSEL_RD, &bIsReady, &nfds));
- if(bIsReady) {
- char buf[8*1024]; /* reception buffer - may hold a partial or multiple messages */
+ localRet = nssel.IsReady(pSel, pThis->pSessions[iTCPSess]->pStrm, NSDSEL_RD, &bIsReady, &nfds);
+ if(bIsReady || localRet != RS_RET_OK) {
+ char buf[128*1024]; /* reception buffer - may hold a partial or multiple messages */
dbgprintf("netstream %p with new data\n", pThis->pSessions[iTCPSess]->pStrm);
/* Receive message */
iRet = pThis->pRcvData(pThis->pSessions[iTCPSess], buf, sizeof(buf), &iRcvd);
switch(iRet) {
case RS_RET_CLOSED:
+ if(pThis->bEmitMsgOnClose) {
+ uchar *pszPeer;
+ int lenPeer;
+ errno = 0;
+ prop.GetString(pThis->pSessions[iTCPSess]->fromHostIP, &pszPeer, &lenPeer);
+ errmsg.LogError(0, RS_RET_PEER_CLOSED_CONN, "Netstream session %p closed by remote peer %s.\n",
+ pThis->pSessions[iTCPSess]->pStrm, pszPeer);
+ }
pThis->pOnRegularClose(pThis->pSessions[iTCPSess]);
tcps_sess.Destruct(&pThis->pSessions[iTCPSess]);
break;
@@ -470,12 +546,13 @@ Run(tcpsrv_t *pThis)
break;
case RS_RET_OK:
/* valid data received, process it! */
- if(tcps_sess.DataRcvd(pThis->pSessions[iTCPSess], buf, iRcvd) != RS_RET_OK) {
+ localRet = tcps_sess.DataRcvd(pThis->pSessions[iTCPSess], buf, iRcvd);
+ if(localRet != RS_RET_OK && localRet != RS_RET_QUEUE_FULL) {
/* in this case, something went awfully wrong.
* We are instructed to terminate the session.
*/
- errmsg.LogError(0, NO_ERRCODE, "Tearing down TCP Session %d - see "
- "previous messages for reason(s)\n", iTCPSess);
+ errmsg.LogError(0, localRet, "Tearing down TCP Session %d - see "
+ "previous messages for reason(s)", iTCPSess);
pThis->pOnErrClose(pThis->pSessions[iTCPSess]);
tcps_sess.Destruct(&pThis->pSessions[iTCPSess]);
}
@@ -499,7 +576,9 @@ finalize_it: /* this is a very special case - this time only we do not exit the
* crashed, which made sense (the rest of the engine was not prepared for
* that) -- rgerhards, 2008-05-19
*/
- /*EMPTY*/;
+ if(pSel != NULL) { /* cleanup missing? happens during err exit! */
+ nssel.Destruct(&pSel);
+ }
}
/* note that this point is usually not reached */
@@ -512,7 +591,10 @@ finalize_it: /* this is a very special case - this time only we do not exit the
/* Standard-Constructor */
BEGINobjConstruct(tcpsrv) /* be sure to specify the object type also in END macro! */
- pThis->iSessMax = TCPSESS_MAX_DEFAULT; /* TODO: useful default ;) */
+ pThis->iSessMax = TCPSESS_MAX_DEFAULT;
+ pThis->iLstnMax = TCPLSTN_MAX_DEFAULT;
+ pThis->addtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER;
+ pThis->OnMsgReceive = NULL;
ENDobjConstruct(tcpsrv)
@@ -534,7 +616,8 @@ tcpsrvConstructFinalize(tcpsrv_t *pThis)
CHKiRet(netstrms.ConstructFinalize(pThis->pNS));
/* set up listeners */
- CHKmalloc(pThis->ppLstn = calloc(TCPLSTN_MAX_DEFAULT, sizeof(netstrm_t*)));
+ CHKmalloc(pThis->ppLstn = calloc(pThis->iLstnMax, sizeof(netstrm_t*)));
+ CHKmalloc(pThis->ppLstnPort = calloc(pThis->iLstnMax, sizeof(tcpLstnPortList_t*)));
iRet = pThis->OpenLstnSocks(pThis);
finalize_it:
@@ -556,10 +639,10 @@ CODESTARTobjDestruct(tcpsrv)
if(pThis->pNS != NULL)
netstrms.Destruct(&pThis->pNS);
- if(pThis->pszDrvrAuthMode != NULL)
- free(pThis->pszDrvrAuthMode);
- if(pThis->ppLstn != NULL)
- free(pThis->ppLstn);
+ free(pThis->pszDrvrAuthMode);
+ free(pThis->ppLstn);
+ free(pThis->ppLstnPort);
+ free(pThis->pszInputName);
ENDobjDestruct(tcpsrv)
@@ -657,6 +740,65 @@ SetUsrP(tcpsrv_t *pThis, void *pUsr)
RETiRet;
}
+static rsRetVal
+SetOnMsgReceive(tcpsrv_t *pThis, rsRetVal (*OnMsgReceive)(tcps_sess_t*, uchar*, int))
+{
+ DEFiRet;
+ assert(OnMsgReceive != NULL);
+ pThis->OnMsgReceive = OnMsgReceive;
+ RETiRet;
+}
+
+
+
+/* Set additional framing to use (if any) -- rgerhards, 2008-12-10 */
+static rsRetVal
+SetAddtlFrameDelim(tcpsrv_t *pThis, int iDelim)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, tcpsrv);
+ pThis->addtlFrameDelim = iDelim;
+ RETiRet;
+}
+
+
+/* Set the input name to use -- rgerhards, 2008-12-10 */
+static rsRetVal
+SetInputName(tcpsrv_t *pThis, uchar *name)
+{
+ uchar *pszName;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, tcpsrv);
+ if(name == NULL)
+ pszName = NULL;
+ else
+ CHKmalloc(pszName = ustrdup(name));
+ free(pThis->pszInputName);
+ pThis->pszInputName = pszName;
+finalize_it:
+ RETiRet;
+}
+
+
+/* Set the ruleset (ptr) to use */
+static rsRetVal
+SetRuleset(tcpsrv_t *pThis, ruleset_t *pRuleset)
+{
+ DEFiRet;
+ pThis->pRuleset = pRuleset;
+ RETiRet;
+}
+
+
+/* Set connection close notification */
+static rsRetVal
+SetNotificationOnRemoteClose(tcpsrv_t *pThis, int bNewVal)
+{
+ DEFiRet;
+ pThis->bEmitMsgOnClose = bNewVal;
+ RETiRet;
+}
+
/* here follows a number of methods that shuffle authentication settings down
* to the drivers. Drivers not supporting these settings may return an error
@@ -680,7 +822,7 @@ SetDrvrAuthMode(tcpsrv_t *pThis, uchar *mode)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, tcpsrv);
- CHKmalloc(pThis->pszDrvrAuthMode = (uchar*)strdup((char*)mode));
+ CHKmalloc(pThis->pszDrvrAuthMode = ustrdup(mode));
finalize_it:
RETiRet;
}
@@ -702,6 +844,20 @@ SetDrvrPermPeers(tcpsrv_t *pThis, permittedPeers_t *pPermPeers)
* -------------------------------------------------------------------------- */
+/* set max number of listeners
+ * this must be called before ConstructFinalize, or it will have no effect!
+ * rgerhards, 2009-08-17
+ */
+static rsRetVal
+SetLstnMax(tcpsrv_t *pThis, int iMax)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, tcpsrv);
+ pThis->iLstnMax = iMax;
+ RETiRet;
+}
+
+
/* set max number of sessions
* this must be called before ConstructFinalize, or it will have no effect!
* rgerhards, 2009-04-09
@@ -735,13 +891,16 @@ CODESTARTobjQueryInterface(tcpsrv)
pIf->ConstructFinalize = tcpsrvConstructFinalize;
pIf->Destruct = tcpsrvDestruct;
- pIf->SessAccept = SessAccept;
+ //pIf->SessAccept = SessAccept;
pIf->configureTCPListen = configureTCPListen;
pIf->create_tcp_socket = create_tcp_socket;
pIf->Run = Run;
pIf->SetUsrP = SetUsrP;
+ pIf->SetInputName = SetInputName;
+ pIf->SetAddtlFrameDelim = SetAddtlFrameDelim;
pIf->SetSessMax = SetSessMax;
+ pIf->SetLstnMax = SetLstnMax;
pIf->SetDrvrMode = SetDrvrMode;
pIf->SetDrvrAuthMode = SetDrvrAuthMode;
pIf->SetDrvrPermPeers = SetDrvrPermPeers;
@@ -755,6 +914,9 @@ CODESTARTobjQueryInterface(tcpsrv)
pIf->SetCBOnDestruct = SetCBOnDestruct;
pIf->SetCBOnRegularClose = SetCBOnRegularClose;
pIf->SetCBOnErrClose = SetCBOnErrClose;
+ pIf->SetOnMsgReceive = SetOnMsgReceive;
+ pIf->SetRuleset = SetRuleset;
+ pIf->SetNotificationOnRemoteClose = SetNotificationOnRemoteClose;
finalize_it:
ENDobjQueryInterface(tcpsrv)
@@ -768,6 +930,8 @@ CODESTARTObjClassExit(tcpsrv)
/* release objects we no longer need */
objRelease(tcps_sess, DONT_LOAD_LIB);
objRelease(conf, CORE_COMPONENT);
+ objRelease(prop, CORE_COMPONENT);
+ objRelease(ruleset, CORE_COMPONENT);
objRelease(glbl, CORE_COMPONENT);
objRelease(errmsg, CORE_COMPONENT);
objRelease(netstrms, DONT_LOAD_LIB);
@@ -791,6 +955,8 @@ BEGINObjClassInit(tcpsrv, 1, OBJ_IS_LOADABLE_MODULE) /* class, version - CHANGE
CHKiRet(objUse(tcps_sess, DONT_LOAD_LIB));
CHKiRet(objUse(conf, CORE_COMPONENT));
CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(ruleset, CORE_COMPONENT));
+ CHKiRet(objUse(prop, CORE_COMPONENT));
/* set our own handlers */
OBJSetMethodHandler(objMethod_DEBUGPRINT, tcpsrvDebugPrint);
diff --git a/tcpsrv.h b/tcpsrv.h
index 280f5083..b8d82163 100644
--- a/tcpsrv.h
+++ b/tcpsrv.h
@@ -23,19 +23,46 @@
#define INCLUDED_TCPSRV_H
#include "obj.h"
+#include "prop.h"
#include "tcps_sess.h"
+/* support for framing anomalies */
+typedef enum ETCPsyslogFramingAnomaly {
+ frame_normal = 0,
+ frame_NetScreen = 1,
+ frame_CiscoIOS = 2
+} eTCPsyslogFramingAnomaly;
+
+
+/* list of tcp listen ports */
+struct tcpLstnPortList_s {
+ uchar *pszPort; /**< the ports the listener shall listen on */
+ prop_t *pInputName;
+ tcpsrv_t *pSrv; /**< pointer to higher-level server instance */
+ ruleset_t *pRuleset; /**< associated ruleset */
+ tcpLstnPortList_t *pNext; /**< next port or NULL */
+};
+
+#define TCPSRV_NO_ADDTL_DELIMITER -1 /* specifies that no additional delimiter is to be used in TCP framing */
+
/* the tcpsrv object */
struct tcpsrv_s {
BEGINobjInstance; /**< Data to implement generic object - MUST be the first data element! */
netstrms_t *pNS; /**< pointer to network stream subsystem */
int iDrvrMode; /**< mode of the stream driver to use */
uchar *pszDrvrAuthMode; /**< auth mode of the stream driver to use */
+ uchar *pszInputName; /**< value to be used as input name */
+ ruleset_t *pRuleset; /**< ruleset to bind to */
permittedPeers_t *pPermPeers;/**< driver's permitted peers */
- int iLstnMax; /**< max nbr of listeners currently supported */
+ bool bEmitMsgOnClose; /**< emit an informational message when the remote peer closes connection */
+ int iLstnCurr; /**< max nbr of listeners currently supported */
netstrm_t **ppLstn; /**< our netstream listners */
+ tcpLstnPortList_t **ppLstnPort; /**< pointer to relevant listen port description */
+ int iLstnMax; /**< max number of listners supported */
int iSessMax; /**< max number of sessions supported */
- char *TCPLstnPort; /**< the port the listener shall listen on */
+ tcpLstnPortList_t *pLstnPorts; /**< head pointer for listen ports */
+
+ int addtlFrameDelim; /**< additional frame delimiter for plain TCP syslog framing (e.g. to handle NetScreen) */
tcps_sess_t **pSessions;/**< array of all of our sessions */
void *pUsr; /**< a user-settable pointer (provides extensibility for "derived classes")*/
/* callbacks */
@@ -50,6 +77,7 @@ struct tcpsrv_s {
rsRetVal (*pOnSessAccept)(tcpsrv_t *, tcps_sess_t*);
rsRetVal (*OnSessConstructFinalize)(void*);
rsRetVal (*pOnSessDestruct)(void*);
+ rsRetVal (*OnMsgReceive)(tcps_sess_t *, uchar *pszMsg, int iLenMsg); /* submit message callback */
};
@@ -59,11 +87,13 @@ BEGINinterface(tcpsrv) /* name must also be changed in ENDinterface macro! */
rsRetVal (*Construct)(tcpsrv_t **ppThis);
rsRetVal (*ConstructFinalize)(tcpsrv_t __attribute__((unused)) *pThis);
rsRetVal (*Destruct)(tcpsrv_t **ppThis);
- void (*configureTCPListen)(tcpsrv_t*, char *cOptarg);
- rsRetVal (*SessAccept)(tcpsrv_t *pThis, tcps_sess_t **ppSess, netstrm_t *pStrm);
+ rsRetVal (*configureTCPListen)(tcpsrv_t*, uchar *pszPort);
+ //rsRetVal (*SessAccept)(tcpsrv_t *pThis, tcpLstnPortList_t*, tcps_sess_t **ppSess, netstrm_t *pStrm);
rsRetVal (*create_tcp_socket)(tcpsrv_t *pThis);
rsRetVal (*Run)(tcpsrv_t *pThis);
/* set methods */
+ rsRetVal (*SetAddtlFrameDelim)(tcpsrv_t*, int);
+ rsRetVal (*SetInputName)(tcpsrv_t*, uchar*);
rsRetVal (*SetUsrP)(tcpsrv_t*, void*);
rsRetVal (*SetCBIsPermittedHost)(tcpsrv_t*, int (*) (struct sockaddr *addr, char*, void*, void*));
rsRetVal (*SetCBOpenLstnSocks)(tcpsrv_t *, rsRetVal (*)(tcpsrv_t*));
@@ -79,10 +109,21 @@ BEGINinterface(tcpsrv) /* name must also be changed in ENDinterface macro! */
rsRetVal (*SetCBOnSessAccept)(tcpsrv_t*, rsRetVal (*) (tcpsrv_t*, tcps_sess_t*));
rsRetVal (*SetCBOnSessDestruct)(tcpsrv_t*, rsRetVal (*) (void*));
rsRetVal (*SetCBOnSessConstructFinalize)(tcpsrv_t*, rsRetVal (*) (void*));
- /* added v4 */
+ /* added v5 */
rsRetVal (*SetSessMax)(tcpsrv_t *pThis, int iMaxSess); /* 2009-04-09 */
+ /* added v6 */
+ rsRetVal (*SetOnMsgReceive)(tcpsrv_t *pThis, rsRetVal (*OnMsgReceive)(tcps_sess_t*, uchar*, int)); /* 2009-05-24 */
+ rsRetVal (*SetRuleset)(tcpsrv_t *pThis, ruleset_t*); /* 2009-06-12 */
+ /* added v7 */
+ rsRetVal (*SetLstnMax)(tcpsrv_t *pThis, int iMaxLstn); /* 2009-08-17 */
+ rsRetVal (*SetNotificationOnRemoteClose)(tcpsrv_t *pThis, int bNewVal); /* 2009-10-01 */
ENDinterface(tcpsrv)
-#define tcpsrvCURR_IF_VERSION 4 /* increment whenever you change the interface structure! */
+#define tcpsrvCURR_IF_VERSION 8 /* increment whenever you change the interface structure! */
+/* change for v4:
+ * - SetAddtlFrameDelim() added -- rgerhards, 2008-12-10
+ * - SetInputName() added -- rgerhards, 2008-12-10
+ * change for v5 and up: see above
+ */
/* prototypes */
diff --git a/template.c b/template.c
index 6fb7ba2b..a5331d57 100644
--- a/template.c
+++ b/template.c
@@ -47,62 +47,64 @@ static struct template *tplRoot = NULL; /* the root of the template list */
static struct template *tplLast = NULL; /* points to the last element of the template list */
static struct template *tplLastStatic = NULL; /* last static element of the template list */
-/* This functions converts a template into a string. It should
- * actually be in template.c, but this requires larger re-structuring
- * of the code (because all the property-access functions are static
- * to this module). I have placed it next to the iov*() functions, as
- * it is somewhat similiar in what it does.
- *
- * The function takes a pointer to a template and a pointer to a msg object.
- * It the creates a string based on the template definition. A pointer
- * to that string is returned to the caller. The caller MUST FREE that
- * pointer when it is no longer needed. If the function fails, NULL
- * is returned.
- * If memory allocation fails in this function, we silently return
- * NULL. The reason is that we can not do anything against it. And
- * if we raise an alert, the memory situation might become even
- * worse. So we prefer to let the caller deal with it.
- * rgerhards, 2007-07-03
+
+
+/* helper to tplToString, extends buffer */
+#define ALLOC_INC 128
+static inline rsRetVal ExtendBuf(uchar **pBuf, size_t *pLenBuf, size_t iMinSize)
+{
+ uchar *pNewBuf;
+ size_t iNewSize;
+ DEFiRet;
+
+ iNewSize = (iMinSize / ALLOC_INC + 1) * ALLOC_INC;
+ CHKmalloc(pNewBuf = (uchar*) realloc(*pBuf, iNewSize));
+ *pBuf = pNewBuf;
+ *pLenBuf = iNewSize;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* This functions converts a template into a string.
*
- * rgerhards, 2007-09-05: I changed the interface to use the standard iRet
- * "calling sequence". This greatly eases complexity when it comes to handling
- * errors in called modules (plus, it is much nicer).
+ * The function takes a pointer to a template and a pointer to a msg object
+ * as well as a pointer to an output buffer and its size. Note that the output
+ * buffer pointer may be NULL, size 0, in which case a new one is allocated.
+ * The outpub buffer is grown as required. It is the caller's duty to free the
+ * buffer when it is done. Note that it is advisable to reuse memory, as this
+ * offers big performance improvements.
+ * rewritten 2009-06-19 rgerhards
*/
-rsRetVal tplToString(struct template *pTpl, msg_t *pMsg, uchar** ppSz)
+rsRetVal tplToString(struct template *pTpl, msg_t *pMsg, uchar **ppBuf, size_t *pLenBuf)
{
DEFiRet;
struct templateEntry *pTpe;
- cstr_t *pCStr;
- unsigned short bMustBeFreed;
+ int iBuf;
+ unsigned short bMustBeFreed = 0;
uchar *pVal;
- size_t iLenVal;
+ size_t iLenVal = 0;
assert(pTpl != NULL);
assert(pMsg != NULL);
- assert(ppSz != NULL);
+ assert(ppBuf != NULL);
+ assert(pLenBuf != NULL);
/* loop through the template. We obtain one value
* and copy it over to our dynamic string buffer. Then, we
* free the obtained value (if requested). We continue this
* loop until we got hold of all values.
*/
- CHKiRet(rsCStrConstruct(&pCStr));
-
pTpe = pTpl->pEntryRoot;
+ iBuf = 0;
while(pTpe != NULL) {
if(pTpe->eEntryType == CONSTANT) {
- CHKiRet_Hdlr(rsCStrAppendStrWithLen(pCStr,
- (uchar *) pTpe->data.constant.pConstant,
- pTpe->data.constant.iLenConstant)
- ) {
- dbgprintf("error %d during tplToString()\n", iRet);
- /* it does not make sense to continue now */
- rsCStrDestruct(&pCStr);
- FINALIZE;
- }
+ pVal = (uchar*) pTpe->data.constant.pConstant;
+ iLenVal = pTpe->data.constant.iLenConstant;
+ bMustBeFreed = 0;
} else if(pTpe->eEntryType == FIELD) {
- pVal = (uchar*) MsgGetProp(pMsg, pTpe, NULL, &bMustBeFreed);
- iLenVal = strlen((char*) pVal);
+ pVal = (uchar*) MsgGetProp(pMsg, pTpe, pTpe->data.field.propid, &iLenVal, &bMustBeFreed);
/* we now need to check if we should use SQL option. In this case,
* we must go over the generated string and escape '\'' characters.
* rgerhards, 2005-09-22: the option values below look somewhat misplaced,
@@ -113,33 +115,83 @@ rsRetVal tplToString(struct template *pTpl, msg_t *pMsg, uchar** ppSz)
doSQLEscape(&pVal, &iLenVal, &bMustBeFreed, 1);
else if(pTpl->optFormatForSQL == 2)
doSQLEscape(&pVal, &iLenVal, &bMustBeFreed, 0);
- /* value extracted, so lets copy */
- CHKiRet_Hdlr(rsCStrAppendStrWithLen(pCStr, (uchar*) pVal, iLenVal)) {
- dbgprintf("error %d during tplToString()\n", iRet);
- /* it does not make sense to continue now */
- rsCStrDestruct(&pCStr);
- if(bMustBeFreed)
- free(pVal);
- FINALIZE;
- }
- if(bMustBeFreed)
- free(pVal);
}
+ /* got source, now copy over */
+ if(iBuf + iLenVal + 1 >= *pLenBuf) /* we reserve one char for the final \0! */
+ CHKiRet(ExtendBuf(ppBuf, pLenBuf, iBuf + iLenVal + 1));
+
+ if(iLenVal > 0) { /* may be zero depending on property */
+ memcpy(*ppBuf + iBuf, pVal, iLenVal);
+ iBuf += iLenVal;
+ }
+
+ if(bMustBeFreed)
+ free(pVal);
+
pTpe = pTpe->pNext;
}
- /* we are done with the template, now let's convert the result into a
- * "real" (usable) string and discard the helper structures.
- */
- CHKiRet(rsCStrFinish(pCStr));
- CHKiRet(rsCStrConvSzStrAndDestruct(pCStr, &pVal, 0));
+ (*ppBuf)[iBuf] = '\0'; /* space was reserved above (see copy) */
finalize_it:
- *ppSz = (iRet == RS_RET_OK) ? pVal : NULL;
+ RETiRet;
+}
+
+
+/* This functions converts a template into an array of strings.
+ * For further general details, see the very similar funtion
+ * tpltoString().
+ * Instead of a string, an array of string pointers is returned by
+ * thus function. The caller is repsonsible for destroying that array as
+ * well as all of its elements. The array is of fixed size. It's end
+ * is indicated by a NULL pointer.
+ * rgerhards, 2009-04-03
+ */
+rsRetVal tplToArray(struct template *pTpl, msg_t *pMsg, uchar*** ppArr)
+{
+ DEFiRet;
+ struct templateEntry *pTpe;
+ uchar **pArr;
+ int iArr;
+ size_t propLen;
+ unsigned short bMustBeFreed;
+ uchar *pVal;
+
+ assert(pTpl != NULL);
+ assert(pMsg != NULL);
+ assert(ppArr != NULL);
+
+ /* loop through the template. We obtain one value, create a
+ * private copy (if necessary), add it to the string array
+ * and then on to the next until we have processed everything.
+ */
+
+ CHKmalloc(pArr = calloc(pTpl->tpenElements + 1, sizeof(uchar*)));
+ iArr = 0;
+
+ pTpe = pTpl->pEntryRoot;
+ while(pTpe != NULL) {
+ if(pTpe->eEntryType == CONSTANT) {
+ CHKmalloc(pArr[iArr] = (uchar*)strdup((char*) pTpe->data.constant.pConstant));
+ } else if(pTpe->eEntryType == FIELD) {
+ pVal = (uchar*) MsgGetProp(pMsg, pTpe, pTpe->data.field.propid, &propLen, &bMustBeFreed);
+ if(bMustBeFreed) { /* if it must be freed, it is our own private copy... */
+ pArr[iArr] = pVal; /* ... so we can use it! */
+ } else {
+ CHKmalloc(pArr[iArr] = (uchar*)strdup((char*) pVal));
+ }
+ }
+ iArr++;
+ pTpe = pTpe->pNext;
+ }
+
+finalize_it:
+ *ppArr = (iRet == RS_RET_OK) ? pArr : NULL;
RETiRet;
}
+
/* Helper to doSQLEscape. This is called if doSQLEscape
* runs out of memory allocating the escaped string.
* Then we are in trouble. We can
@@ -216,21 +268,21 @@ doSQLEscape(uchar **pp, size_t *pLen, unsigned short *pbMustBeFreed, int escapeM
p = *pp;
iLen = *pLen;
- CHKiRet(rsCStrConstruct(&pStrB));
+ CHKiRet(cstrConstruct(&pStrB));
while(*p) {
if(*p == '\'') {
- CHKiRet(rsCStrAppendChar(pStrB, (escapeMode == 0) ? '\'' : '\\'));
+ CHKiRet(cstrAppendChar(pStrB, (escapeMode == 0) ? '\'' : '\\'));
iLen++; /* reflect the extra character */
} else if((escapeMode == 1) && (*p == '\\')) {
- CHKiRet(rsCStrAppendChar(pStrB, '\\'));
+ CHKiRet(cstrAppendChar(pStrB, '\\'));
iLen++; /* reflect the extra character */
}
- CHKiRet(rsCStrAppendChar(pStrB, *p));
+ CHKiRet(cstrAppendChar(pStrB, *p));
++p;
}
- CHKiRet(rsCStrFinish(pStrB));
- CHKiRet(rsCStrConvSzStrAndDestruct(pStrB, &pszGenerated, 0));
+ CHKiRet(cstrFinalize(pStrB));
+ CHKiRet(cstrConvSzStrAndDestruct(pStrB, &pszGenerated, 0));
if(*pbMustBeFreed)
free(*pp); /* discard previous value */
@@ -243,7 +295,7 @@ finalize_it:
if(iRet != RS_RET_OK) {
doSQLEmergencyEscape(*pp, escapeMode);
if(pStrB != NULL)
- rsCStrDestruct(&pStrB);
+ cstrDestruct(&pStrB);
}
RETiRet;
@@ -320,9 +372,8 @@ static int do_Constant(unsigned char **pp, struct template *pTpl)
p = *pp;
- if(rsCStrConstruct(&pStrB) != RS_RET_OK)
+ if(cstrConstruct(&pStrB) != RS_RET_OK)
return 1;
- rsCStrSetAllocIncrement(pStrB, 32);
/* process the message and expand escapes
* (additional escapes can be added here if needed)
*/
@@ -331,22 +382,22 @@ static int do_Constant(unsigned char **pp, struct template *pTpl)
switch(*++p) {
case '\0':
/* the best we can do - it's invalid anyhow... */
- rsCStrAppendChar(pStrB, *p);
+ cstrAppendChar(pStrB, *p);
break;
case 'n':
- rsCStrAppendChar(pStrB, '\n');
+ cstrAppendChar(pStrB, '\n');
++p;
break;
case 'r':
- rsCStrAppendChar(pStrB, '\r');
+ cstrAppendChar(pStrB, '\r');
++p;
break;
case '\\':
- rsCStrAppendChar(pStrB, '\\');
+ cstrAppendChar(pStrB, '\\');
++p;
break;
case '%':
- rsCStrAppendChar(pStrB, '%');
+ cstrAppendChar(pStrB, '%');
++p;
break;
case '0': /* numerical escape sequence */
@@ -363,33 +414,30 @@ static int do_Constant(unsigned char **pp, struct template *pTpl)
while(*p && isdigit((int)*p)) {
i = i * 10 + *p++ - '0';
}
- rsCStrAppendChar(pStrB, i);
+ cstrAppendChar(pStrB, i);
break;
default:
- rsCStrAppendChar(pStrB, *p++);
+ cstrAppendChar(pStrB, *p++);
break;
}
}
else
- rsCStrAppendChar(pStrB, *p++);
+ cstrAppendChar(pStrB, *p++);
}
if((pTpe = tpeConstruct(pTpl)) == NULL) {
- /* OK, we are out of luck. Let's invalidate the
- * entry and that's it.
- */
- pTpe->eEntryType = UNDEFINED;
+ rsCStrDestruct(&pStrB);
return 1;
}
pTpe->eEntryType = CONSTANT;
- rsCStrFinish(pStrB);
+ cstrFinalize(pStrB);
/* We obtain the length from the counted string object
* (before we delete it). Later we might take additional
* benefit from the counted string object.
* 2005-09-09 rgerhards
*/
pTpe->data.constant.iLenConstant = rsCStrLen(pStrB);
- if(rsCStrConvSzStrAndDestruct(pStrB, &pTpe->data.constant.pConstant, 0) != RS_RET_OK)
+ if(cstrConvSzStrAndDestruct(pStrB, &pTpe->data.constant.pConstant, 0) != RS_RET_OK)
return 1;
*pp = p;
@@ -438,6 +486,8 @@ static void doOptions(unsigned char **pp, struct templateEntry *pTpe)
pTpe->data.field.eDateFormat = tplFmtPgSQLDate;
} else if(!strcmp((char*)Buf, "date-rfc3164")) {
pTpe->data.field.eDateFormat = tplFmtRFC3164Date;
+ } else if(!strcmp((char*)Buf, "date-rfc3164-buggyday")) {
+ pTpe->data.field.eDateFormat = tplFmtRFC3164BuggyDate;
} else if(!strcmp((char*)Buf, "date-rfc3339")) {
pTpe->data.field.eDateFormat = tplFmtRFC3339Date;
} else if(!strcmp((char*)Buf, "date-subseconds")) {
@@ -460,6 +510,8 @@ static void doOptions(unsigned char **pp, struct templateEntry *pTpe)
pTpe->data.field.options.bSecPathDrop = 1;
} else if(!strcmp((char*)Buf, "secpath-replace")) {
pTpe->data.field.options.bSecPathReplace = 1;
+ } else if(!strcmp((char*)Buf, "csv")) {
+ pTpe->data.field.options.bCSV = 1;
} else {
dbgprintf("Invalid field option '%s' specified - ignored.\n", Buf);
}
@@ -494,7 +546,7 @@ static int do_Parameter(unsigned char **pp, struct template *pTpl)
p = (unsigned char*) *pp;
- if(rsCStrConstruct(&pStrB) != RS_RET_OK)
+ if(cstrConstruct(&pStrB) != RS_RET_OK)
return 1;
if((pTpe = tpeConstruct(pTpl)) == NULL) {
@@ -505,14 +557,18 @@ static int do_Parameter(unsigned char **pp, struct template *pTpl)
pTpe->eEntryType = FIELD;
while(*p && *p != '%' && *p != ':') {
- rsCStrAppendChar(pStrB, tolower(*p));
+ cstrAppendChar(pStrB, tolower(*p));
++p; /* do NOT do this in tolower()! */
}
- /* got the name*/
- rsCStrFinish(pStrB);
- if(rsCStrConvSzStrAndDestruct(pStrB, &pTpe->data.field.pPropRepl, 0) != RS_RET_OK)
+ /* got the name */
+ cstrFinalize(pStrB);
+
+ if(propNameToID(pStrB, &pTpe->data.field.propid) != RS_RET_OK) {
+ cstrDestruct(&pStrB);
return 1;
+ }
+ cstrDestruct(&pStrB); /* no longer needed, now use ID */
/* Check frompos, if it has an R, then topos should be a regex */
if(*p == ':') {
@@ -924,7 +980,6 @@ void tplDeleteAll(void)
{
struct template *pTpl, *pTplDel;
struct templateEntry *pTpe, *pTpeDel;
- rsRetVal iRetLocal;
BEGINfunc
pTpl = tplRoot;
@@ -947,12 +1002,10 @@ void tplDeleteAll(void)
case FIELD:
/* check if we have a regexp and, if so, delete it */
if(pTpeDel->data.field.has_regex != 0) {
- if((iRetLocal = objUse(regexp, LM_REGEXP_FILENAME)) == RS_RET_OK) {
+ if(objUse(regexp, LM_REGEXP_FILENAME) == RS_RET_OK) {
regexp.regfree(&(pTpeDel->data.field.re));
}
}
- /*dbgprintf("(FIELD), value: '%s'", pTpeDel->data.field.pPropRepl);*/
- free(pTpeDel->data.field.pPropRepl);
break;
}
/*dbgprintf("\n");*/
@@ -975,7 +1028,6 @@ void tplDeleteNew(void)
{
struct template *pTpl, *pTplDel;
struct templateEntry *pTpe, *pTpeDel;
- rsRetVal iRetLocal;
BEGINfunc
@@ -1004,12 +1056,10 @@ void tplDeleteNew(void)
case FIELD:
/* check if we have a regexp and, if so, delete it */
if(pTpeDel->data.field.has_regex != 0) {
- if((iRetLocal = objUse(regexp, LM_REGEXP_FILENAME)) == RS_RET_OK) {
+ if(objUse(regexp, LM_REGEXP_FILENAME) == RS_RET_OK) {
regexp.regfree(&(pTpeDel->data.field.re));
}
}
- /*dbgprintf("(FIELD), value: '%s'", pTpeDel->data.field.pPropRepl);*/
- free(pTpeDel->data.field.pPropRepl);
break;
}
/*dbgprintf("\n");*/
@@ -1058,7 +1108,7 @@ void tplPrintList(void)
pTpe->data.constant.pConstant);
break;
case FIELD:
- dbgprintf("(FIELD), value: '%s' ", pTpe->data.field.pPropRepl);
+ dbgprintf("(FIELD), value: '%d' ", pTpe->data.field.propid);
switch(pTpe->data.field.eDateFormat) {
case tplFmtDefault:
break;
@@ -1105,6 +1155,9 @@ void tplPrintList(void)
if(pTpe->data.field.options.bSPIffNo1stSP) {
dbgprintf("[SP iff no first SP] ");
}
+ if(pTpe->data.field.options.bCSV) {
+ dbgprintf("[format as CSV (RFC4180)]");
+ }
if(pTpe->data.field.options.bDropLastLF) {
dbgprintf("[drop last LF in msg] ");
}
diff --git a/template.h b/template.h
index 04137b09..71e8b428 100644
--- a/template.h
+++ b/template.h
@@ -48,7 +48,7 @@ struct template {
enum EntryTypes { UNDEFINED = 0, CONSTANT = 1, FIELD = 2 };
enum tplFormatTypes { tplFmtDefault = 0, tplFmtMySQLDate = 1,
tplFmtRFC3164Date = 2, tplFmtRFC3339Date = 3, tplFmtPgSQLDate = 4,
- tplFmtSecFrac = 5};
+ tplFmtSecFrac = 5, tplFmtRFC3164BuggyDate = 6};
enum tplFormatCaseConvTypes { tplCaseConvNo = 0, tplCaseConvUpper = 1, tplCaseConvLower = 2 };
#include "msg.h"
@@ -63,7 +63,7 @@ struct templateEntry {
int iLenConstant; /* its length */
} constant;
struct {
- uchar *pPropRepl; /* pointer to property replacer string */
+ propid_t propid; /* property to be used */
unsigned iFromPos; /* for partial strings only chars from this position ... */
unsigned iToPos; /* up to that one... */
#ifdef FEATURE_REGEXP
@@ -94,9 +94,10 @@ struct templateEntry {
unsigned bSpaceCC: 1; /* change control characters to spaceescape? */
unsigned bEscapeCC: 1; /* escape control characters? */
unsigned bDropLastLF: 1; /* drop last LF char in msg (PIX!) */
- unsigned bSecPathDrop: 1; /* drop slashes, replace dots, empty string */
- unsigned bSecPathReplace: 1; /* replace slashes, replace dots, empty string */
- unsigned bSPIffNo1stSP: 1; /* replace slashes, replace dots, empty string */
+ unsigned bSecPathDrop: 1; /* drop slashes, replace dots, empty string */
+ unsigned bSecPathReplace: 1; /* replace slashes, replace dots, empty string */
+ unsigned bSPIffNo1stSP: 1; /* replace slashes, replace dots, empty string */
+ unsigned bCSV: 1; /* format field in CSV (RFC 4180) format */
} options; /* options as bit fields */
} field;
} data;
@@ -125,7 +126,8 @@ void tplLastStaticInit(struct template *tpl);
* BEFORE msg.h, even if your code file does not actually need it.
* rgerhards, 2007-08-06
*/
-rsRetVal tplToString(struct template *pTpl, msg_t *pMsg, uchar** ppSz);
+rsRetVal tplToArray(struct template *pTpl, msg_t *pMsg, uchar*** ppArr);
+rsRetVal tplToString(struct template *pTpl, msg_t *pMsg, uchar** ppSz, size_t *);
rsRetVal doSQLEscape(uchar **pp, size_t *pLen, unsigned short *pbMustBeFreed, int escapeMode);
rsRetVal templateInit();
diff --git a/tests/1.rstest b/tests/1.rstest
index 5c152589..4716e8b3 100644
--- a/tests/1.rstest
+++ b/tests/1.rstest
@@ -4,23 +4,23 @@ in:
'test 1' <> $var or /* some comment */($SEVERITY == -4 +5 -(3 * - 2) and $fromhost == '127.0.0.1') then
$$$
out:
-00000000: PUSHCONSTANT test 1[cstr]
-00000001: PUSHMSGVAR var[cstr]
-00000002: !=
-00000003: PUSHMSGVAR severity[cstr]
-00000004: PUSHCONSTANT 4[nbr]
-00000005: UNARY_MINUS
-00000006: PUSHCONSTANT 5[nbr]
-00000007: +
-00000008: PUSHCONSTANT 3[nbr]
-00000009: PUSHCONSTANT 2[nbr]
-00000010: UNARY_MINUS
-00000011: *
-00000012: -
-00000013: ==
-00000014: PUSHMSGVAR fromhost[cstr]
-00000015: PUSHCONSTANT 127.0.0.1[cstr]
-00000016: ==
+00000000: push_const test 1[cstr]
+00000001: push_msgvar var[cstr]
+00000002: cmp_!=
+00000003: push_msgvar severity[cstr]
+00000004: push_const 4[nbr]
+00000005: unary_minus
+00000006: push_const 5[nbr]
+00000007: add
+00000008: push_const 3[nbr]
+00000009: push_const 2[nbr]
+00000010: unary_minus
+00000011: mul
+00000012: sub
+00000013: cmp_==
+00000014: push_msgvar fromhost[cstr]
+00000015: push_const 127.0.0.1[cstr]
+00000016: cmp_==
00000017: and
00000018: or
$$$
diff --git a/tests/2.rstest b/tests/2.rstest
index 7fb5b799..f0e8205b 100644
--- a/tests/2.rstest
+++ b/tests/2.rstest
@@ -4,7 +4,7 @@ in:
$msg contains 'test' then
$$$
out:
-00000000: PUSHMSGVAR msg[cstr]
-00000001: PUSHCONSTANT test[cstr]
+00000000: push_msgvar msg[cstr]
+00000001: push_const test[cstr]
00000002: contains
$$$
diff --git a/tests/3.rstest b/tests/3.rstest
new file mode 100644
index 00000000..e75d9754
--- /dev/null
+++ b/tests/3.rstest
@@ -0,0 +1,21 @@
+# a simple RainerScript test
+result: 0
+in:
+strlen($msg & strlen('abc')) > 20 +30 + -40 then
+$$$
+out:
+00000000: push_msgvar msg[cstr]
+00000001: push_const abc[cstr]
+00000002: push_const 1[nbr]
+00000003: func_call strlen
+00000004: strconcat
+00000005: push_const 1[nbr]
+00000006: func_call strlen
+00000007: push_const 20[nbr]
+00000008: push_const 30[nbr]
+00000009: add
+00000010: push_const 40[nbr]
+00000011: unary_minus
+00000012: add
+00000013: cmp_>
+$$$
diff --git a/tests/DiagTalker.java b/tests/DiagTalker.java
new file mode 100644
index 00000000..5a6f7dd5
--- /dev/null
+++ b/tests/DiagTalker.java
@@ -0,0 +1,73 @@
+/* A yet very simple tool to talk to imdiag.
+ *
+ * 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.
+ */
+//package com.rsyslog.diag;
+import java.io.*;
+import java.net.*;
+
+public class DiagTalker {
+ public static void main(String[] args) throws IOException {
+
+ Socket diagSocket = null;
+ PrintWriter out = null;
+ BufferedReader in = null;
+ final String host = "127.0.0.1";
+ int port = 13500;
+ if(args.length > 1) {
+ port = Integer.parseInt(args[1]);
+ }
+
+ try {
+ diagSocket = new Socket(host, port);
+ diagSocket.setSoTimeout(0); /* wait for lenghty operations */
+ out = new PrintWriter(diagSocket.getOutputStream(), true);
+ in = new BufferedReader(new InputStreamReader(
+ diagSocket.getInputStream()));
+ } catch (UnknownHostException e) {
+ System.err.println("can not resolve " + host + "!");
+ System.exit(1);
+ } catch (IOException e) {
+ System.err.println("Couldn't get I/O for "
+ + "the connection to: " + host + ".");
+ System.exit(1);
+ }
+
+ BufferedReader stdIn = new BufferedReader(
+ new InputStreamReader(System.in));
+ String userInput;
+
+ try {
+ while ((userInput = stdIn.readLine()) != null) {
+ out.println(userInput);
+ System.out.println("imdiag returns: " + in.readLine());
+ }
+ } catch (SocketException e) {
+ System.err.println("We had a socket exception and consider this to be OK: "
+ + e.getMessage());
+ }
+
+ out.close();
+ in.close();
+ stdIn.close();
+ diagSocket.close();
+ }
+}
+
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 802a3ccb..a438a926 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,18 +1,69 @@
-check_PROGRAMS = rt_init rscript
-TESTS = $(check_PROGRAMS) cfg.sh \
- validation-run.sh
-TESTS_ENVIRONMENT = RSYSLOG_MODDIR='$(abs_top_builddir)'/runtime/.libs/
-#TESTS = $(check_PROGRAMS)
+if ENABLE_TESTBENCH
+TESTRUNS = rt_init rscript
+check_PROGRAMS = $(TESTRUNS) ourtail nettester tcpflood chkseq randomgen inputfilegen
+TESTS = $(TESTRUNS) cfg.sh \
+ validation-run.sh \
+ imtcp-multiport.sh \
+ diskqueue.sh \
+ diskqueue-fsync.sh \
+ manytcp.sh \
+ sndrcv.sh \
+ sndrcv_gzip.sh \
+ asynwr_simple.sh \
+ asynwr_timeout.sh \
+ asynwr_small.sh \
+ asynwr_tinybuf.sh \
+ wr_large_async.sh \
+ wr_large_sync.sh \
+ asynwr_deadlock.sh \
+ asynwr_deadlock2.sh \
+ asynwr_deadlock4.sh \
+ gzipwr_large.sh \
+ gzipwr_large_dynfile.sh \
+ dynfile_invld_async.sh \
+ dynfile_invld_sync.sh \
+ dynfile_invalid2.sh \
+ complex1.sh \
+ queue-persist.sh \
+ pipeaction.sh
+ execonlyonce.sh \
+ queue-persist.sh
+
+if ENABLE_OMSTDOUT
+TESTS += omod-if-array.sh \
+ proprepltest.sh \
+ parsertest.sh \
+ timestamp.sh \
+ inputname.sh \
+ threadingmq.sh \
+ threadingmqaq.sh \
+ fieldtest.sh
+endif
+
+if ENABLE_EXTENDED_TESTS
+TESTS += random.sh
+endif
+
+if ENABLE_IMFILE
+TESTS += imfile-basic.sh
+endif
+check_JAVA = DiagTalker.java
+
+endif # if ENABLE_TESTBENCH
+
+TESTS_ENVIRONMENT = RSYSLOG_MODDIR='$(abs_top_builddir)'/runtime/.libs/
+DISTCLEANFILES=rsyslog.pid '$(abs_top_builddir)'/DiagTalker.class
test_files = testbench.h runtime-dummy.c
-EXTRA_DIST=1.rstest 2.rstest err1.rstest \
+
+EXTRA_DIST= 1.rstest 2.rstest 3.rstest err1.rstest \
validation-run.sh \
testsuites/invalid.conf \
testsuites/valid.conf \
cfg.sh \
cfg1.cfgtest \
cfg1.testin \
- cfg2.cfgtest \
+ cfg2.cfgtest \
cfg2.testin \
cfg3.cfgtest \
cfg3.testin \
@@ -20,14 +71,166 @@ EXTRA_DIST=1.rstest 2.rstest err1.rstest \
cfg4.testin \
DevNull.cfgtest \
err1.rstest \
- NoExistFile.cfgtest
+ NoExistFile.cfgtest \
+ timestamp.sh \
+ testsuites/ts3164.conf \
+ testsuites/mon1digit.ts3164 \
+ testsuites/mon2digit.ts3164 \
+ testsuites/Jan.ts3164 \
+ testsuites/Feb.ts3164 \
+ testsuites/Mar.ts3164 \
+ testsuites/Apr.ts3164 \
+ testsuites/May.ts3164 \
+ testsuites/Jun.ts3164 \
+ testsuites/Jul.ts3164 \
+ testsuites/Aug.ts3164 \
+ testsuites/Sep.ts3164 \
+ testsuites/Oct.ts3164 \
+ testsuites/Nov.ts3164 \
+ testsuites/Dec.ts3164 \
+ testsuites/ts3339.conf \
+ testsuites/master.ts3339 \
+ testsuites/tsmysql.conf \
+ testsuites/master.tsmysql \
+ testsuites/tspgsql.conf \
+ testsuites/master.tspgsql \
+ testsuites/subsecond.conf \
+ testsuites/master.subsecond \
+ testsuites/parse1.conf \
+ testsuites/field1.conf \
+ testsuites/1.parse1 \
+ testsuites/2.parse1 \
+ testsuites/3.parse1 \
+ testsuites/snare.parse1 \
+ testsuites/oversizeTag-1.parse1 \
+ testsuites/weird.parse1 \
+ testsuites/date1.parse1 \
+ testsuites/date2.parse1 \
+ testsuites/date3.parse1 \
+ testsuites/date4.parse1 \
+ testsuites/date5.parse1 \
+ testsuites/rfc3164.parse1 \
+ testsuites/rfc5424-1.parse1 \
+ testsuites/rfc5424-2.parse1 \
+ testsuites/rfc5424-3.parse1 \
+ testsuites/rfc5424-4.parse1 \
+ testsuites/parse3.conf \
+ testsuites/reallife.parse3 \
+ testsuites/parse-nodate.conf \
+ testsuites/samples.parse-nodate \
+ testsuites/parse_invld_regex.conf \
+ testsuites/samples.parse_invld_regex \
+ testsuites/parse-3164-buggyday.conf \
+ testsuites/samples.parse-3164-buggyday \
+ testsuites/snare_ccoff_udp.conf \
+ testsuites/samples.snare_ccoff_udp \
+ testsuites/snare_ccoff_udp2.conf \
+ testsuites/samples.snare_ccoff_udp2 \
+ testsuites/omod-if-array.conf \
+ testsuites/1.omod-if-array \
+ testsuites/1.field1 \
+ killrsyslog.sh \
+ parsertest.sh \
+ fieldtest.sh \
+ diskqueue.sh \
+ testsuites/diskqueue.conf \
+ diskqueue-fsync.sh \
+ testsuites/diskqueue-fsync.conf \
+ imtcp-multiport.sh \
+ testsuites/imtcp-multiport.conf \
+ manytcp.sh \
+ testsuites/manytcp.conf \
+ inputname.sh \
+ testsuites/inputname_imtcp.conf \
+ testsuites/1.inputname_imtcp_12514 \
+ testsuites/1.inputname_imtcp_12515 \
+ testsuites/1.inputname_imtcp_12516 \
+ omod-if-array.sh \
+ diag.sh \
+ testsuites/diag-common.conf \
+ testsuites/diag-common2.conf \
+ queue-persist.sh \
+ queue-persist-drvr.sh \
+ testsuites/queue-persist.conf \
+ threadingmq.sh \
+ testsuites/threadingmq.conf \
+ threadingmqaq.sh \
+ testsuites/threadingmqaq.conf \
+ sndrcv_drvr.sh \
+ sndrcv.sh \
+ testsuites/sndrcv_sender.conf \
+ testsuites/sndrcv_rcvr.conf \
+ sndrcv_gzip.sh \
+ testsuites/sndrcv_gzip_sender.conf \
+ testsuites/sndrcv_gzip_rcvr.conf \
+ pipeaction.sh \
+ testsuites/pipeaction.conf \
+ asynwr_simple.sh \
+ testsuites/asynwr_simple.conf \
+ asynwr_timeout.sh \
+ testsuites/asynwr_timeout.conf \
+ asynwr_small.sh \
+ testsuites/asynwr_small.conf \
+ asynwr_tinybuf.sh \
+ testsuites/asynwr_tinybuf.conf \
+ wr_large_async.sh \
+ wr_large_sync.sh \
+ wr_large.sh \
+ testsuites/wr_large.conf \
+ asynwr_deadlock.sh \
+ testsuites/asynwr_deadlock.conf \
+ asynwr_deadlock2.sh \
+ testsuites/asynwr_deadlock2.conf \
+ asynwr_deadlock4.sh \
+ testsuites/asynwr_deadlock4.conf \
+ gzipwr_large.sh \
+ testsuites/gzipwr_large.conf \
+ gzipwr_large_dynfile.sh \
+ testsuites/gzipwr_large_dynfile.conf \
+ complex1.sh \
+ testsuites/complex1.conf \
+ random.sh \
+ testsuites/random.conf \
+ imfile-basic.sh \
+ testsuites/imfile-basic.conf \
+ dynfile_invld_async.sh \
+ dynfile_invld_sync.sh \
+ dynfile_cachemiss.sh \
+ testsuites/dynfile_cachemiss.conf \
+ dynfile_invalid2.sh \
+ testsuites/dynfile_invalid2.conf \
+ proprepltest.sh \
+ testsuites/rfctag.conf \
+ testsuites/master.rfctag \
+ testsuites/nolimittag.conf \
+ testsuites/master.nolimittag \
+ execonlyonce.sh \
+ testsuites/execonlyonce.conf \
+ testsuites/execonlyonce.data \
+ DiagTalker.java \
+ cfg.sh
+
+ourtail_SOURCES = ourtail.c
+chkseq_SOURCES = chkseq.c
+
+tcpflood_SOURCES = tcpflood.c
+tcpflood_LDADD = $(SOL_LIBS)
+
+randomgen_SOURCES = randomgen.c
+randomgen_LDADD = $(SOL_LIBS)
+
+inputfilegen_SOURCES = inputfilegen.c
+inputfilegen_LDADD = $(SOL_LIBS)
+
+nettester_SOURCES = nettester.c getline.c
+nettester_LDADD = $(SOL_LIBS)
rt_init_SOURCES = rt-init.c $(test_files)
rt_init_CPPFLAGS = -I$(top_srcdir) $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
-rt_init_LDADD = $(RSRT_LIBS) $(ZLIB_LIBS) $(PTHREADS_LIBS)
+rt_init_LDADD = $(RSRT_LIBS) $(ZLIB_LIBS) $(PTHREADS_LIBS) $(SOL_LIBS)
rt_init_LDFLAGS = -export-dynamic
-rscript_SOURCES = rscript.c $(test_files)
+rscript_SOURCES = rscript.c getline.c $(test_files)
rscript_CPPFLAGS = -I$(top_srcdir) $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
-rscript_LDADD = $(RSRT_LIBS) $(ZLIB_LIBS) $(PTHREADS_LIBS)
+rscript_LDADD = $(RSRT_LIBS) $(ZLIB_LIBS) $(PTHREADS_LIBS) $(SOL_LIBS)
rscript_LDFLAGS = -export-dynamic
diff --git a/tests/asynwr_deadlock.sh b/tests/asynwr_deadlock.sh
new file mode 100755
index 00000000..dc08355e
--- /dev/null
+++ b/tests/asynwr_deadlock.sh
@@ -0,0 +1,23 @@
+# This is test case from practice, with the version we introduced it, it
+# caused a deadlock on shutdown. I have added it to the test suite to automatically
+# detect such things in the future.
+#
+# added 2010-03-17 by Rgerhards
+# This file is part of the rsyslog project, released under GPLv3
+echo ================================================================================
+echo TEST: \[asynwr_deadlock.sh\]: a case known to have caused a deadlock in the past
+source $srcdir/diag.sh init
+# uncomment for debugging support:
+#export RSYSLOG_DEBUG="debug nostdout noprintmutexaction"
+#export RSYSLOG_DEBUGLOG="log"
+source $srcdir/diag.sh startup asynwr_deadlock.conf
+# just send one message
+source $srcdir/diag.sh tcpflood -m1
+# sleep is important! need to make sure the instance is inactive
+sleep 1
+# now try shutdown. The actual test is if the process does hang here!
+echo "processing must continue soon"
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown # and wait for it to terminate
+source $srcdir/diag.sh seq-check 0 0
+source $srcdir/diag.sh exit
diff --git a/tests/asynwr_deadlock2.sh b/tests/asynwr_deadlock2.sh
new file mode 100755
index 00000000..1190f67a
--- /dev/null
+++ b/tests/asynwr_deadlock2.sh
@@ -0,0 +1,69 @@
+# This is test case from practice, with the version we introduced it, it
+# caused a deadlock during processing (when the a stream was purged from the
+# dynafile cache).
+# We added this as a standard test in the hopes that iw will help
+# detect such things in the future.
+#
+# The problem that originally caused this test to fail was:
+# We write to files asynchronously (with the async writer thread). There is
+# no signaling done when the file stream is closed. That can lead to the writer
+# process hanging in memory, that in turn leads to the main thread waiting on a
+# condition that never occurs (because it would need to be signalled by the
+# async writer). Even worse, in that case, the async writer was signalled invalid
+# in such a way that when it received a wakeup, it thought it shall not terminate,
+# but received a spurios wakeup due to timeout and no data to write. In that case
+# it (correctly) concluded that it would not need to timeout until a new buffer write
+# was done (in which case it would receive a wakeup). As such, it went into an eternal
+# wait. However, the invalid signaling did not take into account that it did not
+# signal the async writer to shut down. So the main thread went into a condition
+# wait - and thus we had a deadlock. That situation occured only under very specific
+# cirumstances. As far as the analysis goes, the following need to happen:
+# 1. buffers on that file are being flushed
+# 2. no new data arrives
+# 3. the inactivity timeout has not yet expired
+# 4. *then* (and only then) the stream is closed or destructed
+# In that, 1 to 4 are prequisites for the deadlock which will happen in 4. However,
+# for it to happen, we also need the right "timing". There is a race between the
+# main thread and the async writer thread. The deadlock will only happen under
+# the "right" circumstances, which basically means it will not happen always.
+# In order to create this case as reliable as possible, I have used
+# the "$OMFileFlushOnTXEnd on" directive
+# inside my test case. It makes sure that #1 above happens. The test uses a dynafile
+# cache size of 4, and the load generator generates data for 5 different dynafiles.
+# So over time, we will hit a spot where 4 dynafiles are open and the 5th file name
+# is generated. As such, one file needs to be discarded. Thanks to FlushOnTXEnd, we
+# now likely have #2 in place and thanks to the load pattern generated, we most
+# probably have #3 in place. During the dynafile cache displacement of the oldest
+# entry, #4 is generated. At this point, we have the deadlock we are testing for.
+# Note that this deadlock does not necessarily lead to a total lockup of rsyslogd.
+# Parts of it continue to operate. But in our test setup, this means data is
+# received and placed into the main queue. Once it's high water mark is hit, data
+# is still being enqueued, but at a slow rate. So if one is patient enough, the load
+# generator will be able to finish. However, rsyslogd will never process the data
+# it received because it is locked in the deadlock caused by #4 above.
+# Note that "$OMFileFlushOnTXEnd on" is not causing this behaviour. We just use it
+# to (quite) reliably cause the failure condition. The failure described above
+# (in version 4.6.1) was also present when the setting was set to "off", but its
+# occurence was very much less probable - because the perquisites are then much
+# harder to hit. without it, the test may need to run for several hours before
+# we hit all failure conditions.
+#
+# added 2010-03-17 by Rgerhards
+# This file is part of the rsyslog project, released under GPLv3
+echo =================================================================================
+echo TEST: \[asynwr_deadlock2.sh\]: a case known to have caused a deadlock in the past
+source $srcdir/diag.sh init
+# uncomment for debugging support:
+#export RSYSLOG_DEBUG="debug nostdout noprintmutexaction"
+#export RSYSLOG_DEBUGLOG="log"
+source $srcdir/diag.sh startup asynwr_deadlock2.conf
+# send 20000 messages, each close to 2K (non-randomized!), so that we can fill
+# the buffers and hopefully run into the "deadlock".
+source $srcdir/diag.sh tcpflood -m20000 -d1800 -P129 -i1 -f5
+# the sleep below is needed to prevent too-early termination of the tcp listener
+sleep 1
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown # and wait for it to terminate
+cat rsyslog.out.*.log > rsyslog.out.log
+source $srcdir/diag.sh seq-check 1 20000 -E
+source $srcdir/diag.sh exit
diff --git a/tests/asynwr_deadlock4.sh b/tests/asynwr_deadlock4.sh
new file mode 100755
index 00000000..a3452f5b
--- /dev/null
+++ b/tests/asynwr_deadlock4.sh
@@ -0,0 +1,25 @@
+# This is test case from practice, with the version we introduced it, it
+# caused a deadlock during processing.
+# We added this as a standard test in the hopes that iw will help
+# detect such things in the future.
+#
+# This is a test that is constructed similar to asynwr_deadlock2.sh, but
+# can produce problems in a simpler way.
+#
+# added 2010-03-18 by Rgerhards
+# This file is part of the rsyslog project, released under GPLv3
+echo =================================================================================
+echo TEST: \[asynwr_deadlock4.sh\]: a case known to have caused a deadlock in the past
+source $srcdir/diag.sh init
+# uncomment for debugging support:
+#export RSYSLOG_DEBUG="debug nostdout noprintmutexaction"
+#export RSYSLOG_DEBUGLOG="log"
+source $srcdir/diag.sh startup asynwr_deadlock4.conf
+# send 20000 messages, each close to 2K (non-randomized!), so that we can fill
+# the buffers and hopefully run into the "deadlock".
+source $srcdir/diag.sh tcpflood -m20000 -d18 -P129 -i1 -f5
+# sleep is important! need to make sure the instance is inactive
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown # and wait for it to terminate
+source $srcdir/diag.sh seq-check 1 20000 -E
+source $srcdir/diag.sh exit
diff --git a/tests/asynwr_simple.sh b/tests/asynwr_simple.sh
new file mode 100755
index 00000000..eb87443c
--- /dev/null
+++ b/tests/asynwr_simple.sh
@@ -0,0 +1,18 @@
+# This is test driver for testing asynchronous file output.
+#
+# added 2010-03-09 by Rgerhards
+# This file is part of the rsyslog project, released under GPLv3
+echo ===============================================================================
+echo TEST: \[asynwr_simple.sh\]: simple test for async file writing
+source $srcdir/diag.sh init
+# uncomment for debugging support:
+#export RSYSLOG_DEBUG="debug nostdout noprintmutexaction"
+#export RSYSLOG_DEBUGLOG="log"
+source $srcdir/diag.sh startup asynwr_simple.conf
+# send 35555 messages, make sure file size is not a multiple of
+# 10K, the buffer size!
+source $srcdir/diag.sh tcpflood -m35555
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown # and wait for it to terminate
+source $srcdir/diag.sh seq-check 0 35554
+source $srcdir/diag.sh exit
diff --git a/tests/asynwr_small.sh b/tests/asynwr_small.sh
new file mode 100755
index 00000000..97818f6e
--- /dev/null
+++ b/tests/asynwr_small.sh
@@ -0,0 +1,26 @@
+# This tests async writing with only a small set of data. That
+# shall result in data staying in buffers until shutdown, what
+# then will trigger some somewhat complex logic in the stream
+# writer (open, write, close all during the stream close
+# opertion). It is vital that only few messages be sent.
+#
+# The main effort of this test is not (only) to see if we
+# receive the data, but rather to see if we get into an abort
+# condition.
+#
+# added 2010-03-09 by Rgerhards
+#
+# This file is part of the rsyslog project, released under GPLv3
+echo ===============================================================================
+echo TEST: \[asynwr_small.sh\]: test for async file writing for few messages
+source $srcdir/diag.sh init
+# uncomment for debugging support:
+#export RSYSLOG_DEBUG="debug nostdout noprintmutexaction"
+#export RSYSLOG_DEBUGLOG="log"
+source $srcdir/diag.sh startup asynwr_small.conf
+# send 4000 messages
+source $srcdir/diag.sh tcpflood -m2
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown # and wait for it to terminate
+source $srcdir/diag.sh seq-check 0 1
+source $srcdir/diag.sh exit
diff --git a/tests/asynwr_timeout.sh b/tests/asynwr_timeout.sh
new file mode 100755
index 00000000..b0bc5c28
--- /dev/null
+++ b/tests/asynwr_timeout.sh
@@ -0,0 +1,21 @@
+# This test writes to the output buffers, let's the output
+# write timeout (and write data) and then continue. The conf file
+# has a 2 second timeout, so we wait 4 seconds to be on the save side.
+#
+# added 2010-03-09 by Rgerhards
+# This file is part of the rsyslog project, released under GPLv3
+echo ===============================================================================
+echo TEST: \[asynwr_timeout.sh\]: test async file writing timeout writes
+source $srcdir/diag.sh init
+# uncomment for debugging support:
+#export RSYSLOG_DEBUG="debug nostdout noprintmutexaction"
+#export RSYSLOG_DEBUGLOG="log"
+source $srcdir/diag.sh startup asynwr_timeout.conf
+# send 35555 messages, make sure file size is not a multiple of
+# 10K, the buffer size!
+source $srcdir/diag.sh tcpflood -m 35555
+sleep 4 # wait for output writer to write and empty buffer
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown # and wait for it to terminate
+source $srcdir/diag.sh seq-check 0 35554
+source $srcdir/diag.sh exit
diff --git a/tests/asynwr_tinybuf.sh b/tests/asynwr_tinybuf.sh
new file mode 100755
index 00000000..8eae1e26
--- /dev/null
+++ b/tests/asynwr_tinybuf.sh
@@ -0,0 +1,19 @@
+# This tests async writing with a very small output buffer (1 byte!),
+# so it stresses output buffer handling. This also means operations will
+# be somewhat slow, so we send only a small amounts of data.
+#
+# added 2010-03-09 by Rgerhards
+#
+# This file is part of the rsyslog project, released under GPLv3
+echo ===============================================================================
+echo TEST: \[asynwr_tinybuf.sh\]: test async file writing with 1-byte buffer
+source $srcdir/diag.sh init
+# uncomment for debugging support:
+#export RSYSLOG_DEBUG="debug nostdout noprintmutexaction"
+#export RSYSLOG_DEBUGLOG="log"
+source $srcdir/diag.sh startup asynwr_tinybuf.conf
+# send 1000 messages, fairly enough to trigger problems
+source $srcdir/diag.sh tcpflood -m1000
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh seq-check 0 999
+source $srcdir/diag.sh exit
diff --git a/tests/cfg.sh b/tests/cfg.sh
index 99729823..cb838354 100755
--- a/tests/cfg.sh
+++ b/tests/cfg.sh
@@ -36,7 +36,7 @@ echo "local directory"
#
# check empty config file
#
-../tools/rsyslogd -c3 -N1 -f/dev/null 2>&1 |tail --lines=+2 > tmp
+../tools/rsyslogd -c4 -N1 -f/dev/null 2>&1 |./ourtail |head -2 > tmp
cmp tmp $srcdir/DevNull.cfgtest
if [ ! $? -eq 0 ]; then
echo "DevNull.cfgtest failed"
@@ -51,7 +51,7 @@ fi;
#
# check missing config file
#
-../tools/rsyslogd -c3 -N1 -f/This/does/not/exist 2>&1 |tail --lines=+2 > tmp
+../tools/rsyslogd -c4 -N1 -f/This/does/not/exist 2>&1 |./ourtail |head -2 > tmp
cmp tmp $srcdir/NoExistFile.cfgtest
if [ ! $? -eq 0 ]; then
echo "NoExistFile.cfgtest failed"
@@ -74,7 +74,7 @@ exit 0
#
# check config with invalid directive
#
-../tools/rsyslogd -c3 -u2 -N1 -f$srcdir/cfg1.testin 2>&1 |tail --lines=+2 > tmp
+../tools/rsyslogd -c4 -u2 -N1 -f$srcdir/cfg1.testin 2>&1 |./ourtail > tmp
cmp tmp $srcdir/cfg1.cfgtest
if [ ! $? -eq 0 ]; then
echo "cfg1.cfgtest failed"
@@ -91,7 +91,7 @@ fi;
# the one with the invalid config directive, so that we may see
# an effect of the included config ;)
#
-../tools/rsyslogd -c3 -u2 -N1 -f$srcdir/cfg2.testin 2>&1 |tail --lines=+2 > tmp
+../tools/rsyslogd -c4 -u2 -N1 -f$srcdir/cfg2.testin 2>&1 |./ourtail > tmp
cmp tmp $srcdir/cfg2.cfgtest
if [ ! $? -eq 0 ]; then
echo "cfg2.cfgtest failed"
@@ -106,7 +106,7 @@ fi;
#
# check included config file, where included file does not exist
#
-../tools/rsyslogd -c3 -u2 -N1 -f$srcdir/cfg3.testin 2>&1 |tail --lines=+2 > tmp
+../tools/rsyslogd -c4 -u2 -N1 -f$srcdir/cfg3.testin 2>&1 |./ourtail > tmp
cmp tmp $srcdir/cfg3.cfgtest
if [ ! $? -eq 0 ]; then
echo "cfg3.cfgtest failed"
@@ -121,7 +121,7 @@ fi;
#
# check a reasonable complex, but correct, log file
#
-../tools/rsyslogd -c3 -u2 -N1 -f$srcdir/cfg4.testin 2>&1 |tail --lines=+2 > tmp
+../tools/rsyslogd -c4 -u2 -N1 -f$srcdir/cfg4.testin 2>&1 |./ourtail > tmp
cmp tmp $srcdir/cfg4.cfgtest
if [ ! $? -eq 0 ]; then
echo "cfg4.cfgtest failed"
diff --git a/tests/chkseq.c b/tests/chkseq.c
new file mode 100644
index 00000000..b22c8992
--- /dev/null
+++ b/tests/chkseq.c
@@ -0,0 +1,143 @@
+/* Checks if a file consists of line of strictly monotonically
+ * increasing numbers. An expected start and end number may
+ * be set.
+ *
+ * Params
+ * -f<filename> MUST be given!
+ * -s<starting number> -e<ending number>
+ * default for s is 0. -e should be given (else it is also 0)
+ * -d may be specified, in which case duplicate messages are permitted.
+ *
+ * Part of the testbench for rsyslog.
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+
+int main(int argc, char *argv[])
+{
+ FILE *fp;
+ int val;
+ int i;
+ int ret = 0;
+ int scanfOK;
+ int verbose = 0;
+ int bHaveExtraData = 0;
+ int dupsPermitted = 0;
+ int start = 0, end = 0;
+ int opt;
+ int nDups = 0;
+ int edLen; /* length of extra data */
+ static char edBuf[500*1024]; /* buffer for extra data (pretty large to be on the save side...) */
+ char *file = NULL;
+
+ while((opt = getopt(argc, argv, "e:f:ds:vE")) != EOF) {
+ switch((char)opt) {
+ case 'f':
+ file = optarg;
+ break;
+ case 'd':
+ dupsPermitted = 1;
+ break;
+ case 'e':
+ end = atoi(optarg);
+ break;
+ case 's':
+ start = atoi(optarg);
+ break;
+ case 'v':
+ ++verbose;
+ break;
+ case 'E':
+ bHaveExtraData = 1;
+ break;
+ default:printf("Invalid call of chkseq, optchar='%c'\n", opt);
+ printf("Usage: chkseq file -sstart -eend -d -E\n");
+ exit(1);
+ }
+ }
+
+ if(file == NULL) {
+ printf("file must be given!\n");
+ exit(1);
+ }
+
+ if(start > end) {
+ printf("start must be less than or equal end!\n");
+ exit(1);
+ }
+
+ if(verbose) {
+ printf("chkseq: start %d, end %d\n", start, end);
+ }
+
+ /* read file */
+ fp = fopen(file, "r");
+ if(fp == NULL) {
+ printf("error opening file '%s'\n", file);
+ perror(file);
+ exit(1);
+ }
+
+ for(i = start ; i < end+1 ; ++i) {
+ if(bHaveExtraData) {
+ scanfOK = fscanf(fp, "%d,%d,%s\n", &val, &edLen, edBuf) == 3 ? 1 : 0;
+ if(edLen != (int) strlen(edBuf)) {
+ printf("extra data length specified %d, but actually is %ld in record %d\n",
+ edLen, (long) strlen(edBuf), i);
+ exit(1);
+ }
+ } else {
+ scanfOK = fscanf(fp, "%d\n", &val) == 1 ? 1 : 0;
+ }
+ if(!scanfOK) {
+ printf("scanf error in index i=%d\n", i);
+ exit(1);
+ }
+ if(val != i) {
+ if(val == i - 1 && dupsPermitted) {
+ --i;
+ ++nDups;
+ } else {
+ printf("read value %d, but expected value %d\n", val, i);
+ exit(1);
+ }
+ }
+ }
+
+ if(nDups != 0)
+ printf("info: had %d duplicates (this is no error)\n", nDups);
+
+ if(i - 1 != end) {
+ printf("only %d records in file, expected %d\n", i - 1, end);
+ exit(1);
+ }
+
+ if(!feof(fp)) {
+ printf("end of processing, but NOT end of file!\n");
+ exit(1);
+ }
+
+ exit(ret);
+}
diff --git a/tests/complex1.sh b/tests/complex1.sh
new file mode 100755
index 00000000..7f3cd994
--- /dev/null
+++ b/tests/complex1.sh
@@ -0,0 +1,21 @@
+# This is a rather complex test that runs a number of features together.
+#
+# added 2010-03-16 by Rgerhards
+#
+# This file is part of the rsyslog project, released under GPLv3
+echo ====================================================================================
+echo TEST: \[complex1.sh\]: complex test with gzip and multiple action queues
+source $srcdir/diag.sh init
+# uncomment for debugging support:
+#export RSYSLOG_DEBUG="debug nostdout"
+#export RSYSLOG_DEBUGLOG="log"
+source $srcdir/diag.sh startup complex1.conf
+# send 30,000 messages of 400 bytes plus header max, via three dest ports
+source $srcdir/diag.sh tcpflood -m40000 -rd400 -P129 -f5 -n3 -c15 -i1
+sleep 2 # due to large messages, we need this time for the tcp receiver to settle...
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown # and wait for it to terminate
+ls rsyslog.out.*.log
+zcat rsyslog.out.*.log > rsyslog.out.log
+source $srcdir/diag.sh seq-check 1 40000 -E
+source $srcdir/diag.sh exit
diff --git a/tests/diag.sh b/tests/diag.sh
new file mode 100755
index 00000000..806edf80
--- /dev/null
+++ b/tests/diag.sh
@@ -0,0 +1,137 @@
+# this shell script provides commands to the common diag system. It enables
+# test scripts to wait for certain conditions and initiate certain actions.
+# needs support in config file.
+# NOTE: this file should be included with "source diag.sh", as it otherwise is
+# not always able to convey back states to the upper-level test driver
+# begun 2009-05-27 by rgerhards
+# This file is part of the rsyslog project, released under GPLv3
+#valgrind="valgrind --log-fd=1"
+#valgrind="valgrind --tool=drd --log-fd=1"
+#valgrind="valgrind --tool=helgrind --log-fd=1"
+#valgrind="valgrind --tool=exp-ptrcheck --log-fd=1"
+#set -o xtrace
+#export RSYSLOG_DEBUG="debug nostdout printmutexaction"
+#export RSYSLOG_DEBUGLOG="log"
+case $1 in
+ 'init') $srcdir/killrsyslog.sh # kill rsyslogd if it runs for some reason
+ cp $srcdir/testsuites/diag-common.conf diag-common.conf
+ cp $srcdir/testsuites/diag-common2.conf diag-common2.conf
+ rm -f rsyslog.action.*.include
+ rm -f rsyslogd.started work-*.conf rsyslog.random.data
+ rm -f rsyslogd2.started work-*.conf
+ rm -f work rsyslog.out.log rsyslog.out.log.save # common work files
+ rm -f rsyslog.out.*.log work-presort
+ rm -rf test-spool
+ rm -f rsyslog.input
+ rm -f core.* vgcore.*
+ mkdir test-spool
+ ;;
+ 'exit') rm -f rsyslogd.started work-*.conf diag-common.conf
+ rm -f rsyslogd2.started diag-common2.conf rsyslog.action.*.include
+ rm -f work rsyslog.out.log rsyslog.out.log.save # common work files
+ rm -f rsyslog.out.*.log rsyslog.random.data work-presort
+ rm -rf test-spool
+ rm -f rsyslog.input
+ ;;
+ 'startup') # start rsyslogd with default params. $2 is the config file name to use
+ # returns only after successful startup, $3 is the instance (blank or 2!)
+ $valgrind ../tools/rsyslogd -c4 -u2 -n -irsyslog$3.pid -M../runtime/.libs:../.libs -f$srcdir/testsuites/$2 &
+ $srcdir/diag.sh wait-startup $3
+ ;;
+ 'wait-startup') # wait for rsyslogd startup ($2 is the instance)
+ while test ! -f rsyslogd$2.started; do
+ #true
+ sleep 0.1 # if this is not supported by all platforms, use above!
+ done
+ echo "rsyslogd$2 started with pid " `cat rsyslog$2.pid`
+ ;;
+ 'wait-shutdown') # actually, we wait for rsyslog.pid to be deleted. $2 is the
+ # instance
+ while test -f rsyslog$2.pid; do
+ #true
+ sleep 0.1 # if this is not supported by all platforms, use above!
+ done
+ ;;
+ 'wait-queueempty') # wait for main message queue to be empty. $2 is the instance.
+ if [ "$2" == "2" ]
+ then
+ echo WaitMainQueueEmpty | java -classpath $abs_top_builddir DiagTalker
+ else
+ echo WaitMainQueueEmpty | java -classpath $abs_top_builddir DiagTalker 13501
+ fi
+ ;;
+ 'shutdown-when-empty') # shut rsyslogd down when main queue is empty. $2 is the instance.
+ $srcdir/diag.sh wait-queueempty $2
+ kill `cat rsyslog$2.pid`
+ # note: we do not wait for the actual termination!
+ ;;
+ 'shutdown-immediate') # shut rsyslogd down without emptying the queue. $2 is the instance.
+ kill `cat rsyslog.pid`
+ # note: we do not wait for the actual termination!
+ ;;
+ 'tcpflood') # do a tcpflood run and check if it worked params are passed to tcpflood
+ ./tcpflood $2 $3 $4 $5 $6 $7 $8 $9
+ if [ "$?" -ne "0" ]; then
+ echo "error during tcpflood! see rsyslog.out.log.save for what was written"
+ cp rsyslog.out.log rsyslog.out.log.save
+ exit 1
+ fi
+ ;;
+ 'injectmsg') # inject messages via our inject interface (imdiag)
+ echo injecting $3 messages
+ echo injectmsg $2 $3 $4 $5 | java -classpath $abs_top_builddir DiagTalker
+ # TODO: some return state checking? (does it really make sense here?)
+ ;;
+ 'check-mainq-spool') # check if mainqueue spool files exist, if not abort (we just check .qi).
+ echo There must exist some files now:
+ ls -l test-spool
+ if test ! -f test-spool/mainq.qi; then
+ echo "error: mainq.qi does not exist where expected to do so!"
+ ls -l test-spool
+ exit 1
+ fi
+ ;;
+ 'seq-check') # do the usual sequence check to see if everything was properly received. $2 is the instance.
+ rm -f work
+ cp rsyslog.out.log work-presort
+ sort < rsyslog.out.log > work
+ # $4... are just to have the abilit to pass in more options...
+ ./chkseq -fwork -v -s$2 -e$3 $4 $5 $6 $7
+ if [ "$?" -ne "0" ]; then
+ echo "sequence error detected"
+ exit 1
+ fi
+ ;;
+ 'seq-check2') # do the usual sequence check to see if everything was properly received. This is
+ # a duplicateof seq-check, but we could not change its calling conventions without
+ # breaking a lot of exitings test cases, so we preferred to duplicate the code here.
+ rm -f work2
+ sort < rsyslog2.out.log > work2
+ # $4... are just to have the abilit to pass in more options...
+ ./chkseq -fwork2 -v -s$2 -e$3 $4 $5 $6 $7
+ if [ "$?" -ne "0" ]; then
+ echo "sequence error detected"
+ exit 1
+ fi
+ ;;
+ 'gzip-seq-check') # do the usual sequence check, but for gzip files
+ rm -f work
+ ls -l rsyslog.out.log
+ gunzip < rsyslog.out.log | sort > work
+ ls -l work
+ # $4... are just to have the abilit to pass in more options...
+ ./chkseq -fwork -v -s$2 -e$3 $4 $5 $6 $7
+ if [ "$?" -ne "0" ]; then
+ echo "sequence error detected"
+ exit 1
+ fi
+ ;;
+ 'nettester') # perform nettester-based tests
+ # use -v for verbose output!
+ ./nettester -t$2 -i$3
+ if [ "$?" -ne "0" ]; then
+ exit 1
+ fi
+ ;;
+ *) echo "invalid argument" $1
+esac
diff --git a/tests/diskqueue-fsync.sh b/tests/diskqueue-fsync.sh
new file mode 100755
index 00000000..c7745930
--- /dev/null
+++ b/tests/diskqueue-fsync.sh
@@ -0,0 +1,16 @@
+# Test for disk-only queue mode (with fsync for queue files)
+# This test checks if queue files can be correctly written
+# and read back, but it does not test the transition from
+# memory to disk mode for DA queues.
+# added 2009-06-09 by Rgerhards
+# This file is part of the rsyslog project, released under GPLv3
+# uncomment for debugging support:
+echo testing queue disk-only mode, fsync case
+source $srcdir/diag.sh init
+source $srcdir/diag.sh startup diskqueue-fsync.conf
+# 1000 messages should be enough - the disk fsync test is very slow!
+source $srcdir/diag.sh tcpflood -m1000
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown
+source $srcdir/diag.sh seq-check 0 999
+source $srcdir/diag.sh exit
diff --git a/tests/diskqueue.sh b/tests/diskqueue.sh
new file mode 100755
index 00000000..7a50d82e
--- /dev/null
+++ b/tests/diskqueue.sh
@@ -0,0 +1,20 @@
+# Test for disk-only queue mode
+# This test checks if queue files can be correctly written
+# and read back, but it does not test the transition from
+# memory to disk mode for DA queues.
+# added 2009-04-17 by Rgerhards
+# This file is part of the rsyslog project, released under GPLv3
+# uncomment for debugging support:
+echo diskqueue.sh: testing queue disk-only mode
+# uncomment for debugging support:
+#export RSYSLOG_DEBUG="debug nostdout noprintmutexaction"
+#export RSYSLOG_DEBUGLOG="log"
+source $srcdir/diag.sh init
+source $srcdir/diag.sh startup diskqueue.conf
+# 20000 messages should be enough - the disk test is slow enough ;)
+sleep 4
+source $srcdir/diag.sh tcpflood -m20000
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown
+source $srcdir/diag.sh seq-check 0 19999
+source $srcdir/diag.sh exit
diff --git a/tests/dynfile_cachemiss.sh b/tests/dynfile_cachemiss.sh
new file mode 100755
index 00000000..6e2d9cca
--- /dev/null
+++ b/tests/dynfile_cachemiss.sh
@@ -0,0 +1,34 @@
+# This test checks if omfile segfaults when a file open() in dynacache mode fails.
+# The test is mimiced after a real-life scenario (which, of course, was much more
+# complex).
+#
+# added 2010-03-09 by Rgerhards
+#
+# This file is part of the rsyslog project, released under GPLv3
+echo ===============================================================================
+echo TEST: \[dynfile_cachemiss.sh\]: test open fail for dynafiles with `cat rsyslog.action.1.include`
+source $srcdir/diag.sh init
+# uncomment for debugging support:
+#export RSYSLOG_DEBUG="debug nostdout noprintmutexaction"
+#export RSYSLOG_DEBUGLOG="log"
+source $srcdir/diag.sh startup dynfile_cachemiss.conf
+# we send handcrafted message. We have a dynafile cache of 4, and now send one message
+# each to fill up the cache.
+./tcpflood -m1 -M "<129>Mar 10 01:00:00 172.20.245.8 tag msg:rsyslog.out.0.log:0"
+./tcpflood -m1 -M "<129>Mar 10 01:00:00 172.20.245.8 tag msg:rsyslog.out.1.log:1"
+./tcpflood -m1 -M "<129>Mar 10 01:00:00 172.20.245.8 tag msg:rsyslog.out.2.log:2"
+./tcpflood -m1 -M "<129>Mar 10 01:00:00 172.20.245.8 tag msg:rsyslog.out.3.log:3"
+# the next one has caused a segfault in practice
+# note that /proc/rsyslog.error.file must not be creatable
+./tcpflood -m1 -M "<129>Mar 10 01:00:00 172.20.245.8 tag msg:/proc/rsyslog.error.file:boom"
+# some more writes
+./tcpflood -m1 -M "<129>Mar 10 01:00:00 172.20.245.8 tag msg:rsyslog.out.0.log:4"
+./tcpflood -m1 -M "<129>Mar 10 01:00:00 172.20.245.8 tag msg:rsyslog.out.1.log:5"
+./tcpflood -m1 -M "<129>Mar 10 01:00:00 172.20.245.8 tag msg:rsyslog.out.2.log:6"
+./tcpflood -m1 -M "<129>Mar 10 01:00:00 172.20.245.8 tag msg:rsyslog.out.3.log:7"
+# done message generation
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown # and wait for it to terminate
+cat rsyslog.out.*.log > rsyslog.out.log
+source $srcdir/diag.sh seq-check 0 7
+source $srcdir/diag.sh exit
diff --git a/tests/dynfile_invalid2.sh b/tests/dynfile_invalid2.sh
new file mode 100755
index 00000000..cb3ef51e
--- /dev/null
+++ b/tests/dynfile_invalid2.sh
@@ -0,0 +1,34 @@
+# This test checks if omfile segfaults when a file open() in dynacache mode fails.
+# The test is mimiced after a real-life scenario (which, of course, was much more
+# complex).
+#
+# added 2010-03-22 by Rgerhards
+#
+# This file is part of the rsyslog project, released under GPLv3
+echo ===============================================================================
+echo TEST: \[dynfile_invalid2.sh\]: test open fail for dynafiles
+source $srcdir/diag.sh init
+# uncomment for debugging support:
+#export RSYSLOG_DEBUG="debug nostdout noprintmutexaction"
+#export RSYSLOG_DEBUGLOG="log"
+source $srcdir/diag.sh startup dynfile_invalid2.conf
+# we send handcrafted message. We have a dynafile cache of 4, and now send one message
+# each to fill up the cache.
+./tcpflood -m1 -M "<129>Mar 10 01:00:00 172.20.245.8 tag msg:rsyslog.out.0.log:0"
+./tcpflood -m1 -M "<129>Mar 10 01:00:00 172.20.245.8 tag msg:rsyslog.out.1.log:1"
+./tcpflood -m1 -M "<129>Mar 10 01:00:00 172.20.245.8 tag msg:rsyslog.out.2.log:2"
+./tcpflood -m1 -M "<129>Mar 10 01:00:00 172.20.245.8 tag msg:rsyslog.out.3.log:3"
+# the next one has caused a segfault in practice
+# note that /proc/rsyslog.error.file must not be creatable
+./tcpflood -m1 -M "<129>Mar 10 01:00:00 172.20.245.8 tag msg:/proc/rsyslog.error.file:boom"
+# some more writes
+./tcpflood -m1 -M "<129>Mar 10 01:00:00 172.20.245.8 tag msg:rsyslog.out.0.log:4"
+./tcpflood -m1 -M "<129>Mar 10 01:00:00 172.20.245.8 tag msg:rsyslog.out.1.log:5"
+./tcpflood -m1 -M "<129>Mar 10 01:00:00 172.20.245.8 tag msg:rsyslog.out.2.log:6"
+./tcpflood -m1 -M "<129>Mar 10 01:00:00 172.20.245.8 tag msg:rsyslog.out.3.log:7"
+# done message generation
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown # and wait for it to terminate
+cat rsyslog.out.*.log > rsyslog.out.log
+source $srcdir/diag.sh seq-check 0 7
+source $srcdir/diag.sh exit
diff --git a/tests/dynfile_invld_async.sh b/tests/dynfile_invld_async.sh
new file mode 100755
index 00000000..3c9b2045
--- /dev/null
+++ b/tests/dynfile_invld_async.sh
@@ -0,0 +1,2 @@
+echo "\$OMFileAsyncWriting on" > rsyslog.action.1.include
+source $srcdir/dynfile_cachemiss.sh
diff --git a/tests/dynfile_invld_sync.sh b/tests/dynfile_invld_sync.sh
new file mode 100755
index 00000000..cc6b6451
--- /dev/null
+++ b/tests/dynfile_invld_sync.sh
@@ -0,0 +1,2 @@
+echo "\$OMFileAsyncWriting off" > rsyslog.action.1.include
+source $srcdir/dynfile_cachemiss.sh
diff --git a/tests/execonlyonce.sh b/tests/execonlyonce.sh
new file mode 100755
index 00000000..b7f60849
--- /dev/null
+++ b/tests/execonlyonce.sh
@@ -0,0 +1,28 @@
+# Test for the $ActionExecOnlyOnceEveryInterval directive.
+# We inject a couple of messages quickly during the interval,
+# then wait until the interval expires, then quickly inject
+# another set. After that, it is checked if exactly two messages
+# have arrived.
+# The once interval must be set to 3 seconds in the config file.
+# added 2009-11-12 by Rgerhards
+# This file is part of the rsyslog project, released under GPLv3
+echo ===============================================================================
+echo \[execonlyonce.sh\]: test for the $ActionExecOnlyOnceEveryInterval directive
+source $srcdir/diag.sh init
+source $srcdir/diag.sh startup execonlyonce.conf
+source $srcdir/diag.sh tcpflood -m10 -i1
+# now wait until the interval definitely expires
+sleep 4 # one more than the once inerval!
+# and inject another couple of messages
+source $srcdir/diag.sh tcpflood -m10 -i100
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+
+# now we need your custom logic to see if the result is equal to the
+# expected result
+cmp rsyslog.out.log testsuites/execonlyonce.data
+if [ $? -eq 1 ]
+then
+ echo "ERROR, output not as expected"
+ exit 1
+fi
+source $srcdir/diag.sh exit
diff --git a/tests/fieldtest.sh b/tests/fieldtest.sh
new file mode 100755
index 00000000..482fa143
--- /dev/null
+++ b/tests/fieldtest.sh
@@ -0,0 +1,13 @@
+echo test fieldtest via udp
+$srcdir/killrsyslog.sh # kill rsyslogd if it runs for some reason
+
+./nettester -tfield1 -iudp
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
+
+echo test fieldtest via tcp
+./nettester -tfield1 -itcp
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
diff --git a/tests/getline.c b/tests/getline.c
new file mode 100644
index 00000000..617d1b0e
--- /dev/null
+++ b/tests/getline.c
@@ -0,0 +1,57 @@
+/* getline() replacement for platforms that do not have it.
+ *
+ * Part of the testbench for rsyslog.
+ *
+ * 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 <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+
+/* we emulate getline (the dirty way) if we do not have it
+ * We do not try very hard, as this is just a test driver.
+ * rgerhards, 2009-03-31
+ */
+#ifndef HAVE_GETLINE
+ssize_t getline(char **lineptr, size_t *n, FILE *fp)
+{
+ int c;
+ int len = 0;
+
+ if(*lineptr == NULL)
+ *lineptr = malloc(4096); /* quick and dirty! */
+
+ c = fgetc(fp);
+ while(c != EOF && c != '\n') {
+ (*lineptr)[len++] = c;
+ c = fgetc(fp);
+ }
+ if(c != EOF) /* need to add NL? */
+ (*lineptr)[len++] = c;
+
+ (*lineptr)[len] = '\0';
+
+ *n = len;
+ //printf("getline returns: '%s'\n", *lineptr);
+
+ return (len > 0) ? len : -1;
+}
+#endif /* #ifndef HAVE_GETLINE */
diff --git a/tests/gzipwr_large.sh b/tests/gzipwr_large.sh
new file mode 100755
index 00000000..ffce06f6
--- /dev/null
+++ b/tests/gzipwr_large.sh
@@ -0,0 +1,20 @@
+# This tests writing large data records in gzip mode. We use up to 10K
+# record size.
+#
+# added 2010-03-10 by Rgerhards
+#
+# This file is part of the rsyslog project, released under GPLv3
+echo ===============================================================================
+echo TEST: \[gzipwr_large.sh\]: test for gzip file writing for large message sets
+source $srcdir/diag.sh init
+# uncomment for debugging support:
+#export RSYSLOG_DEBUG="debug nostdout noprintmutexaction"
+#export RSYSLOG_DEBUGLOG="log"
+source $srcdir/diag.sh startup gzipwr_large.conf
+# send 4000 messages of 10.000bytes plus header max, randomized
+source $srcdir/diag.sh tcpflood -m4000 -r -d10000 -P129
+sleep 1 # due to large messages, we need this time for the tcp receiver to settle...
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown # and wait for it to terminate
+source $srcdir/diag.sh gzip-seq-check 0 3999 -E
+source $srcdir/diag.sh exit
diff --git a/tests/gzipwr_large_dynfile.sh b/tests/gzipwr_large_dynfile.sh
new file mode 100755
index 00000000..73d44796
--- /dev/null
+++ b/tests/gzipwr_large_dynfile.sh
@@ -0,0 +1,36 @@
+# This tests writing large data records in gzip mode. We also write it to
+# 5 different dynafiles, with a dynafile cache size set to 4. So this stresses
+# both the input side, as well as zip writing, async writing and the dynafile
+# cache logic.
+#
+# This test is a bit timing-dependent on the tcp reception side, so if it fails
+# one may look into the timing first. The main issue is that the testbench
+# currently has no good way to know if the tcp receiver is finished. This is NOT
+# a problem in rsyslogd, but only of the testbench.
+#
+# Note that we do not yet have sufficient support for dynafiles in diag.sh,
+# so we mangle some files here manually.
+#
+# added 2010-03-10 by Rgerhards
+#
+# This file is part of the rsyslog project, released under GPLv3
+echo ====================================================================================
+echo TEST: \[gzipwr_large_dynfile.sh\]: test for gzip file writing for large message sets
+source $srcdir/diag.sh init
+# uncomment for debugging support:
+#export RSYSLOG_DEBUG="debug nostdout"
+#export RSYSLOG_DEBUGLOG="log"
+source $srcdir/diag.sh startup gzipwr_large_dynfile.conf
+# send 4000 messages of 10.000bytes plus header max, randomized
+source $srcdir/diag.sh tcpflood -m4000 -r -d10000 -P129 -f5
+sleep 2 # due to large messages, we need this time for the tcp receiver to settle...
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown # and wait for it to terminate
+gunzip < rsyslog.out.0.log > rsyslog.out.log
+gunzip < rsyslog.out.1.log >> rsyslog.out.log
+gunzip < rsyslog.out.2.log >> rsyslog.out.log
+gunzip < rsyslog.out.3.log >> rsyslog.out.log
+gunzip < rsyslog.out.4.log >> rsyslog.out.log
+#cat rsyslog.out.* > rsyslog.out.log
+source $srcdir/diag.sh seq-check 0 3999 -E
+source $srcdir/diag.sh exit
diff --git a/tests/imfile-basic.sh b/tests/imfile-basic.sh
new file mode 100755
index 00000000..ca6a5d3a
--- /dev/null
+++ b/tests/imfile-basic.sh
@@ -0,0 +1,14 @@
+# This is part of the rsyslog testbench, licensed under GPLv3
+echo [imfile-basic.sh]
+source $srcdir/diag.sh init
+# generate input file first. Note that rsyslog processes it as
+# soon as it start up (so the file should exist at that point).
+./inputfilegen 50000 > rsyslog.input
+ls -l rsyslog.input
+source $srcdir/diag.sh startup imfile-basic.conf
+# sleep a little to give rsyslog a chance to begin processing
+sleep 1
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown # we need to wait until rsyslogd is finished!
+source $srcdir/diag.sh seq-check 0 49999
+source $srcdir/diag.sh exit
diff --git a/tests/imtcp-multiport.sh b/tests/imtcp-multiport.sh
new file mode 100755
index 00000000..ad2b44f8
--- /dev/null
+++ b/tests/imtcp-multiport.sh
@@ -0,0 +1,42 @@
+# Test for multiple ports in imtcp
+# This test checks if multiple tcp listener ports are correctly
+# handled by imtcp
+#
+# NOTE: this test must (and can) be enhanced when we merge in the
+# upgraded tcpflood program
+#
+# added 2009-05-22 by Rgerhards
+# This file is part of the rsyslog project, released under GPLv3
+echo ===============================================================================
+echo \[imtcp-multiport.sh\]: testing imtcp multiple listeners
+source $srcdir/diag.sh init
+source $srcdir/diag.sh startup imtcp-multiport.conf
+source $srcdir/diag.sh tcpflood -p13514 -m10000
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown
+source $srcdir/diag.sh seq-check 0 9999
+source $srcdir/diag.sh exit
+#
+#
+# ### now complete new cycle with other port ###
+#
+#
+source $srcdir/diag.sh init
+source $srcdir/diag.sh startup imtcp-multiport.conf
+source $srcdir/diag.sh tcpflood -p13515 -m10000
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown
+source $srcdir/diag.sh seq-check 0 9999
+source $srcdir/diag.sh exit
+#
+#
+# ### now complete new cycle with other port ###
+#
+#
+source $srcdir/diag.sh init
+source $srcdir/diag.sh startup imtcp-multiport.conf
+source $srcdir/diag.sh tcpflood -p13516 -m10000
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown
+source $srcdir/diag.sh seq-check 0 9999
+source $srcdir/diag.sh exit
diff --git a/tests/inputfilegen.c b/tests/inputfilegen.c
new file mode 100644
index 00000000..26fb79af
--- /dev/null
+++ b/tests/inputfilegen.c
@@ -0,0 +1,23 @@
+/* generate an input file suitable for use by the testbench
+ * Copyright (C) 2011 by Rainer Gerhards and Adiscon GmbH.
+ * Part of rsyslog, licensed under GPLv3
+ */
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char* argv[])
+{
+ int nmsgs;
+ int i;
+
+ if(argc != 2) {
+ fprintf(stderr, "usage: inputfilegen num-messages\n");
+ return 1;
+ }
+
+ nmsgs = atoi(argv[1]);
+ for(i = 0 ; i < nmsgs ; ++i) {
+ printf("msgnum:%8.8d:\n", i);
+ }
+ return 0;
+}
diff --git a/tests/inputname.sh b/tests/inputname.sh
new file mode 100755
index 00000000..e1a58517
--- /dev/null
+++ b/tests/inputname.sh
@@ -0,0 +1,20 @@
+echo testing $InputTCPServerInputName directive
+$srcdir/killrsyslog.sh # kill rsyslogd if it runs for some reason
+
+echo port 12514
+./nettester -tinputname_imtcp_12514 -cinputname_imtcp -itcp -p12514
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
+
+echo port 12515
+./nettester -tinputname_imtcp_12515 -cinputname_imtcp -itcp -p12515
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
+
+echo port 12516
+./nettester -tinputname_imtcp_12516 -cinputname_imtcp -itcp -p12516
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
diff --git a/tests/killrsyslog.sh b/tests/killrsyslog.sh
new file mode 100755
index 00000000..aac24909
--- /dev/null
+++ b/tests/killrsyslog.sh
@@ -0,0 +1,13 @@
+#check if rsyslog instance exists and, if so, kill it
+if [ -e "rsyslog.pid" ]
+then
+ echo rsyslog.pid exists, trying to shut down rsyslogd process `cat rsyslog.pid`.
+ kill -9 `cat rsyslog.pid`
+ sleep 1
+fi
+if [ -e "rsyslog2.pid" ]
+then
+ echo rsyslog2.pid exists, trying to shut down rsyslogd process `cat rsyslog2.pid`.
+ kill -9 `cat rsyslog2.pid`
+ sleep 1
+fi
diff --git a/tests/longrun.sh b/tests/longrun.sh
new file mode 100755
index 00000000..407e9fa9
--- /dev/null
+++ b/tests/longrun.sh
@@ -0,0 +1,30 @@
+# This is a test-aid script to try running some tests through many iterations.
+# It is not yet used in the automated testbench, but I keep this file so that
+# I can use it whenever there is need to. As such, it currently does not have
+# parameters but is expected to be edited as needed. -- rgerhards, 2010-03-10
+#
+# use: ./longrun.sh testname.sh
+#
+# where testname.sh is the test to be run
+# to change other params, you need to edit the settings here below:
+MAXRUNS=10
+DISPLAYALIVE=100
+LOGFILE=runlog
+
+echo "logfile is $LOGFILE"
+echo "executing test $1"
+
+date > $LOGFILE
+
+for (( i=0; $i < 10000; i++ ))
+ do
+ if [ $(( i % DISPLAYALIVE )) -eq 0 ]; then
+ echo "$i iterations done"
+ fi
+ $1 >> $LOGFILE
+ if [ "$?" -ne "0" ]; then
+ echo "Test failed in iteration $i, review $LOGFILE for details!"
+ exit 1
+ fi
+ done
+echo "No failure in $i iterations."
diff --git a/tests/manytcp.sh b/tests/manytcp.sh
new file mode 100755
index 00000000..94a5c035
--- /dev/null
+++ b/tests/manytcp.sh
@@ -0,0 +1,11 @@
+# test many concurrent tcp connections
+source $srcdir/diag.sh init
+source $srcdir/diag.sh startup manytcp.conf
+# the config file specifies exactly 1100 connections
+source $srcdir/diag.sh tcpflood -c1000 -m40000
+# the sleep below is needed to prevent too-early termination of the tcp listener
+sleep 1
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown # we need to wait until rsyslogd is finished!
+source $srcdir/diag.sh seq-check 0 39999
+source $srcdir/diag.sh exit
diff --git a/tests/nettester.c b/tests/nettester.c
new file mode 100644
index 00000000..07a1c052
--- /dev/null
+++ b/tests/nettester.c
@@ -0,0 +1,534 @@
+/* Runs a test suite on the rsyslog (and later potentially
+ * other things).
+ *
+ * The name of the test suite must be given as argv[1]. In this config,
+ * rsyslogd is loaded with config ./testsuites/<name>.conf and then
+ * test cases ./testsuites/ *.<name> are executed on it. This test driver is
+ * suitable for testing cases where a message sent (via UDP) results in
+ * exactly one response. It can not be used in cases where no response
+ * is expected (that would result in a hang of the test driver).
+ * Note: each test suite can contain many tests, but they all need to work
+ * with the same rsyslog configuration.
+ *
+ * Part of the testbench for rsyslog.
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <unistd.h>
+#include <string.h>
+#include <glob.h>
+#include <signal.h>
+#include <netinet/in.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#define EXIT_FAILURE 1
+#define INVALID_SOCKET -1
+/* Name of input file, must match $IncludeConfig in test suite .conf files */
+#define NETTEST_INPUT_CONF_FILE "nettest.input.conf" /* name of input file, must match $IncludeConfig in .conf files */
+
+typedef enum { inputUDP, inputTCP } inputMode_t;
+inputMode_t inputMode = inputTCP; /* input for which tests are to be run */
+static pid_t rsyslogdPid = 0; /* pid of rsyslog instance being tested */
+static char *srcdir; /* global $srcdir, set so that we can run outside of "make check" */
+static char *testSuite = NULL; /* name of current test suite */
+static int iPort = 12514; /* port which shall be used for sending data */
+static char* pszCustomConf = NULL; /* custom config file, use -c conf to specify */
+static int verbose = 0; /* verbose output? -v option */
+static int useDebugEnv = 0; /* activate debugging environment (for rsyslog debug log)? */
+
+/* these two are quick hacks... */
+int iFailed = 0;
+int iTests = 0;
+
+/* provide user-friednly name of input mode
+ */
+static char *inputMode2Str(inputMode_t mode)
+{
+ char *pszMode;
+
+ if(mode == inputUDP)
+ pszMode = "udp";
+ else
+ pszMode = "tcp";
+
+ return pszMode;
+}
+
+
+void readLine(int fd, char *ln)
+{
+ char *orig = ln;
+ char c;
+ int lenRead;
+
+ if(verbose)
+ fprintf(stderr, "begin readLine\n");
+ lenRead = read(fd, &c, 1);
+ while(lenRead == 1 && c != '\n') {
+ if(c == '\0') {
+ *ln = c;
+ fprintf(stderr, "Warning: there was a '\\0'-Byte in the read response "
+ "right after this string: '%s'\n", orig);
+ c = '?';
+ }
+ *ln++ = c;
+ lenRead = read(fd, &c, 1);
+ }
+ *ln = '\0';
+
+ if(verbose)
+ fprintf(stderr, "end readLine, val read '%s'\n", orig);
+}
+
+
+/* send a message via TCP
+ * We open the connection on the initial send, and never close it
+ * (let the OS do that). If a conneciton breaks, we do NOT try to
+ * recover, so all test after that one will fail (and the test
+ * driver probably hang. returns 0 if ok, something else otherwise.
+ * We use traditional framing '\n' at EOR for this tester. It may be
+ * worth considering additional framing modes.
+ * rgerhards, 2009-04-08
+ */
+int
+tcpSend(char *buf, int lenBuf)
+{
+ static int sock = INVALID_SOCKET;
+ struct sockaddr_in addr;
+ int retries;
+
+ if(sock == INVALID_SOCKET) {
+ /* first time, need to connect to target */
+ if((sock=socket(AF_INET, SOCK_STREAM, 0))==-1) {
+ perror("socket()");
+ return(1);
+ }
+
+ memset((char *) &addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(iPort);
+ if(inet_aton("127.0.0.1", &addr.sin_addr)==0) {
+ fprintf(stderr, "inet_aton() failed\n");
+ return(1);
+ }
+ retries = 0;
+ while(1) { /* loop broken inside */
+ if(connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
+ break;
+ } else {
+ if(retries++ == 50) {
+ ++iFailed;
+ fprintf(stderr, "connect() failed\n");
+ return(1);
+ } else {
+ usleep(100000); /* 0.1 sec, these are us! */
+ }
+ }
+ }
+ }
+
+ /* send test data */
+ if(send(sock, buf, lenBuf, 0) != lenBuf) {
+ perror("send test data");
+ fprintf(stderr, "send() failed\n");
+ return(1);
+ }
+
+ /* send record terminator */
+ if(send(sock, "\n", 1, 0) != 1) {
+ perror("send record terminator");
+ fprintf(stderr, "send() failed\n");
+ return(1);
+ }
+
+ return 0;
+}
+
+
+/* send a message via UDP
+ * returns 0 if ok, something else otherwise.
+ */
+int
+udpSend(char *buf, int lenBuf)
+{
+ struct sockaddr_in si_other;
+ int s, slen=sizeof(si_other);
+
+ if((s=socket(AF_INET, SOCK_DGRAM, 0))==-1) {
+ perror("socket()");
+ return(1);
+ }
+
+ memset((char *) &si_other, 0, sizeof(si_other));
+ si_other.sin_family = AF_INET;
+ si_other.sin_port = htons(iPort);
+ if(inet_aton("127.0.0.1", &si_other.sin_addr)==0) {
+ fprintf(stderr, "inet_aton() failed\n");
+ return(1);
+ }
+
+ if(sendto(s, buf, lenBuf, 0, (struct sockaddr*) &si_other, slen)==-1) {
+ perror("sendto");
+ fprintf(stderr, "sendto() failed\n");
+ return(1);
+ }
+
+ close(s);
+ return 0;
+}
+
+
+/* open pipe to test candidate - so far, this is
+ * always rsyslogd and with a fixed config. Later, we may
+ * change this. Returns 0 if ok, something else otherwise.
+ * rgerhards, 2009-03-31
+ */
+int openPipe(char *configFile, pid_t *pid, int *pfd)
+{
+ int pipefd[2];
+ pid_t cpid;
+ char *newargv[] = {"../tools/rsyslogd", "dummy", "-c4", "-u2", "-n", "-irsyslog.pid",
+ "-M../runtime/.libs:../.libs", NULL };
+ char confFile[1024];
+ char *newenviron[] = { NULL };
+ char *newenvironDeb[] = { "RSYSLOG_DEBUG=debug nostdout",
+ "RSYSLOG_DEBUGLOG=log", NULL };
+
+ sprintf(confFile, "-f%s/testsuites/%s.conf", srcdir,
+ (pszCustomConf == NULL) ? configFile : pszCustomConf);
+ newargv[1] = confFile;
+
+ if (pipe(pipefd) == -1) {
+ perror("pipe");
+ exit(EXIT_FAILURE);
+ }
+
+ cpid = fork();
+ if (cpid == -1) {
+ perror("fork");
+ exit(EXIT_FAILURE);
+ }
+
+ if(cpid == 0) { /* Child reads from pipe */
+ fclose(stdout);
+ dup(pipefd[1]);
+ close(pipefd[1]);
+ close(pipefd[0]);
+ fclose(stdin);
+ execve("../tools/rsyslogd", newargv, (useDebugEnv) ? newenvironDeb : newenviron);
+ } else {
+ close(pipefd[1]);
+ *pid = cpid;
+ *pfd = pipefd[0];
+ }
+
+ return(0);
+}
+
+
+/* This function unescapes a string of testdata. That it, escape sequences
+ * are converted into their one-character equivalent. While doing so, it applies
+ * C-like semantics. This was made necessary for easy integration of control
+ * characters inside test cases. -- rgerhards, 2009-03-11
+ * Currently supported:
+ * \\ single backslash
+ * \n, \t, \r as in C
+ * \nnn where nnn is a 1 to 3 character octal sequence
+ * Note that when a problem occurs, the end result is undefined. After all, this
+ * is for a testsuite generatort, it must not be 100% bullet proof (so do not
+ * copy this code into something that must be!). Also note that we do in-memory
+ * unescaping and assume that the string gets shorter but NEVER longer!
+ */
+void unescapeTestdata(char *testdata)
+{
+ char *pDst;
+ char *pSrc;
+ int i;
+ int c;
+
+ pDst = pSrc = testdata;
+ while(*pSrc) {
+ if(*pSrc == '\\') {
+ switch(*++pSrc) {
+ case '\\': *pDst++ = *pSrc++;
+ break;
+ case 'n': *pDst++ = '\n';
+ ++pSrc;
+ break;
+ case 'r': *pDst++ = '\r';
+ ++pSrc;
+ break;
+ case 't': *pDst++ = '\t';
+ ++pSrc;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3': c = *pSrc++ - '0';
+ i = 1; /* we already processed one digit! */
+ while(i < 3 && isdigit(*pSrc)) {
+ c = c * 8 + *pSrc++ - '0';
+ ++i;
+ }
+ *pDst++ = c;
+ break;
+ default: break;
+ }
+ } else {
+ *pDst++ = *pSrc++;
+ }
+ }
+ *pDst = '\0';
+}
+
+
+/* Process a specific test case. File name is provided.
+ * Needs to return 0 if all is OK, something else otherwise.
+ */
+int
+processTestFile(int fd, char *pszFileName)
+{
+ FILE *fp;
+ char *testdata = NULL;
+ char *expected = NULL;
+ int ret = 0;
+ size_t lenLn;
+ char buf[4096];
+
+ if((fp = fopen((char*)pszFileName, "r")) == NULL) {
+ perror((char*)pszFileName);
+ return(2);
+ }
+
+ /* skip comments at start of file */
+
+ while(!feof(fp)) {
+ getline(&testdata, &lenLn, fp);
+ while(!feof(fp)) {
+ if(*testdata == '#')
+ getline(&testdata, &lenLn, fp);
+ else
+ break; /* first non-comment */
+ }
+
+ /* this is not perfect, but works ;) */
+ if(feof(fp))
+ break;
+
+ ++iTests; /* increment test count, we now do one! */
+
+ testdata[strlen(testdata)-1] = '\0'; /* remove \n */
+ /* now we have the test data to send (we could use function pointers here...) */
+ unescapeTestdata(testdata);
+ if(inputMode == inputUDP) {
+ if(udpSend(testdata, strlen(testdata)) != 0)
+ return(2);
+ } else {
+ if(tcpSend(testdata, strlen(testdata)) != 0)
+ return(2);
+ }
+
+ /* next line is expected output
+ * we do not care about EOF here, this will lead to a failure and thus
+ * draw enough attention. -- rgerhards, 2009-03-31
+ */
+ getline(&expected, &lenLn, fp);
+ expected[strlen(expected)-1] = '\0'; /* remove \n */
+
+ /* pull response from server and then check if it meets our expectation */
+ readLine(fd, buf);
+ if(strcmp(expected, buf)) {
+ ++iFailed;
+ printf("\nFile %s:\nExpected Response:\n'%s'\nActual Response:\n'%s'\n",
+ pszFileName, expected, buf);
+ ret = 1;
+ }
+ /* we need to free buffers, as we have potentially modified them! */
+ free(testdata);
+ testdata = NULL;
+ free(expected);
+ expected = NULL;
+ }
+
+ free(expected);
+ fclose(fp);
+ return(ret);
+}
+
+
+/* carry out all tests. Tests are specified via a file name
+ * wildcard. Each of the files is read and the test carried
+ * out.
+ * Returns the number of tests that failed. Zero means all
+ * success.
+ */
+int
+doTests(int fd, char *files)
+{
+ int ret;
+ char *testFile;
+ glob_t testFiles;
+ size_t i = 0;
+ struct stat fileInfo;
+
+ glob(files, GLOB_MARK, NULL, &testFiles);
+
+ for(i = 0; i < testFiles.gl_pathc; i++) {
+ testFile = testFiles.gl_pathv[i];
+
+ if(stat((char*) testFile, &fileInfo) != 0)
+ continue; /* continue with the next file if we can't stat() the file */
+
+ /* all regular files are run through the test logic. Symlinks don't work. */
+ if(S_ISREG(fileInfo.st_mode)) { /* config file */
+ if(verbose) printf("processing test case '%s' ... ", testFile);
+ ret = processTestFile(fd, testFile);
+ if(ret == 0) {
+ if(verbose) printf("successfully completed\n");
+ } else {
+ if(!verbose)
+ printf("test '%s' ", testFile);
+ printf("failed!\n");
+ }
+ }
+ }
+ globfree(&testFiles);
+
+ if(iTests == 0) {
+ printf("Error: no test cases found, no tests executed.\n");
+ iFailed = 1;
+ } else {
+ printf("Number of tests run: %d, number of failures: %d\n", iTests, iFailed);
+ }
+
+ return(iFailed);
+}
+
+/* cleanup */
+void doAtExit(void)
+{
+ int status;
+
+ if(rsyslogdPid != 0) {
+ kill(rsyslogdPid, SIGTERM);
+ waitpid(rsyslogdPid, &status, 0); /* wait until instance terminates */
+ }
+
+ unlink(NETTEST_INPUT_CONF_FILE);
+}
+
+/* Run the test suite. This must be called with exactly one parameter, the
+ * name of the test suite. For details, see file header comment at the top
+ * of this file.
+ * rgerhards, 2009-04-03
+ */
+int main(int argc, char *argv[])
+{
+ int fd;
+ int opt;
+ int ret = 0;
+ FILE *fp;
+ char buf[4096];
+ char testcases[4096];
+
+ while((opt = getopt(argc, argv, "dc:i:p:t:v")) != EOF) {
+ switch((char)opt) {
+ case 'c':
+ pszCustomConf = optarg;
+ break;
+ case 'd':
+ useDebugEnv = 1;
+ break;
+ case 'i':
+ if(!strcmp(optarg, "udp"))
+ inputMode = inputUDP;
+ else if(!strcmp(optarg, "tcp"))
+ inputMode = inputTCP;
+ else {
+ printf("error: unsupported input mode '%s'\n", optarg);
+ exit(1);
+ }
+ break;
+ case 'p':
+ iPort = atoi(optarg);
+ break;
+ case 't':
+ testSuite = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:printf("Invalid call of nettester, invalid option '%c'.\n", opt);
+ printf("Usage: nettester -d -ttestsuite-name -iudp|tcp [-pport] [-ccustomConfFile] \n");
+ exit(1);
+ }
+ }
+
+ if(testSuite == NULL) {
+ printf("error: no testsuite given, need to specify -t testsuite!\n");
+ exit(1);
+ }
+
+ atexit(doAtExit);
+
+ if((srcdir = getenv("srcdir")) == NULL)
+ srcdir = ".";
+
+ if(verbose) printf("Start of nettester run ($srcdir=%s, testsuite=%s, input=%s/%d)\n",
+ srcdir, testSuite, inputMode2Str(inputMode), iPort);
+
+ /* create input config file */
+ if((fp = fopen(NETTEST_INPUT_CONF_FILE, "w")) == NULL) {
+ perror(NETTEST_INPUT_CONF_FILE);
+ printf("error opening input configuration file\n");
+ exit(1);
+ }
+ if(inputMode == inputUDP) {
+ fputs("$ModLoad ../plugins/imudp/.libs/imudp\n", fp);
+ fprintf(fp, "$UDPServerRun %d\n", iPort);
+ } else {
+ fputs("$ModLoad ../plugins/imtcp/.libs/imtcp\n", fp);
+ fprintf(fp, "$InputTCPServerRun %d\n", iPort);
+ }
+ fclose(fp);
+
+ /* start to be tested rsyslogd */
+ openPipe(testSuite, &rsyslogdPid, &fd);
+ readLine(fd, buf);
+
+ /* generate filename */
+ sprintf(testcases, "%s/testsuites/*.%s", srcdir, testSuite);
+ if(doTests(fd, testcases) != 0)
+ ret = 1;
+
+ if(verbose) printf("End of nettester run (%d).\n", ret);
+ exit(ret);
+}
diff --git a/tests/omod-if-array.sh b/tests/omod-if-array.sh
new file mode 100755
index 00000000..2c2a8ef3
--- /dev/null
+++ b/tests/omod-if-array.sh
@@ -0,0 +1,14 @@
+echo test omod-if-array via udp
+$srcdir/killrsyslog.sh # kill rsyslogd if it runs for some reason
+
+./nettester -tomod-if-array -iudp -p4711
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
+
+echo test omod-if-array via tcp
+./nettester -tomod-if-array -itcp
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
+
diff --git a/tests/ourtail.c b/tests/ourtail.c
new file mode 100644
index 00000000..c31babb9
--- /dev/null
+++ b/tests/ourtail.c
@@ -0,0 +1,46 @@
+/* This is a quick and dirty "tail implementation", one which always
+ * skips the first line, but nothing else. I have done this to prevent
+ * the various incompatible options of tail come into my way. One could
+ * probably work around this by using autoconf magic, but for me it
+ * was much quicker writing this small C program, which really should
+ * be portable across all platforms.
+ *
+ * Part of the testbench for rsyslog.
+ *
+ * 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 <stdio.h>
+
+int main(int __attribute__((unused)) argc, char __attribute__((unused)) *argv[])
+{
+ int c;
+
+ for(c = getchar() ; c != EOF && c != '\n' ; c = getchar())
+ /*skip to newline*/;
+
+ if(c == '\n')
+ c = getchar();
+
+ for( ; c != EOF ; c = getchar())
+ putchar(c);
+
+ return 0;
+}
diff --git a/tests/parsertest.sh b/tests/parsertest.sh
new file mode 100755
index 00000000..fc68ab84
--- /dev/null
+++ b/tests/parsertest.sh
@@ -0,0 +1,33 @@
+echo TEST: \[parsertest.sh\]: various parser tests
+source $srcdir/diag.sh init
+source $srcdir/diag.sh nettester parse1 udp
+source $srcdir/diag.sh nettester parse1 tcp
+source $srcdir/diag.sh nettester parse3 udp
+source $srcdir/diag.sh nettester parse3 tcp
+source $srcdir/diag.sh nettester parse_invld_regex udp
+source $srcdir/diag.sh nettester parse_invld_regex tcp
+source $srcdir/diag.sh nettester parse-3164-buggyday udp
+source $srcdir/diag.sh nettester parse-3164-buggyday tcp
+source $srcdir/diag.sh nettester parse-nodate udp
+source $srcdir/diag.sh nettester parse-nodate tcp
+# the following samples can only be run over UDP as they are so
+# malformed they break traditional syslog/tcp framing...
+source $srcdir/diag.sh nettester snare_ccoff_udp udp
+source $srcdir/diag.sh nettester snare_ccoff_udp2 udp
+
+echo \[parsertest.sh]: redoing tests in IPv4-only mode
+source $srcdir/diag.sh nettester parse1 udp -4
+source $srcdir/diag.sh nettester parse1 tcp -4
+source $srcdir/diag.sh nettester parse3 udp -4
+source $srcdir/diag.sh nettester parse3 tcp -4
+source $srcdir/diag.sh nettester parse_invld_regex udp -4
+source $srcdir/diag.sh nettester parse_invld_regex tcp -4
+source $srcdir/diag.sh nettester parse-3164-buggyday udp -4
+source $srcdir/diag.sh nettester parse-3164-buggyday tcp -4
+source $srcdir/diag.sh nettester parse-nodate udp -4
+source $srcdir/diag.sh nettester parse-nodate tcp -4
+# UDP-only tests
+source $srcdir/diag.sh nettester snare_ccoff_udp udp -4
+source $srcdir/diag.sh nettester snare_ccoff_udp2 udp -4
+
+source $srcdir/diag.sh exit
diff --git a/tests/pipeaction.sh b/tests/pipeaction.sh
new file mode 100755
index 00000000..c2201011
--- /dev/null
+++ b/tests/pipeaction.sh
@@ -0,0 +1,33 @@
+# Test for the pipe output action.
+# will create a fifo in the current directory, write to it and
+# then do the usual sequence checks.
+# added 2009-11-05 by RGerhards
+echo ===============================================================================
+echo \[pipeaction.sh\]: testing pipe output action
+
+# create the pipe and start a background process that copies data from
+# it to the "regular" work file
+source $srcdir/diag.sh init
+rm -f rsyslog-testbench-fifo
+mkfifo rsyslog-testbench-fifo
+cp rsyslog-testbench-fifo rsyslog.out.log &
+CPPROCESS=$!
+echo background cp process id is $CPPROCESS
+
+# now do the usual run
+source $srcdir/diag.sh startup pipeaction.conf
+# 20000 messages should be enough
+#source $srcdir/diag.sh tcpflood -m20000
+source $srcdir/diag.sh injectmsg 0 20000
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown
+
+# wait for the cp process to finish, do pipe-specific cleanup
+echo waiting for background cp to terminate...
+wait $CPPROCESS
+rm -f rsyslog-testbench-fifo
+echo background cp has terminated, continue test...
+
+# and continue the usual checks
+source $srcdir/diag.sh seq-check 0 19999
+source $srcdir/diag.sh exit
diff --git a/tests/proprepltest.sh b/tests/proprepltest.sh
new file mode 100755
index 00000000..3c252e52
--- /dev/null
+++ b/tests/proprepltest.sh
@@ -0,0 +1,7 @@
+echo TEST: proprepltest.sh - various tests for the property replacer
+source $srcdir/diag.sh init
+source $srcdir/diag.sh nettester rfctag udp
+source $srcdir/diag.sh nettester rfctag tcp
+source $srcdir/diag.sh nettester nolimittag udp
+source $srcdir/diag.sh nettester nolimittag tcp
+source $srcdir/diag.sh init
diff --git a/tests/queue-persist-drvr.sh b/tests/queue-persist-drvr.sh
new file mode 100755
index 00000000..ea5386a7
--- /dev/null
+++ b/tests/queue-persist-drvr.sh
@@ -0,0 +1,28 @@
+# Test for queue data persisting at shutdown. The
+# plan is to start an instance, emit some data, do a relatively
+# fast shutdown and then re-start the engine to process the
+# remaining data.
+# added 2009-05-27 by Rgerhards
+# This file is part of the rsyslog project, released under GPLv3
+# uncomment for debugging support:
+echo testing memory queue persisting to disk, mode $1
+source $srcdir/diag.sh init
+
+# prepare config
+echo \$MainMsgQueueType $1 > work-queuemode.conf
+echo "*.* :omtesting:sleep 0 1000" > work-delay.conf
+
+# inject 5000 msgs, so that we do not hit the high watermark
+source $srcdir/diag.sh startup queue-persist.conf
+source $srcdir/diag.sh injectmsg 0 5000
+$srcdir/diag.sh shutdown-immediate
+$srcdir/diag.sh wait-shutdown
+source $srcdir/diag.sh check-mainq-spool
+
+# restart engine and have rest processed
+#remove delay
+echo "#" > work-delay.conf
+source $srcdir/diag.sh startup queue-persist.conf
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh seq-check 0 4999
+source $srcdir/diag.sh exit
diff --git a/tests/queue-persist.sh b/tests/queue-persist.sh
new file mode 100755
index 00000000..999655b1
--- /dev/null
+++ b/tests/queue-persist.sh
@@ -0,0 +1,11 @@
+# Test for queue data persisting at shutdown. We use the actual driver
+# to carry out multiple tests with different queue modes
+# added 2009-05-27 by Rgerhards
+# This file is part of the rsyslog project, released under GPLv3
+source $srcdir/queue-persist-drvr.sh LinkedList
+source $srcdir/queue-persist-drvr.sh FixedArray
+# the disk test should not fail, however, the config is extreme and using
+# it more or less is a config error
+source $srcdir/queue-persist-drvr.sh Disk
+# we do not test Direct mode because this absolute can not work in direct mode
+# (maybe we should do a fail-type of test?)
diff --git a/tests/random.sh b/tests/random.sh
new file mode 100755
index 00000000..969d720c
--- /dev/null
+++ b/tests/random.sh
@@ -0,0 +1,18 @@
+# Test if rsyslog survives sending truely random data to it...
+#
+# added 2010-04-01 by Rgerhards
+# This file is part of the rsyslog project, released under GPLv3
+echo ===============================================================================
+echo TEST: \[random.sh\]: testing random data
+source $srcdir/diag.sh init
+source $srcdir/diag.sh startup random.conf
+# generate random data
+./randomgen -f rsyslog.random.data -s 100000
+ls -l rsyslog.random.data
+source $srcdir/diag.sh tcpflood -B -I rsyslog.random.data -c5 -C10
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown # and wait for it to terminate
+# we do not check anything yet, the point is if rsyslog survived ;)
+# TODO: check for exit message, but we'll notice an abort anyhow, so not that important
+rm -f random.data
+source $srcdir/diag.sh exit
diff --git a/tests/randomgen.c b/tests/randomgen.c
new file mode 100644
index 00000000..9ba56954
--- /dev/null
+++ b/tests/randomgen.c
@@ -0,0 +1,130 @@
+/* generates random data for later use in test cases. Of course,
+ * we could generate random data during the testcase itself, but
+ * the core idea is that we record the random data so that we have
+ * a chance to reproduce a problem should it occur. IMHO this
+ * provides the best compromise, by a) having randomness but
+ * b) knowing what was used during the test.
+ *
+ * Params
+ * -f output file name (stdout if not given)
+ * -s size of test data, plain number is size in k, 1MB default
+ * -u uses /dev/urandom instead of libc random number generator
+ * (when available). Note that this is usually much slower.
+ *
+ * Part of the testbench for rsyslog.
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <unistd.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#define EXIT_FAILURE 1
+
+static char *fileName = NULL; /* name of output file */
+static int tryUseURandom = 0; /* try to use /dev/urandom? */
+static long long fileSize = 1024*1024; /* file size in K, 1MB default */
+
+
+/* generate the random file. This code really can be improved (e.g. read /dev/urandom
+ * when available)
+ */
+static inline void
+genFile()
+{
+ long i;
+ FILE *fp;
+ FILE *rfp = NULL;
+
+ if(fileName == NULL) {
+ fp = stdout;
+ } else {
+ if((fp = fopen(fileName, "w")) == NULL) {
+ perror(fileName);
+ }
+ }
+
+ /* try to use /dev/urandom, if available */
+ if(tryUseURandom)
+ rfp = fopen("/dev/urandom", "r");
+
+ if(rfp == NULL) {
+ /* fallback, use libc random number generator */
+ for(i = 0 ; i < fileSize ; ++i) {
+ if(fputc((char) rand(), fp) == EOF) {
+ perror(fileName);
+ exit(1);
+ }
+ }
+ } else {
+ /* use /dev/urandom */
+ printf("using /dev/urandom");
+ for(i = 0 ; i < fileSize ; ++i) {
+ if(fputc(fgetc(rfp), fp) == EOF) {
+ perror(fileName);
+ exit(1);
+ }
+ }
+ }
+
+ if(fileName != NULL)
+ fclose(fp);
+}
+
+
+/* Run the test.
+ * rgerhards, 2009-04-03
+ */
+int main(int argc, char *argv[])
+{
+ int ret = 0;
+ int opt;
+
+ srand(time(NULL)); /* seed is good enough for our needs */
+
+ while((opt = getopt(argc, argv, "f:s:u")) != -1) {
+ switch (opt) {
+ case 'f': fileName = optarg;
+ break;
+ case 's': fileSize = atol(optarg) * 1024;
+ break;
+ case 'u': tryUseURandom = 1;
+ break;
+ default: printf("invalid option '%c' or value missing - terminating...\n", opt);
+ exit (1);
+ break;
+ }
+ }
+
+ printf("generating random data file '%s' of %ldkb - may take a short while...\n",
+ fileName, (long) (fileSize / 1024));
+ genFile();
+
+ exit(ret);
+}
diff --git a/tests/rscript.c b/tests/rscript.c
index d4e8caeb..6361aec4 100644
--- a/tests/rscript.c
+++ b/tests/rscript.c
@@ -24,6 +24,7 @@
*/
#include "config.h"
#include <stdio.h>
+#include <string.h>
#include <glob.h>
#include <sys/stat.h>
@@ -39,6 +40,7 @@ DEFobjCurrIf(ctok)
DEFobjCurrIf(ctok_token)
DEFobjCurrIf(vmprg)
+
BEGINInit
CODESTARTInit
pErrObj = "expr"; CHKiRet(objUse(expr, CORE_COMPONENT));
@@ -102,8 +104,8 @@ PerformTest(cstr_t *pstrIn, rsRetVal iRetExpected, cstr_t *pstrOut)
if(strcmp((char*)rsCStrGetSzStr(pstrPrg), (char*)rsCStrGetSzStr(pstrOut))) {
printf("error: compiled program different from expected result!\n");
- printf("generated vmprg:\n%s\n", rsCStrGetSzStr(pstrPrg));
- printf("expected:\n%s\n", rsCStrGetSzStr(pstrOut));
+ printf("generated vmprg (%d bytes):\n%s\n", (int)strlen((char*)rsCStrGetSzStr(pstrPrg)), rsCStrGetSzStr(pstrPrg));
+ printf("expected (%d bytes):\n%s\n", (int)strlen((char*)rsCStrGetSzStr(pstrOut)), rsCStrGetSzStr(pstrOut));
ABORT_FINALIZE(RS_RET_ERR);
}
@@ -138,6 +140,7 @@ ProcessTestFile(uchar *pszFileName)
size_t lenLn;
cstr_t *pstrIn = NULL;
cstr_t *pstrOut = NULL;
+ int iParse;
rsRetVal iRetExpected;
DEFiRet;
@@ -160,10 +163,11 @@ ProcessTestFile(uchar *pszFileName)
/* once we had a comment, the next line MUST be "result: <nbr>". Anything
* after nbr is simply ignored.
*/
- if(sscanf(lnptr, "result: %d", &iRetExpected) != 1) {
+ if(sscanf(lnptr, "result: %d", &iParse) != 1) {
printf("error in result line, scanf failed, line: '%s'\n", lnptr);
ABORT_FINALIZE(RS_RET_ERR);
}
+ iRetExpected = iParse;
getline(&lnptr, &lenLn, fp); CHKEOF;
/* and now we look for "in:" (and again ignore the rest...) */
diff --git a/tests/rt-init.c b/tests/rt-init.c
index aaac7ed1..66a9ad32 100644
--- a/tests/rt-init.c
+++ b/tests/rt-init.c
@@ -21,10 +21,10 @@
*
* A copy of the GPL can be found in the file "COPYING" in this distribution.
*/
-#include <stdio.h>
-
+#include "config.h"
#include "rsyslog.h"
#include "testbench.h"
+#include <stdio.h> /* must be last, else we get a zlib compile error on some platforms */
MODULE_TYPE_TESTBENCH
diff --git a/tests/runtime-dummy.c b/tests/runtime-dummy.c
index 9cddd913..38e6bba1 100644
--- a/tests/runtime-dummy.c
+++ b/tests/runtime-dummy.c
@@ -25,7 +25,9 @@
*
* A copy of the GPL can be found in the file "COPYING" in this distribution.
*/
+#include "config.h"
#include <stdlib.h>
+#include "rsyslog.h"
int bReduceRepeatMsgs = 0;
int repeatinterval = 30;
@@ -37,5 +39,7 @@ void cflineClassic(void) {};
void selectorAddList(void) {};
void selectorConstruct(void) {};
void selectorDestruct(void) {};
+void getFIOPName(void) {};
+ruleset_t *pCurrRuleset;
/* these are required by some dynamically loaded modules */
diff --git a/tests/sndrcv.sh b/tests/sndrcv.sh
new file mode 100755
index 00000000..2fc3bd82
--- /dev/null
+++ b/tests/sndrcv.sh
@@ -0,0 +1,9 @@
+# This tests two rsyslog instances. Instance
+# TWO sends data to instance ONE. A number of messages is injected into
+# the instance 2 and we finally check if all those messages
+# arrived at instance 1.
+# added 2009-11-11 by Rgerhards
+# This file is part of the rsyslog project, released under GPLv3
+echo ===============================================================================
+echo \[sndrcv.sh\]: testing sending and receiving via tcp
+source $srcdir/sndrcv_drvr.sh sndrcv 50000
diff --git a/tests/sndrcv_drvr.sh b/tests/sndrcv_drvr.sh
new file mode 100755
index 00000000..3d613069
--- /dev/null
+++ b/tests/sndrcv_drvr.sh
@@ -0,0 +1,50 @@
+# This is test driver for testing two rsyslog instances. It can be
+# utilized by any test that just needs two instances with different
+# config files, where messages are injected in instance TWO and
+# (with whatever rsyslog mechanism) being relayed over to instance ONE,
+# where they are written to the log file. After the run, the completeness
+# of that log file is checked.
+# The code is almost the same, but the config files differ (probably greatly)
+# for different test cases. As such, this driver needs to be called with the
+# config file name ($2). From that name, the sender and receiver config file
+# names are automatically generated.
+# So: $1 config file name, $2 number of messages
+#
+# A note on TLS testing: the current testsuite (in git!) already contains
+# TLS test cases. However, getting these test cases correct is not simple.
+# That's not a problem with the code itself, but rater a problem with
+# synchronization in the test environment. So I have deciced to keep the
+# TLS tests in, but not yet actually utilize them. This is most probably
+# left as an excercise for future (devel) releases. -- rgerhards, 2009-11-11
+#
+# added 2009-11-11 by Rgerhards
+# This file is part of the rsyslog project, released under GPLv3
+# uncomment for debugging support:
+source $srcdir/diag.sh init
+# start up the instances
+#export RSYSLOG_DEBUG="debug nostdout noprintmutexaction"
+#export RSYSLOG_DEBUGLOG="log"
+source $srcdir/diag.sh startup $1_rcvr.conf
+source $srcdir/diag.sh wait-startup
+#export RSYSLOG_DEBUGLOG="log2"
+#valgrind="valgrind"
+source $srcdir/diag.sh startup $1_sender.conf 2
+source $srcdir/diag.sh wait-startup 2
+# may be needed by TLS (once we do it): sleep 30
+
+# now inject the messages into instance 2. It will connect to instance 1,
+# and that instance will record the data.
+source $srcdir/diag.sh tcpflood -m$2 -i1
+sleep 2 # make sure all data is received in input buffers
+# shut down sender when everything is sent, receiver continues to run concurrently
+# may be needed by TLS (once we do it): sleep 60
+source $srcdir/diag.sh shutdown-when-empty 2
+source $srcdir/diag.sh wait-shutdown 2
+# now it is time to stop the receiver as well
+source $srcdir/diag.sh shutdown-when-empty
+source $srcdir/diag.sh wait-shutdown
+
+# may be needed by TLS (once we do it): sleep 60
+# do the final check
+source $srcdir/diag.sh seq-check 1 $2
+source $srcdir/diag.sh exit
diff --git a/tests/sndrcv_gzip.sh b/tests/sndrcv_gzip.sh
new file mode 100755
index 00000000..4931f3d0
--- /dev/null
+++ b/tests/sndrcv_gzip.sh
@@ -0,0 +1,7 @@
+# This test is similar to tcpsndrcv, but it forwards messages in
+# zlib-compressed format (our own syslog extension).
+# rgerhards, 2009-11-11
+# This file is part of the rsyslog project, released under GPLv3
+echo ===============================================================================
+echo \[sndrcv_gzip.sh\]: testing sending and receiving via tcp in zlib mode
+source $srcdir/sndrcv_drvr.sh sndrcv_gzip 50000
diff --git a/tests/tcpflood.c b/tests/tcpflood.c
new file mode 100644
index 00000000..138706aa
--- /dev/null
+++ b/tests/tcpflood.c
@@ -0,0 +1,476 @@
+/* Opens a large number of tcp connections and sends
+ * messages over them. This is used for stress-testing.
+ *
+ * Params
+ * -t target address (default 127.0.0.1)
+ * -p target port (default 13514)
+ * -n number of target ports (targets are in range -p..(-p+-n-1)
+ * -c number of connections (default 1)
+ * -m number of messages to send (connection is random)
+ * -i initial message number (optional)
+ * -P PRI to be used for generated messages (default is 167).
+ * Specify the plain number without leading zeros
+ * -d amount of extra data to add to message. If present, the
+ * number itself will be added as third field, and the data
+ * bytes as forth. Add -r to randomize the amount of extra
+ * data included in the range 1..(value of -d).
+ * -r randomize amount of extra data added (-d must be > 0)
+ * -f support for testing dynafiles. If given, include a dynafile ID
+ * in the range 0..(f-1) as the SECOND field, shifting all field values
+ * one field to the right. Zero (default) disables this functionality.
+ * -M the message to be sent. Disables all message format options, as
+ * only that exact same message is sent.
+ * -I read specified input file, do NOT generate own test data. The test
+ * completes when eof is reached.
+ * -B The specified file (-I) is binary. No data processing is done by
+ * tcpflood. If multiple connections are specified, data is read in
+ * chunks and spread across the connections without taking any record
+ * delemiters into account.
+ * -C when input from a file is read, this file is transmitted -C times
+ * (C like cycle, running out of meaningful option switches ;))
+ *
+ * Part of the testbench for rsyslog.
+ *
+ * Copyright 2009, 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 <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <unistd.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <sys/resource.h>
+
+#define EXIT_FAILURE 1
+#define INVALID_SOCKET -1
+/* Name of input file, must match $IncludeConfig in test suite .conf files */
+#define NETTEST_INPUT_CONF_FILE "nettest.input.conf" /* name of input file, must match $IncludeConfig in .conf files */
+
+#define MAX_EXTRADATA_LEN 100*1024
+
+static char *targetIP = "127.0.0.1";
+static char *msgPRI = "167";
+static int targetPort = 13514;
+static int numTargetPorts = 1;
+static int dynFileIDs = 0;
+static int extraDataLen = 0; /* amount of extra data to add to message */
+static int bRandomizeExtraData = 0; /* randomize amount of extra data added */
+static int numMsgsToSend; /* number of messages to send */
+static int numConnections = 1; /* number of connections to create */
+static int *sockArray; /* array of sockets to use */
+static int msgNum = 0; /* initial message number to start with */
+static int bShowProgress = 1; /* show progress messages */
+static char *MsgToSend = NULL; /* if non-null, this is the actual message to send */
+static int bBinaryFile = 0; /* is -I file binary */
+static char *dataFile = NULL; /* name of data file, if NULL, generate own data */
+static int numFileIterations = 1;/* how often is file data to be sent? */
+FILE *dataFP = NULL; /* file pointer for data file, if used */
+
+
+/* open a single tcp connection
+ */
+int openConn(int *fd)
+{
+ int sock;
+ struct sockaddr_in addr;
+ int port;
+ int retries = 0;
+ int rnd;
+
+ if((sock=socket(AF_INET, SOCK_STREAM, 0))==-1) {
+ perror("socket()");
+ return(1);
+ }
+
+ /* randomize port if required */
+ if(numTargetPorts > 1) {
+ rnd = rand(); /* easier if we need value for debug messages ;) */
+ port = targetPort + (rnd % numTargetPorts);
+ } else {
+ port = targetPort;
+ }
+ memset((char *) &addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ if(inet_aton(targetIP, &addr.sin_addr)==0) {
+ fprintf(stderr, "inet_aton() failed\n");
+ return(1);
+ }
+ while(1) { /* loop broken inside */
+ if(connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
+ break;
+ } else {
+ if(retries++ == 50) {
+ perror("connect()");
+ fprintf(stderr, "connect() failed\n");
+ return(1);
+ } else {
+ usleep(100000); /* ms = 1000 us! */
+ }
+ }
+ }
+
+ *fd = sock;
+ return 0;
+}
+
+
+/* open all requested tcp connections
+ * this includes allocating the connection array
+ */
+int openConnections(void)
+{
+ int i;
+ char msgBuf[128];
+ size_t lenMsg;
+
+ if(bShowProgress)
+ write(1, " open connections", sizeof(" open connections")-1);
+ sockArray = calloc(numConnections, sizeof(int));
+ for(i = 0 ; i < numConnections ; ++i) {
+ if(i % 10 == 0) {
+ if(bShowProgress)
+ printf("\r%5.5d", i);
+ //lenMsg = sprintf(msgBuf, "\r%5.5d", i);
+ //write(1, msgBuf, lenMsg);
+ }
+ if(openConn(&(sockArray[i])) != 0) {
+ printf("error in trying to open connection i=%d\n", i);
+ return 1;
+ }
+ }
+ lenMsg = sprintf(msgBuf, "\r%5.5d open connections\n", i);
+ write(1, msgBuf, lenMsg);
+
+ return 0;
+}
+
+
+/* we also close all connections because otherwise we may get very bad
+ * timing for the syslogd - it may not be able to process all incoming
+ * messages fast enough if we immediately shut down.
+ * TODO: it may be an interesting excercise to handle that situation
+ * at the syslogd level, too
+ * rgerhards, 2009-04-14
+ */
+void closeConnections(void)
+{
+ int i;
+ size_t lenMsg;
+ char msgBuf[128];
+
+ if(bShowProgress)
+ write(1, " close connections", sizeof(" close connections")-1);
+ for(i = 0 ; i < numConnections ; ++i) {
+ if(i % 10 == 0) {
+ if(bShowProgress) {
+ lenMsg = sprintf(msgBuf, "\r%5.5d", i);
+ write(1, msgBuf, lenMsg);
+ }
+ }
+ close(sockArray[i]);
+ }
+ lenMsg = sprintf(msgBuf, "\r%5.5d close connections\n", i);
+ write(1, msgBuf, lenMsg);
+
+}
+
+
+/* generate the message to be sent according to program command line parameters.
+ * this has been moved to its own function as we now have various different ways
+ * of constructing test messages. -- rgerhards, 2010-03-31
+ */
+static inline void
+genMsg(char *buf, size_t maxBuf, int *pLenBuf)
+{
+ int edLen; /* actual extra data length to use */
+ char extraData[MAX_EXTRADATA_LEN + 1];
+ char dynFileIDBuf[128] = "";
+ static int numMsgsGen = 0;
+ int done;
+
+ if(dataFP != NULL) {
+ /* get message from file */
+ do {
+ done = 1;
+ *pLenBuf = fread(buf, 1, 1024, dataFP);
+ if(feof(dataFP)) {
+ if(--numFileIterations > 0) {
+ rewind(dataFP);
+ done = 0; /* need new iteration */
+ } else {
+ *pLenBuf = 0;
+ goto finalize_it;
+ }
+ }
+ } while(!done); /* Attention: do..while()! */
+ } else if(MsgToSend == NULL) {
+ if(dynFileIDs > 0) {
+ snprintf(dynFileIDBuf, maxBuf, "%d:", rand() % dynFileIDs);
+ }
+ if(extraDataLen == 0) {
+ *pLenBuf = snprintf(buf, maxBuf, "<%s>Mar 1 01:00:00 172.20.245.8 tag msgnum:%s%8.8d:\n",
+ msgPRI, dynFileIDBuf, msgNum);
+ } else {
+ if(bRandomizeExtraData)
+ edLen = ((long) rand() + extraDataLen) % extraDataLen + 1;
+ else
+ edLen = extraDataLen;
+ memset(extraData, 'X', edLen);
+ extraData[edLen] = '\0';
+ *pLenBuf = snprintf(buf, maxBuf, "<%s>Mar 1 01:00:00 172.20.245.8 tag msgnum:%s%8.8d:%d:%s\n",
+ msgPRI, dynFileIDBuf, msgNum, edLen, extraData);
+ }
+ } else {
+ /* use fixed message format from command line */
+ *pLenBuf = snprintf(buf, maxBuf, "%s\n", MsgToSend);
+ }
+
+ if(numMsgsGen++ >= numMsgsToSend)
+ *pLenBuf = 0; /* indicate end of run */
+
+finalize_it: ;
+}
+
+/* send messages to the tcp connections we keep open. We use
+ * a very basic format that helps identify the message
+ * (via msgnum:<number>: e.g. msgnum:00000001:). This format is suitable
+ * for extracton to field-based properties.
+ * The first numConnection messages are sent sequentially, as are the
+ * last. All messages in between are sent over random connections.
+ * Note that message numbers start at 0.
+ */
+int sendMessages(void)
+{
+ int i = 0;
+ int socknum;
+ int lenBuf;
+ int lenSend;
+ char *statusText;
+ char buf[MAX_EXTRADATA_LEN + 1024];
+
+ if(dataFile == NULL) {
+ printf("Sending %d messages.\n", numMsgsToSend);
+ statusText = "messages";
+ } else {
+ printf("Sending file '%s' %d times.\n", dataFile, numFileIterations);
+ statusText = "kb";
+ }
+ if(bShowProgress)
+ printf("\r%8.8d %s sent", 0, statusText);
+ while(1) { /* broken inside loop! */
+ if(i < numConnections)
+ socknum = i;
+ else if(i >= numMsgsToSend - numConnections)
+ socknum = i - (numMsgsToSend - numConnections);
+ else {
+ int rnd = rand();
+ //socknum = rand() % numConnections;
+ socknum = rnd % numConnections;
+ }
+ genMsg(buf, sizeof(buf), &lenBuf); /* generate the message to send according to params */
+ if(lenBuf == 0)
+ break; /* end of processing! */
+ lenSend = send(sockArray[socknum], buf, lenBuf, 0);
+ if(lenSend != lenBuf) {
+ printf("\r%5.5d\n", i);
+ fflush(stdout);
+ perror("send test data");
+ printf("send() failed at socket %d, index %d, msgNum %d\n",
+ sockArray[socknum], i, msgNum);
+ fflush(stderr);
+ return(1);
+ }
+ if(i % 100 == 0) {
+ if(bShowProgress)
+ printf("\r%8.8d", i);
+ }
+ ++msgNum;
+ ++i;
+ }
+ printf("\r%8.8d %s sent\n", i, statusText);
+
+ return 0;
+}
+
+
+/* send a message via TCP
+ * We open the connection on the initial send, and never close it
+ * (let the OS do that). If a conneciton breaks, we do NOT try to
+ * recover, so all test after that one will fail (and the test
+ * driver probably hang. returns 0 if ok, something else otherwise.
+ * We use traditional framing '\n' at EOR for this tester. It may be
+ * worth considering additional framing modes.
+ * rgerhards, 2009-04-08
+ */
+int
+tcpSend(char *buf, int lenBuf)
+{
+ static int sock = INVALID_SOCKET;
+ struct sockaddr_in addr;
+
+ if(sock == INVALID_SOCKET) {
+ /* first time, need to connect to target */
+ if((sock=socket(AF_INET, SOCK_STREAM, 0))==-1) {
+ perror("socket()");
+ return(1);
+ }
+
+ memset((char *) &addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(13514);
+ if(inet_aton("127.0.0.1", &addr.sin_addr)==0) {
+ fprintf(stderr, "inet_aton() failed\n");
+ return(1);
+ }
+ if(connect(sock, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
+ fprintf(stderr, "connect() failed\n");
+ return(1);
+ }
+ }
+
+ /* send test data */
+ if(send(sock, buf, lenBuf, 0) != lenBuf) {
+ perror("send test data");
+ fprintf(stderr, "send() failed\n");
+ return(1);
+ }
+
+ /* send record terminator */
+ if(send(sock, "\n", 1, 0) != 1) {
+ perror("send record terminator");
+ fprintf(stderr, "send() failed\n");
+ return(1);
+ }
+
+ return 0;
+}
+
+
+/* Run the test.
+ * rgerhards, 2009-04-03
+ */
+int main(int argc, char *argv[])
+{
+ int ret = 0;
+ int opt;
+ struct sigaction sigAct;
+ struct rlimit maxFiles;
+ static char buf[1024];
+
+ srand(time(NULL)); /* seed is good enough for our needs */
+
+ /* on Solaris, we do not HAVE MSG_NOSIGNAL, so for this reason
+ * we block SIGPIPE (not an issue for this program)
+ */
+ memset(&sigAct, 0, sizeof(sigAct));
+ sigemptyset(&sigAct.sa_mask);
+ sigAct.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &sigAct, NULL);
+
+ setvbuf(stdout, buf, _IONBF, 48);
+
+ if(!isatty(1))
+ bShowProgress = 0;
+
+ while((opt = getopt(argc, argv, "f:t:p:c:C:m:i:I:P:d:n:M:rB")) != -1) {
+ switch (opt) {
+ case 't': targetIP = optarg;
+ break;
+ case 'p': targetPort = atoi(optarg);
+ break;
+ case 'n': numTargetPorts = atoi(optarg);
+ break;
+ case 'c': numConnections = atoi(optarg);
+ break;
+ case 'C': numFileIterations = atoi(optarg);
+ break;
+ case 'm': numMsgsToSend = atoi(optarg);
+ break;
+ case 'i': msgNum = atoi(optarg);
+ break;
+ case 'P': msgPRI = optarg;
+ break;
+ case 'd': extraDataLen = atoi(optarg);
+ if(extraDataLen > MAX_EXTRADATA_LEN) {
+ fprintf(stderr, "-d max is %d!\n",
+ MAX_EXTRADATA_LEN);
+ exit(1);
+ }
+ break;
+ case 'r': bRandomizeExtraData = 1;
+ break;
+ case 'f': dynFileIDs = atoi(optarg);
+ break;
+ case 'M': MsgToSend = optarg;
+ break;
+ case 'I': dataFile = optarg;
+ /* in this mode, we do not know the num messages to send, so
+ * we set a (high) number to keep the code happy.
+ */
+ numMsgsToSend = 1000000;
+ break;
+ case 'B': bBinaryFile = 1;
+ break;
+ default: printf("invalid option '%c' or value missing - terminating...\n", opt);
+ exit (1);
+ break;
+ }
+ }
+
+ if(numConnections > 100) {
+ maxFiles.rlim_cur = numConnections + 50;
+ maxFiles.rlim_max = numConnections + 50;
+ if(setrlimit(RLIMIT_NOFILE, &maxFiles) < 0) {
+ perror("set number of open files");
+ fprintf(stderr,
+ "could net set sufficiently large number of "
+ "open files for required connection count!\n");
+ exit(1);
+ }
+ }
+
+ if(dataFile != NULL) {
+ if((dataFP = fopen(dataFile, "r")) == NULL) {
+ perror(dataFile);
+ exit(1);
+ }
+ }
+
+ if(openConnections() != 0) {
+ printf("error opening connections\n");
+ exit(1);
+ }
+
+ if(sendMessages() != 0) {
+ printf("error sending messages\n");
+ exit(1);
+ }
+
+ printf("End of tcpflood Run\n");
+
+ exit(ret);
+}
diff --git a/tests/testsuites/1.field1 b/tests/testsuites/1.field1
new file mode 100644
index 00000000..54751171
--- /dev/null
+++ b/tests/testsuites/1.field1
@@ -0,0 +1,3 @@
+<167>Mar 6 16:57:54 172.20.245.8 %PIX-7-710005: DROP_url_www.sina.com.cn:IN=eth1 OUT=eth0 SRC=192.168.10.78 DST=61.172.201.194 LEN=1182 TOS=0x00 PREC=0x00 TTL=63 ID=14368 DF PROTO=TCP SPT=33343 DPT=80 WINDOW=92 RES=0x00 ACK PSH URGP=0
+DROP_url_www.sina.com.cn:IN=eth1
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/1.inputname_imtcp_12514 b/tests/testsuites/1.inputname_imtcp_12514
new file mode 100644
index 00000000..178b1724
--- /dev/null
+++ b/tests/testsuites/1.inputname_imtcp_12514
@@ -0,0 +1,3 @@
+<167>Mar 6 16:57:54 172.20.245.8 %PIX-7-710005: MSG
+12514
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/1.inputname_imtcp_12515 b/tests/testsuites/1.inputname_imtcp_12515
new file mode 100644
index 00000000..d616098b
--- /dev/null
+++ b/tests/testsuites/1.inputname_imtcp_12515
@@ -0,0 +1,3 @@
+<167>Mar 6 16:57:54 172.20.245.8 %PIX-7-710005: MSG
+12515
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/1.inputname_imtcp_12516 b/tests/testsuites/1.inputname_imtcp_12516
new file mode 100644
index 00000000..8e6997ce
--- /dev/null
+++ b/tests/testsuites/1.inputname_imtcp_12516
@@ -0,0 +1,3 @@
+<167>Mar 6 16:57:54 172.20.245.8 %PIX-7-710005: MSG
+12516
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/1.omod-if-array b/tests/testsuites/1.omod-if-array
new file mode 100644
index 00000000..c464b19c
--- /dev/null
+++ b/tests/testsuites/1.omod-if-array
@@ -0,0 +1,2 @@
+<167>Mar 6 16:57:54 172.20.245.8 %PIX-7-710005: UDP request discarded from SERVER1/2741 to test_app:255.255.255.255/61601
+167,Mar 6 16:57:54,172.20.245.8,%PIX-7-710005,%PIX-7-710005:,
diff --git a/tests/testsuites/1.parse1 b/tests/testsuites/1.parse1
new file mode 100644
index 00000000..5ae655e6
--- /dev/null
+++ b/tests/testsuites/1.parse1
@@ -0,0 +1,3 @@
+<167>Mar 6 16:57:54 172.20.245.8 %PIX-7-710005: UDP request discarded from SERVER1/2741 to test_app:255.255.255.255/61601
+167,local4,debug,Mar 6 16:57:54,172.20.245.8,%PIX-7-710005,%PIX-7-710005:, UDP request discarded from SERVER1/2741 to test_app:255.255.255.255/61601
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/1.retry.conf b/tests/testsuites/1.retry.conf
new file mode 100644
index 00000000..c464b19c
--- /dev/null
+++ b/tests/testsuites/1.retry.conf
@@ -0,0 +1,2 @@
+<167>Mar 6 16:57:54 172.20.245.8 %PIX-7-710005: UDP request discarded from SERVER1/2741 to test_app:255.255.255.255/61601
+167,Mar 6 16:57:54,172.20.245.8,%PIX-7-710005,%PIX-7-710005:,
diff --git a/tests/testsuites/2.parse1 b/tests/testsuites/2.parse1
new file mode 100644
index 00000000..628e06df
--- /dev/null
+++ b/tests/testsuites/2.parse1
@@ -0,0 +1,3 @@
+<38>Mar 27 19:06:53 source_server sshd(pam_unix)[12750]: session opened for user foo by (uid=0)
+38,auth,info,Mar 27 19:06:53,source_server,sshd(pam_unix),sshd(pam_unix)[12750]:, session opened for user foo by (uid=0)
+# yet another real-life sample where we had some issues with
diff --git a/tests/testsuites/3.parse1 b/tests/testsuites/3.parse1
new file mode 100644
index 00000000..a6b4e884
--- /dev/null
+++ b/tests/testsuites/3.parse1
@@ -0,0 +1,3 @@
+<38>Apr 6 15:07:10 lxcvs07 sshd(pam_unix)[31738]: session closed for user cvsadmin
+38,auth,info,Apr 6 15:07:10,lxcvs07,sshd(pam_unix),sshd(pam_unix)[31738]:, session closed for user cvsadmin
+# yet another real-life sample where we had some issues with
diff --git a/tests/testsuites/Apr.ts3164 b/tests/testsuites/Apr.ts3164
new file mode 100644
index 00000000..3134f224
--- /dev/null
+++ b/tests/testsuites/Apr.ts3164
@@ -0,0 +1,3 @@
+<167>Apr 6 16:57:54 172.20.245.8 TAG: MSG
+Apr 6 16:57:54
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/Aug.ts3164 b/tests/testsuites/Aug.ts3164
new file mode 100644
index 00000000..d9a721eb
--- /dev/null
+++ b/tests/testsuites/Aug.ts3164
@@ -0,0 +1,3 @@
+<167>Aug 6 16:57:54 172.20.245.8 TAG: MSG
+Aug 6 16:57:54
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/Dec.ts3164 b/tests/testsuites/Dec.ts3164
new file mode 100644
index 00000000..080ba401
--- /dev/null
+++ b/tests/testsuites/Dec.ts3164
@@ -0,0 +1,3 @@
+<167>Dec 6 16:57:54 172.20.245.8 TAG: MSG
+Dec 6 16:57:54
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/Feb.ts3164 b/tests/testsuites/Feb.ts3164
new file mode 100644
index 00000000..d1eaaa33
--- /dev/null
+++ b/tests/testsuites/Feb.ts3164
@@ -0,0 +1,3 @@
+<167>Feb 6 16:57:54 172.20.245.8 TAG: MSG
+Feb 6 16:57:54
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/Jan.ts3164 b/tests/testsuites/Jan.ts3164
new file mode 100644
index 00000000..0cb1c8e2
--- /dev/null
+++ b/tests/testsuites/Jan.ts3164
@@ -0,0 +1,3 @@
+<167>Jan 6 16:57:54 172.20.245.8 TAG: MSG
+Jan 6 16:57:54
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/Jul.ts3164 b/tests/testsuites/Jul.ts3164
new file mode 100644
index 00000000..562e1ec4
--- /dev/null
+++ b/tests/testsuites/Jul.ts3164
@@ -0,0 +1,3 @@
+<167>Jul 6 16:57:54 172.20.245.8 TAG: MSG
+Jul 6 16:57:54
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/Jun.ts3164 b/tests/testsuites/Jun.ts3164
new file mode 100644
index 00000000..ede27e0e
--- /dev/null
+++ b/tests/testsuites/Jun.ts3164
@@ -0,0 +1,3 @@
+<167>Jun 6 16:57:54 172.20.245.8 TAG: MSG
+Jun 6 16:57:54
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/Mar.ts3164 b/tests/testsuites/Mar.ts3164
new file mode 100644
index 00000000..55dd5bc2
--- /dev/null
+++ b/tests/testsuites/Mar.ts3164
@@ -0,0 +1,3 @@
+<167>Mar 6 16:57:54 172.20.245.8 TAG: MSG
+Mar 6 16:57:54
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/May.ts3164 b/tests/testsuites/May.ts3164
new file mode 100644
index 00000000..72a5a301
--- /dev/null
+++ b/tests/testsuites/May.ts3164
@@ -0,0 +1,3 @@
+<167>May 6 16:57:54 172.20.245.8 TAG: MSG
+May 6 16:57:54
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/Nov.ts3164 b/tests/testsuites/Nov.ts3164
new file mode 100644
index 00000000..e8f00e01
--- /dev/null
+++ b/tests/testsuites/Nov.ts3164
@@ -0,0 +1,3 @@
+<167>Nov 6 16:57:54 172.20.245.8 TAG: MSG
+Nov 6 16:57:54
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/Oct.ts3164 b/tests/testsuites/Oct.ts3164
new file mode 100644
index 00000000..01423fef
--- /dev/null
+++ b/tests/testsuites/Oct.ts3164
@@ -0,0 +1,3 @@
+<167>Oct 6 16:57:54 172.20.245.8 TAG: MSG
+Oct 6 16:57:54
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/Sep.ts3164 b/tests/testsuites/Sep.ts3164
new file mode 100644
index 00000000..6c9e48e0
--- /dev/null
+++ b/tests/testsuites/Sep.ts3164
@@ -0,0 +1,3 @@
+<167>Sep 6 16:57:54 172.20.245.8 TAG: MSG
+Sep 6 16:57:54
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/asynwr_deadlock.conf b/tests/testsuites/asynwr_deadlock.conf
new file mode 100644
index 00000000..dc4045b0
--- /dev/null
+++ b/tests/testsuites/asynwr_deadlock.conf
@@ -0,0 +1,14 @@
+# rgerhards, 2010-03-09
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+$InputTCPServerRun 13514
+
+$template outfmt,"%msg:F,58:2%\n"
+
+$OMFileFlushOnTXEnd on
+$OMFileFlushInterval 10
+$OMFileFlushIOBufferSize 10k
+$OMFileAsyncWriting on
+:msg, contains, "msgnum:" ./rsyslog.out.log;outfmt
diff --git a/tests/testsuites/asynwr_deadlock2.conf b/tests/testsuites/asynwr_deadlock2.conf
new file mode 100644
index 00000000..07811613
--- /dev/null
+++ b/tests/testsuites/asynwr_deadlock2.conf
@@ -0,0 +1,16 @@
+# rgerhards, 2010-03-17
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+$InputTCPServerRun 13514
+
+$template outfmt,"%msg:F,58:3%,%msg:F,58:4%,%msg:F,58:5%\n"
+$template dynfile,"rsyslog.out.%msg:F,58:2%.log" # use multiple dynafiles
+
+$OMFileFlushOnTXEnd on
+$OMFileFlushInterval 10
+$OMFileIOBufferSize 10k
+$OMFileAsyncWriting on
+$DynaFileCacheSize 4
+local0.* ?dynfile;outfmt
diff --git a/tests/testsuites/asynwr_deadlock4.conf b/tests/testsuites/asynwr_deadlock4.conf
new file mode 100644
index 00000000..f4308ff1
--- /dev/null
+++ b/tests/testsuites/asynwr_deadlock4.conf
@@ -0,0 +1,16 @@
+# rgerhards, 2010-03-17
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+$InputTCPServerRun 13514
+
+$template outfmt,"%msg:F,58:3%,%msg:F,58:4%,%msg:F,58:5%\n"
+$template dynfile,"rsyslog.out.log" # use multiple dynafiles
+
+$OMFileFlushOnTXEnd on
+$OMFileFlushInterval 10
+$OMFileIOBufferSize 10k
+$OMFileAsyncWriting on
+$DynaFileCacheSize 4
+local0.* ?dynfile;outfmt
diff --git a/tests/testsuites/asynwr_simple.conf b/tests/testsuites/asynwr_simple.conf
new file mode 100644
index 00000000..44b03f2b
--- /dev/null
+++ b/tests/testsuites/asynwr_simple.conf
@@ -0,0 +1,15 @@
+# simple async writing test
+# rgerhards, 2010-03-09
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+$InputTCPServerRun 13514
+
+$template outfmt,"%msg:F,58:2%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+$OMFileFlushOnTXEnd off
+$OMFileFlushInterval 2
+$OMFileFlushIOBufferSize 10k
+$OMFileAsyncWriting on
+:msg, contains, "msgnum:" ?dynfile;outfmt
diff --git a/tests/testsuites/asynwr_small.conf b/tests/testsuites/asynwr_small.conf
new file mode 100644
index 00000000..f04ce962
--- /dev/null
+++ b/tests/testsuites/asynwr_small.conf
@@ -0,0 +1,14 @@
+# simple async writing test
+# rgerhards, 2010-03-09
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+$InputTCPServerRun 13514
+
+$template outfmt,"%msg:F,58:2%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+$OMFileFlushOnTXEnd off
+$OMFileFlushInterval 2
+$OMFileAsyncWriting on
+:msg, contains, "msgnum:" ?dynfile;outfmt
diff --git a/tests/testsuites/asynwr_timeout.conf b/tests/testsuites/asynwr_timeout.conf
new file mode 100644
index 00000000..44b03f2b
--- /dev/null
+++ b/tests/testsuites/asynwr_timeout.conf
@@ -0,0 +1,15 @@
+# simple async writing test
+# rgerhards, 2010-03-09
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+$InputTCPServerRun 13514
+
+$template outfmt,"%msg:F,58:2%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+$OMFileFlushOnTXEnd off
+$OMFileFlushInterval 2
+$OMFileFlushIOBufferSize 10k
+$OMFileAsyncWriting on
+:msg, contains, "msgnum:" ?dynfile;outfmt
diff --git a/tests/testsuites/asynwr_tinybuf.conf b/tests/testsuites/asynwr_tinybuf.conf
new file mode 100644
index 00000000..01dec4d8
--- /dev/null
+++ b/tests/testsuites/asynwr_tinybuf.conf
@@ -0,0 +1,15 @@
+# simple async writing test
+# rgerhards, 2010-03-09
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+$InputTCPServerRun 13514
+
+$template outfmt,"%msg:F,58:2%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+$OMFileFlushOnTXEnd off
+$OMFileFlushInterval 2
+$OMFileIOBufferSize 1
+$OMFileAsyncWriting on
+:msg, contains, "msgnum:" ?dynfile;outfmt
diff --git a/tests/testsuites/complex1.conf b/tests/testsuites/complex1.conf
new file mode 100644
index 00000000..9e2441d4
--- /dev/null
+++ b/tests/testsuites/complex1.conf
@@ -0,0 +1,81 @@
+# complex test case with multiple actions in gzip mode
+# rgerhards, 2009-05-22
+$MaxMessageSize 10k
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+
+$template outfmt,"%msg:F,58:3%,%msg:F,58:4%,%msg:F,58:5%\n"
+$template dynfile,"rsyslog.out.%inputname%.%msg:F,58:2%.log"
+
+## RULESET with listener
+$Ruleset R13514
+# queue params:
+$ActionQueueTimeoutShutdown 60000
+$ActionQueueSize 5000
+$ActionQueueSaveOnShutdown on
+$ActionQueueHighWaterMark 4900
+$ActionQueueLowWaterMark 3500
+$ActionQueueType FixedArray
+$ActionQueueWorkerThreads 1
+# action params:
+$OMFileFlushOnTXEnd off
+$OMFileZipLevel 6
+#$OMFileIOBufferSize 256k
+$DynaFileCacheSize 4
+$omfileFlushInterval 1
+*.* ?dynfile;outfmt
+# listener
+$InputTCPServerInputName 13514
+$InputTCPServerBindRuleset R13514
+$InputTCPServerRun 13514
+
+
+## RULESET with listener
+$Ruleset R13515
+# queue params:
+$ActionQueueTimeoutShutdown 60000
+$ActionQueueSize 5000
+$ActionQueueSaveOnShutdown on
+$ActionQueueHighWaterMark 4900
+$ActionQueueLowWaterMark 3500
+$ActionQueueType FixedArray
+$ActionQueueWorkerThreads 1
+# action params:
+$OMFileFlushOnTXEnd off
+$OMFileZipLevel 6
+$OMFileIOBufferSize 256k
+$DynaFileCacheSize 4
+$omfileFlushInterval 1
+*.* ?dynfile;outfmt
+# listener
+$InputTCPServerInputName 13515
+$InputTCPServerBindRuleset R13515
+$InputTCPServerRun 13515
+
+
+
+## RULESET with listener
+$Ruleset R13516
+# queue params:
+$ActionQueueTimeoutShutdown 60000
+$ActionQueueSize 5000
+$ActionQueueSaveOnShutdown on
+$ActionQueueHighWaterMark 4900
+$ActionQueueLowWaterMark 3500
+$ActionQueueType FixedArray
+$ActionQueueWorkerThreads 1
+# action params:
+$OMFileFlushOnTXEnd off
+$OMFileZipLevel 6
+$OMFileIOBufferSize 256k
+$DynaFileCacheSize 4
+$omfileFlushInterval 1
+*.* ?dynfile;outfmt
+# listener
+$InputTCPServerInputName 13516
+$InputTCPServerBindRuleset R13516
+$InputTCPServerRun 13516
+
+
diff --git a/tests/testsuites/date1.parse1 b/tests/testsuites/date1.parse1
new file mode 100644
index 00000000..ffc7c373
--- /dev/null
+++ b/tests/testsuites/date1.parse1
@@ -0,0 +1,3 @@
+<38> Mar 7 19:06:53 example tag: testmessage (only date actually tested)
+38,auth,info,Mar 7 19:06:53,example,tag,tag:, testmessage (only date actually tested)
+# one space in front of the date
diff --git a/tests/testsuites/date2.parse1 b/tests/testsuites/date2.parse1
new file mode 100644
index 00000000..8d587d9d
--- /dev/null
+++ b/tests/testsuites/date2.parse1
@@ -0,0 +1,3 @@
+<38>Mar 7 19:06:53 example tag: testmessage (only date actually tested)
+38,auth,info,Mar 7 19:06:53,example,tag,tag:, testmessage (only date actually tested)
+# only one space between "Mar" and "7"
diff --git a/tests/testsuites/date3.parse1 b/tests/testsuites/date3.parse1
new file mode 100644
index 00000000..940d261e
--- /dev/null
+++ b/tests/testsuites/date3.parse1
@@ -0,0 +1,3 @@
+<38>Mar 7 2008 19:06:53: example tag: testmessage (only date actually tested)
+38,auth,info,Mar 7 19:06:53,example,tag,tag:, testmessage (only date actually tested)
+# the year should not be there, nor the colon after the date, but we accept it...
diff --git a/tests/testsuites/date4.parse1 b/tests/testsuites/date4.parse1
new file mode 100644
index 00000000..eee5fb09
--- /dev/null
+++ b/tests/testsuites/date4.parse1
@@ -0,0 +1,3 @@
+<38>Mar 7 2008 19:06:53 example tag: testmessage (only date actually tested)
+38,auth,info,Mar 7 19:06:53,example,tag,tag:, testmessage (only date actually tested)
+# the year should not be there, but we accept it...
diff --git a/tests/testsuites/date5.parse1 b/tests/testsuites/date5.parse1
new file mode 100644
index 00000000..be32e605
--- /dev/null
+++ b/tests/testsuites/date5.parse1
@@ -0,0 +1,3 @@
+<38>Mar 7 19:06:53: example tag: testmessage (only date actually tested)
+38,auth,info,Mar 7 19:06:53,example,tag,tag:, testmessage (only date actually tested)
+# colon after timestamp is strictly not ok, but we accept it
diff --git a/tests/testsuites/diag-common.conf b/tests/testsuites/diag-common.conf
new file mode 100644
index 00000000..9e9e28fe
--- /dev/null
+++ b/tests/testsuites/diag-common.conf
@@ -0,0 +1,16 @@
+# This is a config include file. It sets up rsyslog so that the
+# diag system can successfully be used. Also, it generates a file
+# "rsyslogd.started" after rsyslogd is initialized. This config file
+# should be included in all tests that intend to use common code for
+# controlling the daemon.
+# NOTE: we assume that rsyslogd's current working directory is
+# ./tests (or the distcheck equivalent), in particlular that this
+# config file resides in the testsuites subdirectory.
+# rgerhards, 2009-05-27
+$ModLoad ../plugins/imdiag/.libs/imdiag
+$IMDiagServerRun 13500
+
+$template startupfile,"rsyslogd.started" # trick to use relative path names!
+:syslogtag, contains, "rsyslogd" ?startupfile
+
+$ErrorMessagesToStderr off
diff --git a/tests/testsuites/diag-common2.conf b/tests/testsuites/diag-common2.conf
new file mode 100644
index 00000000..94f7e87f
--- /dev/null
+++ b/tests/testsuites/diag-common2.conf
@@ -0,0 +1,16 @@
+# This is a config include file. It sets up rsyslog so that the
+# diag system can successfully be used. Also, it generates a file
+# "rsyslogd.started" after rsyslogd is initialized. This config file
+# should be included in all tests that intend to use common code for
+# controlling the daemon.
+# NOTE: we assume that rsyslogd's current working directory is
+# ./tests (or the distcheck equivalent), in particlular that this
+# config file resides in the testsuites subdirectory.
+# rgerhards, 2009-05-27
+$ModLoad ../plugins/imdiag/.libs/imdiag
+$IMDiagServerRun 13501
+
+$template startupfile,"rsyslogd2.started" # trick to use relative path names!
+:syslogtag, contains, "rsyslogd" ?startupfile
+
+$ErrorMessagesToStderr off
diff --git a/tests/testsuites/diskqueue-fsync.conf b/tests/testsuites/diskqueue-fsync.conf
new file mode 100644
index 00000000..0a02c6ce
--- /dev/null
+++ b/tests/testsuites/diskqueue-fsync.conf
@@ -0,0 +1,17 @@
+# Test for queue disk mode (see .sh file for details)
+# rgerhards, 2009-04-17
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$InputTCPServerRun 13514
+
+# set spool locations and switch queue to disk-only mode
+$WorkDirectory test-spool
+$MainMsgQueueSyncQueueFiles on
+$MainMsgQueueTimeoutShutdown 10000
+$MainMsgQueueFilename mainq
+$MainMsgQueueType disk
+
+$template outfmt,"%msg:F,58:2%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+:msg, contains, "msgnum:" ?dynfile;outfmt
diff --git a/tests/testsuites/diskqueue.conf b/tests/testsuites/diskqueue.conf
new file mode 100644
index 00000000..a992c5a5
--- /dev/null
+++ b/tests/testsuites/diskqueue.conf
@@ -0,0 +1,16 @@
+# Test for queue disk mode (see .sh file for details)
+# rgerhards, 2009-04-17
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+$InputTCPServerRun 13514
+
+# set spool locations and switch queue to disk-only mode
+$WorkDirectory test-spool
+$MainMsgQueueFilename mainq
+$MainMsgQueueType disk
+
+$template outfmt,"%msg:F,58:2%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+:msg, contains, "msgnum:" ?dynfile;outfmt
diff --git a/tests/testsuites/dynfile_cachemiss.conf b/tests/testsuites/dynfile_cachemiss.conf
new file mode 100644
index 00000000..273ff176
--- /dev/null
+++ b/tests/testsuites/dynfile_cachemiss.conf
@@ -0,0 +1,14 @@
+# simple async writing test
+# rgerhards, 2010-03-09
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+$InputTCPServerRun 13514
+
+$template outfmt,"%msg:F,58:3%\n"
+$template dynfile,"%msg:F,58:2%.log" # complete name is in message
+$OMFileFlushOnTXEnd on
+$DynaFileCacheSize 4
+$IncludeConfig rsyslog.action.1.include
+local0.* ?dynfile;outfmt
diff --git a/tests/testsuites/dynfile_invalid2.conf b/tests/testsuites/dynfile_invalid2.conf
new file mode 100644
index 00000000..6d94c40d
--- /dev/null
+++ b/tests/testsuites/dynfile_invalid2.conf
@@ -0,0 +1,14 @@
+# simple async writing test
+# rgerhards, 2010-03-22
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+$InputTCPServerRun 13514
+
+$template outfmt,"%msg:F,58:3%\n"
+$template dynfile,"%msg:F,58:2%.log" # complete name is in message
+$OMFileFlushOnTXEnd off
+$DynaFileCacheSize 4
+$omfileFlushInterval 1
+local0.* ?dynfile;outfmt
diff --git a/tests/testsuites/execonlyonce.conf b/tests/testsuites/execonlyonce.conf
new file mode 100644
index 00000000..085b970e
--- /dev/null
+++ b/tests/testsuites/execonlyonce.conf
@@ -0,0 +1,12 @@
+# see the equally-named .sh file for details
+# rgerhards, 2009-11-12
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+$InputTCPServerRun 13514
+
+$template outfmt,"%msg:F,58:2%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+$ActionExecOnlyOnceEveryInterval 3
+:msg, contains, "msgnum:" ?dynfile;outfmt
diff --git a/tests/testsuites/execonlyonce.data b/tests/testsuites/execonlyonce.data
new file mode 100644
index 00000000..3c54f3d4
--- /dev/null
+++ b/tests/testsuites/execonlyonce.data
@@ -0,0 +1,2 @@
+00000001
+00000100
diff --git a/tests/testsuites/field1.conf b/tests/testsuites/field1.conf
new file mode 100644
index 00000000..1ff833dd
--- /dev/null
+++ b/tests/testsuites/field1.conf
@@ -0,0 +1,8 @@
+$ModLoad ../plugins/omstdout/.libs/omstdout
+$IncludeConfig nettest.input.conf # This picks the to be tested input from the test driver!
+
+$ErrorMessagesToStderr off
+
+# use a special format that we can easily parse in expect
+$template fmt,"%msg:F,32:2%\n"
+*.* :omstdout:;fmt
diff --git a/tests/testsuites/gzipwr_large.conf b/tests/testsuites/gzipwr_large.conf
new file mode 100644
index 00000000..54ad3bb3
--- /dev/null
+++ b/tests/testsuites/gzipwr_large.conf
@@ -0,0 +1,15 @@
+# simple async writing test
+# rgerhards, 2010-03-09
+$MaxMessageSize 10k
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+$InputTCPServerRun 13514
+
+$template outfmt,"%msg:F,58:2%,%msg:F,58:3%,%msg:F,58:4%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+$OMFileFlushOnTXEnd off
+$OMFileZipLevel 6
+$OMFileIOBufferSize 256k
+local0.* ?dynfile;outfmt
diff --git a/tests/testsuites/gzipwr_large_dynfile.conf b/tests/testsuites/gzipwr_large_dynfile.conf
new file mode 100644
index 00000000..3a1b255a
--- /dev/null
+++ b/tests/testsuites/gzipwr_large_dynfile.conf
@@ -0,0 +1,17 @@
+# simple async writing test
+# rgerhards, 2010-03-09
+$MaxMessageSize 10k
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+$InputTCPServerRun 13514
+
+$template outfmt,"%msg:F,58:3%,%msg:F,58:4%,%msg:F,58:5%\n"
+$template dynfile,"rsyslog.out.%msg:F,58:2%.log" # use multiple dynafiles
+$OMFileFlushOnTXEnd off
+$OMFileZipLevel 6
+$OMFileIOBufferSize 256k
+$DynaFileCacheSize 4
+$omfileFlushInterval 1
+local0.* ?dynfile;outfmt
diff --git a/tests/testsuites/imfile-basic.conf b/tests/testsuites/imfile-basic.conf
new file mode 100644
index 00000000..9fb9b5ca
--- /dev/null
+++ b/tests/testsuites/imfile-basic.conf
@@ -0,0 +1,12 @@
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imfile/.libs/imfile
+$InputFileName ./rsyslog.input
+$InputFileTag file:
+$InputFileStateFile stat-file1
+$InputFileSeverity error
+$InputFileFacility local7
+$InputRunFileMonitor
+
+$template outfmt,"%msg:F,58:2%\n"
+:msg, contains, "msgnum:" ./rsyslog.out.log;outfmt
diff --git a/tests/testsuites/imtcp-multiport.conf b/tests/testsuites/imtcp-multiport.conf
new file mode 100644
index 00000000..ccdc15fb
--- /dev/null
+++ b/tests/testsuites/imtcp-multiport.conf
@@ -0,0 +1,13 @@
+# Test for queue disk mode (see .sh file for details)
+# rgerhards, 2009-05-22
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+$InputTCPServerRun 13514
+$InputTCPServerRun 13515
+$InputTCPServerRun 13516
+
+$template outfmt,"%msg:F,58:2%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+:msg, contains, "msgnum:" ?dynfile;outfmt
diff --git a/tests/testsuites/inputname_imtcp.conf b/tests/testsuites/inputname_imtcp.conf
new file mode 100644
index 00000000..a25eab37
--- /dev/null
+++ b/tests/testsuites/inputname_imtcp.conf
@@ -0,0 +1,19 @@
+# This is a special case, thus we define the inputs ourselfs
+$ModLoad ../plugins/omstdout/.libs/omstdout
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+
+$InputTCPServerInputname 12514
+$InputTCPServerRun 12514
+
+$InputTCPServerInputname 12515
+$InputTCPServerRun 12515
+
+$InputTCPServerInputname 12516
+$InputTCPServerRun 12516
+
+$ErrorMessagesToStderr off
+
+# use a special format that we can easily parse in expect
+$template fmt,"%inputname%\n"
+*.* :omstdout:;fmt
diff --git a/tests/testsuites/manytcp.conf b/tests/testsuites/manytcp.conf
new file mode 100644
index 00000000..eb9db257
--- /dev/null
+++ b/tests/testsuites/manytcp.conf
@@ -0,0 +1,13 @@
+# Test for tcp "flood" testing
+# rgerhards, 2009-04-08
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+$MaxOpenFiles 2000
+$InputTCPMaxSessions 1100
+$InputTCPServerRun 13514
+
+$template outfmt,"%msg:F,58:2%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+:msg, contains, "msgnum:" ?dynfile;outfmt
diff --git a/tests/testsuites/master.nolimittag b/tests/testsuites/master.nolimittag
new file mode 100644
index 00000000..502d9d5d
--- /dev/null
+++ b/tests/testsuites/master.nolimittag
@@ -0,0 +1,11 @@
+<167>Mar 6 16:57:54 172.20.245.8 TAG: Rest of message...
++TAG:+
+# now one char, no colon
+<167>Mar 6 16:57:54 172.20.245.8 0 Rest of message...
++0+
+# Now exactly with 32 characters
+<167>Mar 6 16:57:54 172.20.245.8 01234567890123456789012345678901 Rest of message...
++01234567890123456789012345678901+
+# Now oversize, should be completely output with this config
+<167>Mar 6 16:57:54 172.20.245.8 01234567890123456789012345678901-toolong Rest of message...
++01234567890123456789012345678901-toolong+
diff --git a/tests/testsuites/master.rfctag b/tests/testsuites/master.rfctag
new file mode 100644
index 00000000..3f1e0c66
--- /dev/null
+++ b/tests/testsuites/master.rfctag
@@ -0,0 +1,11 @@
+<167>Mar 6 16:57:54 172.20.245.8 TAG: Rest of message...
++TAG:+
+# now one char, no colon
+<167>Mar 6 16:57:54 172.20.245.8 0 Rest of message...
++0+
+# Now exactly with 32 characters
+<167>Mar 6 16:57:54 172.20.245.8 01234567890123456789012345678901 Rest of message...
++01234567890123456789012345678901+
+# Now oversize, should be truncated with this config
+<167>Mar 6 16:57:54 172.20.245.8 01234567890123456789012345678901-toolong Rest of message...
++01234567890123456789012345678901+
diff --git a/tests/testsuites/master.subsecond b/tests/testsuites/master.subsecond
new file mode 100644
index 00000000..ee924877
--- /dev/null
+++ b/tests/testsuites/master.subsecond
@@ -0,0 +1,8 @@
+<34>1 2003-01-23T12:34:56.003Z mymachine.example.com su - ID47 - MSG
+003
+# full precision
+<34>1 2003-01-23T12:34:56.123456Z mymachine.example.com su - ID47 - MSG
+123456
+# without
+<34>1 2003-01-23T12:34:56Z mymachine.example.com su - ID47 - MSG
+0
diff --git a/tests/testsuites/master.ts3339 b/tests/testsuites/master.ts3339
new file mode 100644
index 00000000..b4dd5f39
--- /dev/null
+++ b/tests/testsuites/master.ts3339
@@ -0,0 +1,22 @@
+<34>1 2003-11-11T22:14:15.003Z mymachine.example.com su - ID47 - MSG
+2003-11-11T22:14:15.003Z
+# next test
+<34>1 2003-01-11T22:14:15.003Z mymachine.example.com su - ID47 - MSG
+2003-01-11T22:14:15.003Z
+# next test
+<34>1 2003-11-01T22:04:15.003Z mymachine.example.com su - ID47 - MSG
+2003-11-01T22:04:15.003Z
+# next test
+<34>1 2003-11-11T02:14:15.003Z mymachine.example.com su - ID47 - MSG
+2003-11-11T02:14:15.003Z
+# next test
+<34>1 2003-11-11T22:04:05.003Z mymachine.example.com su - ID47 - MSG
+2003-11-11T22:04:05.003Z
+# next test
+<34>1 2003-11-11T22:04:05.003+02:00 mymachine.example.com su - ID47 - MSG
+2003-11-11T22:04:05.003+02:00
+# next test
+<34>1 2003-11-11T22:04:05.003+01:30 mymachine.example.com su - ID47 - MSG
+2003-11-11T22:04:05.003+01:30
+<34>1 2003-11-11T22:04:05.123456+01:30 mymachine.example.com su - ID47 - MSG
+2003-11-11T22:04:05.123456+01:30
diff --git a/tests/testsuites/master.tsmysql b/tests/testsuites/master.tsmysql
new file mode 100644
index 00000000..dc6d85be
--- /dev/null
+++ b/tests/testsuites/master.tsmysql
@@ -0,0 +1,2 @@
+<34>1 2003-01-23T12:34:56.003Z mymachine.example.com su - ID47 - MSG
+20030123123456
diff --git a/tests/testsuites/master.tspgsql b/tests/testsuites/master.tspgsql
new file mode 100644
index 00000000..d7ac19ff
--- /dev/null
+++ b/tests/testsuites/master.tspgsql
@@ -0,0 +1,2 @@
+<34>1 2003-01-23T12:34:56.003Z mymachine.example.com su - ID47 - MSG
+2003-01-23 12:34:56
diff --git a/tests/testsuites/mon1digit.ts3164 b/tests/testsuites/mon1digit.ts3164
new file mode 100644
index 00000000..0cb1c8e2
--- /dev/null
+++ b/tests/testsuites/mon1digit.ts3164
@@ -0,0 +1,3 @@
+<167>Jan 6 16:57:54 172.20.245.8 TAG: MSG
+Jan 6 16:57:54
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/mon2digit.ts3164 b/tests/testsuites/mon2digit.ts3164
new file mode 100644
index 00000000..9606961c
--- /dev/null
+++ b/tests/testsuites/mon2digit.ts3164
@@ -0,0 +1,3 @@
+<167>Jan 16 16:57:54 172.20.245.8 TAG: MSG
+Jan 16 16:57:54
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/nolimittag.conf b/tests/testsuites/nolimittag.conf
new file mode 100644
index 00000000..0b6ec387
--- /dev/null
+++ b/tests/testsuites/nolimittag.conf
@@ -0,0 +1,8 @@
+$ModLoad ../plugins/omstdout/.libs/omstdout
+$IncludeConfig nettest.input.conf # This picks the to be tested input from the test driver!
+
+$ErrorMessagesToStderr off
+
+# use a special format
+$template fmt,"+%syslogtag%+\n"
+*.* :omstdout:;fmt
diff --git a/tests/testsuites/omod-if-array.conf b/tests/testsuites/omod-if-array.conf
new file mode 100644
index 00000000..d88db166
--- /dev/null
+++ b/tests/testsuites/omod-if-array.conf
@@ -0,0 +1,13 @@
+# Test config for array-passing output module interface
+# (stanard string passing is already tested via the other test inside
+# the testbench, so we do not need to focus on that)
+# rgerhards, 2009-04-03
+$ModLoad ../plugins/omstdout/.libs/omstdout
+$IncludeConfig nettest.input.conf # This picks the to be tested input from the test driver!
+
+$ActionOMStdoutArrayInterface on
+$ErrorMessagesToStderr off
+
+# do NOT remove \n, that would hang the test driver!
+$template expect,"%PRI%%timestamp%%hostname%%programname%%syslogtag%\n"
+*.* :omstdout:;expect
diff --git a/tests/testsuites/oversizeTag-1.parse1 b/tests/testsuites/oversizeTag-1.parse1
new file mode 100644
index 00000000..d45ba1f2
--- /dev/null
+++ b/tests/testsuites/oversizeTag-1.parse1
@@ -0,0 +1,2 @@
+<38>Mar 27 19:06:53 source_server 0123456789012345678901234567890123456789: MSG part
+38,auth,info,Mar 27 19:06:53,source_server,0123456789012345678901234567890123456789,0123456789012345678901234567890123456789:, MSG part
diff --git a/tests/testsuites/parse-3164-buggyday.conf b/tests/testsuites/parse-3164-buggyday.conf
new file mode 100644
index 00000000..937f423a
--- /dev/null
+++ b/tests/testsuites/parse-3164-buggyday.conf
@@ -0,0 +1,8 @@
+$ModLoad ../plugins/omstdout/.libs/omstdout
+$IncludeConfig nettest.input.conf # This picks the to be tested input from the test driver!
+
+$ErrorMessagesToStderr off
+
+# use a special format that we can easily parse in expect
+$template expect,"%PRI%,%syslogfacility-text%,%syslogseverity-text%,%timestamp:::date-rfc3164-buggyday%,%hostname%,%programname%,%syslogtag%,%msg%\n"
+*.* :omstdout:;expect
diff --git a/tests/testsuites/parse-nodate.conf b/tests/testsuites/parse-nodate.conf
new file mode 100644
index 00000000..570638d9
--- /dev/null
+++ b/tests/testsuites/parse-nodate.conf
@@ -0,0 +1,14 @@
+# test is a test config that does not include the timestamp. This is necessary to
+# test some illformed messages that do not contain a date. In that case, the system's
+# current timestamp is used, and that of course is a bit hard to compare against
+# a fixed template. So the solution in this case is to use a format that does
+# not contain any timestamp. Maybe not optimal, but it works ;)
+# rgerhards, 2010-03-19
+$ModLoad ../plugins/omstdout/.libs/omstdout
+$IncludeConfig nettest.input.conf # This picks the to be tested input from the test driver!
+
+$ErrorMessagesToStderr off
+
+# use a special format that we can easily parse
+$template fmt,"%PRI%,%syslogfacility-text%,%syslogseverity-text%,%hostname%,%programname%,%syslogtag%,%msg%\n"
+*.* :omstdout:;fmt
diff --git a/tests/testsuites/parse1.conf b/tests/testsuites/parse1.conf
new file mode 100644
index 00000000..947a05a8
--- /dev/null
+++ b/tests/testsuites/parse1.conf
@@ -0,0 +1,8 @@
+$ModLoad ../plugins/omstdout/.libs/omstdout
+$IncludeConfig nettest.input.conf # This picks the to be tested input from the test driver!
+
+$ErrorMessagesToStderr off
+
+# use a special format that we can easily parse in expect
+$template expect,"%PRI%,%syslogfacility-text%,%syslogseverity-text%,%timestamp%,%hostname%,%programname%,%syslogtag%,%msg%\n"
+*.* :omstdout:;expect
diff --git a/tests/testsuites/parse1udp.conf b/tests/testsuites/parse1udp.conf
new file mode 100644
index 00000000..0fb7d16d
--- /dev/null
+++ b/tests/testsuites/parse1udp.conf
@@ -0,0 +1,9 @@
+$ModLoad ../plugins/omstdout/.libs/omstdout
+$ModLoad ../plugins/imudp/.libs/imudp
+$UDPServerRun 12514
+
+$ErrorMessagesToStderr off
+
+# use a special format that we can easily parse in expect
+$template expect,"%PRI%,%syslogfacility-text%,%syslogseverity-text%,%timestamp%,%hostname%,%programname%,%syslogtag%,%msg%\n"
+*.* :omstdout:;expect
diff --git a/tests/testsuites/parse3.conf b/tests/testsuites/parse3.conf
new file mode 100644
index 00000000..8a3cb317
--- /dev/null
+++ b/tests/testsuites/parse3.conf
@@ -0,0 +1,10 @@
+# note: we need to strip off the TZ designator in the rfc3339 timestamp
+# as this test otherwise fails in different timezones!
+$ModLoad ../plugins/omstdout/.libs/omstdout
+$IncludeConfig nettest.input.conf # This picks the to be tested input from the test driver!
+
+$ErrorMessagesToStderr off
+
+# use a special format that we can easily parse in expect
+$Template output,"%timereported:1:19:date-rfc3339,csv%, %hostname:::csv%, %programname:::csv%, %syslogtag:R,ERE,0,BLANK:[0-9]+--end:csv%, %syslogseverity:::csv%, %msg:::drop-last-lf,csv%\n"
+*.* :omstdout:;output
diff --git a/tests/testsuites/parse_invld_regex.conf b/tests/testsuites/parse_invld_regex.conf
new file mode 100644
index 00000000..d18a2b3c
--- /dev/null
+++ b/tests/testsuites/parse_invld_regex.conf
@@ -0,0 +1,10 @@
+# note: we need to strip off the TZ designator in the rfc3339 timestamp
+# as this test otherwise fails in different timezones!
+$ModLoad ../plugins/omstdout/.libs/omstdout
+$IncludeConfig nettest.input.conf # This picks the to be tested input from the test driver!
+
+$ErrorMessagesToStderr off
+
+# use a special format that we can easily parse in expect
+$Template output,"%timereported:1:19:date-rfc3339,csv%, %hostname:::csv%, %programname:::csv%, %syslogtag:R,ERE,0,BLANK:[0-9+--end:csv%, %syslogseverity:::csv%, %msg:::drop-last-lf,csv%\n"
+*.* :omstdout:;output
diff --git a/tests/testsuites/pipeaction.conf b/tests/testsuites/pipeaction.conf
new file mode 100644
index 00000000..f58b6d65
--- /dev/null
+++ b/tests/testsuites/pipeaction.conf
@@ -0,0 +1,16 @@
+# Test for pipe output action (see .sh file for details)
+# rgerhards, 2009-11-05
+$IncludeConfig diag-common.conf
+
+$MainMsgQueueTimeoutShutdown 10000
+
+# set spool locations and switch queue to disk-only mode
+$WorkDirectory test-spool
+$MainMsgQueueFilename mainq
+$MainMsgQueueType disk
+
+$template outfmt,"%msg:F,58:2%\n"
+# with pipes, we do not need to use absolute path names, so
+# we can simply refer to our working pipe via the usual relative
+# path name
+:msg, contains, "msgnum:" |rsyslog-testbench-fifo;outfmt
diff --git a/tests/testsuites/queue-persist.conf b/tests/testsuites/queue-persist.conf
new file mode 100644
index 00000000..8903042d
--- /dev/null
+++ b/tests/testsuites/queue-persist.conf
@@ -0,0 +1,21 @@
+# Test for persisting messages on shutdown
+# rgerhards, 2009-04-17
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 1
+$MainMsgQueueSaveOnShutdown on
+$InputTCPServerRun 13514
+
+$ModLoad ../plugins/omtesting/.libs/omtesting
+
+# set spool locations and switch queue to disk-only mode
+$WorkDirectory test-spool
+$MainMsgQueueFilename mainq
+$IncludeConfig work-queuemode.conf
+
+$template outfmt,"%msg:F,58:2%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+:msg, contains, "msgnum:" ?dynfile;outfmt
+
+$IncludeConfig work-delay.conf
diff --git a/tests/testsuites/random.conf b/tests/testsuites/random.conf
new file mode 100644
index 00000000..a7079df1
--- /dev/null
+++ b/tests/testsuites/random.conf
@@ -0,0 +1,13 @@
+# we write to /dev/null, as we have no chance to verify the output
+# in any case. What we really check is that rsyslogd does not
+# segfault or otherwise abort.
+# rgerhards, 2010-04-01
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+$InputTCPServerRun 13514
+
+$template outfmt,"%rawmsg%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+*.* /dev/null
diff --git a/tests/testsuites/reallife.parse3 b/tests/testsuites/reallife.parse3
new file mode 100644
index 00000000..dad3f56e
--- /dev/null
+++ b/tests/testsuites/reallife.parse3
@@ -0,0 +1,15 @@
+# New tests should be added to this file if there is no specific
+# reason for not doing that. Initially, we could only handle one test
+# case per file, but this restriction has been removed some time ago.
+# So it is less troublesome (and easier to overlook) to have all related
+# tests in a single file.
+# This file contains a lot of real-life samples (of course mangled so
+# that they can not be traced back to the original submitter). Note
+# that IP addr 192.0.2.1 is specifically set aside for testing and
+# documentation by IANA.
+# rgerhards, 2009-10-19
+<175>Oct 16 2009 23:47:31 hostname tag This is a message
+"2009-10-16T23:47:31", "hostname", "tag", "", "7", " This is a message"
+#
+<175>Oct 16 2009 23:47:31 hostname tag[1234] This is a message
+"2009-10-16T23:47:31", "hostname", "tag", "1234", "7", " This is a message"
diff --git a/tests/testsuites/rfc3164.parse1 b/tests/testsuites/rfc3164.parse1
new file mode 100644
index 00000000..e7a5fa18
--- /dev/null
+++ b/tests/testsuites/rfc3164.parse1
@@ -0,0 +1,4 @@
+<34>Oct 11 22:14:15 mymachine su: 'su root' failed for lonvick on /dev/pts/8
+34,auth,crit,Oct 11 22:14:15,mymachine,su,su:, 'su root' failed for lonvick on /dev/pts/8
+#Example from RFC3164, section 5.4
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/rfc5424-1.parse1 b/tests/testsuites/rfc5424-1.parse1
new file mode 100644
index 00000000..23836c9f
--- /dev/null
+++ b/tests/testsuites/rfc5424-1.parse1
@@ -0,0 +1,3 @@
+#Example from RFC5424, section 6.5 / sample 1
+<34>1 2003-10-11T22:14:15.003Z mymachine.example.com su - ID47 - BOM'su root' failed for lonvick on /dev/pts/8
+34,auth,crit,Oct 11 22:14:15,mymachine.example.com,,su,- BOM'su root' failed for lonvick on /dev/pts/8
diff --git a/tests/testsuites/rfc5424-2.parse1 b/tests/testsuites/rfc5424-2.parse1
new file mode 100644
index 00000000..a86fbc35
--- /dev/null
+++ b/tests/testsuites/rfc5424-2.parse1
@@ -0,0 +1,4 @@
+<165>1 2003-08-24T05:14:15.000003-07:00 192.0.2.1 myproc 8710 - - %% It's time to make the do-nuts.
+165,local4,notice,Aug 24 05:14:15,192.0.2.1,,myproc[8710],- %% It's time to make the do-nuts.
+#Example from RFC5424, section 6.5 / sample 2
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/rfc5424-3.parse1 b/tests/testsuites/rfc5424-3.parse1
new file mode 100644
index 00000000..6ad4073d
--- /dev/null
+++ b/tests/testsuites/rfc5424-3.parse1
@@ -0,0 +1,4 @@
+<165>1 2003-10-11T22:14:15.003Z mymachine.example.com evntslog - ID47 [exampleSDID@32473 iut="3" eventSource= "Application" eventID="1011"][examplePriority@32473 class="high"]
+165,local4,notice,Oct 11 22:14:15,mymachine.example.com,,evntslog,
+#Example from RFC5424, section 6.5 / sample 4
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/rfc5424-4.parse1 b/tests/testsuites/rfc5424-4.parse1
new file mode 100644
index 00000000..ecf27e14
--- /dev/null
+++ b/tests/testsuites/rfc5424-4.parse1
@@ -0,0 +1,4 @@
+<165>1 2003-10-11T22:14:15.003Z mymachine.example.com evntslog - ID47 [exampleSDID@32473 iut="3" eventSource= "Application" eventID="1011"] BOMAn application event log entry...
+165,local4,notice,Oct 11 22:14:15,mymachine.example.com,,evntslog,BOMAn application event log entry...
+#Example from RFC5424, section 6.5 / sample 3
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/rfctag.conf b/tests/testsuites/rfctag.conf
new file mode 100644
index 00000000..8619e89e
--- /dev/null
+++ b/tests/testsuites/rfctag.conf
@@ -0,0 +1,9 @@
+$ModLoad ../plugins/omstdout/.libs/omstdout
+$IncludeConfig nettest.input.conf # This picks the to be tested input from the test driver!
+
+$ErrorMessagesToStderr off
+
+# use a special format
+# Note: the plus signs are necessary to detect truncated logs!
+$template fmt,"+%syslogtag:1:32%+\n"
+*.* :omstdout:;fmt
diff --git a/tests/testsuites/samples.parse-3164-buggyday b/tests/testsuites/samples.parse-3164-buggyday
new file mode 100644
index 00000000..e21df980
--- /dev/null
+++ b/tests/testsuites/samples.parse-3164-buggyday
@@ -0,0 +1,6 @@
+# in 3164-buggyday mode, we need to have a leading zero in front of the day
+<38> Mar 7 19:06:53 example tag: testmessage (only date actually tested)
+38,auth,info,Mar 07 19:06:53,example,tag,tag:, testmessage (only date actually tested)
+# and now one with a complete date:
+<38> Mar 17 19:06:53 example tag: testmessage (only date actually tested)
+38,auth,info,Mar 17 19:06:53,example,tag,tag:, testmessage (only date actually tested)
diff --git a/tests/testsuites/samples.parse-nodate b/tests/testsuites/samples.parse-nodate
new file mode 100644
index 00000000..7f16181c
--- /dev/null
+++ b/tests/testsuites/samples.parse-nodate
@@ -0,0 +1,6 @@
+<27>xapi: [error|xen3|15|Guest liveness monitor D:bca30ab3f1c1|master_connection] Connection to master died. I will continue to retry indefinitely (supressing future logging of this message)
+27,daemon,err,localhost,xapi,xapi:, [error|xen3|15|Guest liveness monitor D:bca30ab3f1c1|master_connection] Connection to master died. I will continue to retry indefinitely (supressing future logging of this message)
+# a message with just text (as permitted by rfc 3164)
+# it is questionable if the current sample result is really correct as of 3164!
+This is a message!
+13,user,notice,This,is,is, a message!
diff --git a/tests/testsuites/samples.parse_invld_regex b/tests/testsuites/samples.parse_invld_regex
new file mode 100644
index 00000000..0d0e4ce3
--- /dev/null
+++ b/tests/testsuites/samples.parse_invld_regex
@@ -0,0 +1,16 @@
+# New tests should be added to this file if there is no specific
+# reason for not doing that. Initially, we could only handle one test
+# case per file, but this restriction has been removed some time ago.
+# So it is less troublesome (and easier to overlook) to have all related
+# tests in a single file.
+# the actual message is not important. There is an error inside the conf
+# file, and all messages will trigger the same problem.
+# NOTE: it is correct that the "BAD REGULAR EXPRESSION" error message is
+# *NOT* run through the rest of the propety replace, in specific through
+# the CSV escaper. We do not do this because it could potentially lead
+# to an obfuscated error message, and thus making problems hard to find. As
+# this is a real error case, there is no problem in not obeying to the
+# configured format.
+# rgerhards, 2010-02-08
+<175>Feb 08 2008 23:47:31 hostname tag This is a message
+"2008-02-08T23:47:31", "hostname", "tag", **NO MATCH** **BAD REGULAR EXPRESSION**, "7", " This is a message"
diff --git a/tests/testsuites/samples.snare_ccoff_udp b/tests/testsuites/samples.snare_ccoff_udp
new file mode 100644
index 00000000..010e44d5
--- /dev/null
+++ b/tests/testsuites/samples.snare_ccoff_udp
@@ -0,0 +1,14 @@
+# see comments in snare_ccoff_udp.conf
+# note that some of these samples look pretty wild, but they are
+# *real* cases (just mangled to anonymize them...)
+# Sample 1 - note the absence of PRI!
+windowsserver MSWinEventLog 1 Security 1167 Fri Mar 19 15:33:30 2010 540 Security SYSTEM User Success Audit WINDOWSSERVER Logon/Logoff Successful Network Logon: User Name: WINDOWSSERVER$ Domain: DOMX Logon ID: (0x0,0xF88396) Logon Type: 3 Logon Process: Kerberos Authentication Package: Kerberos Workstation Name: Logon GUID: {79b6eb79-7bcc-8a2e-7dad-953c51dc00fd} Caller User Name: - Caller Domain: - Caller Logon ID: - Caller Process ID: - Transited Services: - Source Network Address: 10.11.11.3 Source Port: 3306 733\n
+13,user,notice,localhost,windowsserver,windowsserver MSWinEventLog 1 Security 1167 Fri, Mar 19 15:33:30 2010 540 Security SYSTEM User Success Audit WINDOWSSERVER Logon/Logoff Successful Network Logon: User Name: WINDOWSSERVER$ Domain: DOMX Logon ID: (0x0,0xF88396) Logon Type: 3 Logon Process: Kerberos Authentication Package: Kerberos Workstation Name: Logon GUID: {79b6eb79-7bcc-8a2e-7dad-953c51dc00fd} Caller User Name: - Caller Domain: - Caller Logon ID: - Caller Process ID: - Transited Services: - Source Network Address: 10.11.11.3 Source Port: 3306 733
+# Sample 2
+# the samples below need to be disabled for the "workaround patch" for the message
+# parser to work. They need to be re-enabled once a final solution has been crafted
+#windowsserver MSWinEventLog 1 Security 1166 Fri Mar 19 15:33:30 2010 576 Security SYSTEM User Success Audit WINDOWSSERVER Logon/Logoff Special privileges assigned to new logon: User Name: WINDOWSSERVER$ Domain: DOMX Logon ID: (0x0,0xF88396) Privileges: SeSecurityPrivilege SeBackupPrivilege SeRestorePrivilege SeTakeOwnershipPrivilege SeDebugPrivilege SeSystemEnvironmentPrivilege SeLoadDriverPrivilege SeImpersonatePrivilege SeEnableDelegationPrivilege 732\n
+#13,user,notice,localhost,windowsserver,windowsserver MSWinEventLog 1 Security 1166 Fri, Mar 19 15:33:30 2010 576 Security SYSTEM User Success Audit WINDOWSSERVER Logon/Logoff Special privileges assigned to new logon: User Name: WINDOWSSERVER$ Domain: DOMX Logon ID: (0x0,0xF88396) Privileges: SeSecurityPrivilege SeBackupPrivilege SeRestorePrivilege SeTakeOwnershipPrivilege SeDebugPrivilege SeSystemEnvironmentPrivilege SeLoadDriverPrivilege SeImpersonatePrivilege SeEnableDelegationPrivilege 732
+# Sample 3
+#windowsserver MSWinEventLog 1 Security 1165 Fri Mar 19 15:33:30 2010 538 Security SYSTEM User Success Audit WINDOWSSERVER Logon/Logoff User Logoff: User Name: WINDOWSSERVER$ Domain: DOMX Logon ID: (0x0,0xF8830B) Logon Type: 3 731\n
+#13,user,notice,localhost,windowsserver,windowsserver MSWinEventLog 1 Security 1165 Fri, Mar 19 15:33:30 2010 538 Security SYSTEM User Success Audit WINDOWSSERVER Logon/Logoff User Logoff: User Name: WINDOWSSERVER$ Domain: DOMX Logon ID: (0x0,0xF8830B) Logon Type: 3 731
diff --git a/tests/testsuites/samples.snare_ccoff_udp2 b/tests/testsuites/samples.snare_ccoff_udp2
new file mode 100644
index 00000000..337cd97c
--- /dev/null
+++ b/tests/testsuites/samples.snare_ccoff_udp2
@@ -0,0 +1,26 @@
+# see comments in snare_ccoff_udp.conf
+# note that some of these samples look pretty wild, but they are
+# *real* cases (just mangled to anonymize them...)
+#
+# NOTE
+# The current responses are probably not correct (handling of messages without PRI).
+# However, we keep them inside the test to be consistent. We should look at how
+# PRI-less messages are handled and once we have fixed that, the test cases may need
+# to be adapted. We do NOT try to preserve misbehaviour on such seriously malformed
+# messages.
+#
+# this is a very simple test, though not snare-based
+test
+insert into windows (Message, Facility,FromHost, Priority, DeviceReportedTime, ReceivedAt, InfoUnitID, SysLogTag) values ('', 1, 'test',5, '20100321185328', '20100321185328', 1, '')
+# and yet another one we have seen in practice
+UX=Abcd-efg-hij-klmno; XXXXX=1111111111, Z123=192.12.231.245:11111, S1234=123456789, XXXXXX=111111111
+insert into windows (Message, Facility,FromHost, Priority, DeviceReportedTime, ReceivedAt, InfoUnitID, SysLogTag) values (' XXXXX=1111111111, Z123=192.12.231.245:11111, S1234=123456789, XXXXXX=111111111', 1, 'localhost',5, '20100321185328', '20100321185328', 1, 'UX=Abcd-efg-hij-klmno;')
+# Sample 1 - note the absence of PRI!
+windowsserver MSWinEventLog 1 Security 1167 Fri Mar 19 15:33:30 2010 540 Security SYSTEM User Success Audit WINDOWSSERVER Logon/Logoff Successful Network Logon: User Name: WINDOWSSERVER$ Domain: DOMX Logon ID: (0x0,0xF88396) Logon Type: 3 Logon Process: Kerberos Authentication Package: Kerberos Workstation Name: Logon GUID: {79b6eb79-7bcc-8a2e-7dad-953c51dc00fd} Caller User Name: - Caller Domain: - Caller Logon ID: - Caller Process ID: - Transited Services: - Source Network Address: 10.11.11.3 Source Port: 3306 733\n
+insert into windows (Message, Facility,FromHost, Priority, DeviceReportedTime, ReceivedAt, InfoUnitID, SysLogTag) values (' Mar 19 15:33:30 2010 540 Security SYSTEM User Success Audit WINDOWSSERVER Logon/Logoff Successful Network Logon: User Name: WINDOWSSERVER$ Domain: DOMX Logon ID: (0x0,0xF88396) Logon Type: 3 Logon Process: Kerberos Authentication Package: Kerberos Workstation Name: Logon GUID: {79b6eb79-7bcc-8a2e-7dad-953c51dc00fd} Caller User Name: - Caller Domain: - Caller Logon ID: - Caller Process ID: - Transited Services: - Source Network Address: 10.11.11.3 Source Port: 3306 733', 1, 'localhost',5, '20100321185328', '20100321185328', 1, 'windowsserver MSWinEventLog 1 Security 1167 Fri')
+# Sample 2
+windowsserver MSWinEventLog 1 Security 1166 Fri Mar 19 15:33:30 2010 576 Security SYSTEM User Success Audit WINDOWSSERVER Logon/Logoff Special privileges assigned to new logon: User Name: WINDOWSSERVER$ Domain: DOMX Logon ID: (0x0,0xF88396) Privileges: SeSecurityPrivilege SeBackupPrivilege SeRestorePrivilege SeTakeOwnershipPrivilege SeDebugPrivilege SeSystemEnvironmentPrivilege SeLoadDriverPrivilege SeImpersonatePrivilege SeEnableDelegationPrivilege 732\n
+insert into windows (Message, Facility,FromHost, Priority, DeviceReportedTime, ReceivedAt, InfoUnitID, SysLogTag) values (' Mar 19 15:33:30 2010 576 Security SYSTEM User Success Audit WINDOWSSERVER Logon/Logoff Special privileges assigned to new logon: User Name: WINDOWSSERVER$ Domain: DOMX Logon ID: (0x0,0xF88396) Privileges: SeSecurityPrivilege SeBackupPrivilege SeRestorePrivilege SeTakeOwnershipPrivilege SeDebugPrivilege SeSystemEnvironmentPrivilege SeLoadDriverPrivilege SeImpersonatePrivilege SeEnableDelegationPrivilege 732', 1, 'localhost',5, '20100321185328', '20100321185328', 1, 'windowsserver MSWinEventLog 1 Security 1166 Fri')
+# Sample 3
+windowsserver MSWinEventLog 1 Security 1165 Fri Mar 19 15:33:30 2010 538 Security SYSTEM User Success Audit WINDOWSSERVER Logon/Logoff User Logoff: User Name: WINDOWSSERVER$ Domain: DOMX Logon ID: (0x0,0xF8830B) Logon Type: 3 731\n
+insert into windows (Message, Facility,FromHost, Priority, DeviceReportedTime, ReceivedAt, InfoUnitID, SysLogTag) values (' Mar 19 15:33:30 2010 538 Security SYSTEM User Success Audit WINDOWSSERVER Logon/Logoff User Logoff: User Name: WINDOWSSERVER$ Domain: DOMX Logon ID: (0x0,0xF8830B) Logon Type: 3 731', 1, 'localhost',5, '20100321185328', '20100321185328', 1, 'windowsserver MSWinEventLog 1 Security 1165 Fri')
diff --git a/tests/testsuites/snare.parse1 b/tests/testsuites/snare.parse1
new file mode 100644
index 00000000..550b0703
--- /dev/null
+++ b/tests/testsuites/snare.parse1
@@ -0,0 +1,83 @@
+# some parse test build around data in snare-format
+<141>Mar 10 09:30:20 zuse.xysystems.local MSWinEventLog\0111\011Security\011563\011Wed Mar 10 09:30:15 2010\011538\011Security\011XYWS011$\011User\011Success Audit\011ZUSE\011Logon/Logoff\011\011User Logoff: User Name: XYWS011$ Domain: XYZSYSTEMS Logon ID: (0x0,0x5984789C) Logon Type: 3 \011552
+141,local1,notice,Mar 10 09:30:20,zuse.xysystems.local,MSWinEventLog#0111#011Security#011563#011Wed,MSWinEventLog#0111#011Security#011563#011Wed, Mar 10 09:30:15 2010#011538#011Security#011XYWS011$#011User#011Success Audit#011ZUSE#011Logon/Logoff#011#011User Logoff: User Name: XYWS011$ Domain: XYZSYSTEMS Logon ID: (0x0,0x5984789C) Logon Type: 3 #011552
+#
+# NEXT MESSAGE
+#
+Mar 10 09:30:20 zuse.xysystems.local MSWinEventLog\0111\011Security\011564\011Wed Mar 10 09:30:19 2010\011540\011Security\011BACKUP1$\011User\011Success Audit\011ZUSE\011Logon/Logoff\011\011Successful Network Logon: User Name: BACKUP1$ Domain: XYZSYSTEMS Logon ID: (0x0,0x59848DB4) Logon Type: 3 Logon Process: Kerberos Authentication Package: Kerberos Workstation Name: Logon GUID: {f6f65903-1932-d229-4b75-64816121d569} Caller User Name: - Caller Domain: - Caller Logon ID: - Caller Process ID: - Transited Services: - Source Network Address: 172.16.0.31 Source Port: 0 \011553
+13,user,notice,Mar 10 09:30:20,zuse.xysystems.local,MSWinEventLog#0111#011Security#011564#011Wed,MSWinEventLog#0111#011Security#011564#011Wed, Mar 10 09:30:19 2010#011540#011Security#011BACKUP1$#011User#011Success Audit#011ZUSE#011Logon/Logoff#011#011Successful Network Logon: User Name: BACKUP1$ Domain: XYZSYSTEMS Logon ID: (0x0,0x59848DB4) Logon Type: 3 Logon Process: Kerberos Authentication Package: Kerberos Workstation Name: Logon GUID: {f6f65903-1932-d229-4b75-64816121d569} Caller User Name: - Caller Domain: - Caller Logon ID: - Caller Process ID: - Transited Services: - Source Network Address: 172.16.0.31 Source Port: 0 #011553
+#
+# NEXT MESSAGE
+#
+<141>Mar 10 09:30:25 zuse.xysystems.local MSWinEventLog\0111\011Security\011566\011Wed Mar 10 09:30:21 2010\011540\011Security\011aadminps\011User\011Success Audit\011ZUSE\011Logon/Logoff\011\011Successful Network Logon: User Name: aadminps Domain: XYSYSTEMS Logon ID: (0x0,0x5984973C) Logon Type: 3 Logon Process: Authz Authentication Package: Kerberos Workstation Name: ZUSE Logon GUID: - Caller User Name: ZUSE$ Caller Domain: XYSYSTEMS Caller Logon ID: (0x0,0x3E7) Caller Process ID: 1004 Transited Services: - Source Network Address: - Source Port: - \011555
+141,local1,notice,Mar 10 09:30:25,zuse.xysystems.local,MSWinEventLog#0111#011Security#011566#011Wed,MSWinEventLog#0111#011Security#011566#011Wed, Mar 10 09:30:21 2010#011540#011Security#011aadminps#011User#011Success Audit#011ZUSE#011Logon/Logoff#011#011Successful Network Logon: User Name: aadminps Domain: XYSYSTEMS Logon ID: (0x0,0x5984973C) Logon Type: 3 Logon Process: Authz Authentication Package: Kerberos Workstation Name: ZUSE Logon GUID: - Caller User Name: ZUSE$ Caller Domain: XYSYSTEMS Caller Logon ID: (0x0,0x3E7) Caller Process ID: 1004 Transited Services: - Source Network Address: - Source Port: - #011555
+#
+# NEXT MESSAGE
+#
+<141>Mar 10 09:30:25 zuse.xysystems.local MSWinEventLog\0111\011Security\011567\011Wed Mar 10 09:30:21 2010\011538\011Security\011aadminps\011User\011Success Audit\011ZUSE\011Logon/Logoff\011\011User Logoff: User Name: aadminps Domain: XYSYSTEMS Logon ID: (0x0,0x5984973C) Logon Type: 3 \011556
+141,local1,notice,Mar 10 09:30:25,zuse.xysystems.local,MSWinEventLog#0111#011Security#011567#011Wed,MSWinEventLog#0111#011Security#011567#011Wed, Mar 10 09:30:21 2010#011538#011Security#011aadminps#011User#011Success Audit#011ZUSE#011Logon/Logoff#011#011User Logoff: User Name: aadminps Domain: XYSYSTEMS Logon ID: (0x0,0x5984973C) Logon Type: 3 #011556
+#
+# NEXT MESSAGE
+#
+<141>Mar 10 09:30:25 zuse.xysystems.local MSWinEventLog\0111\011Security\011568\011Wed Mar 10 09:30:25 2010\011540\011Security\011ANONYMOUS LOGON\011Well Known Group\011Success Audit\011ZUSE\011Logon/Logoff\011\011Successful Network Logon: User Name: Domain: Logon ID: (0x0,0x5984AB6F) Logon Type: 3 Logon Process: NtLmSsp Authentication Package: NTLM Workstation Name: XYWS083 Logon GUID: - Caller User Name: - Caller Domain: - Caller Logon ID: - Caller Process ID: - Transited Services: - Source Network Address: 172.16.3.91 Source Port: 0 \011557
+141,local1,notice,Mar 10 09:30:25,zuse.xysystems.local,MSWinEventLog#0111#011Security#011568#011Wed,MSWinEventLog#0111#011Security#011568#011Wed, Mar 10 09:30:25 2010#011540#011Security#011ANONYMOUS LOGON#011Well Known Group#011Success Audit#011ZUSE#011Logon/Logoff#011#011Successful Network Logon: User Name: Domain: Logon ID: (0x0,0x5984AB6F) Logon Type: 3 Logon Process: NtLmSsp Authentication Package: NTLM Workstation Name: XYWS083 Logon GUID: - Caller User Name: - Caller Domain: - Caller Logon ID: - Caller Process ID: - Transited Services: - Source Network Address: 172.16.3.91 Source Port: 0 #011557
+#
+# NEXT MESSAGE
+#
+<141>Mar 10 09:30:25 zuse.xysystems.local MSWinEventLog\0111\011Security\011569\011Wed Mar 10 09:30:25 2010\011540\011Security\011SYSTEM\011User\011Success Audit\011ZUSE\011Logon/Logoff\011\011Successful Network Logon: User Name: ZUSE$ Domain: XYSYSTEMS Logon ID: (0x0,0x5984ACA7) Logon Type: 3 Logon Process: Kerberos Authentication Package: Kerberos Workstation Name: Logon GUID: {20014d9a-ce6c-6834-d1ed-607c08f0b6a7} Caller User Name: - Caller Domain: - Caller Logon ID: - Caller Process ID: - Transited Services: - Source Network Address: 172.16.0.15 Source Port: 2318 \011558
+141,local1,notice,Mar 10 09:30:25,zuse.xysystems.local,MSWinEventLog#0111#011Security#011569#011Wed,MSWinEventLog#0111#011Security#011569#011Wed, Mar 10 09:30:25 2010#011540#011Security#011SYSTEM#011User#011Success Audit#011ZUSE#011Logon/Logoff#011#011Successful Network Logon: User Name: ZUSE$ Domain: XYSYSTEMS Logon ID: (0x0,0x5984ACA7) Logon Type: 3 Logon Process: Kerberos Authentication Package: Kerberos Workstation Name: Logon GUID: {20014d9a-ce6c-6834-d1ed-607c08f0b6a7} Caller User Name: - Caller Domain: - Caller Logon ID: - Caller Process ID: - Transited Services: - Source Network Address: 172.16.0.15 Source Port: 2318 #011558
+#
+# NEXT MESSAGE
+#
+<141>Mar 10 09:30:25 zuse.xysystems.local MSWinEventLog\0111\011Security\011570\011Wed Mar 10 09:30:25 2010\011538\011Security\011SYSTEM\011User\011Success Audit\011ZUSE\011Logon/Logoff\011\011User Logoff: User Name: ZUSE$ Domain: XYSYSTEMS Logon ID: (0x0,0x5984ACA7) Logon Type: 3 \011559
+141,local1,notice,Mar 10 09:30:25,zuse.xysystems.local,MSWinEventLog#0111#011Security#011570#011Wed,MSWinEventLog#0111#011Security#011570#011Wed, Mar 10 09:30:25 2010#011538#011Security#011SYSTEM#011User#011Success Audit#011ZUSE#011Logon/Logoff#011#011User Logoff: User Name: ZUSE$ Domain: XYSYSTEMS Logon ID: (0x0,0x5984ACA7) Logon Type: 3 #011559
+#
+# NEXT MESSAGE
+#
+<141>Mar 10 09:30:25 zuse.xysystems.local MSWinEventLog\0111\011Security\011571\011Wed Mar 10 09:30:25 2010\011540\011Security\011SYSTEM\011User\011Success Audit\011ZUSE\011Logon/Logoff\011\011Successful Network Logon: User Name: ZUSE$ Domain: XYSYSTEMS Logon ID: (0x0,0x5984AD7C) Logon Type: 3 Logon Process: Kerberos Authentication Package: Kerberos Workstation Name: Logon GUID: {20014d9a-ce6c-6834-d1ed-607c08f0b6a7} Caller User Name: - Caller Domain: - Caller Logon ID: - Caller Process ID: - Transited Services: - Source Network Address: 172.16.0.15 Source Port: 2319 \011560\
+141,local1,notice,Mar 10 09:30:25,zuse.xysystems.local,MSWinEventLog#0111#011Security#011571#011Wed,MSWinEventLog#0111#011Security#011571#011Wed, Mar 10 09:30:25 2010#011540#011Security#011SYSTEM#011User#011Success Audit#011ZUSE#011Logon/Logoff#011#011Successful Network Logon: User Name: ZUSE$ Domain: XYSYSTEMS Logon ID: (0x0,0x5984AD7C) Logon Type: 3 Logon Process: Kerberos Authentication Package: Kerberos Workstation Name: Logon GUID: {20014d9a-ce6c-6834-d1ed-607c08f0b6a7} Caller User Name: - Caller Domain: - Caller Logon ID: - Caller Process ID: - Transited Services: - Source Network Address: 172.16.0.15 Source Port: 2319 #011560
+#
+# NEXT MESSAGE
+#
+<141>Mar 10 09:30:25 zuse.xysystems.local MSWinEventLog\0111\011Security\011572\011Wed Mar 10 09:30:25 2010\011538\011Security\011SYSTEM\011User\011Success Audit\011ZUSE\011Logon/Logoff\011\011User Logoff: User Name: ZUSE$ Domain: XYSYSTEMS Logon ID: (0x0,0x5984AD7C) Logon Type: 3 \011561
+141,local1,notice,Mar 10 09:30:25,zuse.xysystems.local,MSWinEventLog#0111#011Security#011572#011Wed,MSWinEventLog#0111#011Security#011572#011Wed, Mar 10 09:30:25 2010#011538#011Security#011SYSTEM#011User#011Success Audit#011ZUSE#011Logon/Logoff#011#011User Logoff: User Name: ZUSE$ Domain: XYSYSTEMS Logon ID: (0x0,0x5984AD7C) Logon Type: 3 #011561
+#
+# NEXT MESSAGE
+#
+<141>Mar 10 09:30:25 zuse.xysystems.local MSWinEventLog\0111\011Security\011573\011Wed Mar 10 09:30:25 2010\011680\011Security\011ettore.trezzani\011User\011Success Audit\011ZUSE\011Account Logon\011\011Logon attempt by: MICROSOFT_AUTHENTICATION_PACKAGE_V1_0 Logon account: ettore.trezzani Source Workstation: XYWS083 Error Code: 0x0 \011562
+141,local1,notice,Mar 10 09:30:25,zuse.xysystems.local,MSWinEventLog#0111#011Security#011573#011Wed,MSWinEventLog#0111#011Security#011573#011Wed, Mar 10 09:30:25 2010#011680#011Security#011ettore.trezzani#011User#011Success Audit#011ZUSE#011Account Logon#011#011Logon attempt by: MICROSOFT_AUTHENTICATION_PACKAGE_V1_0 Logon account: ettore.trezzani Source Workstation: XYWS083 Error Code: 0x0 #011562
+#
+# NEXT MESSAGE
+#
+<141>Mar 10 09:30:25 zuse.xysystems.local MSWinEventLog\0111\011Security\011574\011Wed Mar 10 09:30:25 2010\011540\011Security\011ettore.trezzani\011User\011Success Audit\011ZUSE\011Logon/Logoff\011\011Successful Network Logon: User Name: ettore.trezzani Domain: XYSYSTEMS Logon ID: (0x0,0x5984ADD5) Logon Type: 3 Logon Process: NtLmSsp Authentication Package: NTLM Workstation Name: XYWS083 Logon GUID: - Caller User Name: - Caller Domain: - Caller Logon ID: - Caller Process ID: - Transited Services: - Source Network Address: 172.16.3.91 Source Port: 0 \011563
+141,local1,notice,Mar 10 09:30:25,zuse.xysystems.local,MSWinEventLog#0111#011Security#011574#011Wed,MSWinEventLog#0111#011Security#011574#011Wed, Mar 10 09:30:25 2010#011540#011Security#011ettore.trezzani#011User#011Success Audit#011ZUSE#011Logon/Logoff#011#011Successful Network Logon: User Name: ettore.trezzani Domain: XYSYSTEMS Logon ID: (0x0,0x5984ADD5) Logon Type: 3 Logon Process: NtLmSsp Authentication Package: NTLM Workstation Name: XYWS083 Logon GUID: - Caller User Name: - Caller Domain: - Caller Logon ID: - Caller Process ID: - Transited Services: - Source Network Address: 172.16.3.91 Source Port: 0 #011563
+#
+# NEXT MESSAGE
+#
+<141>Mar 10 09:30:25 zuse.xysystems.local MSWinEventLog\0111\011Security\011575\011Wed Mar 10 09:30:25 2010\011540\011Security\011SYSTEM\011User\011Success Audit\011ZUSE\011Logon/Logoff\011\011Successful Network Logon: User Name: ZUSE$ Domain: XYSYSTEMS Logon ID: (0x0,0x5984AE49) Logon Type: 3 Logon Process: Kerberos Authentication Package: Kerberos Workstation Name: Logon GUID: {20014d9a-ce6c-6834-d1ed-607c08f0b6a7} Caller User Name: - Caller Domain: - Caller Logon ID: - Caller Process ID: - Transited Services: - Source Network Address: 172.16.0.15 Source Port: 2320 \011564
+141,local1,notice,Mar 10 09:30:25,zuse.xysystems.local,MSWinEventLog#0111#011Security#011575#011Wed,MSWinEventLog#0111#011Security#011575#011Wed, Mar 10 09:30:25 2010#011540#011Security#011SYSTEM#011User#011Success Audit#011ZUSE#011Logon/Logoff#011#011Successful Network Logon: User Name: ZUSE$ Domain: XYSYSTEMS Logon ID: (0x0,0x5984AE49) Logon Type: 3 Logon Process: Kerberos Authentication Package: Kerberos Workstation Name: Logon GUID: {20014d9a-ce6c-6834-d1ed-607c08f0b6a7} Caller User Name: - Caller Domain: - Caller Logon ID: - Caller Process ID: - Transited Services: - Source Network Address: 172.16.0.15 Source Port: 2320 #011564
+#
+# NEXT MESSAGE
+#
+<141>Mar 10 09:30:25 zuse.xysystems.local MSWinEventLog\0111\011Security\011576\011Wed Mar 10 09:30:25 2010\011538\011Security\011SYSTEM\011User\011Success Audit\011ZUSE\011Logon/Logoff\011\011User Logoff: User Name: ZUSE$ Domain: XYSYSTEMS Logon ID: (0x0,0x5984AE49) Logon Type: 3 \011565
+141,local1,notice,Mar 10 09:30:25,zuse.xysystems.local,MSWinEventLog#0111#011Security#011576#011Wed,MSWinEventLog#0111#011Security#011576#011Wed, Mar 10 09:30:25 2010#011538#011Security#011SYSTEM#011User#011Success Audit#011ZUSE#011Logon/Logoff#011#011User Logoff: User Name: ZUSE$ Domain: XYSYSTEMS Logon ID: (0x0,0x5984AE49) Logon Type: 3 #011565
+#
+# NEXT MESSAGE
+#
+<141>Mar 10 09:30:25 zuse.xysystems.local MSWinEventLog\0111\011Security\011577\011Wed Mar 10 09:30:25 2010\011540\011Security\011SYSTEM\011User\011Success Audit\011ZUSE\011Logon/Logoff\011\011Successful Network Logon: User Name: ZUSE$ Domain: XYSYSTEMS Logon ID: (0x0,0x5984AF00) Logon Type: 3 Logon Process: Kerberos Authentication Package: Kerberos Workstation Name: Logon GUID: {20014d9a-ce6c-6834-d1ed-607c08f0b6a7} Caller User Name: - Caller Domain: - Caller Logon ID: - Caller Process ID: - Transited Services: - Source Network Address: 172.16.0.15 Source Port: 2321 \011566
+141,local1,notice,Mar 10 09:30:25,zuse.xysystems.local,MSWinEventLog#0111#011Security#011577#011Wed,MSWinEventLog#0111#011Security#011577#011Wed, Mar 10 09:30:25 2010#011540#011Security#011SYSTEM#011User#011Success Audit#011ZUSE#011Logon/Logoff#011#011Successful Network Logon: User Name: ZUSE$ Domain: XYSYSTEMS Logon ID: (0x0,0x5984AF00) Logon Type: 3 Logon Process: Kerberos Authentication Package: Kerberos Workstation Name: Logon GUID: {20014d9a-ce6c-6834-d1ed-607c08f0b6a7} Caller User Name: - Caller Domain: - Caller Logon ID: - Caller Process ID: - Transited Services: - Source Network Address: 172.16.0.15 Source Port: 2321 #011566
+#
+# NEXT MESSAGE
+#
+<141>Mar 10 09:30:25 zuse.xysystems.local MSWinEventLog\0111\011Security\011578\011Wed Mar 10 09:30:25 2010\011538\011Security\011SYSTEM\011User\011Success Audit\011ZUSE\011Logon/Logoff\011\011User Logoff: User Name: ZUSE$ Domain: XYSYSTEMS Logon ID: (0x0,0x5984AF00) Logon Type: 3 \011567
+141,local1,notice,Mar 10 09:30:25,zuse.xysystems.local,MSWinEventLog#0111#011Security#011578#011Wed,MSWinEventLog#0111#011Security#011578#011Wed, Mar 10 09:30:25 2010#011538#011Security#011SYSTEM#011User#011Success Audit#011ZUSE#011Logon/Logoff#011#011User Logoff: User Name: ZUSE$ Domain: XYSYSTEMS Logon ID: (0x0,0x5984AF00) Logon Type: 3 #011567
+#
+# NEXT MESSAGE
+#
+<141>Mar 10 09:30:25 zuse.xysystems.local MSWinEventLog\0111\011Security\011579\011Wed Mar 10 09:30:25 2010\011538\011Security\011ANONYMOUS LOGON\011Well Known Group\011Success Audit\011ZUSE\011Logon/Logoff\011\011User Logoff: User Name: ANONYMOUS LOGON Domain: NT AUTHORITY Logon ID: (0x0,0x5984AB6F) Logon Type: 3 \011568
+141,local1,notice,Mar 10 09:30:25,zuse.xysystems.local,MSWinEventLog#0111#011Security#011579#011Wed,MSWinEventLog#0111#011Security#011579#011Wed, Mar 10 09:30:25 2010#011538#011Security#011ANONYMOUS LOGON#011Well Known Group#011Success Audit#011ZUSE#011Logon/Logoff#011#011User Logoff: User Name: ANONYMOUS LOGON Domain: NT AUTHORITY Logon ID: (0x0,0x5984AB6F) Logon Type: 3 #011568
+#
+# NEXT MESSAGE
+#
+<141>Mar 10 09:30:30 zuse.xysystems.local MSWinEventLog\0111\011Security\011580\011Wed Mar 10 09:30:29 2010\011540\011Security\011XYWSBADGE$\011User\011Success Audit\011ZUSE\011Logon/Logoff\011\011Successful Network Logon: User Name: XYWSBADGE$ Domain: XYSYSTEMS Logon ID: (0x0,0x59852D73) Logon Type: 3 Logon Process: Kerberos Authentication Package: Kerberos Workstation Name: Logon GUID: {4bc3c075-5a77-4648-5822-bfdf88b4c211} Caller User Name: - Caller Domain: - Caller Logon ID: - Caller Process ID: - Transited Services: - Source Network Address: 172.16.3.18 Source Port: 0 \011569
+141,local1,notice,Mar 10 09:30:30,zuse.xysystems.local,MSWinEventLog#0111#011Security#011580#011Wed,MSWinEventLog#0111#011Security#011580#011Wed, Mar 10 09:30:29 2010#011540#011Security#011XYWSBADGE$#011User#011Success Audit#011ZUSE#011Logon/Logoff#011#011Successful Network Logon: User Name: XYWSBADGE$ Domain: XYSYSTEMS Logon ID: (0x0,0x59852D73) Logon Type: 3 Logon Process: Kerberos Authentication Package: Kerberos Workstation Name: Logon GUID: {4bc3c075-5a77-4648-5822-bfdf88b4c211} Caller User Name: - Caller Domain: - Caller Logon ID: - Caller Process ID: - Transited Services: - Source Network Address: 172.16.3.18 Source Port: 0 #011569
diff --git a/tests/testsuites/snare_ccoff_udp.conf b/tests/testsuites/snare_ccoff_udp.conf
new file mode 100644
index 00000000..6abbedf4
--- /dev/null
+++ b/tests/testsuites/snare_ccoff_udp.conf
@@ -0,0 +1,21 @@
+# This test some real-world snare cases. I don't like snare (no wonder
+# as I have written EventReporter, the ultimate Windows-to-Syslog tool),
+# but besides that snare generates severely malformed messages that
+# really stress-test the rsyslog engine. They deserve to be beaten by someone ;)
+# This test needs to be run over UDP only, as snare puts LF INTO some of the messages,
+# which makes it impossible to try these out via traditional syslog/tcp
+# added 2010-03-21 rgerhards
+$ModLoad ../plugins/omstdout/.libs/omstdout
+$IncludeConfig nettest.input.conf # This picks the to be tested input from the test driver!
+
+$ErrorMessagesToStderr off
+
+# snare usses HT as field delimiter, so many users have turned off
+# control character escaping to make any sense at all from these messages...
+$EscapeControlCharactersOnReceive off
+
+# use a special format that we can easily check. We do NOT include a timestamp because
+# the malformed snare messages usually do not contain one (and we can not check against
+# the system time in our test cases).
+$template fmt,"%PRI%,%syslogfacility-text%,%syslogseverity-text%,%hostname%,%programname%,%syslogtag%,%msg%\n"
+*.* :omstdout:;fmt
diff --git a/tests/testsuites/snare_ccoff_udp2.conf b/tests/testsuites/snare_ccoff_udp2.conf
new file mode 100644
index 00000000..9115c14f
--- /dev/null
+++ b/tests/testsuites/snare_ccoff_udp2.conf
@@ -0,0 +1,17 @@
+# Similar to snare_ccoff_udp_2, but with a different template. This template
+# has triggered problems in the past, thus a test is granted.
+# added 2010-03-21 rgerhards
+$ModLoad ../plugins/omstdout/.libs/omstdout
+$IncludeConfig nettest.input.conf # This picks the to be tested input from the test driver!
+
+$ErrorMessagesToStderr off
+
+# snare usses HT as field delimiter, so many users have turned off
+# control character escaping to make any sense at all from these messages...
+$EscapeControlCharactersOnReceive off
+
+# we need to use a fixed timestamp, as otherwise we can not compare :(
+# This could be improved in later versions of the testing tools, but requires
+# modification to the rsyslog core...
+$template fmt,"insert into windows (Message, Facility,FromHost, Priority, DeviceReportedTime, ReceivedAt, InfoUnitID, SysLogTag) values ('%msg:::space-cc%', %syslogfacility%, '%HOSTNAME%',%syslogpriority%, '20100321185328', '20100321185328', %iut%, '%syslogtag:::space-cc%')\n",sql
+*.* :omstdout:;fmt
diff --git a/tests/testsuites/sndrcv_gzip_rcvr.conf b/tests/testsuites/sndrcv_gzip_rcvr.conf
new file mode 100644
index 00000000..6f7ce34b
--- /dev/null
+++ b/tests/testsuites/sndrcv_gzip_rcvr.conf
@@ -0,0 +1,11 @@
+# see equally-named shell file for details
+# rgerhards, 2009-11-11
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+# then SENDER sends to this port (not tcpflood!)
+$InputTCPServerRun 13515
+
+$template outfmt,"%msg:F,58:2%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+:msg, contains, "msgnum:" ?dynfile;outfmt
diff --git a/tests/testsuites/sndrcv_gzip_sender.conf b/tests/testsuites/sndrcv_gzip_sender.conf
new file mode 100644
index 00000000..c874c068
--- /dev/null
+++ b/tests/testsuites/sndrcv_gzip_sender.conf
@@ -0,0 +1,8 @@
+# see tcpsndrcv.sh for details
+# rgerhards, 2009-11-11
+$IncludeConfig diag-common2.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$InputTCPServerRun 13514
+
+*.* @@127.0.0.1:13515
diff --git a/tests/testsuites/sndrcv_rcvr.conf b/tests/testsuites/sndrcv_rcvr.conf
new file mode 100644
index 00000000..6f7ce34b
--- /dev/null
+++ b/tests/testsuites/sndrcv_rcvr.conf
@@ -0,0 +1,11 @@
+# see equally-named shell file for details
+# rgerhards, 2009-11-11
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+# then SENDER sends to this port (not tcpflood!)
+$InputTCPServerRun 13515
+
+$template outfmt,"%msg:F,58:2%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+:msg, contains, "msgnum:" ?dynfile;outfmt
diff --git a/tests/testsuites/sndrcv_sender.conf b/tests/testsuites/sndrcv_sender.conf
new file mode 100644
index 00000000..c874c068
--- /dev/null
+++ b/tests/testsuites/sndrcv_sender.conf
@@ -0,0 +1,8 @@
+# see tcpsndrcv.sh for details
+# rgerhards, 2009-11-11
+$IncludeConfig diag-common2.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$InputTCPServerRun 13514
+
+*.* @@127.0.0.1:13515
diff --git a/tests/testsuites/sndrcv_tls_anon_rcvr.conf b/tests/testsuites/sndrcv_tls_anon_rcvr.conf
new file mode 100644
index 00000000..01143b22
--- /dev/null
+++ b/tests/testsuites/sndrcv_tls_anon_rcvr.conf
@@ -0,0 +1,22 @@
+# see equally-named shell file for details
+# this is the config fil for the TLS server
+# rgerhards, 2009-11-11
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+
+# certificates
+$DefaultNetstreamDriverCAFile testsuites/x.509/ca.pem
+$DefaultNetstreamDriverCertFile testsuites/x.509/client-cert.pem
+$DefaultNetstreamDriverKeyFile testsuites/x.509/client-key.pem
+
+$DefaultNetstreamDriver gtls # use gtls netstream driver
+
+# then SENDER sends to this port (not tcpflood!)
+$InputTCPServerStreamDriverMode 1
+$InputTCPServerStreamDriverAuthMode anon
+$InputTCPServerRun 13515
+
+$template outfmt,"%msg:F,58:2%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+:msg, contains, "msgnum:" ?dynfile;outfmt
diff --git a/tests/testsuites/sndrcv_tls_anon_sender.conf b/tests/testsuites/sndrcv_tls_anon_sender.conf
new file mode 100644
index 00000000..4a944455
--- /dev/null
+++ b/tests/testsuites/sndrcv_tls_anon_sender.conf
@@ -0,0 +1,19 @@
+# see tcpsndrcv.sh for details
+# this is the TLS client
+# rgerhards, 2009-11-11
+$IncludeConfig diag-common2.conf
+
+# certificates
+$DefaultNetstreamDriverCAFile testsuites/x.509/ca.pem
+$DefaultNetstreamDriverCertFile testsuites/x.509/client-cert.pem
+$DefaultNetstreamDriverKeyFile testsuites/x.509/client-key.pem
+
+# Note: no TLS for the listener, this is for tcpflood!
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$InputTCPServerRun 13514
+
+# set up the action
+$DefaultNetstreamDriver gtls # use gtls netstream driver
+$ActionSendStreamDriverMode 1 # require TLS for the connection
+$ActionSendStreamDriverAuthMode anon
+*.* @@127.0.0.1:13515
diff --git a/tests/testsuites/subsecond.conf b/tests/testsuites/subsecond.conf
new file mode 100644
index 00000000..58c26cc7
--- /dev/null
+++ b/tests/testsuites/subsecond.conf
@@ -0,0 +1,8 @@
+$ModLoad ../plugins/omstdout/.libs/omstdout
+$IncludeConfig nettest.input.conf # This picks the to be tested input from the test driver!
+
+$ErrorMessagesToStderr off
+
+# use a special format
+$template fmt,"%timestamp:::date-subseconds%\n"
+*.* :omstdout:;fmt
diff --git a/tests/testsuites/threadingmq.conf b/tests/testsuites/threadingmq.conf
new file mode 100644
index 00000000..b98f9b5a
--- /dev/null
+++ b/tests/testsuites/threadingmq.conf
@@ -0,0 +1,16 @@
+# Threading test, we run a tcp flood to via an
+# engine instructed to use multiple threads
+# rgerhards, 2009-06-26
+$IncludeConfig diag-common.conf
+
+$MainMsgQueueTimeoutShutdown 100000
+
+$MainMsgQueueWorkerThreadMinimumMessages 10
+$MainMsgQueueWorkerThreads 5
+
+$template outfmt,"%msg:F,58:2%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+# write quickly to the output file:
+$OMFileFlushOnTXEnd off
+$OMFileIOBufferSize 256k
+:msg, contains, "msgnum:" ?dynfile;outfmt
diff --git a/tests/testsuites/threadingmqaq.conf b/tests/testsuites/threadingmqaq.conf
new file mode 100644
index 00000000..f0d39057
--- /dev/null
+++ b/tests/testsuites/threadingmqaq.conf
@@ -0,0 +1,20 @@
+# Threading test, we run a tcp flood to via an
+# engine instructed to use multiple threads
+# rgerhards, 2009-06-26
+$IncludeConfig diag-common.conf
+
+$MainMsgQueueTimeoutShutdown 10000
+
+$MainMsgQueueWorkerThreadMinimumMessages 10
+$MainMsgQueueWorkerThreads 5
+
+$template outfmt,"%msg:F,58:2%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+# write quickly to the output file:
+$OMFileFlushOnTXEnd off
+$OMFileIOBufferSize 256k
+# This time, also run the action queue detached
+$ActionQueueWorkerThreadMinimumMessages 10
+$ActionQueueWorkerThreads 5
+$ActionQueueType LinkedList
+:msg, contains, "msgnum:" ?dynfile;outfmt
diff --git a/tests/testsuites/ts3164.conf b/tests/testsuites/ts3164.conf
new file mode 100644
index 00000000..7aa6a8ef
--- /dev/null
+++ b/tests/testsuites/ts3164.conf
@@ -0,0 +1,8 @@
+$ModLoad ../plugins/omstdout/.libs/omstdout
+$IncludeConfig nettest.input.conf # This picks the to be tested input from the test driver!
+
+$ErrorMessagesToStderr off
+
+# use a special format
+$template fmt,"%timestamp:::date-rfc3164%\n"
+*.* :omstdout:;fmt
diff --git a/tests/testsuites/ts3339.conf b/tests/testsuites/ts3339.conf
new file mode 100644
index 00000000..df8f23ac
--- /dev/null
+++ b/tests/testsuites/ts3339.conf
@@ -0,0 +1,8 @@
+$ModLoad ../plugins/omstdout/.libs/omstdout
+$IncludeConfig nettest.input.conf # This picks the to be tested input from the test driver!
+
+$ErrorMessagesToStderr off
+
+# use a special format
+$template fmt,"%timestamp:::date-rfc3339%\n"
+*.* :omstdout:;fmt
diff --git a/tests/testsuites/tsmysql.conf b/tests/testsuites/tsmysql.conf
new file mode 100644
index 00000000..f97d4b0a
--- /dev/null
+++ b/tests/testsuites/tsmysql.conf
@@ -0,0 +1,8 @@
+$ModLoad ../plugins/omstdout/.libs/omstdout
+$IncludeConfig nettest.input.conf # This picks the to be tested input from the test driver!
+
+$ErrorMessagesToStderr off
+
+# use a special format
+$template fmt,"%timestamp:::date-mysql%\n"
+*.* :omstdout:;fmt
diff --git a/tests/testsuites/tspgsql.conf b/tests/testsuites/tspgsql.conf
new file mode 100644
index 00000000..eb18c091
--- /dev/null
+++ b/tests/testsuites/tspgsql.conf
@@ -0,0 +1,8 @@
+$ModLoad ../plugins/omstdout/.libs/omstdout
+$IncludeConfig nettest.input.conf # This picks the to be tested input from the test driver!
+
+$ErrorMessagesToStderr off
+
+# use a special format
+$template fmt,"%timestamp:::date-pgsql%\n"
+*.* :omstdout:;fmt
diff --git a/tests/testsuites/upcase-date.parse1 b/tests/testsuites/upcase-date.parse1
new file mode 100644
index 00000000..2d21222a
--- /dev/null
+++ b/tests/testsuites/upcase-date.parse1
@@ -0,0 +1,4 @@
+<6>AUG 10 22:18:24 2009 netips-warden2-p [audit] user=[*SMS] src=192.168.11.11 iface=5 access=9 Update State Reset
+6,kern,info,Aug 10 22:18:24,2009,,, netips-warden2-p [audit] user=[*SMS] src=192.168.11.11 iface=5 access=9 Update State Reset
+#Example from RFC3164, section 5.4
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/weird.parse1 b/tests/testsuites/weird.parse1
new file mode 100644
index 00000000..e8b90c74
--- /dev/null
+++ b/tests/testsuites/weird.parse1
@@ -0,0 +1,37 @@
+# some really weird samples, some of them seen in practice,
+# some other deliberately generated. The main point is that they
+# should not cause an abort...
+<14>Aug 30 23:00:05 X4711 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+14,user,info,Aug 30 23:00:05,X4711,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,
+# important: the following line has a SP at the end of the line!
+<14>Aug 30 23:00:05 X4711
+14,user,info,Aug 30 23:00:05,X4711,,,
+# and this one NOT
+<14>Aug 30 23:00:05 X4711
+14,user,info,Aug 30 23:00:05,X4711,,,
+# there is a SP at the end of the line
+<14>Aug 30 23:00:05
+14,user,info,Aug 30 23:00:05,localhost,,,
+# and here is no SP at the end of the line
+<14>Aug 30 23:00:05
+14,user,info,Aug 30 23:00:05,localhost,,,
+# unfortunately, I can not test missing dates with this test suite, because
+# we would have the current date in the response, which we can not check against
+#
+# and now the same tests with RFC3339 data - this can make a difference
+# as a different date parser is involved.
+#
+<14>2010-08-30T23:00:05Z X4711 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+14,user,info,Aug 30 23:00:05,X4711,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,
+# important: the following line has a SP at the end of the line!
+<14>2010-08-30T23:00:05Z X4711
+14,user,info,Aug 30 23:00:05,X4711,,,
+# and this one NOT
+<14>2010-08-30T23:00:05Z X4711
+14,user,info,Aug 30 23:00:05,X4711,,,
+# there is a SP at the end of the line
+<14>2010-08-30T23:00:05Z
+14,user,info,Aug 30 23:00:05,localhost,,,
+# and here is no SP at the end of the line
+<14>2010-08-30T23:00:05Z
+14,user,info,Aug 30 23:00:05,localhost,,,
diff --git a/tests/testsuites/wr_large.conf b/tests/testsuites/wr_large.conf
new file mode 100644
index 00000000..b64f132b
--- /dev/null
+++ b/tests/testsuites/wr_large.conf
@@ -0,0 +1,16 @@
+# simple async writing test
+# rgerhards, 2010-03-09
+$MaxMessageSize 10k
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+$InputTCPServerRun 13514
+
+$template outfmt,"%msg:F,58:2%,%msg:F,58:3%,%msg:F,58:4%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+$OMFileFlushOnTXEnd off
+$OMFileFlushInterval 2
+$OMFileIOBufferSize 256k
+$IncludeConfig rsyslog.action.1.include
+local0.* ?dynfile;outfmt
diff --git a/tests/testsuites/x.509/ca-key.pem b/tests/testsuites/x.509/ca-key.pem
new file mode 100644
index 00000000..1e1a0b26
--- /dev/null
+++ b/tests/testsuites/x.509/ca-key.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDZnIJGJH80j2DPBXdxgmFmBRMoBnpwQb8yhRJcJacaWigRAhp4
+wdo07rR+EpuBJHD/5ImIygUwCj/XWAs4JKm3LqK2ih1gUy/s6Tg2O5t3k11kdjEH
+MKUxDOLs441dEwERPQtePEoy2POzViIyy959ZJorkdnwC4LBKdQVLEELlwIDAQAB
+AoGAEQWvoRoAw1VF3tvQHJZ01Pyno3ViRX63HJYROhkN6b9MrAvsky6iyYo0nzoI
+ZQE7P6EaaxNWdYwPs2IlOoaPqeos1sGVDaK/JFuja/DduoXBdCy9RFWRaugDX/1U
+iMtjtu29euvegP0r2RIxaIl9dapF5alNH5MLMyBl7XTB+/kCQQDiwHnW8jS1paSc
+/risF6Ie5rKuUfVDG8hqMEiKyczSHwUVYushwCclshjM6E1TBFZqMz/8PbFW51pK
+OzFS2s6/AkEA9a4044RL3AWe37LIU4hbz2Y+auRvPh8x4i2cWLzdok8Rc1EHDGLN
+eHBoOQ3Q2nQS94cOx6HxpRztzBgiwpTRKQJADX9BgV7nbkyO0N2EppG9j7NRvXiZ
+bcYwlsmK99/tNjCsf8pkjpy+d8rzGPdW6vMeJbIpQ910OeUJhdOiKvllRwJBAIw3
+rP/dVd5xZseNpj/mp1+rnxwq3EK8UyAfoAgVYvlr3y3NpRQwn8yJezJ07CqB7QFR
+F+JgTyZJaH7/l3cusGECQQCM3HmkADAKxX6RwKe8X0Kj/36rjXEMNoq0ZdXOB7Qz
+f5N6og4Da9y/ZO+XMo6P3XR/TYIYrMD8nuoR33X69kb1
+-----END RSA PRIVATE KEY-----
diff --git a/tests/testsuites/x.509/ca.pem b/tests/testsuites/x.509/ca.pem
new file mode 100644
index 00000000..a733eb86
--- /dev/null
+++ b/tests/testsuites/x.509/ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICyzCCAjagAwIBAgIESFo2XjALBgkqhkiG9w0BAQUwezELMAkGA1UEBhMCVVMx
+EDAOBgNVBAoTB1NvbWVPcmcxDzANBgNVBAsTBlNvbWVPVTESMBAGA1UEBxMJU29t
+ZXdoZXJlMQswCQYDVQQIEwJDQTEoMCYGA1UEAxMfc29tZU5hbWUgKG5vdCBuZWNl
+c3NhcmlseSBETlMhKTAeFw0wODA2MTkxMDM1MTJaFw0xODA2MTcxMDM1MjVaMHsx
+CzAJBgNVBAYTAlVTMRAwDgYDVQQKEwdTb21lT3JnMQ8wDQYDVQQLEwZTb21lT1Ux
+EjAQBgNVBAcTCVNvbWV3aGVyZTELMAkGA1UECBMCQ0ExKDAmBgNVBAMTH3NvbWVO
+YW1lIChub3QgbmVjZXNzYXJpbHkgRE5TISkwgZwwCwYJKoZIhvcNAQEBA4GMADCB
+iAKBgNmcgkYkfzSPYM8Fd3GCYWYFEygGenBBvzKFElwlpxpaKBECGnjB2jTutH4S
+m4EkcP/kiYjKBTAKP9dYCzgkqbcuoraKHWBTL+zpODY7m3eTXWR2MQcwpTEM4uzj
+jV0TARE9C148SjLY87NWIjLL3n1kmiuR2fALgsEp1BUsQQuXAgMBAAGjYzBhMA8G
+A1UdEwEB/wQFMAMBAf8wHgYDVR0RBBcwFYETc29tZW9uZUBleGFtcGxlLm5ldDAP
+BgNVHQ8BAf8EBQMDBwQAMB0GA1UdDgQWBBT7/paNEKc65bcNe0NIhsj4cpl7iTAL
+BgkqhkiG9w0BAQUDgYEAlv9ge8Koways837OLoZIam0s7wQCcwd9rWE05caps7BU
+T4bfgab9U/e9mmrf3V/zXmtU6y8hhTXF5AcZv3/EmCVwsPRotgrJ+rHXTv5e2PO7
+/8C3K2Lhc89gF4qf4xZwlZU70RasKgCzZa5ivS2Y8pW6LUu6eqqgVw3pPJbW3TE=
+-----END CERTIFICATE-----
diff --git a/tests/testsuites/x.509/client-cert.pem b/tests/testsuites/x.509/client-cert.pem
new file mode 100644
index 00000000..5bf39f81
--- /dev/null
+++ b/tests/testsuites/x.509/client-cert.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICijCCAfWgAwIBAgIESFo7ITALBgkqhkiG9w0BAQUwezELMAkGA1UEBhMCVVMx
+EDAOBgNVBAoTB1NvbWVPcmcxDzANBgNVBAsTBlNvbWVPVTESMBAGA1UEBxMJU29t
+ZXdoZXJlMQswCQYDVQQIEwJDQTEoMCYGA1UEAxMfc29tZU5hbWUgKG5vdCBuZWNl
+c3NhcmlseSBETlMhKTAeFw0wODA2MTkxMDU1MzJaFw0xMTAzMTYxMDU1MzlaMA0x
+CzAJBgNVBAYTAlVTMIGcMAsGCSqGSIb3DQEBAQOBjAAwgYgCgYC+f6yCet2WJgmw
+tgukOReI+avRHOfr2hLhIQkSzCOiNi0tNWMKmaQWw/D+y1FvLRq0wLDUyJK/36rB
+67HKfscoNeClKTS8jhAs1mPjT57iyuoqK6VW/d2JoofklRCgDIZQrNfxHiOO+kN3
+ShLmkGqxkA3YyUty/JmF6PKWYIhQWQIDAQABo4GPMIGMMAwGA1UdEwEB/wQCMAAw
+HQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMB0GA1UdEQQWMBSCEmNsaWVu
+dC5leGFtcGxlLm5ldDAdBgNVHQ4EFgQUrDcwsuOF4RiHn0eboCplJSiUhfcwHwYD
+VR0jBBgwFoAU+/6WjRCnOuW3DXtDSIbI+HKZe4kwCwYJKoZIhvcNAQEFA4GBAAAh
+niy9ORW2AIb6lk/sa3iYczeYpGzxDM9bLZ1xSoIdoHM/v9gPG/WpAZ4ECHjx+Yk8
+4B/9gvaAmMi0FmcoIBQaEOe2P8tcIuzmum3N2F27F2+J4httiNDLJoseWVnXJUvS
+dPyVOrKXdl5vVFpmViI5P+VzzMqbAQ6oNlMXIh6e
+-----END CERTIFICATE-----
diff --git a/tests/testsuites/x.509/client-key.pem b/tests/testsuites/x.509/client-key.pem
new file mode 100644
index 00000000..05641213
--- /dev/null
+++ b/tests/testsuites/x.509/client-key.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQC+f6yCet2WJgmwtgukOReI+avRHOfr2hLhIQkSzCOiNi0tNWMK
+maQWw/D+y1FvLRq0wLDUyJK/36rB67HKfscoNeClKTS8jhAs1mPjT57iyuoqK6VW
+/d2JoofklRCgDIZQrNfxHiOO+kN3ShLmkGqxkA3YyUty/JmF6PKWYIhQWQIDAQAB
+AoGAVxrM+BqTIJlC/Ay5lP1QAB9di3ACserUkCFJY1F5h63rCU1sfIfVKl2s3+x6
+z3GZ0QV8tccCpv5wN1x8vqEqkbOvddM3rzpGkEC5PoyfCzuQBun1wnHK/JKjrfk5
+PvcaP60eTNjHZC7w78gOJJCzgzsEMrndtE+55diPmqGVtXMCQQDTZBy5WK8gZwMO
+rRz1BKKyBeMYMfTJoJafGfxp0H8AUbTa0V2eb+el3kuzPCm3FQ6IgaHyGj2WqkAw
+M0bfAfdXAkEA5rLna1t+2SCtgSd1DotndA4EsH4skBq9kFeD2/8T6Pf13zmBOq6O
+4aNEOhgBE/R9/MI4XoU9MbOlkZvKvDuXzwJADdWSb6rXIza6o34+0+Yuw5nRB+dV
+DtD8qoLn2wDzHtE6Fcv35YOLVHac26kHTd0J63MYZyDCgRa5Rq5EaBnX1wJAQYRF
+XKPbXmZ9X9SI1dyZQMhKZKUwmqw9caSo+e1zBhKFbSOzo6q3QTVQxv7SL4ybyxCN
+WaqVOmw+dR+9b7+s2QJAdNAw3r418rWKFKJJNTSqSqr1sYqiKvrQL6w2dpdpAeY4
+3VDCz/7/F9AEn3R7K3fZLQ7W6M62LSEjxxc1Y3LIpQ==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/testsuites/x.509/machine-cert.pem b/tests/testsuites/x.509/machine-cert.pem
new file mode 100644
index 00000000..fa2fd36e
--- /dev/null
+++ b/tests/testsuites/x.509/machine-cert.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC7TCCAligAwIBAgIESFo4GTALBgkqhkiG9w0BAQUwezELMAkGA1UEBhMCVVMx
+EDAOBgNVBAoTB1NvbWVPcmcxDzANBgNVBAsTBlNvbWVPVTESMBAGA1UEBxMJU29t
+ZXdoZXJlMQswCQYDVQQIEwJDQTEoMCYGA1UEAxMfc29tZU5hbWUgKG5vdCBuZWNl
+c3NhcmlseSBETlMhKTAeFw0wODA2MTkxMDQyNTRaFw0xMTAzMTYxMDQyNTdaMG8x
+CzAJBgNVBAYTAlVTMRAwDgYDVQQKEwdTb21lT3JnMQ8wDQYDVQQLEwZTb21lT1Ux
+EjAQBgNVBAcTCVNvbWV3aGVyZTELMAkGA1UECBMCQ0ExHDAaBgNVBAMTE21hY2hp
+bmUuZXhhbXBsZS5uZXQwgZwwCwYJKoZIhvcNAQEBA4GMADCBiAKBgLJOW6lIHv8u
+c6Ez7tiir64vI3aRuDmUACPybyWtyWqrLebzYtg+borWHj9y5di54NB5wpQhZQsQ
+U2awNqanzUYeLGqbecbuxuLtsKlZ4knax+PwHOBTmIcN1SjbpII27Toe0VwHE5Vd
+sygFFyorto6OeNLPrIcTFfwXQ2sVw325AgMBAAGjgZAwgY0wDAYDVR0TAQH/BAIw
+ADAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwHgYDVR0RBBcwFYITbWFj
+aGluZS5leGFtcGxlLm5ldDAdBgNVHQ4EFgQUDOHD29GdMfoDWwev4uDvItkLKKww
+HwYDVR0jBBgwFoAU+/6WjRCnOuW3DXtDSIbI+HKZe4kwCwYJKoZIhvcNAQEFA4GB
+AMt1iED7QzFL2Qk6VivoFY15S2XGF8rJTd3l00bwyLA5qLyLBGlB6z4qkYu7/7SW
+5r7tet+1DezgHrj/1eU289m410wnQB8fGwcVLp6OX2PAlhNmVLcsipiN6rielAcP
+aIg/VlBtoCFp/ymTLKgvh6DLKWhRUkFPqO2WtcQ3UUo+
+-----END CERTIFICATE-----
diff --git a/tests/testsuites/x.509/machine-key.pem b/tests/testsuites/x.509/machine-key.pem
new file mode 100644
index 00000000..808f00c9
--- /dev/null
+++ b/tests/testsuites/x.509/machine-key.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQCyTlupSB7/LnOhM+7Yoq+uLyN2kbg5lAAj8m8lrclqqy3m82LY
+Pm6K1h4/cuXYueDQecKUIWULEFNmsDamp81GHixqm3nG7sbi7bCpWeJJ2sfj8Bzg
+U5iHDdUo26SCNu06HtFcBxOVXbMoBRcqK7aOjnjSz6yHExX8F0NrFcN9uQIDAQAB
+AoGABHJs2c95Km8bpikX62I/VG5LiaD/wbvdtwfMWtm3PMhRKEHotLD169OERJvW
+fK3CHCD1R+F/ViPNmLGLY2Oq/GqKjhKjg4sqAznw8TImBSgXCFho4sl38z+luP1o
+TXFDgfV5HDDW1/F5kJlBIfXBLFdl4VO7E0ZnFt4FqSDRW2MCQQDRun/sBGM4i9hM
+QdC+QwrdcgCScBpzbz4YXtI9TyGEqNahg8kXgIVUbzDdRmG68G2M98USzRs5DWB7
+YvYwmRoPAkEA2aUdUpFRb/n7XfsAiFLYOk96C82iCCQpJi0si34zlCAEbCRbQ6zw
+gVDMCMSccnnWrVzqtxfN+rXycFTNyDFTtwJAPRwymfrNTnSxGcczo7y1NcE6GXFA
+w9HuLfuzFtov0g/AOl/EAG0abHfZrSAM6gOUaDbp3YiWHhGfw1QamB6EUQJAClTb
+MnsxeXZNZ2Wt3crI9uOk8IB/a5GD3osQbUK9Yg+vBg8nweuoswrJ1LS4lHqSJUKe
+5bgckAUpEAoGhrVIuwJBAKIuqx/cSjF4Oa9xT6DzBRe7vAlKFq62lUV5SLfoSEgY
+L5dvPBgAD0Styglny1s0Bu5FTlkxlFOMvUAD/O5hsQw=
+-----END RSA PRIVATE KEY-----
diff --git a/tests/testsuites/x.509/request.pem b/tests/testsuites/x.509/request.pem
new file mode 100644
index 00000000..c612325c
--- /dev/null
+++ b/tests/testsuites/x.509/request.pem
@@ -0,0 +1,10 @@
+-----BEGIN NEW CERTIFICATE REQUEST-----
+MIIBWDCBxAIBADANMQswCQYDVQQGEwJVUzCBnDALBgkqhkiG9w0BAQEDgYwAMIGI
+AoGAvn+sgnrdliYJsLYLpDkXiPmr0Rzn69oS4SEJEswjojYtLTVjCpmkFsPw/stR
+by0atMCw1MiSv9+qweuxyn7HKDXgpSk0vI4QLNZj40+e4srqKiulVv3diaKH5JUQ
+oAyGUKzX8R4jjvpDd0oS5pBqsZAN2MlLcvyZhejylmCIUFkCAwEAAaARMA8GCSqG
+SIb3DQEJBzECEwAwCwYJKoZIhvcNAQEFA4GBAA6mBaHFuRvcJVNoU7wDFcDexjvC
+QLpDpFRSbKcKdNEQLBRD8ZNVOY4WBXQE2pE84//QnygQPKPCHSqUVdPPBabi5y2E
+A2XvgYyKsrFbsrpKrVkPz5oQB4V7FRytQaQoBi//BSOu3dMaimLcAhfNQZCrQeu8
+SYWdJi5OPvrYGvgT
+-----END NEW CERTIFICATE REQUEST-----
diff --git a/tests/threadingmq.sh b/tests/threadingmq.sh
new file mode 100755
index 00000000..bdb5f35e
--- /dev/null
+++ b/tests/threadingmq.sh
@@ -0,0 +1,16 @@
+# test many concurrent tcp connections
+# we send 100,000 messages in the hopes that his puts at least a little bit
+# of pressure on the threading subsystem. To really prove it, we would need to
+# push messages for several minutes, but that takes too long during the
+# automatted tests (hint: do this manually after suspect changes). Thankfully,
+# in practice many threading bugs result in an abort rather quickly and these
+# should be covered by this test here.
+# rgerhards, 2009-06-26
+echo TEST: threadingmq.sh - main queue concurrency
+source $srcdir/diag.sh init
+source $srcdir/diag.sh startup threadingmq.conf
+source $srcdir/diag.sh injectmsg 0 100000
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown
+source $srcdir/diag.sh seq-check 0 99999
+source $srcdir/diag.sh exit
diff --git a/tests/threadingmqaq.sh b/tests/threadingmqaq.sh
new file mode 100755
index 00000000..b7764821
--- /dev/null
+++ b/tests/threadingmqaq.sh
@@ -0,0 +1,18 @@
+# test many concurrent tcp connections
+# we send 100,000 messages in the hopes that his puts at least a little bit
+# of pressure on the threading subsystem. To really prove it, we would need to
+# push messages for several minutes, but that takes too long during the
+# automatted tests (hint: do this manually after suspect changes). Thankfully,
+# in practice many threading bugs result in an abort rather quickly and these
+# should be covered by this test here.
+# rgerhards, 2009-06-26
+echo TEST: threadingmqaq.sh - main/action queue concurrency
+source $srcdir/diag.sh init
+source $srcdir/diag.sh startup threadingmqaq.conf
+#source $srcdir/diag.sh tcpflood -c2 -m100000
+#source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh injectmsg 0 100000
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown
+source $srcdir/diag.sh seq-check 0 99999
+source $srcdir/diag.sh exit
diff --git a/tests/timestamp.sh b/tests/timestamp.sh
new file mode 100755
index 00000000..7699a4af
--- /dev/null
+++ b/tests/timestamp.sh
@@ -0,0 +1,13 @@
+echo various timestamp tests
+source $srcdir/diag.sh init
+source $srcdir/diag.sh nettester ts3164 udp
+source $srcdir/diag.sh nettester ts3164 tcp
+source $srcdir/diag.sh nettester ts3339 udp
+source $srcdir/diag.sh nettester ts3339 tcp
+source $srcdir/diag.sh nettester tsmysql udp
+source $srcdir/diag.sh nettester tsmysql tcp
+source $srcdir/diag.sh nettester tspgsql udp
+source $srcdir/diag.sh nettester tspgsql tcp
+source $srcdir/diag.sh nettester subsecond udp
+source $srcdir/diag.sh nettester subsecond tcp
+source $srcdir/diag.sh init
diff --git a/tests/validation-run.sh b/tests/validation-run.sh
index 10981290..2e922283 100755
--- a/tests/validation-run.sh
+++ b/tests/validation-run.sh
@@ -22,17 +22,18 @@
# A copy of the GPL can be found in the file "COPYING" in this distribution.
#set -x
echo "testing a failed configuration verification run"
-../tools/rsyslogd -u2 -c3 -N1 -f$srcdir/testsuites/invalid.conf
+../tools/rsyslogd -dn -u2 -c4 -N1 -f$srcdir/testsuites/invalid.conf -M../runtime/.libs:../.libs
if [ $? -ne 1 ]; then
exit 1
fi
echo testing a valid config verification run
-../tools/rsyslogd -u2 -c3 -N1 -f$srcdir/testsuites/valid.conf
+../tools/rsyslogd -u2 -c4 -N1 -f$srcdir/testsuites/valid.conf -M../runtime/.libs:../.libs
if [ $? -ne 0 ]; then
exit 1
fi
echo testing empty config file
-../tools/rsyslogd -u2 -c3 -N1 -f/dev/null
+../tools/rsyslogd -u2 -c4 -N1 -f/dev/null -M../runtime/.libs:../.libs
if [ $? -ne 1 ]; then
exit 1
fi
+echo SUCCESS: validation run tests
diff --git a/tests/wr_large.sh b/tests/wr_large.sh
new file mode 100755
index 00000000..84f12989
--- /dev/null
+++ b/tests/wr_large.sh
@@ -0,0 +1,16 @@
+# This tests async writing large data records. We use up to 10K
+# record size.
+
+# added 2010-03-10 by Rgerhards
+#
+# This file is part of the rsyslog project, released under GPLv3
+cat rsyslog.action.1.include
+source $srcdir/diag.sh init
+source $srcdir/diag.sh startup wr_large.conf
+# send 4000 messages of 10.000bytes plus header max, randomized
+source $srcdir/diag.sh tcpflood -m4000 -r -d10000 -P129
+sleep 1 # due to large messages, we need this time for the tcp receiver to settle...
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown # and wait for it to terminate
+source $srcdir/diag.sh seq-check 0 3999 -E
+source $srcdir/diag.sh exit
diff --git a/tests/wr_large_async.sh b/tests/wr_large_async.sh
new file mode 100755
index 00000000..88a1acf8
--- /dev/null
+++ b/tests/wr_large_async.sh
@@ -0,0 +1,14 @@
+# This tests async writing large data records. We use up to 10K
+# record size.
+
+# added 2010-03-10 by Rgerhards
+#
+# This file is part of the rsyslog project, released under GPLv3
+echo ===============================================================================
+echo TEST: \[wr_large_async.sh\]: test for file writing for large message sets
+source $srcdir/diag.sh init
+# uncomment for debugging support:
+#export RSYSLOG_DEBUG="debug nostdout noprintmutexaction"
+#export RSYSLOG_DEBUGLOG="log"
+echo "\$OMFileAsyncWriting on" > rsyslog.action.1.include
+source $srcdir/wr_large.sh
diff --git a/tests/wr_large_sync.sh b/tests/wr_large_sync.sh
new file mode 100755
index 00000000..a1c4fd77
--- /dev/null
+++ b/tests/wr_large_sync.sh
@@ -0,0 +1,14 @@
+# This tests async writing large data records. We use up to 10K
+# record size.
+
+# added 2010-03-10 by Rgerhards
+#
+# This file is part of the rsyslog project, released under GPLv3
+echo ===============================================================================
+echo TEST: \[wr_large_sync.sh\]: test for file writing for large message sets
+source $srcdir/diag.sh init
+# uncomment for debugging support:
+#export RSYSLOG_DEBUG="debug nostdout noprintmutexaction"
+#export RSYSLOG_DEBUGLOG="log"
+echo "\$OMFileAsyncWriting off" > rsyslog.action.1.include
+source $srcdir/wr_large.sh
diff --git a/threads.c b/threads.c
index 61ea8f29..051903de 100644
--- a/threads.c
+++ b/threads.c
@@ -127,7 +127,7 @@ static void* thrdStarter(void *arg)
assert(pThis != NULL);
assert(pThis->pUsrThrdMain != NULL);
- /* block all signalsi */
+ /* block all signals */
sigset_t sigSet;
sigfillset(&sigSet);
pthread_sigmask(SIG_BLOCK, &sigSet, NULL);
@@ -151,7 +151,6 @@ rsRetVal thrdCreate(rsRetVal (*thrdMain)(thrdInfo_t*), rsRetVal(*afterRun)(thrdI
{
DEFiRet;
thrdInfo_t *pThis;
- int i;
assert(thrdMain != NULL);
@@ -159,7 +158,7 @@ rsRetVal thrdCreate(rsRetVal (*thrdMain)(thrdInfo_t*), rsRetVal(*afterRun)(thrdI
pThis->bIsActive = 1;
pThis->pUsrThrdMain = thrdMain;
pThis->pAfterRun = afterRun;
- i = pthread_create(&pThis->thrdID, NULL, thrdStarter, pThis);
+ pthread_create(&pThis->thrdID, NULL, thrdStarter, pThis);
CHKiRet(llAppend(&llThrds, NULL, pThis));
finalize_it:
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 582ad9e3..1497d3be 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -13,6 +13,8 @@ rsyslogd_SOURCES = \
omfwd.h \
omfile.c \
omfile.h \
+ ompipe.c \
+ ompipe.h \
omdiscard.c \
omdiscard.h \
iminternal.c \
@@ -22,12 +24,15 @@ rsyslogd_SOURCES = \
\
../dirty.h
rsyslogd_CPPFLAGS = $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
-rsyslogd_LDADD = $(ZLIB_LIBS) $(PTHREADS_LIBS) $(RSRT_LIBS)
+rsyslogd_LDADD = $(ZLIB_LIBS) $(PTHREADS_LIBS) $(RSRT_LIBS) $(SOL_LIBS)
rsyslogd_LDFLAGS = -export-dynamic
if ENABLE_DIAGTOOLS
-sbin_PROGRAMS += rsyslog_diag_hostname
+sbin_PROGRAMS += rsyslog_diag_hostname msggen zpipe
rsyslog_diag_hostname_SOURCES = gethostn.c
+zpipe_SOURCES = zpipe.c
+zpipe_LDADD = -lz
+msggen_SOURCES = msggen.c
endif
EXTRA_DIST = $(man_MANS)
diff --git a/tools/msggen.c b/tools/msggen.c
new file mode 100644
index 00000000..29ade3a7
--- /dev/null
+++ b/tools/msggen.c
@@ -0,0 +1,39 @@
+/* msggen - a small diagnostic utility that does very quick
+ * syslog() calls.
+ *
+ * Copyright 2008 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <syslog.h>
+
+int main(int __attribute__((unused)) argc, char __attribute__((unused)) *argv[])
+{
+ int i;
+
+ openlog("msggen", 0 , LOG_LOCAL0);
+
+ for(i = 0 ; i < 10 ; ++i)
+ syslog(LOG_NOTICE, "This is message number %d", i);
+
+ closelog();
+ return 0;
+}
diff --git a/tools/omfile.c b/tools/omfile.c
index d6bc23e7..487cf8a0 100644
--- a/tools/omfile.c
+++ b/tools/omfile.c
@@ -1,18 +1,22 @@
/* omfile.c
* This is the implementation of the build-in file output module.
*
- * Handles: eTypeCONSOLE, eTypeTTY, eTypeFILE, eTypePIPE
- *
* NOTE: read comments in module-template.h to understand how this file
* works!
*
* File begun on 2007-07-21 by RGerhards (extracted from syslogd.c)
- * This file is under development and has not yet arrived at being fully
- * self-contained and a real object. So far, it is mostly an excerpt
- * of the "old" message code without any modifications. However, it
- * helps to have things at the right place one we go to the meat of it.
*
- * Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH.
+ * A large re-write of this file was done in June, 2009. The focus was
+ * to introduce many more features (like zipped writing), clean up the code
+ * and make it more reliable. In short, that rewrite tries to provide a new
+ * solid basis for the next three to five years to come. During it, bugs
+ * may have been introduced ;) -- rgerhards, 2009-06-04
+ *
+ * Note that as of 2010-02-28 this module does no longer handle
+ * pipes. These have been moved to ompipe, to reduced the entanglement
+ * between the two different functionalities. -- rgerhards
+ *
+ * Copyright 2007-2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -41,10 +45,15 @@
#include <assert.h>
#include <errno.h>
#include <ctype.h>
+#include <libgen.h>
#include <unistd.h>
#include <sys/file.h>
-#include "syslogd.h"
+#ifdef OS_SOLARIS
+# include <fcntl.h>
+#endif
+
+#include "conf.h"
#include "syslogd-types.h"
#include "srUtils.h"
#include "template.h"
@@ -53,6 +62,9 @@
#include "cfsysline.h"
#include "module-template.h"
#include "errmsg.h"
+#include "stream.h"
+#include "unicode-helper.h"
+#include "atomic.h"
MODULE_TYPE_OUTPUT
@@ -60,17 +72,23 @@ MODULE_TYPE_OUTPUT
*/
DEF_OMOD_STATIC_DATA
DEFobjCurrIf(errmsg)
+DEFobjCurrIf(strm)
/* The following structure is a dynafile name cache entry.
*/
struct s_dynaFileCacheEntry {
- uchar *pName; /* name currently open, if dynamic name */
- short fd; /* name associated with file name in cache */
+ uchar *pName; /* name currently open, if dynamic name */
+ strm_t *pStrm; /* our output stream */
time_t lastUsed; /* for LRU - last access */
};
typedef struct s_dynaFileCacheEntry dynaFileCacheEntry;
+#define IOBUF_DFLT_SIZE 1024 /* default size for io buffers */
+#define FLUSH_INTRVL_DFLT 1 /* default buffer flush interval (in seconds) */
+#define USE_ASYNCWRITER_DFLT 0 /* default buffer use async writer */
+#define FLUSHONTX_DFLT 1 /* default for flush on TX end */
+
/* globals for default values */
static int iDynaFileCacheSize = 10; /* max cache for dynamic files */
static int fCreateMode = 0644; /* mode to use when creating files */
@@ -82,19 +100,19 @@ static uid_t dirUID; /* UID to be used for newly created directories */
static uid_t dirGID; /* GID to be used for newly created directories */
static int bCreateDirs = 1;/* auto-create directories for dynaFiles: 0 - no, 1 - yes */
static int bEnableSync = 0;/* enable syncing of files (no dash in front of pathname in conf): 0 - no, 1 - yes */
-static uchar *pszTplName = NULL; /* name of the default template to use */
+static int iZipLevel = 0; /* zip compression mode (0..9 as usual) */
+static bool bFlushOnTXEnd = FLUSHONTX_DFLT;/* flush write buffers when transaction has ended? */
+static int64 iIOBufSize = IOBUF_DFLT_SIZE; /* size of an io buffer */
+static int iFlushInterval = FLUSH_INTRVL_DFLT; /* how often flush the output buffer on inactivity? */
+static int bUseAsyncWriter = USE_ASYNCWRITER_DFLT; /* should we enable asynchronous writing? */
+uchar *pszFileDfltTplName = NULL; /* name of the default template to use */
/* end globals for default values */
typedef struct _instanceData {
uchar f_fname[MAXFNAME];/* file or template name (display only) */
- short fd; /* file descriptor for (current) file */
- enum {
- eTypeFILE,
- eTypeTTY,
- eTypeCONSOLE,
- eTypePIPE
- } fileType;
+ strm_t *pStrm; /* our output stream */
+ strmType_t strmType; /* stream type, used for named pipes */
char bDynamicName; /* 0 - static name, 1 - dynamic name (with properties) */
int fCreateMode; /* file creation mode for open() */
int fDirCreateMode; /* creation mode for mkdir() */
@@ -113,8 +131,13 @@ typedef struct _instanceData {
* pointer points to the overall structure.
*/
dynaFileCacheEntry **dynCache;
- off_t f_sizeLimit; /* file size limit, 0 = no limit */
- char *f_sizeLimitCmd; /* command to carry out when size limit is reached */
+ off_t iSizeLimit; /* file size limit, 0 = no limit */
+ uchar *pszSizeLimitCmd; /* command to carry out when size limit is reached */
+ int iZipLevel; /* zip mode to use for this selector */
+ int iIOBufSize; /* size of associated io buffer */
+ int iFlushInterval; /* how fast flush buffer on inactivity? */
+ bool bFlushOnTXEnd; /* flush write buffers when transaction has ended? */
+ bool bUseAsyncWriter; /* use async stream writer? */
} instanceData;
@@ -128,24 +151,23 @@ ENDisCompatibleWithFeature
BEGINdbgPrintInstInfo
CODESTARTdbgPrintInstInfo
if(pData->bDynamicName) {
- dbgprintf("[dynamic]\n\ttemplate='%s'"
- "\tfile cache size=%d\n"
- "\tcreate directories: %s\n"
- "\tfile owner %d, group %d\n"
- "\tdirectory owner %d, group %d\n"
- "\tfail if owner/group can not be set: %s\n",
- pData->f_fname,
- pData->iDynaFileCacheSize,
- pData->bCreateDirs ? "yes" : "no",
- pData->fileUID, pData->fileGID,
- pData->dirUID, pData->dirGID,
- pData->bFailOnChown ? "yes" : "no"
- );
+ dbgprintf("[dynamic]\n");
} else { /* regular file */
- dbgprintf("%s", pData->f_fname);
- if (pData->fd == -1)
- dbgprintf(" (unused)");
+ dbgprintf("%s%s\n", pData->f_fname,
+ (pData->pStrm == NULL) ? " (unused)" : "");
}
+
+ dbgprintf("\ttemplate='%s'\n", pData->f_fname);
+ dbgprintf("\tuse async writer=%d\n", pData->bUseAsyncWriter);
+ dbgprintf("\tflush on TX end=%d\n", pData->bFlushOnTXEnd);
+ dbgprintf("\tflush interval=%d\n", pData->iFlushInterval);
+ dbgprintf("\tfile cache size=%d\n", pData->iDynaFileCacheSize);
+ dbgprintf("\tcreate directories: %s\n", pData->bCreateDirs ? "yes" : "no");
+ dbgprintf("\tfile owner %d, group %d\n", pData->fileUID, pData->fileGID);
+ dbgprintf("\tdirectory owner %d, group %d\n", pData->dirUID, pData->dirGID);
+ dbgprintf("\tdir create mode 0%3.3o, file create mode 0%3.3o\n",
+ pData->fDirCreateMode, pData->fCreateMode);
+ dbgprintf("\tfail if owner/group can not be set: %s\n", pData->bFailOnChown ? "yes" : "no");
ENDdbgPrintInstInfo
@@ -164,17 +186,17 @@ rsRetVal setDynaFileCacheSize(void __attribute__((unused)) *pVal, int iNewVal)
errmsg.LogError(0, RS_RET_VAL_OUT_OF_RANGE, "%s", errMsg);
iRet = RS_RET_VAL_OUT_OF_RANGE;
iNewVal = 1;
- } else if(iNewVal > 10000) {
+ } else if(iNewVal > 1000) {
snprintf((char*) errMsg, sizeof(errMsg)/sizeof(uchar),
- "DynaFileCacheSize maximum is 10,000 (%d given), changed to 10,000.", iNewVal);
+ "DynaFileCacheSize maximum is 1,000 (%d given), changed to 1,000.", iNewVal);
errno = 0;
errmsg.LogError(0, RS_RET_VAL_OUT_OF_RANGE, "%s", errMsg);
iRet = RS_RET_VAL_OUT_OF_RANGE;
- iNewVal = 10000;
+ iNewVal = 1000;
}
iDynaFileCacheSize = iNewVal;
- dbgprintf("DynaFileCacheSize changed to %d.\n", iNewVal);
+ DBGPRINTF("DynaFileCacheSize changed to %d.\n", iNewVal);
RETiRet;
}
@@ -195,14 +217,6 @@ static rsRetVal cflineParseOutchannel(instanceData *pData, uchar* p, omodStringR
struct outchannel *pOch;
char szBuf[128]; /* should be more than sufficient */
- /* this must always be a file, because we can not set a size limit
- * on a pipe...
- * rgerhards 2005-06-21: later, this will be a separate type, but let's
- * emulate things for the time being. When everything runs, we can
- * extend it...
- */
- pData->fileType = eTypeFILE;
-
++p; /* skip '$' */
i = 0;
/* get outchannel name */
@@ -237,80 +251,21 @@ static rsRetVal cflineParseOutchannel(instanceData *pData, uchar* p, omodStringR
}
/* OK, we finally got a correct template. So let's use it... */
- strncpy((char*) pData->f_fname, (char*) pOch->pszFileTemplate, MAXFNAME);
- pData->f_sizeLimit = pOch->uSizeLimit;
+ ustrncpy(pData->f_fname, pOch->pszFileTemplate, MAXFNAME);
+ pData->iSizeLimit = pOch->uSizeLimit;
/* WARNING: It is dangerous "just" to pass the pointer. As we
* never rebuild the output channel description, this is acceptable here.
*/
- pData->f_sizeLimitCmd = (char*) pOch->cmdOnSizeLimit;
+ pData->pszSizeLimitCmd = pOch->cmdOnSizeLimit;
-RUNLOG_VAR("%p", pszTplName);
iRet = cflineParseTemplateName(&p, pOMSR, iEntry, iTplOpts,
- (pszTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszTplName);
+ (pszFileDfltTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszFileDfltTplName);
finalize_it:
RETiRet;
}
-/* rgerhards 2005-06-21: Try to resolve a size limit
- * situation. This first runs the command, and then
- * checks if we are still above the treshold.
- * returns 0 if ok, 1 otherwise
- * TODO: consider moving the initial check in here, too
- */
-int resolveFileSizeLimit(instanceData *pData)
-{
- uchar *pParams;
- uchar *pCmd;
- uchar *p;
- off_t actualFileSize;
- ASSERT(pData != NULL);
-
- if(pData->f_sizeLimitCmd == NULL)
- return 1; /* nothing we can do in this case... */
-
- /* the execProg() below is probably not great, but at least is is
- * fairly secure now. Once we change the way file size limits are
- * handled, we should also revisit how this command is run (and
- * with which parameters). rgerhards, 2007-07-20
- */
- /* we first check if we have command line parameters. We assume this,
- * when we have a space in the program name. If we find it, everything after
- * the space is treated as a single argument.
- */
- if((pCmd = (uchar*)strdup((char*)pData->f_sizeLimitCmd)) == NULL) {
- /* there is not much we can do - we make syslogd close the file in this case */
- return 1;
- }
-
- for(p = pCmd ; *p && *p != ' ' ; ++p) {
- /* JUST SKIP */
- }
-
- if(*p == ' ') {
- *p = '\0'; /* pretend string-end */
- pParams = p+1;
- } else
- pParams = NULL;
-
- execProg(pCmd, 1, pParams);
-
- free(pCmd);
-
- pData->fd = open((char*) pData->f_fname, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY,
- pData->fCreateMode);
-
- actualFileSize = lseek(pData->fd, 0, SEEK_END);
- if(actualFileSize >= pData->f_sizeLimit) {
- /* OK, it didn't work out... */
- return 1;
- }
-
- return 0;
-}
-
-
/* This function deletes an entry from the dynamic file name
* cache. A pointer to the cache must be passed in as well
* as the index of the to-be-deleted entry. This index may
@@ -318,40 +273,45 @@ int resolveFileSizeLimit(instanceData *pData)
* function immediately returns. Parameter bFreeEntry is 1
* if the entry should be d_free()ed and 0 if not.
*/
-static void dynaFileDelCacheEntry(dynaFileCacheEntry **pCache, int iEntry, int bFreeEntry)
+static rsRetVal
+dynaFileDelCacheEntry(dynaFileCacheEntry **pCache, int iEntry, int bFreeEntry)
{
+ DEFiRet;
ASSERT(pCache != NULL);
- BEGINfunc;
-
if(pCache[iEntry] == NULL)
FINALIZE;
- dbgprintf("Removed entry %d for file '%s' from dynaCache.\n", iEntry,
- pCache[iEntry]->pName == NULL ? "[OPEN FAILED]" : (char*)pCache[iEntry]->pName);
- /* if the name is NULL, this is an improperly initilized entry which
- * needs to be discarded. In this case, neither the file is to be closed
- * not the name to be freed.
- */
+ DBGPRINTF("Removed entry %d for file '%s' from dynaCache.\n", iEntry,
+ pCache[iEntry]->pName == NULL ? UCHAR_CONSTANT("[OPEN FAILED]") : pCache[iEntry]->pName);
+
if(pCache[iEntry]->pName != NULL) {
- close(pCache[iEntry]->fd);
d_free(pCache[iEntry]->pName);
pCache[iEntry]->pName = NULL;
}
+ if(pCache[iEntry]->pStrm != NULL) {
+ strm.Destruct(&pCache[iEntry]->pStrm);
+ if(pCache[iEntry]->pStrm != NULL) /* safety check -- TODO: remove if no longer necessary */
+ abort();
+ }
+
if(bFreeEntry) {
d_free(pCache[iEntry]);
pCache[iEntry] = NULL;
}
finalize_it:
- ENDfunc;
+ RETiRet;
}
-/* This function frees the dynamic file name cache.
+/* This function frees all dynamic file name cache entries and closes the
+ * relevant files. Part of Shutdown and HUP processing.
+ * rgerhards, 2008-10-23
*/
-static void dynaFileFreeCache(instanceData *pData)
+static inline void
+dynaFileFreeCacheEntries(instanceData *pData)
{
register int i;
ASSERT(pData != NULL);
@@ -360,50 +320,64 @@ static void dynaFileFreeCache(instanceData *pData)
for(i = 0 ; i < pData->iCurrCacheSize ; ++i) {
dynaFileDelCacheEntry(pData->dynCache, i, 1);
}
+ pData->iCurrElt = -1; /* invalidate current element */
+ ENDfunc;
+}
+
+/* This function frees the dynamic file name cache.
+ */
+static void dynaFileFreeCache(instanceData *pData)
+{
+ ASSERT(pData != NULL);
+
+ BEGINfunc;
+ dynaFileFreeCacheEntries(pData);
if(pData->dynCache != NULL)
d_free(pData->dynCache);
ENDfunc;
}
-/* This is a shared code for both static and dynamic files.
+/* This is now shared code for all types of files. It simply prepares
+ * file access, which, among others, means the the file wil be opened
+ * and any directories in between will be created (based on config, of
+ * course). -- rgerhards, 2008-10-22
+ * changed to iRet interface - 2009-03-19
*/
-static void prepareFile(instanceData *pData, uchar *newFileName)
+static rsRetVal
+prepareFile(instanceData *pData, uchar *newFileName)
{
- if(access((char*)newFileName, F_OK) == 0) {
- /* file already exists */
- pData->fd = open((char*) newFileName, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY,
- pData->fCreateMode);
- } else {
- pData->fd = -1;
+ int fd;
+ DEFiRet;
+
+ if(access((char*)newFileName, F_OK) != 0) {
/* file does not exist, create it (and eventually parent directories */
if(pData->bCreateDirs) {
- /* we fist need to create parent dirs if they are missing
+ /* We first need to create parent dirs if they are missing.
* We do not report any errors here ourselfs but let the code
* fall through to error handler below.
*/
- if(makeFileParentDirs(newFileName, strlen((char*)newFileName),
+ if(makeFileParentDirs(newFileName, ustrlen(newFileName),
pData->fDirCreateMode, pData->dirUID,
pData->dirGID, pData->bFailOnChown) != 0) {
- return; /* we give up */
+ ABORT_FINALIZE(RS_RET_ERR); /* we give up */
}
}
/* no matter if we needed to create directories or not, we now try to create
* the file. -- rgerhards, 2008-12-18 (based on patch from William Tisater)
*/
- pData->fd = open((char*) newFileName, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY,
+ fd = open((char*) newFileName, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY|O_CLOEXEC,
pData->fCreateMode);
- if(pData->fd != -1) {
+ if(fd != -1) {
/* check and set uid/gid */
if(pData->fileUID != (uid_t)-1 || pData->fileGID != (gid_t) -1) {
/* we need to set owner/group */
- if(fchown(pData->fd, pData->fileUID,
- pData->fileGID) != 0) {
+ if(fchown(fd, pData->fileUID, pData->fileGID) != 0) {
if(pData->bFailOnChown) {
int eSave = errno;
- close(pData->fd);
- pData->fd = -1;
+ close(fd);
+ fd = -1;
errno = eSave;
}
/* we will silently ignore the chown() failure
@@ -411,8 +385,43 @@ static void prepareFile(instanceData *pData, uchar *newFileName)
*/
}
}
+ close(fd); /* close again, as we need a stream further on */
}
}
+
+ /* the copies below are clumpsy, but there is no way around given the
+ * anomalies in dirname() and basename() [they MODIFY the provided buffer...]
+ */
+ uchar szNameBuf[MAXFNAME];
+ uchar szDirName[MAXFNAME];
+ uchar szBaseName[MAXFNAME];
+ ustrncpy(szNameBuf, newFileName, MAXFNAME);
+ ustrncpy(szDirName, (uchar*)dirname((char*)szNameBuf), MAXFNAME);
+ ustrncpy(szNameBuf, newFileName, MAXFNAME);
+ ustrncpy(szBaseName, (uchar*)basename((char*)szNameBuf), MAXFNAME);
+
+ CHKiRet(strm.Construct(&pData->pStrm));
+ CHKiRet(strm.SetFName(pData->pStrm, szBaseName, ustrlen(szBaseName)));
+ CHKiRet(strm.SetDir(pData->pStrm, szDirName, ustrlen(szDirName)));
+ CHKiRet(strm.SetiZipLevel(pData->pStrm, pData->iZipLevel));
+ CHKiRet(strm.SetsIOBufSize(pData->pStrm, (size_t) pData->iIOBufSize));
+ CHKiRet(strm.SettOperationsMode(pData->pStrm, STREAMMODE_WRITE_APPEND));
+ CHKiRet(strm.SettOpenMode(pData->pStrm, fCreateMode));
+ CHKiRet(strm.SetbSync(pData->pStrm, pData->bSyncFile));
+ CHKiRet(strm.SetsType(pData->pStrm, pData->strmType));
+ CHKiRet(strm.SetiSizeLimit(pData->pStrm, pData->iSizeLimit));
+ /* set the flush interval only if we actually use it - otherwise it will activate
+ * async processing, which is a real performance waste if we do not do buffered
+ * writes! -- rgerhards, 2009-07-06
+ */
+ if(pData->bUseAsyncWriter)
+ CHKiRet(strm.SetiFlushInterval(pData->pStrm, pData->iFlushInterval));
+ if(pData->pszSizeLimitCmd != NULL)
+ CHKiRet(strm.SetpszSizeLimitCmd(pData->pStrm, ustrdup(pData->pszSizeLimitCmd)));
+ CHKiRet(strm.ConstructFinalize(pData->pStrm));
+
+finalize_it:
+ RETiRet;
}
@@ -424,13 +433,16 @@ static void prepareFile(instanceData *pData, uchar *newFileName)
* be written.
* This is a helper to writeFile(). rgerhards, 2007-07-03
*/
-static int prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts)
+static inline rsRetVal
+prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts)
{
time_t ttOldest; /* timestamp of oldest element */
int iOldest;
int i;
int iFirstFree;
+ rsRetVal localRet;
dynaFileCacheEntry **pCache;
+ DEFiRet;
ASSERT(pData != NULL);
ASSERT(newFileName != NULL);
@@ -441,29 +453,31 @@ static int prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsg
* I *hope* this will be a performance enhancement.
*/
if( (pData->iCurrElt != -1)
- && !strcmp((char*) newFileName, (char*) pCache[pData->iCurrElt]->pName)) {
+ && !ustrcmp(newFileName, pCache[pData->iCurrElt]->pName)) {
/* great, we are all set */
- pCache[pData->iCurrElt]->lastUsed = time(NULL); /* update timestamp for LRU */
- return 0;
+ pCache[pData->iCurrElt]->lastUsed = time(NULL); /* update timestamp for LRU */ // TODO: optimize time call!
+ // LRU needs only a strictly monotonically increasing counter, so such a one could do
+ FINALIZE;
}
/* ok, no luck. Now let's search the table if we find a matching spot.
* While doing so, we also prepare for creation of a new one.
*/
+ pData->iCurrElt = -1; /* invalid current element pointer */
iFirstFree = -1; /* not yet found */
iOldest = 0; /* we assume the first element to be the oldest - that will change as we loop */
ttOldest = time(NULL) + 1; /* there must always be an older one */
for(i = 0 ; i < pData->iCurrCacheSize ; ++i) {
- if(pCache[i] == NULL) {
+ if(pCache[i] == NULL || pCache[i]->pName == NULL) {
if(iFirstFree == -1)
iFirstFree = i;
} else { /* got an element, let's see if it matches */
- if(!strcmp((char*) newFileName, (char*) pCache[i]->pName)) {
+ if(!ustrcmp(newFileName, pCache[i]->pName)) { // RG: name == NULL?
/* we found our element! */
- pData->fd = pCache[i]->fd;
+ pData->pStrm = pCache[i]->pStrm;
pData->iCurrElt = i;
pCache[i]->lastUsed = time(NULL); /* update timestamp for LRU */
- return 0;
+ FINALIZE;
}
/* did not find it - so lets keep track of the counters for LRU */
if(pCache[i]->lastUsed < ttOldest) {
@@ -474,49 +488,92 @@ static int prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsg
}
/* we have not found an entry */
+
+ /* invalidate iCurrElt as we may error-exit out of this function when the currrent
+ * iCurrElt has been freed or otherwise become unusable. This is a precaution, and
+ * performance-wise it may be better to do that in each of the exits. However, that
+ * is error-prone, so I prefer to do it here. -- rgerhards, 2010-03-02
+ */
+ pData->iCurrElt = -1;
+ /* similarly, we need to set the current pStrm to NULL, because otherwise, if prepareFile() fails,
+ * we may end up using an old stream. This bug depends on how exactly prepareFile fails,
+ * but it* could be triggered in the common case of a failed open() system call.
+ * rgerhards, 2010-03-22
+ */
+ pData->pStrm = NULL;
+
if(iFirstFree == -1 && (pData->iCurrCacheSize < pData->iDynaFileCacheSize)) {
/* there is space left, so set it to that index */
iFirstFree = pData->iCurrCacheSize++;
}
+// RG: this is the begin of a potential problem area
+ /* Note that the following code sequence does not work with the cache entry itself,
+ * but rather with pData->pStrm, the (sole) stream pointer in the non-dynafile case.
+ * The cache array is only updated after the open was successful. -- rgerhards, 2010-03-21
+ */
if(iFirstFree == -1) {
dynaFileDelCacheEntry(pCache, iOldest, 0);
iFirstFree = iOldest; /* this one *is* now free ;) */
} else {
/* we need to allocate memory for the cache structure */
- pCache[iFirstFree] = (dynaFileCacheEntry*) calloc(1, sizeof(dynaFileCacheEntry));
- if(pCache[iFirstFree] == NULL) {
- dbgprintf("prepareDynfile(): could not alloc mem, discarding this request\n");
- return -1;
- }
+ /* TODO: performance note: we could alloc all entries on startup, thus saving malloc
+ * overhead -- this may be something to consider in v5...
+ */
+ CHKmalloc(pCache[iFirstFree] = (dynaFileCacheEntry*) calloc(1, sizeof(dynaFileCacheEntry)));
}
/* Ok, we finally can open the file */
- prepareFile(pData, newFileName);
+ localRet = prepareFile(pData, newFileName); /* ignore exact error, we check fd below */
- /* file is either open now or an error state set */
- if(pData->fd == -1) {
+ /* file is either open now or an error state set */ // RG: better check localRet?
+ if(pData->pStrm == NULL) {
/* do not report anything if the message is an internally-generated
* message. Otherwise, we could run into a never-ending loop. The bad
* news is that we also lose errors on startup messages, but so it is.
*/
- if(iMsgOpts & INTERNAL_MSG)
- dbgprintf("Could not open dynaFile, discarding message\n");
- else
- errmsg.LogError(0, NO_ERRCODE, "Could not open dynamic file '%s' - discarding message", (char*)newFileName);
- dynaFileDelCacheEntry(pCache, iFirstFree, 1);
- pData->iCurrElt = -1;
- return -1;
+ if(iMsgOpts & INTERNAL_MSG) {
+ DBGPRINTF("Could not open dynaFile, discarding message\n");
+ } else {
+ errmsg.LogError(0, NO_ERRCODE, "Could not open dynamic file '%s' - discarding message", newFileName);
+ }
+ ABORT_FINALIZE(localRet);
}
- pCache[iFirstFree]->fd = pData->fd;
- pCache[iFirstFree]->pName = (uchar*)strdup((char*)newFileName); /* TODO: check for NULL (very unlikely) */
+ if((pCache[iFirstFree]->pName = ustrdup(newFileName)) == NULL) {
+ strm.Destruct(&pData->pStrm); /* need to free failed entry! */
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ }
+ pCache[iFirstFree]->pStrm = pData->pStrm;
pCache[iFirstFree]->lastUsed = time(NULL);
pData->iCurrElt = iFirstFree;
- dbgprintf("Added new entry %d for file cache, file '%s'.\n",
- iFirstFree, newFileName);
+ DBGPRINTF("Added new entry %d for file cache, file '%s'.\n", iFirstFree, newFileName);
- return 0;
+finalize_it:
+ RETiRet;
+}
+
+
+/* do the actual write process. This function is to be called once we are ready for writing.
+ * It will do buffered writes and persist data only when the buffer is full. Note that we must
+ * be careful to detect when the file handle changed.
+ * rgerhards, 2009-06-03
+ */
+static rsRetVal
+doWrite(instanceData *pData, uchar *pszBuf, int lenBuf)
+{
+ DEFiRet;
+ ASSERT(pData != NULL);
+ ASSERT(pszBuf != NULL);
+
+dbgprintf("doWrite, pData->pStrm %p, lenBuf %d\n", pData->pStrm, lenBuf);
+ if(pData->pStrm != NULL){
+ CHKiRet(strm.Write(pData->pStrm, pszBuf, lenBuf));
+ FINALIZE;
+ }
+
+finalize_it:
+ RETiRet;
}
@@ -524,9 +581,9 @@ static int prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsg
* will be called for all outputs using file semantics,
* for example also for pipes.
*/
-static rsRetVal writeFile(uchar **ppString, unsigned iMsgOpts, instanceData *pData)
+static rsRetVal
+writeFile(uchar **ppString, unsigned iMsgOpts, instanceData *pData)
{
- off_t actualFileSize;
DEFiRet;
ASSERT(pData != NULL);
@@ -535,94 +592,30 @@ static rsRetVal writeFile(uchar **ppString, unsigned iMsgOpts, instanceData *pDa
* check if it still is ok or a new file needs to be created
*/
if(pData->bDynamicName) {
- if(prepareDynFile(pData, ppString[1], iMsgOpts) != 0)
- ABORT_FINALIZE(RS_RET_ERR);
- }
-
- /* create the message based on format specified */
-again:
- /* check if we have a file size limit and, if so,
- * obey to it.
- */
- if(pData->f_sizeLimit != 0) {
- actualFileSize = lseek(pData->fd, 0, SEEK_END);
- if(actualFileSize >= pData->f_sizeLimit) {
- char errMsg[256];
- /* for now, we simply disable a file once it is
- * beyond the maximum size. This is better than having
- * us aborted by the OS... rgerhards 2005-06-21
- */
- (void) close(pData->fd);
- /* try to resolve the situation */
- if(resolveFileSizeLimit(pData) != 0) {
- /* didn't work out, so disable... */
- snprintf(errMsg, sizeof(errMsg),
- "no longer writing to file %s; grown beyond configured file size of %lld bytes, actual size %lld - configured command did not resolve situation",
- pData->f_fname, (long long) pData->f_sizeLimit, (long long) actualFileSize);
- errno = 0;
- errmsg.LogError(0, RS_RET_DISABLE_ACTION, "%s", errMsg);
- ABORT_FINALIZE(RS_RET_DISABLE_ACTION);
- } else {
- snprintf(errMsg, sizeof(errMsg),
- "file %s had grown beyond configured file size of %lld bytes, actual size was %lld - configured command resolved situation",
- pData->f_fname, (long long) pData->f_sizeLimit, (long long) actualFileSize);
- errno = 0;
- errmsg.LogError(0, NO_ERRCODE, "%s", errMsg);
- }
+ CHKiRet(prepareDynFile(pData, ppString[1], iMsgOpts));
+ } else { /* "regular", non-dynafile */
+ if(pData->pStrm == NULL) {
+ CHKiRet(prepareFile(pData, pData->f_fname));
}
}
- if (write(pData->fd, ppString[0], strlen((char*)ppString[0])) < 0) {
- int e = errno;
-
- /* If a named pipe is full, just ignore it for now
- - mrn 24 May 96 */
- if (pData->fileType == eTypePIPE && e == EAGAIN)
- ABORT_FINALIZE(RS_RET_OK);
-
- /* If the filesystem is filled up, just ignore
- * it for now and continue writing when possible
- * based on patch for sysklogd by Martin Schulze on 2007-05-24
- */
- if (pData->fileType == eTypeFILE && e == ENOSPC)
- ABORT_FINALIZE(RS_RET_OK);
-
- (void) close(pData->fd);
- /*
- * Check for EBADF on TTY's due to vhangup()
- * Linux uses EIO instead (mrn 12 May 96)
- */
- if ((pData->fileType == eTypeTTY || pData->fileType == eTypeCONSOLE)
-#ifdef linux
- && e == EIO) {
-#else
- && e == EBADF) {
-#endif
- pData->fd = open((char*) pData->f_fname, O_WRONLY|O_APPEND|O_NOCTTY);
- if (pData->fd < 0) {
- iRet = RS_RET_DISABLE_ACTION;
- errmsg.LogError(0, NO_ERRCODE, "%s", pData->f_fname);
- } else {
- untty();
- goto again;
- }
- } else {
- iRet = RS_RET_DISABLE_ACTION;
- errno = e;
- errmsg.LogError(0, NO_ERRCODE, "%s", pData->f_fname);
- }
- } else if (pData->bSyncFile) {
- fsync(pData->fd);
- }
+ CHKiRet(doWrite(pData, ppString[0], strlen(CHAR_CONVERT(ppString[0]))));
finalize_it:
+ if(iRet != RS_RET_OK) {
+ /* in v5, we shall return different states for message-caused failure (but only there!) */
+ if(pData->strmType == STREAMTYPE_NAMED_PIPE)
+ iRet = RS_RET_DISABLE_ACTION; /* this is the traditional semantic -- rgerhards, 2010-01-15 */
+ else
+ iRet = RS_RET_SUSPENDED;
+ }
RETiRet;
}
BEGINcreateInstance
CODESTARTcreateInstance
- pData->fd = -1;
+ pData->pStrm = NULL;
ENDcreateInstance
@@ -630,8 +623,8 @@ BEGINfreeInstance
CODESTARTfreeInstance
if(pData->bDynamicName) {
dynaFileFreeCache(pData);
- } else if(pData->fd != -1)
- close(pData->fd);
+ } else if(pData->pStrm != NULL)
+ strm.Destruct(&pData->pStrm);
ENDfreeInstance
@@ -641,45 +634,32 @@ ENDtryResume
BEGINdoAction
CODESTARTdoAction
- dbgprintf(" (%s)\n", pData->f_fname);
- /* pData->fd == -1 is an indicator that the we couldn't
- * open the file at startup. For dynaFiles, this is ok,
- * all others are doomed.
- */
- if(pData->bDynamicName || (pData->fd != -1))
- iRet = writeFile(ppString, iMsgOpts, pData);
+ DBGPRINTF("file to log to: %s\n", pData->f_fname);
+ CHKiRet(writeFile(ppString, iMsgOpts, pData));
+ if(pData->bFlushOnTXEnd) {
+ /* TODO v5: do this in endTransaction only! */
+ CHKiRet(strm.Flush(pData->pStrm));
+ }
+finalize_it:
ENDdoAction
BEGINparseSelectorAct
CODESTARTparseSelectorAct
- /* yes, the if below is redundant, but I need it now. Will go away as
- * the code further changes. -- rgerhards, 2007-07-25
- */
- if(*p == '$' || *p == '?' || *p == '|' || *p == '/' || *p == '-') {
- if((iRet = createInstance(&pData)) != RS_RET_OK) {
- ENDfunc
- return iRet; /* this can not use RET_iRet! */
- }
- } else {
- /* this is not clean, but we need it for the time being
- * TODO: remove when cleaning up modularization
- */
- ENDfunc
- return RS_RET_CONFLINE_UNPROCESSED;
- }
+ if(!(*p == '$' || *p == '?' || *p == '/' || *p == '.' || *p == '-'))
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+
+ CHKiRet(createInstance(&pData));
if(*p == '-') {
pData->bSyncFile = 0;
p++;
} else {
- pData->bSyncFile = bEnableSync ? 1 : 0;
+ pData->bSyncFile = bEnableSync;
}
+ pData->iSizeLimit = 0; /* default value, use outchannels to configure! */
- pData->f_sizeLimit = 0; /* default value, use outchannels to configure! */
-
- switch (*p)
- {
+ switch(*p) {
case '$':
CODE_STD_STRING_REQUESTparseSelectorAct(1)
/* rgerhards 2005-06-21: this is a special setting for output-channel
@@ -689,13 +669,8 @@ CODESTARTparseSelectorAct
* rgerhards, 2007-07-24: output-channels will go away. We keep them
* for compatibility reasons, but seems to have been a bad idea.
*/
- if((iRet = cflineParseOutchannel(pData, p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS)) == RS_RET_OK) {
- pData->bDynamicName = 0;
- pData->fCreateMode = fCreateMode; /* preserve current setting */
- pData->fDirCreateMode = fDirCreateMode; /* preserve current setting */
- pData->fd = open((char*) pData->f_fname, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY,
- pData->fCreateMode);
- }
+ CHKiRet(cflineParseOutchannel(pData, p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS));
+ pData->bDynamicName = 0;
break;
case '?': /* This is much like a regular file handle, but we need to obtain
@@ -703,89 +678,71 @@ CODESTARTparseSelectorAct
*/
CODE_STD_STRING_REQUESTparseSelectorAct(2)
++p; /* eat '?' */
- if((iRet = cflineParseFileName(p, (uchar*) pData->f_fname, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
- (pszTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszTplName))
- != RS_RET_OK)
- break;
+ CHKiRet(cflineParseFileName(p, (uchar*) pData->f_fname, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
+ (pszFileDfltTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszFileDfltTplName));
/* "filename" is actually a template name, we need this as string 1. So let's add it
* to the pOMSR. -- rgerhards, 2007-07-27
*/
- if((iRet = OMSRsetEntry(*ppOMSR, 1, (uchar*)strdup((char*) pData->f_fname), OMSR_NO_RQD_TPL_OPTS)) != RS_RET_OK)
- break;
+ CHKiRet(OMSRsetEntry(*ppOMSR, 1, ustrdup(pData->f_fname), OMSR_NO_RQD_TPL_OPTS));
pData->bDynamicName = 1;
pData->iCurrElt = -1; /* no current element */
- pData->fCreateMode = fCreateMode; /* freeze current setting */
- pData->fDirCreateMode = fDirCreateMode; /* preserve current setting */
- pData->bCreateDirs = bCreateDirs;
- pData->bFailOnChown = bFailOnChown;
- pData->fileUID = fileUID;
- pData->fileGID = fileGID;
- pData->dirUID = dirUID;
- pData->dirGID = dirGID;
- pData->iDynaFileCacheSize = iDynaFileCacheSize; /* freeze current setting */
- /* we now allocate the cache table. We use calloc() intentionally, as we
- * need all pointers to be initialized to NULL pointers.
- */
- if((pData->dynCache = (dynaFileCacheEntry**)
- calloc(iDynaFileCacheSize, sizeof(dynaFileCacheEntry*))) == NULL) {
- iRet = RS_RET_OUT_OF_MEMORY;
- dbgprintf("Could not allocate memory for dynaFileCache - selector disabled.\n");
- }
+ /* we now allocate the cache table */
+ CHKmalloc(pData->dynCache = (dynaFileCacheEntry**)
+ calloc(iDynaFileCacheSize, sizeof(dynaFileCacheEntry*)));
break;
- case '|':
+ /* case '|': while pipe support has been removed, I leave the code in in case we
+ * need high-performance pipes at a later stage (unlikely). -- rgerhards, 2010-02-28
+ */
case '/':
+ case '.':
CODE_STD_STRING_REQUESTparseSelectorAct(1)
- /* rgerhards, 2007-0726: first check if file or pipe */
+ /* we now have *almost* the same semantics for files and pipes, but we still need
+ * to know we deal with a pipe, because we must do non-blocking opens in that case
+ * (to keep consistent with traditional semantics and prevent rsyslog from hanging).
+ */
if(*p == '|') {
- pData->fileType = eTypePIPE;
++p;
+ pData->strmType = STREAMTYPE_NAMED_PIPE;
} else {
- pData->fileType = eTypeFILE;
+ pData->strmType = STREAMTYPE_FILE_SINGLE;
}
- /* rgerhards 2004-11-17: from now, we need to have different
- * processing, because after the first comma, the template name
- * to use is specified. So we need to scan for the first coma first
- * and then look at the rest of the line.
- */
- if((iRet = cflineParseFileName(p, (uchar*) pData->f_fname, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
- (pszTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszTplName))
- != RS_RET_OK)
- break;
+ CHKiRet(cflineParseFileName(p, (uchar*) pData->f_fname, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
+ (pszFileDfltTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszFileDfltTplName));
pData->bDynamicName = 0;
- pData->fCreateMode = fCreateMode; /* preserve current setting */
- pData->fDirCreateMode = fDirCreateMode;
- pData->bCreateDirs = bCreateDirs;
- pData->bFailOnChown = bFailOnChown;
- pData->fileUID = fileUID;
- pData->fileGID = fileGID;
- pData->dirUID = dirUID;
- pData->dirGID = dirGID;
-
- if(pData->fileType == eTypePIPE) {
- pData->fd = open((char*) pData->f_fname, O_RDWR|O_NONBLOCK);
- } else {
- prepareFile(pData, pData->f_fname);
- }
-
- if ( pData->fd < 0 ){
- pData->fd = -1;
- dbgprintf("Error opening log file: %s\n", pData->f_fname);
- errmsg.LogError(0, NO_ERRCODE, "%s", pData->f_fname);
- break;
- }
- if (isatty(pData->fd)) {
- pData->fileType = eTypeTTY;
- untty();
- }
- if (strcmp((char*) p, _PATH_CONSOLE) == 0)
- pData->fileType = eTypeCONSOLE;
break;
default:
- iRet = RS_RET_CONFLINE_UNPROCESSED;
- break;
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+ }
+
+ /* freeze current paremeters for this action */
+ pData->iDynaFileCacheSize = iDynaFileCacheSize;
+ pData->fCreateMode = fCreateMode;
+ pData->fDirCreateMode = fDirCreateMode;
+ pData->bCreateDirs = bCreateDirs;
+ pData->bFailOnChown = bFailOnChown;
+ pData->fileUID = fileUID;
+ pData->fileGID = fileGID;
+ pData->dirUID = dirUID;
+ pData->dirGID = dirGID;
+ pData->iZipLevel = iZipLevel;
+ pData->bFlushOnTXEnd = bFlushOnTXEnd;
+ pData->iIOBufSize = (int) iIOBufSize;
+ pData->iFlushInterval = iFlushInterval;
+ pData->bUseAsyncWriter = bUseAsyncWriter;
+
+ if(pData->bDynamicName == 0) {
+ /* try open and emit error message if not possible. At this stage, we ignore the
+ * return value of prepareFile, this is taken care of in later steps.
+ */
+ prepareFile(pData, pData->f_fname);
+
+ if(pData->pStrm == NULL) {
+ DBGPRINTF("Error opening log file: %s\n", pData->f_fname);
+ errmsg.LogError(0, RS_RET_NO_FILE_ACCESS, "Could no open output file '%s'", pData->f_fname);
+ }
}
CODE_STD_FINALIZERparseSelectorAct
ENDparseSelectorAct
@@ -806,25 +763,45 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
fDirCreateMode = 0700;
bCreateDirs = 1;
bEnableSync = 0;
- if(pszTplName != NULL) {
- free(pszTplName);
- pszTplName = NULL;
+ iZipLevel = 0;
+ bFlushOnTXEnd = FLUSHONTX_DFLT;
+ iIOBufSize = IOBUF_DFLT_SIZE;
+ iFlushInterval = FLUSH_INTRVL_DFLT;
+ bUseAsyncWriter = USE_ASYNCWRITER_DFLT;
+ if(pszFileDfltTplName != NULL) {
+ free(pszFileDfltTplName);
+ pszFileDfltTplName = NULL;
}
return RS_RET_OK;
}
+BEGINdoHUP
+CODESTARTdoHUP
+ if(pData->bDynamicName) {
+ dynaFileFreeCacheEntries(pData);
+ } else {
+ if(pData->pStrm != NULL) {
+ strm.Destruct(&pData->pStrm);
+ pData->pStrm = NULL;
+ }
+ }
+ENDdoHUP
+
+
BEGINmodExit
CODESTARTmodExit
- if(pszTplName != NULL)
- free(pszTplName);
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(strm, CORE_COMPONENT);
+ free(pszFileDfltTplName);
ENDmodExit
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_OMOD_QUERIES
+CODEqueryEtryPt_doHUP
ENDqueryEtryPt
@@ -833,7 +810,13 @@ CODESTARTmodInit
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(strm, CORE_COMPONENT));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"dynafilecachesize", 0, eCmdHdlrInt, (void*) setDynaFileCacheSize, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileziplevel", 0, eCmdHdlrInt, NULL, &iZipLevel, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileflushinterval", 0, eCmdHdlrInt, NULL, &iFlushInterval, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileasyncwriting", 0, eCmdHdlrBinary, NULL, &bUseAsyncWriter, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileflushontxend", 0, eCmdHdlrBinary, NULL, &bFlushOnTXEnd, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileiobuffersize", 0, eCmdHdlrSize, NULL, &iIOBufSize, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"dirowner", 0, eCmdHdlrUID, NULL, &dirUID, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"dirgroup", 0, eCmdHdlrGID, NULL, &dirGID, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"fileowner", 0, eCmdHdlrUID, NULL, &fileUID, STD_LOADABLE_MODULE_ID));
@@ -843,7 +826,7 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(omsdRegCFSLineHdlr((uchar *)"createdirs", 0, eCmdHdlrBinary, NULL, &bCreateDirs, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"failonchownfailure", 0, eCmdHdlrBinary, NULL, &bFailOnChown, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionfileenablesync", 0, eCmdHdlrBinary, NULL, &bEnableSync, STD_LOADABLE_MODULE_ID));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionfiledefaulttemplate", 0, eCmdHdlrGetWord, NULL, &pszTplName, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionfiledefaulttemplate", 0, eCmdHdlrGetWord, NULL, &pszFileDfltTplName, NULL));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
/* vi:set ai:
diff --git a/tools/omfile.h b/tools/omfile.h
index 03e081f3..8dca6a88 100644
--- a/tools/omfile.h
+++ b/tools/omfile.h
@@ -3,7 +3,7 @@
*
* File begun on 2007-07-21 by RGerhards (extracted from syslogd.c)
*
- * Copyright 2007 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007-2010 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -28,7 +28,11 @@
/* prototypes */
rsRetVal modInitFile(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*);
+/* the define below is dirty, but we need it for ompipe integration. There is no
+ * other way to have the functionality (well, one way would be to go through the
+ * globals, but that seems not yet justified. -- rgerhards, 2010-03-01
+ */
+uchar *pszFileDfltTplName;
#endif /* #ifndef OMFILE_H_INCLUDED */
-/*
- * vi:set ai:
+/* vi:set ai:
*/
diff --git a/tools/omfwd.c b/tools/omfwd.c
index eb023344..aadeec6a 100644
--- a/tools/omfwd.c
+++ b/tools/omfwd.c
@@ -48,6 +48,7 @@
#endif
#include <pthread.h>
#include "syslogd.h"
+#include "conf.h"
#include "syslogd-types.h"
#include "srUtils.h"
#include "net.h"
@@ -82,12 +83,15 @@ typedef struct _instanceData {
permittedPeers_t *pPermPeers;
int iStrmDrvrMode;
char *f_hname;
- int *pSockArray; /* sockets to use for UDP */
+ int *pSockArray; /* sockets to use for UDP */
int bIsConnected; /* are we connected to remote host? 0 - no, 1 - yes, UDP means addr resolved */
struct addrinfo *f_addr;
- int compressionLevel; /* 0 - no compression, else level for zlib */
+ int compressionLevel; /* 0 - no compression, else level for zlib */
char *port;
int protocol;
+ int iUDPRebindInterval; /* rebind interval */
+ int iTCPRebindInterval; /* rebind interval */
+ int nXmit; /* number of transmissions since last (re-)bind */
# define FORW_UDP 0
# define FORW_TCP 1
/* following fields for TCP-based delivery */
@@ -97,12 +101,35 @@ typedef struct _instanceData {
/* config data */
static uchar *pszTplName = NULL; /* name of the default template to use */
static uchar *pszStrmDrvr = NULL; /* name of the stream driver to use */
-static short iStrmDrvrMode = 0; /* mode for stream driver, driver-dependent (0 mostly means plain tcp) */
-static short bResendLastOnRecon = 0; /* should the last message be re-sent on a successful reconnect? */
+static int iStrmDrvrMode = 0; /* mode for stream driver, driver-dependent (0 mostly means plain tcp) */
+static int bResendLastOnRecon = 0; /* should the last message be re-sent on a successful reconnect? */
static uchar *pszStrmDrvrAuthMode = NULL; /* authentication mode to use */
+static int iUDPRebindInterval = 0; /* support for automatic re-binding (load balancers!). 0 - no rebind */
+static int iTCPRebindInterval = 0; /* support for automatic re-binding (load balancers!). 0 - no rebind */
static permittedPeers_t *pPermPeers = NULL;
+static rsRetVal doTryResume(instanceData *pData);
+
+/* Close the UDP sockets.
+ * rgerhards, 2009-05-29
+ */
+static rsRetVal
+closeUDPSockets(instanceData *pData)
+{
+ DEFiRet;
+ assert(pData != NULL);
+ if(pData->pSockArray != NULL) {
+ net.closeUDPListenSockets(pData->pSockArray);
+ pData->pSockArray = NULL;
+ freeaddrinfo(pData->f_addr);
+ pData->f_addr = NULL;
+ }
+pData->bIsConnected = 0; // TODO: remove this variable altogether
+ RETiRet;
+}
+
+
/* get the syslog forward port from selector_t. The passed in
* struct must be one that is setup for forwarding.
* rgerhards, 2007-06-28
@@ -148,30 +175,19 @@ ENDisCompatibleWithFeature
BEGINfreeInstance
CODESTARTfreeInstance
- if(pData->f_addr != NULL) { /* TODO: is the check ok? */
- freeaddrinfo(pData->f_addr);
- pData->f_addr = NULL;
- }
- if(pData->port != NULL)
- free(pData->port);
-
/* final cleanup */
DestructTCPInstanceData(pData);
- if(pData->pSockArray != NULL)
- net.closeUDPListenSockets(pData->pSockArray);
+ closeUDPSockets(pData);
if(pData->protocol == FORW_TCP) {
tcpclt.Destruct(&pData->pTCPClt);
}
- if(pData->f_hname != NULL)
- free(pData->f_hname);
- if(pData->pszStrmDrvr != NULL)
- free(pData->pszStrmDrvr);
- if(pData->pszStrmDrvrAuthMode != NULL)
- free(pData->pszStrmDrvrAuthMode);
- if(pData->pPermPeers != NULL)
- net.DestructPermittedPeers(&pData->pPermPeers);
+ free(pData->port);
+ free(pData->f_hname);
+ free(pData->pszStrmDrvr);
+ free(pData->pszStrmDrvrAuthMode);
+ net.DestructPermittedPeers(&pData->pPermPeers);
ENDfreeInstance
@@ -192,6 +208,16 @@ static rsRetVal UDPSend(instanceData *pData, char *msg, size_t len)
unsigned lsent = 0;
int bSendSuccess;
+ if(pData->iUDPRebindInterval && (pData->nXmit++ % pData->iUDPRebindInterval == 0)) {
+ dbgprintf("omfwd dropping UDP 'connection' (as configured)\n");
+ pData->nXmit = 1; /* else we have an addtl wrap at 2^31-1 */
+ CHKiRet(closeUDPSockets(pData));
+ }
+
+ if(pData->pSockArray == NULL) {
+ CHKiRet(doTryResume(pData));
+ }
+
if(pData->pSockArray != NULL) {
/* we need to track if we have success sending to the remote
* peer. Success is indicated by at least one sendto() call
@@ -224,6 +250,7 @@ static rsRetVal UDPSend(instanceData *pData, char *msg, size_t len)
}
}
+finalize_it:
RETiRet;
}
@@ -384,7 +411,7 @@ CODESTARTtryResume
ENDtryResume
BEGINdoAction
- char *psz; /* temporary buffering */
+ char *psz = NULL; /* temporary buffering */
register unsigned l;
int iMaxLine;
CODESTARTdoAction
@@ -431,11 +458,14 @@ CODESTARTdoAction
* rgerhards, 2006-11-30
*/
dbgprintf("Compression failed, sending uncompressed message\n");
+ free(out);
} else if(destLen+1 < l) {
/* only use compression if there is a gain in using it! */
dbgprintf("there is gain in compression, so we do it\n");
psz = (char*) out;
l = destLen + 1; /* take care for the "z" at message start! */
+ } else {
+ free(out);
}
++destLen;
}
@@ -456,6 +486,12 @@ CODESTARTdoAction
}
}
finalize_it:
+# ifdef USE_NETZIP
+ if((psz != NULL) && (psz != (char*) ppString[0])) {
+ /* we need to free temporary buffer, alloced above - Naoya Nakazawa, 2010-01-11 */
+ free(psz);
+ }
+# endif
ENDdoAction
@@ -479,7 +515,6 @@ finalize_it:
BEGINparseSelectorAct
uchar *q;
int i;
- int bErr;
rsRetVal localRet;
struct addrinfo;
TCPFRAMINGMODE tcp_framing = TCP_FRAMING_OCTET_STUFFING;
@@ -602,7 +637,6 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
}
/* now skip to template */
- bErr = 0;
while(*p && *p != ';' && *p != '#' && !isspace((int) *p))
++p; /*JUST SKIP*/
@@ -615,7 +649,10 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
} else {
CHKmalloc(pData->f_hname = strdup((char*) q));
}
-dbgprintf("hostname '%s', port '%s'\n", pData->f_hname, pData->port);
+
+ /* copy over config data as needed */
+ pData->iUDPRebindInterval = iUDPRebindInterval;
+ pData->iTCPRebindInterval = iTCPRebindInterval;
/* process template */
CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
@@ -630,6 +667,7 @@ dbgprintf("hostname '%s', port '%s'\n", pData->f_hname, pData->port);
CHKiRet(tcpclt.SetSendFrame(pData->pTCPClt, TCPSendFrame));
CHKiRet(tcpclt.SetSendPrepRetry(pData->pTCPClt, TCPSendPrepRetry));
CHKiRet(tcpclt.SetFraming(pData->pTCPClt, tcp_framing));
+ CHKiRet(tcpclt.SetRebindInterval(pData->pTCPClt, pData->iTCPRebindInterval));
pData->iStrmDrvrMode = iStrmDrvrMode;
if(pszStrmDrvr != NULL)
CHKmalloc(pData->pszStrmDrvr = (uchar*)strdup((char*)pszStrmDrvr));
@@ -700,6 +738,8 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
/* we now must reset all non-string values */
iStrmDrvrMode = 0;
bResendLastOnRecon = 0;
+ iUDPRebindInterval = 0;
+ iTCPRebindInterval = 0;
return RS_RET_OK;
}
@@ -714,6 +754,8 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(net,LM_NET_FILENAME));
CHKiRet(regCfSysLineHdlr((uchar *)"actionforwarddefaulttemplate", 0, eCmdHdlrGetWord, NULL, &pszTplName, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionsendtcprebindinterval", 0, eCmdHdlrInt, NULL, &iTCPRebindInterval, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionsendudprebindinterval", 0, eCmdHdlrInt, NULL, &iUDPRebindInterval, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionsendstreamdriver", 0, eCmdHdlrGetWord, NULL, &pszStrmDrvr, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionsendstreamdrivermode", 0, eCmdHdlrInt, NULL, &iStrmDrvrMode, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionsendstreamdriverauthmode", 0, eCmdHdlrGetWord, NULL, &pszStrmDrvrAuthMode, NULL));
diff --git a/tools/ompipe.c b/tools/ompipe.c
new file mode 100644
index 00000000..541b6552
--- /dev/null
+++ b/tools/ompipe.c
@@ -0,0 +1,240 @@
+/* ompipe.c
+ * This is the implementation of the build-in pipe output module.
+ * Note that this module stems back to the "old" (4.4.2 and below)
+ * omfile. There were some issues with the new omfile code and pipes
+ * (namely in regard to xconsole), so we took out the pipe code and moved
+ * that to a separate module. That a) immediately solves the issue for a
+ * less common use case and probably makes it much easier to enhance
+ * file and pipe support (now independently) in the future (we always
+ * needed to think about pipes in omfile so far, what we now no longer
+ * need to, hopefully resulting in reduction of complexity).
+ *
+ * NOTE: read comments in module-template.h to understand how this pipe
+ * works!
+ *
+ * Copyright 2007-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 <errno.h>
+#include <unistd.h>
+#include <sys/file.h>
+
+#include "syslogd.h"
+#include "syslogd-types.h"
+#include "srUtils.h"
+#include "template.h"
+#include "ompipe.h"
+#include "omfile.h" /* for dirty trick: access to $ActionFileDefaultTemplate value */
+#include "cfsysline.h"
+#include "module-template.h"
+#include "conf.h"
+#include "errmsg.h"
+
+MODULE_TYPE_OUTPUT
+
+/* internal structures
+ */
+DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+
+
+/* globals for default values */
+/* end globals for default values */
+
+
+typedef struct _instanceData {
+ uchar f_fname[MAXFNAME];/* pipe or template name (display only) */
+ short fd; /* pipe descriptor for (current) pipe */
+} instanceData;
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURERepeatedMsgReduction)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ dbgprintf("pipe %s", pData->f_fname);
+ if (pData->fd == -1)
+ dbgprintf(" (unused)");
+ENDdbgPrintInstInfo
+
+
+/* This is now shared code for all types of files. It simply prepares
+ * pipe access, which, among others, means the the pipe wil be opened
+ * and any directories in between will be created (based on config, of
+ * course). -- rgerhards, 2008-10-22
+ * changed to iRet interface - 2009-03-19
+ */
+static inline rsRetVal
+preparePipe(instanceData *pData)
+{
+ DEFiRet;
+ pData->fd = open((char*) pData->f_fname, O_RDWR|O_NONBLOCK|O_CLOEXEC);
+ RETiRet;
+}
+
+
+/* rgerhards 2004-11-11: write to a pipe output. This
+ * will be called for all outputs using pipe semantics,
+ * for example also for pipes.
+ */
+static rsRetVal writePipe(uchar **ppString, instanceData *pData)
+{
+ int iLenWritten;
+ DEFiRet;
+
+ ASSERT(pData != NULL);
+
+ if(pData->fd == -1) {
+ rsRetVal iRetLocal;
+ iRetLocal = preparePipe(pData);
+ if((iRetLocal != RS_RET_OK) || (pData->fd == -1))
+ ABORT_FINALIZE(RS_RET_SUSPENDED); /* whatever the failure was, we need to retry */
+ }
+
+ /* create the message based on format specified */
+ iLenWritten = write(pData->fd, ppString[0], strlen((char*)ppString[0]));
+ if(iLenWritten < 0) {
+ int e = errno;
+ char errStr[1024];
+ rs_strerror_r(errno, errStr, sizeof(errStr));
+ DBGPRINTF("pipe (%d) write error %d: %s\n", pData->fd, e, errStr);
+
+ /* If a named pipe is full, we suspend this action for a while */
+ if(e == EAGAIN)
+ ABORT_FINALIZE(RS_RET_SUSPENDED);
+
+ close(pData->fd);
+ pData->fd = -1; /* tell that fd is no longer open! */
+ iRet = RS_RET_SUSPENDED;
+ errno = e;
+ errmsg.LogError(0, NO_ERRCODE, "%s", pData->f_fname);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+BEGINcreateInstance
+CODESTARTcreateInstance
+ pData->fd = -1;
+ENDcreateInstance
+
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ if(pData->fd != -1)
+ close(pData->fd);
+ENDfreeInstance
+
+
+BEGINtryResume
+CODESTARTtryResume
+ENDtryResume
+
+BEGINdoAction
+CODESTARTdoAction
+ DBGPRINTF(" (%s)\n", pData->f_fname);
+ iRet = writePipe(ppString, pData);
+ENDdoAction
+
+
+BEGINparseSelectorAct
+CODESTARTparseSelectorAct
+ /* yes, the if below is redundant, but I need it now. Will go away as
+ * the code further changes. -- rgerhards, 2007-07-25
+ */
+ if(*p == '|') {
+ if((iRet = createInstance(&pData)) != RS_RET_OK) {
+ ENDfunc
+ return iRet; /* this can not use RET_iRet! */
+ }
+ } else {
+ /* this is not clean, but we need it for the time being
+ * TODO: remove when cleaning up modularization
+ */
+ ENDfunc
+ return RS_RET_CONFLINE_UNPROCESSED;
+ }
+
+ CODE_STD_STRING_REQUESTparseSelectorAct(1)
+ ++p;
+ /* rgerhards 2004-11-17: from now, we need to have different
+ * processing, because after the first comma, the template name
+ * to use is specified. So we need to scan for the first coma first
+ * and then look at the rest of the line.
+ */
+ CHKiRet(cflineParseFileName(p, (uchar*) pData->f_fname, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
+ (pszFileDfltTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszFileDfltTplName));
+
+ /* at this stage, we ignore the return value of preparePipe, this is taken
+ * care of in later steps. -- rgerhards, 2009-03-19
+ */
+ preparePipe(pData);
+
+ if(pData->fd < 0 ) {
+ pData->fd = -1;
+ DBGPRINTF("Error opening log pipe: %s\n", pData->f_fname);
+ errmsg.LogError(0, RS_RET_NO_FILE_ACCESS, "Could no open output pipe '%s'", pData->f_fname);
+ }
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+
+BEGINdoHUP
+CODESTARTdoHUP
+ if(pData->fd != -1) {
+ close(pData->fd);
+ pData->fd = -1;
+ }
+ENDdoHUP
+
+
+BEGINmodExit
+CODESTARTmodExit
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_OMOD_QUERIES
+CODEqueryEtryPt_doHUP
+ENDqueryEtryPt
+
+
+BEGINmodInit(Pipe)
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ENDmodInit
+/* vi:set ai:
+ */
diff --git a/tools/ompipe.h b/tools/ompipe.h
new file mode 100644
index 00000000..d17346c5
--- /dev/null
+++ b/tools/ompipe.h
@@ -0,0 +1,31 @@
+/* ompipe.h
+ * These are the definitions for the build-in pipe output module.
+ *
+ * Copyright 2007-2010 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This pipe 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 pipe "COPYING" in this distribution.
+ */
+#ifndef OMPIPE_H_INCLUDED
+#define OMPIPE_H_INCLUDED 1
+
+/* prototypes */
+rsRetVal modInitPipe(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*);
+
+#endif /* #ifndef OMPIPE_H_INCLUDED */
+/* vi:set ai:
+ */
diff --git a/tools/omshell.c b/tools/omshell.c
index 7b815869..f8a68527 100644
--- a/tools/omshell.c
+++ b/tools/omshell.c
@@ -38,7 +38,7 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
-#include "syslogd.h"
+#include "conf.h"
#include "syslogd-types.h"
#include "srUtils.h"
#include "omshell.h"
diff --git a/tools/omusrmsg.c b/tools/omusrmsg.c
index 830bbc87..768baca7 100644
--- a/tools/omusrmsg.c
+++ b/tools/omusrmsg.c
@@ -50,7 +50,15 @@
#include <assert.h>
#include <signal.h>
#include <sys/param.h>
-#include <utmp.h>
+#ifdef HAVE_UTMP_H
+# include <utmp.h>
+# define STRUCTUTMP struct utmp
+# define UTNAME ut_name
+#else
+# include <utmpx.h>
+# define STRUCTUTMP struct utmpx
+# define UTNAME ut_user
+#endif
#include <unistd.h>
#include <sys/uio.h>
#include <sys/stat.h>
@@ -66,7 +74,7 @@
#include "srUtils.h"
#include "stringbuf.h"
#include "syslogd-types.h"
-#include "syslogd.h"
+#include "conf.h"
#include "omusrmsg.h"
#include "module-template.h"
#include "errmsg.h"
@@ -124,19 +132,25 @@ ENDdbgPrintInstInfo
* need! rgerhards 2005-03-18
*/
#ifdef OS_BSD
+/* Since version 900007, FreeBSD has a POSIX compliant <utmpx.h> */
+#if defined(__FreeBSD__) && (__FreeBSD_version >= 900007)
+# define setutent(void) setutxent(void)
+# define getutent(void) getutxent(void)
+# define endutent(void) endutxent(void)
+#else
static FILE *BSD_uf = NULL;
void setutent(void)
{
assert(BSD_uf == NULL);
if ((BSD_uf = fopen(_PATH_UTMP, "r")) == NULL) {
- errmsg.LogError(NO_ERRCODE, "%s", _PATH_UTMP);
+ errmsg.LogError(0, NO_ERRCODE, "%s", _PATH_UTMP);
return;
}
}
-struct utmp* getutent(void)
+STRUCTUTMP* getutent(void)
{
- static struct utmp st_utmp;
+ static STRUCTUTMP st_utmp;
if(fread((char *)&st_utmp, sizeof(st_utmp), 1, BSD_uf) != 1)
return NULL;
@@ -149,6 +163,7 @@ void endutent(void)
fclose(BSD_uf);
BSD_uf = NULL;
}
+#endif /* if defined(__FreeBSD__) */
#endif /* #ifdef OS_BSD */
@@ -173,8 +188,8 @@ static rsRetVal wallmsg(uchar* pMsg, instanceData *pData)
int errnoSave;
int ttyf;
int wrRet;
- struct utmp ut;
- struct utmp *uptr;
+ STRUCTUTMP ut;
+ STRUCTUTMP *uptr;
struct stat statb;
DEFiRet;
@@ -187,13 +202,13 @@ static rsRetVal wallmsg(uchar* pMsg, instanceData *pData)
while((uptr = getutent())) {
memcpy(&ut, uptr, sizeof(ut));
/* is this slot used? */
- if(ut.ut_name[0] == '\0')
+ if(ut.UTNAME[0] == '\0')
continue;
#ifndef OS_BSD
if(ut.ut_type != USER_PROCESS)
continue;
#endif
- if(!(strncmp (ut.ut_name,"LOGIN", 6))) /* paranoia */
+ if(!(strncmp (ut.UTNAME,"LOGIN", 6))) /* paranoia */
continue;
/* should we send the message to this user? */
@@ -203,7 +218,7 @@ static rsRetVal wallmsg(uchar* pMsg, instanceData *pData)
i = MAXUNAMES;
break;
}
- if(strncmp(pData->uname[i], ut.ut_name, UNAMESZ) == 0)
+ if(strncmp(pData->uname[i], ut.UTNAME, UNAMESZ) == 0)
break;
}
if(i == MAXUNAMES) /* user not found? */
@@ -234,7 +249,6 @@ static rsRetVal wallmsg(uchar* pMsg, instanceData *pData)
}
}
close(ttyf);
- ttyf = -1;
}
}
diff --git a/tools/pidfile.c b/tools/pidfile.c
index 2be13da6..e7744513 100644
--- a/tools/pidfile.c
+++ b/tools/pidfile.c
@@ -97,7 +97,7 @@ int write_pid (char *pidfile)
int fd;
int pid;
- if ( ((fd = open(pidfile, O_RDWR|O_CREAT, 0644)) == -1)
+ if ( ((fd = open(pidfile, O_RDWR|O_CREAT|O_CLOEXEC, 0644)) == -1)
|| ((f = fdopen(fd, "r+")) == NULL) ) {
fprintf(stderr, "Can't open or create %s.\n", pidfile);
return 0;
diff --git a/tools/regexp.c b/tools/regexp.c
index c8e4c681..e8bba4f4 100644
--- a/tools/regexp.c
+++ b/tools/regexp.c
@@ -26,6 +26,7 @@
*
* A copy of the GPL can be found in the file "COPYING" in this distribution.
*/
+#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
diff --git a/tools/rsyslog.conf.5 b/tools/rsyslog.conf.5
index 0a2422c6..e17da974 100644
--- a/tools/rsyslog.conf.5
+++ b/tools/rsyslog.conf.5
@@ -80,7 +80,7 @@ used like this:
.IP
$ModLoad imudp
.IP
-$InputUDPServerRun 514
+$UDPServerRun 514
.TP
.I imtcp
Input plugin for plain TCP syslog. Replaces the deprecated -t
@@ -164,8 +164,8 @@ a pattern of facilities and priorities belonging to the specified action.
The selector field itself again consists of two parts, a facility and a
priority, separated by a period ('.'). Both parts are case insensitive and can
also be specified as decimal numbers, but don't do that, you have been warned.
-Both facilities and priorities are described in rsyslog(3). The names mentioned
-below correspond to the similar LOG_-values in /usr/include/rsyslog.h.
+Both facilities and priorities are described in syslog(3). The names mentioned
+below correspond to the similar LOG_-values in /usr/include/syslog.h.
The facility is one of the following keywords: auth, authpriv, cron, daemon,
kern, lpr, mail, mark, news, security (same as auth), syslog, user, uucp and
@@ -200,11 +200,11 @@ to overwrite the preceding ones. Using this behavior you can exclude some
priorities from the pattern.
Rsyslogd has a syntax extension to the original BSD source, that makes its use
-more intuitively. You may precede every priority with an equation sign ('=') to
+more intuitively. You may precede every priority with an equals sign ('=') to
specify only this single priority and not any of the above. You may also (both
is valid, too) precede the priority with an exclamation mark ('!') to ignore
all that priorities, either exact this one or this and any higher priority. If
-you use both extensions than the exclamation mark must occur before the equation
+you use both extensions than the exclamation mark must occur before the equals
sign, just use it intuitively.
.SH ACTIONS
diff --git a/tools/syslogd.c b/tools/syslogd.c
index 10283568..12d94e9a 100644
--- a/tools/syslogd.c
+++ b/tools/syslogd.c
@@ -72,16 +72,23 @@
#include <stdarg.h>
#include <time.h>
#include <assert.h>
-#include <libgen.h>
-#ifdef __sun
+#ifdef OS_SOLARIS
# include <errno.h>
+# include <fcntl.h>
+# include <stropts.h>
+# include <sys/termios.h>
+# include <sys/types.h>
#else
+# include <libgen.h>
# include <sys/errno.h>
#endif
+
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/file.h>
+#include <sys/resource.h>
+#include <grp.h>
#if HAVE_SYS_TIMESPEC_H
# include <sys/timespec.h>
@@ -120,15 +127,21 @@
#include "omusrmsg.h"
#include "omfwd.h"
#include "omfile.h"
+#include "ompipe.h"
#include "omdiscard.h"
#include "threads.h"
#include "queue.h"
#include "stream.h"
#include "conf.h"
-#include "vm.h"
#include "errmsg.h"
#include "datetime.h"
-#include "sysvar.h"
+#include "parser.h"
+#include "unicode-helper.h"
+#include "ruleset.h"
+#include "rule.h"
+#include "net.h"
+#include "vm.h"
+#include "prop.h"
/* definitions for objects we access */
DEFobjCurrIf(obj)
@@ -136,29 +149,17 @@ DEFobjCurrIf(glbl)
DEFobjCurrIf(datetime)
DEFobjCurrIf(conf)
DEFobjCurrIf(expr)
-DEFobjCurrIf(vm)
-DEFobjCurrIf(var)
DEFobjCurrIf(module)
DEFobjCurrIf(errmsg)
+DEFobjCurrIf(rule)
+DEFobjCurrIf(ruleset)
+DEFobjCurrIf(prop)
DEFobjCurrIf(net) /* TODO: make go away! */
/* forward definitions */
static rsRetVal GlobalClassExit(void);
-/* We define our own set of syslog defintions so that we
- * do not need to rely on (possibly different) implementations.
- * 2007-07-19 rgerhards
- */
-/* missing definitions for solaris
- * 2006-02-16 Rger
- */
-#ifdef __sun
-# define LOG_AUTHPRIV LOG_AUTH
-#endif
-#define INTERNAL_NOPRI 0x10 /* the "no priority" priority */
-#define LOG_FTP (11<<3) /* ftp daemon */
-
#ifndef UTMP_FILE
#ifdef UTMP_FILENAME
@@ -177,7 +178,11 @@ static rsRetVal GlobalClassExit(void);
#endif
#ifndef _PATH_MODDIR
-#define _PATH_MODDIR "/lib/rsyslog/"
+# if defined(__FreeBSD__)
+# define _PATH_MODDIR "/usr/local/lib/rsyslog/"
+# else
+# define _PATH_MODDIR "/lib/rsyslog/"
+# endif
#endif
#if defined(SYSLOGD_PIDNAME)
@@ -206,20 +211,18 @@ static rsRetVal GlobalClassExit(void);
# endif
#endif
-#ifndef _PATH_DEV
-# define _PATH_DEV "/dev/"
-#endif
-
#ifndef _PATH_TTY
-#define _PATH_TTY "/dev/tty"
+# define _PATH_TTY "/dev/tty"
#endif
+static prop_t *pInternalInputName = NULL; /* there is only one global inputName for all internally-generated messages */
+static prop_t *pLocalHostIP = NULL; /* there is only one global IP for all internally-generated messages */
static uchar *ConfFile = (uchar*) _PATH_LOGCONF; /* read-only after startup */
static char *PidFile = _PATH_LOGPID; /* read-only after startup */
static pid_t myPid; /* our pid for use in self-generated messages, e.g. on startup */
/* mypid is read-only after the initial fork() */
-static int restart = 0; /* do restart (config read) - multithread safe */
+static int bHadHUP = 0; /* did we have a HUP? */
static int bParseHOSTNAMEandTAG = 1; /* global config var: should the hostname and tag be
* parsed inside message - rgerhards, 2006-03-13 */
@@ -238,8 +241,6 @@ int repeatinterval[2] = { 30, 60 }; /* # of secs before flush */
#define LIST_DELIMITER ':' /* delimiter between two hosts */
-struct filed *Files = NULL; /* read-only after init() (but beware of sigusr1!) */
-
static pid_t ppid; /* This is a quick and dirty hack used for spliting main/startup thread */
typedef struct legacyOptsLL_s {
@@ -249,15 +250,15 @@ typedef struct legacyOptsLL_s {
legacyOptsLL_t *pLegacyOptsLL = NULL;
/* global variables for config file state */
-static int bDropTrailingLF = 1; /* drop trailing LF's on reception? */
+int bDropTrailingLF = 1; /* drop trailing LF's on reception? */
int iCompatibilityMode = 0; /* version we should be compatible with; 0 means sysklogd. It is
the default, so if no -c<n> option is given, we make ourselvs
as compatible to sysklogd as possible. */
static int bDebugPrintTemplateList = 1;/* output template list in debug mode? */
static int bDebugPrintCfSysLineHandlerList = 1;/* output cfsyslinehandler list in debug mode? */
static int bDebugPrintModuleList = 1;/* output module list in debug mode? */
-static uchar cCCEscapeChar = '\\';/* character to be used to start an escape sequence for control chars */
-static int bEscapeCCOnRcv = 1; /* escape control characters on reception: 0 - no, 1 - yes */
+uchar cCCEscapeChar = '\\';/* character to be used to start an escape sequence for control chars */
+int bEscapeCCOnRcv = 1; /* escape control characters on reception: 0 - no, 1 - yes */
static int bErrMsgToStderr = 1; /* print error messages to stderr (in addition to everything else)? */
int bReduceRepeatMsgs; /* reduce repeated message - 0 - no, 1 - yes */
int bActExecWhenPrevSusp; /* execute action only when previous one was suspended? */
@@ -271,11 +272,14 @@ static int bHaveMainQueue = 0;/* set to 1 if the main queue - in queueing mode -
* If the main queue is either not yet ready or not running in
* queueing mode (mode DIRECT!), then this is set to 0.
*/
+static int uidDropPriv = 0; /* user-id to which priveleges should be dropped to (AFTER init()!) */
+static int gidDropPriv = 0; /* group-id to which priveleges should be dropped to (AFTER init()!) */
extern int errno;
+static uchar *pszConfDAGFile = NULL; /* name of config DAG file, non-NULL means generate one */
/* main message queue and its configuration parameters */
-static queue_t *pMsgQueue = NULL; /* the main message queue */
+static qqueue_t *pMsgQueue = NULL; /* the main message queue */
static int iMainMsgQueueSize = 10000; /* size of the main message queue above */
static int iMainMsgQHighWtrMark = 8000; /* high water mark for disk-assisted queues */
static int iMainMsgQLowWtrMark = 2000; /* low water mark for disk-assisted queues */
@@ -286,7 +290,8 @@ static queueType_t MainMsgQueType = QUEUETYPE_FIXED_ARRAY; /* type of the main m
static uchar *pszMainMsgQFName = NULL; /* prefix for the main message queue file */
static int64 iMainMsgQueMaxFileSize = 1024*1024;
static int iMainMsgQPersistUpdCnt = 0; /* persist queue info every n updates */
-static int iMainMsgQtoQShutdown = 0; /* queue shutdown */
+static int bMainMsgQSyncQeueFiles = 0; /* sync queue files on every write? */
+static int iMainMsgQtoQShutdown = 1500; /* queue shutdown (ms) */
static int iMainMsgQtoActShutdown = 1000; /* action shutdown (in phase 2) */
static int iMainMsgQtoEnq = 2000; /* timeout for queue enque */
static int iMainMsgQtoWrkShutdown = 60000; /* timeout for worker thread shutdown */
@@ -301,7 +306,8 @@ static int iMainMsgQueueDeqtWinToHr = 25; /* hour begin of time frame when que
/* support for simple textual representation of FIOP names
* rgerhards, 2005-09-27
*/
-static char* getFIOPName(unsigned iFIOP)
+char*
+getFIOPName(unsigned iFIOP)
{
char *pRet;
switch(iFIOP) {
@@ -338,10 +344,8 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
bDebugPrintModuleList = 1;
bEscapeCCOnRcv = 1; /* default is to escape control characters */
bReduceRepeatMsgs = 0;
- if(pszMainMsgQFName != NULL) {
- free(pszMainMsgQFName);
- pszMainMsgQFName = NULL;
- }
+ free(pszMainMsgQFName);
+ pszMainMsgQFName = NULL;
iMainMsgQueueSize = 10000;
iMainMsgQHighWtrMark = 8000;
iMainMsgQLowWtrMark = 2000;
@@ -350,7 +354,8 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
iMainMsgQueMaxFileSize = 1024 * 1024;
iMainMsgQueueNumWorkers = 1;
iMainMsgQPersistUpdCnt = 0;
- iMainMsgQtoQShutdown = 0;
+ bMainMsgQSyncQeueFiles = 0;
+ iMainMsgQtoQShutdown = 1500;
iMainMsgQtoActShutdown = 1000;
iMainMsgQtoEnq = 2000;
iMainMsgQtoWrkShutdown = 60000;
@@ -385,7 +390,6 @@ static char **crunch_list(char *list);
static void reapchild();
static void debug_switch();
static void sighup_handler();
-static void freeSelectors(void);
static void processImInternal(void);
@@ -400,65 +404,27 @@ static int usage(void)
}
-/* function to destruct a selector_t object
- * rgerhards, 2007-08-01
+/* ------------------------------ some support functions for imdiag ------------------------------ *
+ * This is a bit dirty, but the only way to do it, at least with reasonable effort.
+ * rgerhards, 2009-05-25
*/
-rsRetVal
-selectorDestruct(void *pVal)
-{
- selector_t *pThis = (selector_t *) pVal;
-
- assert(pThis != NULL);
-
- if(pThis->pCSHostnameComp != NULL)
- rsCStrDestruct(&pThis->pCSHostnameComp);
- if(pThis->pCSProgNameComp != NULL)
- rsCStrDestruct(&pThis->pCSProgNameComp);
-
- if(pThis->f_filter_type == FILTER_PROP) {
- if(pThis->f_filterData.prop.pCSPropName != NULL)
- rsCStrDestruct(&pThis->f_filterData.prop.pCSPropName);
- if(pThis->f_filterData.prop.pCSCompValue != NULL)
- rsCStrDestruct(&pThis->f_filterData.prop.pCSCompValue);
- } else if(pThis->f_filter_type == FILTER_EXPR) {
- if(pThis->f_filterData.f_expr != NULL)
- expr.Destruct(&pThis->f_filterData.f_expr);
- }
-
- llDestroy(&pThis->llActList);
- free(pThis);
-
- return RS_RET_OK;
-}
-
-/* function to construct a selector_t object
- * rgerhards, 2007-08-01
+/* return back the approximate current number of messages in the main message queue
*/
rsRetVal
-selectorConstruct(selector_t **ppThis)
+diagGetMainMsgQSize(int *piSize)
{
DEFiRet;
- selector_t *pThis;
-
- assert(ppThis != NULL);
-
- if((pThis = (selector_t*) calloc(1, sizeof(selector_t))) == NULL) {
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- }
- CHKiRet(llInit(&pThis->llActList, actionDestruct, NULL, NULL));
-
-finalize_it:
- if(iRet != RS_RET_OK) {
- if(pThis != NULL) {
- selectorDestruct(pThis);
- }
- }
- *ppThis = pThis;
+ assert(piSize != NULL);
+ *piSize = (pMsgQueue->pqDA != NULL) ? pMsgQueue->pqDA->iQueueSize : 0;
+ *piSize += pMsgQueue->iQueueSize;
RETiRet;
}
+/* ------------------------------ end support functions for imdiag ------------------------------ */
+
+
/* rgerhards, 2005-10-24: crunch_list is called only during option processing. So
* it is never called once rsyslogd is running (not even when HUPed). This code
* contains some exits, but they are considered safe because they only happen
@@ -521,7 +487,7 @@ static char **crunch_list(char *list)
#if 0
count=0;
while (result[count])
- dbgprintf("#%d: %s\n", count, StripDomains[count++]);
+ DBGPRINTF("#%d: %s\n", count, StripDomains[count++]);
#endif
return result;
}
@@ -530,7 +496,7 @@ static char **crunch_list(char *list)
void untty(void)
#ifdef HAVE_SETSID
{
- if ( !Debug ) {
+ if(!Debug) {
setsid();
}
return;
@@ -539,18 +505,18 @@ void untty(void)
{
int i;
- if ( !Debug ) {
- i = open(_PATH_TTY, O_RDWR);
+ if(!Debug) {
+ i = open(_PATH_TTY, O_RDWR|O_CLOEXEC);
if (i >= 0) {
# if !defined(__hpux)
- (void) ioctl(i, (int) TIOCNOTTY, (char *)0);
+ (void) ioctl(i, (int) TIOCNOTTY, NULL);
# else
/* TODO: we need to implement something for HP UX! -- rgerhards, 2008-03-04 */
/* actually, HP UX should have setsid, so the code directly above should
* trigger. So the actual question is why it doesn't do that...
*/
# endif
- (void) close(i);
+ close(i);
}
}
}
@@ -582,23 +548,37 @@ void untty(void)
* Interface change: added new parameter "InputName", permits the input to provide
* a string that identifies it. May be NULL, but must be a valid char* pointer if
* non-NULL.
+ *
+ * rgerhards, 2008-10-06:
+ * Interface change: added new parameter "stTime", which enables the caller to provide
+ * a timestamp that is to be used as timegenerated instead of the current system time.
+ * This is meant to facilitate performance optimization. Some inputs support such modes.
+ * If stTime is NULL, the current system time is used.
+ *
+ * rgerhards, 2008-10-09:
+ * interface change: bParseHostname removed, now in flags
*/
-rsRetVal printline(uchar *hname, uchar *hnameIP, uchar *msg, int bParseHost, int flags, flowControl_t flowCtlType,
- uchar *pszInputName)
+static inline rsRetVal printline(uchar *hname, uchar *hnameIP, uchar *msg, int flags, flowControl_t flowCtlType,
+ prop_t *pInputName, struct syslogTime *stTime, time_t ttGenTime)
{
DEFiRet;
register uchar *p;
int pri;
msg_t *pMsg;
+ prop_t *propFromHost = NULL;
+ prop_t *propFromHostIP = NULL;
/* Now it is time to create the message object (rgerhards) */
- CHKiRet(msgConstruct(&pMsg));
- if(pszInputName != NULL)
- MsgSetInputName(pMsg, (char*) pszInputName);
+ if(stTime == NULL) {
+ CHKiRet(msgConstruct(&pMsg));
+ } else {
+ CHKiRet(msgConstructWithTime(&pMsg, stTime, ttGenTime));
+ }
+ if(pInputName != NULL)
+ MsgSetInputName(pMsg, pInputName);
MsgSetFlowControlType(pMsg, flowCtlType);
- MsgSetRawMsg(pMsg, (char*)msg);
+ MsgSetRawMsgWOSize(pMsg, (char*)msg);
- pMsg->bParseHOSTNAME = bParseHost;
/* test for special codes */
pri = DEFUPRI;
p = msg;
@@ -623,18 +603,13 @@ rsRetVal printline(uchar *hname, uchar *hnameIP, uchar *msg, int bParseHost, int
* the message was received from (that, for obvious reasons,
* being the local host). rgerhards 2004-11-16
*/
- if(bParseHost == 0)
- MsgSetHOSTNAME(pMsg, (char*)hname);
- MsgSetRcvFrom(pMsg, (char*)hname);
- CHKiRet(MsgSetRcvFromIP(pMsg, hnameIP));
-
- /* rgerhards 2004-11-19: well, well... we've now seen that we
- * have the "hostname problem" also with the traditional Unix
- * message. As we like to emulate it, we need to add the hostname
- * to it.
- */
- if(MsgSetUxTradMsg(pMsg, (char*)p) != 0)
- ABORT_FINALIZE(RS_RET_ERR);
+ if((pMsg->msgFlags & PARSE_HOSTNAME) == 0)
+ MsgSetHOSTNAME(pMsg, hname, ustrlen(hname));
+ MsgSetRcvFromStr(pMsg, hname, ustrlen(hname), &propFromHost);
+ CHKiRet(MsgSetRcvFromIPStr(pMsg, hnameIP, ustrlen(hnameIP), &propFromHostIP));
+ MsgSetAfterPRIOffs(pMsg, p - msg);
+ prop.Destruct(&propFromHost);
+ prop.Destruct(&propFromHostIP);
logmsg(pMsg, flags);
@@ -685,10 +660,19 @@ finalize_it:
* Interface change: added new parameter "InputName", permits the input to provide
* a string that identifies it. May be NULL, but must be a valid char* pointer if
* non-NULL.
+ *
+ * rgerhards, 2008-10-06:
+ * Interface change: added new parameter "stTime", which enables the caller to provide
+ * a timestamp that is to be used as timegenerated instead of the current system time.
+ * This is meant to facilitate performance optimization. Some inputs support such modes.
+ * If stTime is NULL, the current system time is used.
+ *
+ * rgerhards, 2008-10-09:
+ * interface change: bParseHostname removed, now in flags
*/
rsRetVal
-parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int bParseHost, int flags, flowControl_t flowCtlType,
- uchar *pszInputName)
+parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int flags, flowControl_t flowCtlType,
+ prop_t *pInputName, struct syslogTime *stTime, time_t ttGenTime)
{
DEFiRet;
register int iMsg;
@@ -715,9 +699,6 @@ parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int bPa
* TODO: optimize buffer handling */
iMaxLine = glbl.GetMaxLine();
CHKmalloc(tmpline = malloc(sizeof(uchar) * (iMaxLine + 1)));
-# ifdef USE_NETZIP
- CHKmalloc(deflateBuf = malloc(sizeof(uchar) * (iMaxLine + 1)));
-# endif
/* we first check if we have a NUL character at the very end of the
* message. This seems to be a frequent problem with a number of senders.
@@ -730,7 +711,7 @@ parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int bPa
* rgerhards, 2007-09-14
*/
if(*(msg + len - 1) == '\0') {
- dbgprintf("dropped NUL at very end of message\n");
+ DBGPRINTF("dropped NUL at very end of message\n");
len--;
}
@@ -740,7 +721,7 @@ parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int bPa
* turn on/off this handling. rgerhards, 2007-07-23
*/
if(bDropTrailingLF && *(msg + len - 1) == '\n') {
- dbgprintf("dropped LF at very end of message (DropTrailingLF is set)\n");
+ DBGPRINTF("dropped LF at very end of message (DropTrailingLF is set)\n");
len--;
}
@@ -763,8 +744,9 @@ parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int bPa
*/
int ret;
iLenDefBuf = iMaxLine;
+ CHKmalloc(deflateBuf = malloc(sizeof(uchar) * (iMaxLine + 1)));
ret = uncompress((uchar *) deflateBuf, &iLenDefBuf, (uchar *) msg+1, len-1);
- dbgprintf("Compressed message uncompressed with status %d, length: new %ld, old %d.\n",
+ DBGPRINTF("Compressed message uncompressed with status %d, length: new %ld, old %d.\n",
ret, (long) iLenDefBuf, len-1);
/* Now check if the uncompression worked. If not, there is not much we can do. In
* that case, we log an error message but ignore the message itself. Storing the
@@ -801,7 +783,7 @@ parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int bPa
*/
if(iMsg == iMaxLine) {
*(pMsg + iMsg) = '\0'; /* space *is* reserved for this! */
- printline(hname, hnameIP, tmpline, bParseHost, flags, flowCtlType, pszInputName);
+ printline(hname, hnameIP, tmpline, flags, flowCtlType, pInputName, stTime, ttGenTime);
} else {
/* This case in theory never can happen. If it happens, we have
* a logic error. I am checking for it, because if I would not,
@@ -810,7 +792,7 @@ parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int bPa
* (I couldn't do any more smart things anyway...).
* rgerhards, 2007-9-20
*/
- dbgprintf("internal error: iMsg > max msg size in printchopped()\n");
+ DBGPRINTF("internal error: iMsg > max msg size in parseAndSubmitMessage()\n");
}
FINALIZE; /* in this case, we are done... nothing left we can do */
}
@@ -853,7 +835,7 @@ parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int bPa
*(pMsg + iMsg) = '\0'; /* space *is* reserved for this! */
/* typically, we should end up here! */
- printline(hname, hnameIP, tmpline, bParseHost, flags, flowCtlType, pszInputName);
+ printline(hname, hnameIP, tmpline, flags, flowCtlType, pInputName, stTime, ttGenTime);
finalize_it:
if(tmpline != NULL)
@@ -896,21 +878,20 @@ logmsgInternal(int iErr, int pri, uchar *msg, int flags)
DEFiRet;
CHKiRet(msgConstruct(&pMsg));
- MsgSetInputName(pMsg, "rsyslogd");
- MsgSetUxTradMsg(pMsg, (char*)msg);
- MsgSetRawMsg(pMsg, (char*)msg);
- MsgSetHOSTNAME(pMsg, (char*)glbl.GetLocalHostName());
- MsgSetRcvFrom(pMsg, (char*)glbl.GetLocalHostName());
- MsgSetRcvFromIP(pMsg, (uchar*)"127.0.0.1");
+ MsgSetInputName(pMsg, pInternalInputName);
+ MsgSetRawMsgWOSize(pMsg, (char*)msg);
+ MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName(), ustrlen(glbl.GetLocalHostName()));
+ MsgSetRcvFrom(pMsg, glbl.GetLocalHostNameProp());
+ MsgSetRcvFromIP(pMsg, pLocalHostIP);
/* check if we have an error code associated and, if so,
* adjust the tag. -- rgerhards, 2008-06-27
*/
if(iErr == NO_ERRCODE) {
- MsgSetTAG(pMsg, "rsyslogd:");
+ MsgSetTAG(pMsg, UCHAR_CONSTANT("rsyslogd:"), sizeof("rsyslogd:") - 1);
} else {
- snprintf((char*)pszTag, sizeof(pszTag), "rsyslogd%d:", iErr);
+ size_t len = snprintf((char*)pszTag, sizeof(pszTag), "rsyslogd%d:", iErr);
pszTag[32] = '\0'; /* just to make sure... */
- MsgSetTAG(pMsg, (char*)pszTag);
+ MsgSetTAG(pMsg, pszTag, len);
}
pMsg->iFacility = LOG_FAC(pri);
pMsg->iSeverity = LOG_PRI(pri);
@@ -942,228 +923,6 @@ finalize_it:
RETiRet;
}
-/* This functions looks at the given message and checks if it matches the
- * provided filter condition. If so, it returns true, else it returns
- * false. This is a helper to logmsg() and meant to drive the decision
- * process if a message is to be processed or not. As I expect this
- * decision code to grow more complex over time AND logmsg() is already
- * a very lengthy function, I thought a separate function is more appropriate.
- * 2005-09-19 rgerhards
- * 2008-02-25 rgerhards: changed interface, now utilizes iRet, bProcessMsg
- * returns is message should be procesed.
- */
-static rsRetVal shouldProcessThisMessage(selector_t *f, msg_t *pMsg, int *bProcessMsg)
-{
- DEFiRet;
- unsigned short pbMustBeFreed;
- char *pszPropVal;
- int bRet = 0;
- vm_t *pVM = NULL;
- var_t *pResult = NULL;
-
- assert(f != NULL);
- assert(pMsg != NULL);
-
- /* we first have a look at the global, BSD-style block filters (for tag
- * and host). Only if they match, we evaluate the actual filter.
- * rgerhards, 2005-10-18
- */
- if(f->eHostnameCmpMode == HN_NO_COMP) {
- /* EMPTY BY INTENSION - we check this value first, because
- * it is the one most often used, so this saves us time!
- */
- } else if(f->eHostnameCmpMode == HN_COMP_MATCH) {
- if(rsCStrSzStrCmp(f->pCSHostnameComp, (uchar*) getHOSTNAME(pMsg), getHOSTNAMELen(pMsg))) {
- /* not equal, so we are already done... */
- dbgprintf("hostname filter '+%s' does not match '%s'\n",
- rsCStrGetSzStrNoNULL(f->pCSHostnameComp), getHOSTNAME(pMsg));
- FINALIZE;
- }
- } else { /* must be -hostname */
- if(!rsCStrSzStrCmp(f->pCSHostnameComp, (uchar*) getHOSTNAME(pMsg), getHOSTNAMELen(pMsg))) {
- /* not equal, so we are already done... */
- dbgprintf("hostname filter '-%s' does not match '%s'\n",
- rsCStrGetSzStrNoNULL(f->pCSHostnameComp), getHOSTNAME(pMsg));
- FINALIZE;
- }
- }
-
- if(f->pCSProgNameComp != NULL) {
- int bInv = 0, bEqv = 0, offset = 0;
- if(*(rsCStrGetSzStrNoNULL(f->pCSProgNameComp)) == '-') {
- if(*(rsCStrGetSzStrNoNULL(f->pCSProgNameComp) + 1) == '-')
- offset = 1;
- else {
- bInv = 1;
- offset = 1;
- }
- }
- if(!rsCStrOffsetSzStrCmp(f->pCSProgNameComp, offset, (uchar*) getProgramName(pMsg), getProgramNameLen(pMsg)))
- bEqv = 1;
-
- if((!bEqv && !bInv) || (bEqv && bInv)) {
- /* not equal or inverted selection, so we are already done... */
- dbgprintf("programname filter '%s' does not match '%s'\n",
- rsCStrGetSzStrNoNULL(f->pCSProgNameComp), getProgramName(pMsg));
- FINALIZE;
- }
- }
-
- /* done with the BSD-style block filters */
-
- if(f->f_filter_type == FILTER_PRI) {
- /* skip messages that are incorrect priority */
- if ( (f->f_filterData.f_pmask[pMsg->iFacility] == TABLE_NOPRI) || \
- ((f->f_filterData.f_pmask[pMsg->iFacility] & (1<<pMsg->iSeverity)) == 0) )
- bRet = 0;
- else
- bRet = 1;
- } else if(f->f_filter_type == FILTER_EXPR) {
- CHKiRet(vm.Construct(&pVM));
- CHKiRet(vm.ConstructFinalize(pVM));
- CHKiRet(vm.SetMsg(pVM, pMsg));
- CHKiRet(vm.ExecProg(pVM, f->f_filterData.f_expr->pVmprg));
- CHKiRet(vm.PopBoolFromStack(pVM, &pResult));
- dbgprintf("result of expression evaluation: %lld\n", pResult->val.num);
- /* VM is destructed on function exit */
- bRet = (pResult->val.num) ? 1 : 0;
- } else {
- assert(f->f_filter_type == FILTER_PROP); /* assert() just in case... */
- pszPropVal = MsgGetProp(pMsg, NULL, f->f_filterData.prop.pCSPropName, &pbMustBeFreed);
-
- /* Now do the compares (short list currently ;)) */
- switch(f->f_filterData.prop.operation ) {
- case FIOP_CONTAINS:
- if(rsCStrLocateInSzStr(f->f_filterData.prop.pCSCompValue, (uchar*) pszPropVal) != -1)
- bRet = 1;
- break;
- case FIOP_ISEQUAL:
- if(rsCStrSzStrCmp(f->f_filterData.prop.pCSCompValue,
- (uchar*) pszPropVal, strlen(pszPropVal)) == 0)
- bRet = 1; /* process message! */
- break;
- case FIOP_STARTSWITH:
- if(rsCStrSzStrStartsWithCStr(f->f_filterData.prop.pCSCompValue,
- (uchar*) pszPropVal, strlen(pszPropVal)) == 0)
- bRet = 1; /* process message! */
- break;
- case FIOP_REGEX:
- if(rsCStrSzStrMatchRegex(f->f_filterData.prop.pCSCompValue,
- (unsigned char*) pszPropVal) == 0)
- bRet = 1;
- break;
- default:
- /* here, it handles NOP (for performance reasons) */
- assert(f->f_filterData.prop.operation == FIOP_NOP);
- bRet = 1; /* as good as any other default ;) */
- break;
- }
-
- /* now check if the value must be negated */
- if(f->f_filterData.prop.isNegated)
- bRet = (bRet == 1) ? 0 : 1;
-
- if(Debug) {
- dbgprintf("Filter: check for property '%s' (value '%s') ",
- rsCStrGetSzStrNoNULL(f->f_filterData.prop.pCSPropName),
- pszPropVal);
- if(f->f_filterData.prop.isNegated)
- dbgprintf("NOT ");
- dbgprintf("%s '%s': %s\n",
- getFIOPName(f->f_filterData.prop.operation),
- rsCStrGetSzStrNoNULL(f->f_filterData.prop.pCSCompValue),
- bRet ? "TRUE" : "FALSE");
- }
-
- /* cleanup */
- if(pbMustBeFreed)
- free(pszPropVal);
- }
-
-finalize_it:
- /* destruct in any case, not just on error, but it makes error handling much easier */
- if(pVM != NULL)
- vm.Destruct(&pVM);
-
- if(pResult != NULL)
- var.Destruct(&pResult);
-
- *bProcessMsg = bRet;
- RETiRet;
-}
-
-
-/* helper to processMsg(), used to call the configured actions. It is
- * executed from within llExecFunc() of the action list.
- * rgerhards, 2007-08-02
- */
-typedef struct processMsgDoActions_s {
- int bPrevWasSuspended; /* was the previous action suspended? */
- msg_t *pMsg;
-} processMsgDoActions_t;
-DEFFUNC_llExecFunc(processMsgDoActions)
-{
- DEFiRet;
- rsRetVal iRetMod; /* return value of module - we do not always pass that back */
- action_t *pAction = (action_t*) pData;
- processMsgDoActions_t *pDoActData = (processMsgDoActions_t*) pParam;
-
- assert(pAction != NULL);
-
- if((pAction->bExecWhenPrevSusp == 1) && (pDoActData->bPrevWasSuspended == 0)) {
- dbgprintf("not calling action because the previous one is not suspended\n");
- ABORT_FINALIZE(RS_RET_OK);
- }
-
- iRetMod = actionCallAction(pAction, pDoActData->pMsg);
- if(iRetMod == RS_RET_DISCARDMSG) {
- ABORT_FINALIZE(RS_RET_DISCARDMSG);
- } else if(iRetMod == RS_RET_SUSPENDED) {
- /* indicate suspension for next module to be called */
- pDoActData->bPrevWasSuspended = 1;
- } else {
- pDoActData->bPrevWasSuspended = 0;
- }
-
-finalize_it:
- RETiRet;
-}
-
-
-/* Process (consume) a received message. Calls the actions configured.
- * rgerhards, 2005-10-13
- */
-static void
-processMsg(msg_t *pMsg)
-{
- selector_t *f;
- int bContinue;
- int bProcessMsg;
- processMsgDoActions_t DoActData;
- rsRetVal iRet;
-
- BEGINfunc
- assert(pMsg != NULL);
-
- /* log the message to the particular outputs */
-
- bContinue = 1;
- for (f = Files; f != NULL && bContinue ; f = f->f_next) {
- /* first check the filters... */
- iRet = shouldProcessThisMessage(f, pMsg, &bProcessMsg);
- if(!bProcessMsg) {
- continue;
- }
-
- /* ok -- from here, we have action-specific code, nothing really selector-specific -- rger 2007-08-01 */
- DoActData.pMsg = pMsg;
- DoActData.bPrevWasSuspended = 0;
- if(llExecFunc(&f->llActList, processMsgDoActions, (void*)&DoActData) == RS_RET_DISCARDMSG)
- bContinue = 0;
- }
- ENDfunc
-}
-
/* The consumer of dequeued messages. This function is called by the
* queue engine on dequeueing of a message. It runs on a SEPARATE
@@ -1178,7 +937,10 @@ msgConsumer(void __attribute__((unused)) *notNeeded, void *pUsr)
assert(pMsg != NULL);
- processMsg(pMsg);
+ if((pMsg->msgFlags & NEEDS_PARSING) != 0) {
+ parseMsg(pMsg);
+ }
+ ruleset.ProcessMsg(pMsg);
msgDestruct(&pMsg);
RETiRet;
@@ -1191,12 +953,14 @@ msgConsumer(void __attribute__((unused)) *notNeeded, void *pUsr)
* to after the terminating SP. The caller must ensure that the
* provided buffer is large enough to hold the to be extracted value.
* Returns 0 if everything is fine or 1 if either the field is not
- * SP-terminated or any other error occurs.
- * rger, 2005-11-24
+ * SP-terminated or any other error occurs. -- rger, 2005-11-24
+ * The function now receives the size of the string and makes sure
+ * that it does not process more than that. The *pLenStr counter is
+ * updated on exit. -- rgerhards, 2009-09-23
*/
-static int parseRFCField(char **pp2parse, char *pResult)
+static int parseRFCField(uchar **pp2parse, uchar *pResult, int *pLenStr)
{
- char *p2parse;
+ uchar *p2parse;
int iRet = 0;
assert(pp2parse != NULL);
@@ -1206,14 +970,17 @@ static int parseRFCField(char **pp2parse, char *pResult)
p2parse = *pp2parse;
/* this is the actual parsing loop */
- while(*p2parse && *p2parse != ' ') {
+ while(*pLenStr > 0 && *p2parse != ' ') {
*pResult++ = *p2parse++;
+ --(*pLenStr);
}
- if(*p2parse == ' ')
+ if(*pLenStr > 0 && *p2parse == ' ') {
++p2parse; /* eat SP, but only if not at end of string */
- else
+ --(*pLenStr);
+ } else {
iRet = 1; /* there MUST be an SP! */
+ }
*pResult = '\0';
/* set the new parse pointer */
@@ -1229,20 +996,24 @@ static int parseRFCField(char **pp2parse, char *pResult)
* to after the terminating SP. The caller must ensure that the
* provided buffer is large enough to hold the to be extracted value.
* Returns 0 if everything is fine or 1 if either the field is not
- * SP-terminated or any other error occurs.
- * rger, 2005-11-24
+ * SP-terminated or any other error occurs. -- rger, 2005-11-24
+ * The function now receives the size of the string and makes sure
+ * that it does not process more than that. The *pLenStr counter is
+ * updated on exit. -- rgerhards, 2009-09-23
*/
-static int parseRFCStructuredData(char **pp2parse, char *pResult)
+static int parseRFCStructuredData(uchar **pp2parse, uchar *pResult, int *pLenStr)
{
- char *p2parse;
+ uchar *p2parse;
int bCont = 1;
int iRet = 0;
+ int lenStr;
assert(pp2parse != NULL);
assert(*pp2parse != NULL);
assert(pResult != NULL);
p2parse = *pp2parse;
+ lenStr = *pLenStr;
/* this is the actual parsing loop
* Remeber: structured data starts with [ and includes any characters
@@ -1250,44 +1021,59 @@ static int parseRFCStructuredData(char **pp2parse, char *pResult)
* structured data. There may also be \] inside the structured data, which
* do NOT terminate an element.
*/
- if(*p2parse != '[')
+ if(lenStr == 0 || *p2parse != '[')
return 1; /* this is NOT structured data! */
if(*p2parse == '-') { /* empty structured data? */
*pResult++ = '-';
++p2parse;
+ --lenStr;
} else {
while(bCont) {
- if(*p2parse == '\0') {
- iRet = 1; /* this is not valid! */
- bCont = 0;
+ if(lenStr < 2) {
+ /* we now need to check if we have only structured data */
+ if(lenStr > 0 && *p2parse == ']') {
+ *pResult++ = *p2parse;
+ p2parse++;
+ lenStr--;
+ bCont = 0;
+ } else {
+ iRet = 1; /* this is not valid! */
+ bCont = 0;
+ }
} else if(*p2parse == '\\' && *(p2parse+1) == ']') {
/* this is escaped, need to copy both */
*pResult++ = *p2parse++;
*pResult++ = *p2parse++;
+ lenStr -= 2;
} else if(*p2parse == ']' && *(p2parse+1) == ' ') {
/* found end, just need to copy the ] and eat the SP */
*pResult++ = *p2parse;
p2parse += 2;
+ lenStr -= 2;
bCont = 0;
} else {
*pResult++ = *p2parse++;
+ --lenStr;
}
}
}
- if(*p2parse == ' ')
+ if(lenStr > 0 && *p2parse == ' ') {
++p2parse; /* eat SP, but only if not at end of string */
- else
+ --lenStr;
+ } else {
iRet = 1; /* there MUST be an SP! */
+ }
*pResult = '\0';
/* set the new parse pointer */
*pp2parse = p2parse;
+ *pLenStr = lenStr;
return 0;
}
-/* parse a RFC-formatted syslog message. This function returns
+/* parse a RFC5424-formatted syslog message. This function returns
* 0 if processing of the message shall continue and 1 if something
* went wrong and this messe should be ignored. This function has been
* implemented in the effort to support syslog-protocol. Please note that
@@ -1304,26 +1090,30 @@ static int parseRFCStructuredData(char **pp2parse, char *pResult)
*
* rger, 2005-11-24
*/
-static int parseRFCSyslogMsg(msg_t *pMsg, int flags)
+int parseRFCSyslogMsg(msg_t *pMsg, int flags)
{
- char *p2parse;
- char *pBuf;
+ uchar *p2parse;
+ uchar *pBuf;
+ int lenMsg;
int bContParse = 1;
+ BEGINfunc
assert(pMsg != NULL);
- assert(pMsg->pszUxTradMsg != NULL);
- p2parse = (char*) pMsg->pszUxTradMsg;
+ assert(pMsg->pszRawMsg != NULL);
+ p2parse = pMsg->pszRawMsg + pMsg->offAfterPRI; /* point to start of text, after PRI */
+ lenMsg = pMsg->iLenRawMsg - pMsg->offAfterPRI;
- /* do a sanity check on the version and eat it */
+ /* do a sanity check on the version and eat it (the caller checked this already) */
assert(p2parse[0] == '1' && p2parse[1] == ' ');
p2parse += 2;
+ lenMsg -= 2;
/* Now get us some memory we can use as a work buffer while parsing.
* We simply allocated a buffer sufficiently large to hold all of the
* message, so we can not run into any troubles. I think this is
* more wise then to use individual buffers.
*/
- if((pBuf = malloc(sizeof(char)* strlen(p2parse) + 1)) == NULL)
+ if((pBuf = malloc(sizeof(uchar) * (lenMsg + 1))) == NULL)
return 1;
/* IMPORTANT NOTE:
@@ -1334,55 +1124,51 @@ static int parseRFCSyslogMsg(msg_t *pMsg, int flags)
*/
/* TIMESTAMP */
- if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse) == RS_RET_OK) {
+ if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse, &lenMsg) == RS_RET_OK) {
if(flags & IGNDATE) {
/* we need to ignore the msg data, so simply copy over reception date */
memcpy(&pMsg->tTIMESTAMP, &pMsg->tRcvdAt, sizeof(struct syslogTime));
}
} else {
- dbgprintf("no TIMESTAMP detected!\n");
+ DBGPRINTF("no TIMESTAMP detected!\n");
bContParse = 0;
}
/* HOSTNAME */
if(bContParse) {
- parseRFCField(&p2parse, pBuf);
- MsgSetHOSTNAME(pMsg, pBuf);
- } else {
- /* we can not parse, so we get the system we
- * received the data from.
- */
- MsgSetHOSTNAME(pMsg, getRcvFrom(pMsg));
+ parseRFCField(&p2parse, pBuf, &lenMsg);
+ MsgSetHOSTNAME(pMsg, pBuf, ustrlen(pBuf));
}
/* APP-NAME */
if(bContParse) {
- parseRFCField(&p2parse, pBuf);
- MsgSetAPPNAME(pMsg, pBuf);
+ parseRFCField(&p2parse, pBuf, &lenMsg);
+ MsgSetAPPNAME(pMsg, (char*)pBuf);
}
/* PROCID */
if(bContParse) {
- parseRFCField(&p2parse, pBuf);
- MsgSetPROCID(pMsg, pBuf);
+ parseRFCField(&p2parse, pBuf, &lenMsg);
+ MsgSetPROCID(pMsg, (char*)pBuf);
}
/* MSGID */
if(bContParse) {
- parseRFCField(&p2parse, pBuf);
- MsgSetMSGID(pMsg, pBuf);
+ parseRFCField(&p2parse, pBuf, &lenMsg);
+ MsgSetMSGID(pMsg, (char*)pBuf);
}
/* STRUCTURED-DATA */
if(bContParse) {
- parseRFCStructuredData(&p2parse, pBuf);
- MsgSetStructuredData(pMsg, pBuf);
+ parseRFCStructuredData(&p2parse, pBuf, &lenMsg);
+ MsgSetStructuredData(pMsg, (char*)pBuf);
}
/* MSG */
- MsgSetMSG(pMsg, p2parse);
+ MsgSetMSGoffs(pMsg, p2parse - pMsg->pszRawMsg);
free(pBuf);
+ ENDfunc
return 0; /* all ok */
}
@@ -1400,40 +1186,41 @@ static int parseRFCSyslogMsg(msg_t *pMsg, int flags)
* but I thought I log it in this comment.
* rgerhards, 2006-01-10
*/
-static int parseLegacySyslogMsg(msg_t *pMsg, int flags)
+int parseLegacySyslogMsg(msg_t *pMsg, int flags)
{
- char *p2parse;
- char *pBuf;
- char *pWork;
- cstr_t *pStrB;
- int iCnt;
- int bTAGCharDetected;
+ uchar *p2parse;
+ int lenMsg;
+ int i; /* general index for parsing */
+ uchar bufParseTAG[CONF_TAG_MAXSIZE];
+ uchar bufParseHOSTNAME[CONF_HOSTNAME_MAXSIZE];
BEGINfunc
assert(pMsg != NULL);
- assert(pMsg->pszUxTradMsg != NULL);
- p2parse = (char*) pMsg->pszUxTradMsg;
+ assert(pMsg->pszRawMsg != NULL);
+ lenMsg = pMsg->iLenRawMsg - pMsg->offAfterPRI; /* note: offAfterPRI is already the number of PRI chars (do not add one!) */
+ p2parse = pMsg->pszRawMsg + pMsg->offAfterPRI; /* point to start of text, after PRI */
/* Check to see if msg contains a timestamp. We start by assuming
- * that the message timestamp is the time of reciption (which we
+ * that the message timestamp is the time of reception (which we
* generated ourselfs and then try to actually find one inside the
* message. There we go from high-to low precison and are done
* when we find a matching one. -- rgerhards, 2008-09-16
*/
- if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse) == RS_RET_OK) {
+ if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse, &lenMsg) == RS_RET_OK) {
/* we are done - parse pointer is moved by ParseTIMESTAMP3339 */;
- } else if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &p2parse) == RS_RET_OK) {
+ } else if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &p2parse, &lenMsg) == RS_RET_OK) {
/* we are done - parse pointer is moved by ParseTIMESTAMP3164 */;
- } else if(*p2parse == ' ') { /* try to see if it is slighly malformed - HP procurve seems to do that sometimes */
+ } else if(*p2parse == ' ' && lenMsg > 1) { /* try to see if it is slighly malformed - HP procurve seems to do that sometimes */
++p2parse; /* move over space */
- if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &p2parse) == RS_RET_OK) {
+ --lenMsg;
+ if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &p2parse, &lenMsg) == RS_RET_OK) {
/* indeed, we got it! */
/* we are done - parse pointer is moved by ParseTIMESTAMP3164 */;
- } else {
- /* parse pointer needs to be restored, as we moved it off-by-one
+ } else {/* parse pointer needs to be restored, as we moved it off-by-one
* for this try.
*/
--p2parse;
+ ++lenMsg;
}
}
@@ -1460,50 +1247,32 @@ static int parseLegacySyslogMsg(msg_t *pMsg, int flags)
* If I find them, I set a simple flag but continue. After parsing, I check the flag.
* If it was set, then we most probably do not have a hostname but a TAG. Thus, I change
* the fields. I think this logic shall work with any type of syslog message.
+ * rgerhards, 2009-06-23: and I now have extended this logic to every character
+ * that is not a valid hostname.
*/
- bTAGCharDetected = 0;
- if(pMsg->bParseHOSTNAME) {
- /* TODO: quick and dirty memory allocation */
- /* the memory allocated is far too much in most cases. But on the plus side,
- * it is quite fast... - rgerhards, 2007-09-20
- */
- if((pBuf = malloc(sizeof(char)* (strlen(p2parse) +1))) == NULL)
- return 1;
- pWork = pBuf;
- /* this is the actual parsing loop */
- while(*p2parse && *p2parse != ' ' && *p2parse != ':') {
- if(*p2parse == '[' || *p2parse == ']' || *p2parse == '/')
- bTAGCharDetected = 1;
- *pWork++ = *p2parse++;
+ if(lenMsg > 0 && flags & PARSE_HOSTNAME) {
+ i = 0;
+ while(i < lenMsg && (isalnum(p2parse[i]) || p2parse[i] == '.' || p2parse[i] == '.'
+ || p2parse[i] == '_' || p2parse[i] == '-') && i < (CONF_HOSTNAME_MAXSIZE - 1)) {
+ bufParseHOSTNAME[i] = p2parse[i];
+ ++i;
+ }
+
+ if(i == lenMsg) {
+ /* we have a message that is empty immediately after the hostname,
+ * but the hostname thus is valid! -- rgerhards, 2010-02-22
+ */
+ p2parse += i;
+ lenMsg -= i;
+ bufParseHOSTNAME[i] = '\0';
+ MsgSetHOSTNAME(pMsg, bufParseHOSTNAME, i);
+ } else if(i > 0 && p2parse[i] == ' ' && isalnum(p2parse[i-1])) {
+ /* we got a hostname! */
+ p2parse += i + 1; /* "eat" it (including SP delimiter) */
+ lenMsg -= i + 1;
+ bufParseHOSTNAME[i] = '\0';
+ MsgSetHOSTNAME(pMsg, bufParseHOSTNAME, i);
}
- /* we need to handle ':' seperately, because it terminates the
- * TAG - so we also need to terminate the parser here!
- * rgerhards, 2007-09-10 *p2parse points to a valid address here in
- * any case. We can reach this point only if we are at end of string,
- * or we have a ':' or ' '. What the if below does is check if we are
- * not at end of string and, if so, advance the parse pointer. If we
- * are already at end of string, *p2parse is equal to '\0', neither if
- * will be true and the parse pointer remain as is. This is perfectly
- * well.
- */
- if(*p2parse == ':') {
- bTAGCharDetected = 1;
- /* We will move hostname to tag, so preserve ':' (otherwise we
- * will needlessly change the message format) */
- *pWork++ = *p2parse++;
- } else if(*p2parse == ' ')
- ++p2parse;
- *pWork = '\0';
- MsgAssignHOSTNAME(pMsg, pBuf);
- }
- /* check if we seem to have a TAG */
- if(bTAGCharDetected) {
- /* indeed, this smells like a TAG, so lets use it for this. We take
- * the HOSTNAME from the sender system instead.
- */
- dbgprintf("HOSTNAME contains invalid characters, assuming it to be a TAG.\n");
- moveHOSTNAMEtoTAG(pMsg);
- MsgSetHOSTNAME(pMsg, getRcvFrom(pMsg));
}
/* now parse TAG - that should be present in message from all sources.
@@ -1519,69 +1288,39 @@ static int parseLegacySyslogMsg(msg_t *pMsg, int flags)
* in RFC3164...). We now receive the full size, but will modify the
* outputs so that only 32 characters max are used by default.
*/
- /* The following code in general is quick & dirty - I need to get
- * it going for a test, rgerhards 2004-11-16 */
- /* lol.. we tried to solve it, just to remind ourselfs that 32 octets
- * is the max size ;) we need to shuffle the code again... Just for
- * the records: the code is currently clean, but we could optimize it! */
- if(!bTAGCharDetected) {
- uchar *pszTAG;
- if(rsCStrConstruct(&pStrB) != RS_RET_OK)
- return 1;
- rsCStrSetAllocIncrement(pStrB, 33);
- pWork = pBuf;
- iCnt = 0;
- while(*p2parse && *p2parse != ':' && *p2parse != ' ') {
- rsCStrAppendChar(pStrB, *p2parse++);
- ++iCnt;
- }
- if(*p2parse == ':') {
- ++p2parse;
- rsCStrAppendChar(pStrB, ':');
- }
- rsCStrFinish(pStrB);
-
- rsCStrConvSzStrAndDestruct(pStrB, &pszTAG, 1);
- if(pszTAG == NULL)
- { /* rger, 2005-11-10: no TAG found - this implies that what
- * we have considered to be the HOSTNAME is most probably the
- * TAG. We consider it so probable, that we now adjust it
- * that way. So we pick up the previously set hostname, assign
- * it to tag and use the sender system (from IP stack) as
- * the hostname. This situation is the standard case with
- * stock BSD syslogd.
- */
- dbgprintf("No TAG in message, assuming that HOSTNAME is missing.\n");
- moveHOSTNAMEtoTAG(pMsg);
- MsgSetHOSTNAME(pMsg, getRcvFrom(pMsg));
- } else { /* we have a TAG, so we can happily set it ;) */
- MsgAssignTAG(pMsg, pszTAG);
- }
- } else {
- /* we have no TAG, so we ... */
- /*DO NOTHING*/;
+ i = 0;
+ while(lenMsg > 0 && *p2parse != ':' && *p2parse != ' ' && i < CONF_TAG_MAXSIZE) {
+ bufParseTAG[i++] = *p2parse++;
+ --lenMsg;
}
- } else {
- /* we enter this code area when the user has instructed rsyslog NOT
+ if(lenMsg > 0 && *p2parse == ':') {
+ ++p2parse;
+ --lenMsg;
+ bufParseTAG[i++] = ':';
+ }
+
+ /* no TAG can only be detected if the message immediatly ends, in which case an empty TAG
+ * is considered OK. So we do not need to check for empty TAG. -- rgerhards, 2009-06-23
+ */
+ bufParseTAG[i] = '\0'; /* terminate string */
+ MsgSetTAG(pMsg, bufParseTAG, i);
+ } else {/* we enter this code area when the user has instructed rsyslog NOT
* to parse HOSTNAME and TAG - rgerhards, 2006-03-13
*/
- if(!(flags & INTERNAL_MSG))
- {
- dbgprintf("HOSTNAME and TAG not parsed by user configuraton.\n");
- MsgSetHOSTNAME(pMsg, getRcvFrom(pMsg));
+ if(!(flags & INTERNAL_MSG)) {
+ DBGPRINTF("HOSTNAME and TAG not parsed by user configuraton.\n");
}
}
/* The rest is the actual MSG */
- MsgSetMSG(pMsg, p2parse);
+ MsgSetMSGoffs(pMsg, p2parse - pMsg->pszRawMsg);
ENDfunc
return 0; /* all ok */
}
-/* submit a fully created message to the main message queue. The message is
- * fully processed and parsed, so no parsing at all happens. This is primarily
+/* submit a message to the main message queue. This is primarily
* a hook to prevent the need for callers to know about the main message queue
* (which may change in the future as we will probably have multiple rule
* sets and thus queues...).
@@ -1595,7 +1334,29 @@ submitMsg(msg_t *pMsg)
ISOBJ_TYPE_assert(pMsg, msg);
MsgPrepareEnqueue(pMsg);
- queueEnqObj(pMsgQueue, pMsg->flowCtlType, (void*) pMsg);
+ qqueueEnqObj(pMsgQueue, pMsg->flowCtlType, (void*) pMsg);
+
+ RETiRet;
+}
+
+
+/* submit multiple messages at once, very similar to submitMsg, just
+ * for multi_submit_t.
+ * rgerhards, 2009-06-16
+ */
+rsRetVal
+multiSubmitMsg(multi_submit_t *pMultiSub)
+{
+ int i;
+ DEFiRet;
+ assert(pMultiSub != NULL);
+
+ for(i = 0 ; i < pMultiSub->nElem ; ++i) {
+ MsgPrepareEnqueue(pMultiSub->ppMsgs[i]);
+ }
+
+ iRet = qqueueMultiEnqObj(pMsgQueue, pMultiSub);
+ pMultiSub->nElem = 0;
RETiRet;
}
@@ -1626,9 +1387,10 @@ logmsg(msg_t *pMsg, int flags)
BEGINfunc
assert(pMsg != NULL);
- assert(pMsg->pszUxTradMsg != NULL);
- msg = (char*) pMsg->pszUxTradMsg;
- dbgprintf("logmsg: flags %x, from '%s', msg %s\n", flags, getRcvFrom(pMsg), msg);
+ assert(pMsg->pszRawMsg != NULL);
+
+ msg = (char*) pMsg->pszRawMsg + pMsg->offAfterPRI; /* point to start of text, after PRI */
+ DBGPRINTF("logmsg: flags %x, from '%s', msg %s\n", flags, getRcvFrom(pMsg), msg);
/* rger 2005-11-24 (happy thanksgiving!): we now need to check if we have
* a traditional syslog message or one formatted according to syslog-protocol.
@@ -1636,14 +1398,14 @@ logmsg(msg_t *pMsg, int flags)
* -protocol VERSION field for the detection.
*/
if(msg[0] == '1' && msg[1] == ' ') {
- dbgprintf("Message has syslog-protocol format.\n");
+ DBGPRINTF("Message has syslog-protocol format.\n");
setProtocolVersion(pMsg, 1);
if(parseRFCSyslogMsg(pMsg, flags) == 1) {
msgDestruct(&pMsg);
return;
}
} else { /* we have legacy syslog */
- dbgprintf("Message has legacy syslog format.\n");
+ DBGPRINTF("Message has legacy syslog format.\n");
setProtocolVersion(pMsg, 0);
if(parseLegacySyslogMsg(pMsg, flags) == 1) {
msgDestruct(&pMsg);
@@ -1656,7 +1418,7 @@ logmsg(msg_t *pMsg, int flags)
/* now submit the message to the main queue - then we are done */
pMsg->msgFlags = flags;
MsgPrepareEnqueue(pMsg);
- queueEnqObj(pMsgQueue, pMsg->flowCtlType, (void*) pMsg);
+ qqueueEnqObj(pMsgQueue, pMsg->flowCtlType, (void*) pMsg);
ENDfunc
}
@@ -1683,7 +1445,6 @@ reapchild()
DEFFUNC_llExecFunc(flushRptdMsgsActions)
{
action_t *pAction = (action_t*) pData;
-
assert(pAction != NULL);
BEGINfunc
@@ -1693,7 +1454,7 @@ DEFFUNC_llExecFunc(flushRptdMsgsActions)
* in an acceptable way. -- rgerhards, 2008-09-16
*/
if (pAction->f_prevcount && time(NULL) >= REPEATTIME(pAction)) {
- dbgprintf("flush %s: repeated %d times, %d sec.\n",
+ DBGPRINTF("flush %s: repeated %d times, %d sec.\n",
module.GetStateName(pAction->pMod), pAction->f_prevcount,
repeatinterval[pAction->f_repeatcount]);
actionWriteToAction(pAction);
@@ -1706,32 +1467,38 @@ DEFFUNC_llExecFunc(flushRptdMsgsActions)
}
-/* This method flushes reapeat messages.
+/* This method flushes repeat messages.
*/
static void
doFlushRptdMsgs(void)
{
- register selector_t *f;
-
- /* see if we need to flush any "message repeated n times"...
- * Note that this interferes with objects running on other threads.
- * We are using appropriate locking inside the function to handle that.
- */
- for (f = Files; f != NULL ; f = f->f_next) {
- llExecFunc(&f->llActList, flushRptdMsgsActions, NULL);
- }
+ ruleset.IterateAllActions(flushRptdMsgsActions, NULL);
}
static void debug_switch()
{
+ time_t tTime;
+ struct tm tp;
struct sigaction sigAct;
+ time(&tTime);
+ localtime_r(&tTime, &tp);
if(debugging_on == 0) {
debugging_on = 1;
- dbgprintf("Switching debugging_on to true\n");
+ dbgprintf("\n");
+ dbgprintf("\n");
+ dbgprintf("********************************************************************************\n");
+ dbgprintf("Switching debugging_on to true at %2.2d:%2.2d:%2.2d\n",
+ tp.tm_hour, tp.tm_min, tp.tm_sec);
+ dbgprintf("********************************************************************************\n");
} else {
- dbgprintf("Switching debugging_on to false\n");
+ dbgprintf("********************************************************************************\n");
+ dbgprintf("Switching debugging_on to false at %2.2d:%2.2d:%2.2d\n",
+ tp.tm_hour, tp.tm_min, tp.tm_sec);
+ dbgprintf("********************************************************************************\n");
+ dbgprintf("\n");
+ dbgprintf("\n");
debugging_on = 0;
}
@@ -1875,7 +1642,7 @@ void legacyOptsParseTCP(char ch, char *arg)
* a minimal delay, but it is much cleaner than the approach of doing everything
* inside the signal handler.
* rgerhards, 2005-10-26
- * Note: we do not call dbgprintf() as this may cause us to block in case something
+ * Note: we do not call DBGPRINTF() as this may cause us to block in case something
* with the threading is wrong.
*/
static void doDie(int sig)
@@ -1904,10 +1671,19 @@ static void doDie(int sig)
static void
freeAllDynMemForTermination(void)
{
- if(pszMainMsgQFName != NULL)
- free(pszMainMsgQFName);
- if(pModDir != NULL)
- free(pModDir);
+ free(pszMainMsgQFName);
+ free(pModDir);
+ free(pszConfDAGFile);
+}
+
+
+/* Finalize and destruct all actions.
+ */
+static inline void
+destructAllActions(void)
+{
+ ruleset.DestructAllActions();
+ bHaveMainQueue = 0; // flag that internal messages need to be temporarily stored
}
@@ -1924,7 +1700,7 @@ die(int sig)
{
char buf[256];
- dbgprintf("exiting on signal %d\n", sig);
+ DBGPRINTF("exiting on signal %d\n", sig);
/* IMPORTANT: we should close the inputs first, and THEN send our termination
* message. If we do it the other way around, logmsgInternal() may block on
@@ -1939,8 +1715,8 @@ die(int sig)
*/
/* close the inputs */
- dbgprintf("Terminating input threads...\n");
- thrdTerminateAll(); /* TODO: inputs only, please */
+ DBGPRINTF("Terminating input threads...\n");
+ thrdTerminateAll();
/* and THEN send the termination log message (see long comment above) */
if (sig) {
@@ -1953,17 +1729,17 @@ die(int sig)
}
/* drain queue (if configured so) and stop main queue worker thread pool */
- dbgprintf("Terminating main queue...\n");
- queueDestruct(&pMsgQueue);
+ DBGPRINTF("Terminating main queue...\n");
+ qqueueDestruct(&pMsgQueue);
pMsgQueue = NULL;
/* Free ressources and close connections. This includes flushing any remaining
* repeated msgs.
*/
- dbgprintf("Terminating outputs...\n");
- freeSelectors();
+ DBGPRINTF("Terminating outputs...\n");
+ destructAllActions();
- dbgprintf("all primary multi-thread sources have been terminated - now doing aux cleanup...\n");
+ DBGPRINTF("all primary multi-thread sources have been terminated - now doing aux cleanup...\n");
/* rger 2005-02-22
* now clean up the in-memory structures. OK, the OS
* would also take care of that, but if we do it
@@ -1986,6 +1762,12 @@ die(int sig)
legacyOptsFree();
+ /* destruct our global properties */
+ if(pInternalInputName != NULL)
+ prop.Destruct(&pInternalInputName);
+ if(pLocalHostIP != NULL)
+ prop.Destruct(&pLocalHostIP);
+
/* terminate the remaining classes */
GlobalClassExit();
@@ -1997,11 +1779,11 @@ die(int sig)
* rgerhards, 2007-08-03
* I have added some code now, but all that mod init/de-init should be moved to
* init, so that modules are unloaded and reloaded on HUP to. Eventually it should go
- * into freeSelectors() - but that needs to be seen. -- rgerhards, 2007-08-09
+ * into destructAllActions() - but that needs to be seen. -- rgerhards, 2007-08-09
*/
module.UnloadAndDestructAll(eMOD_LINK_ALL);
- dbgprintf("Clean shutdown completed, bye\n");
+ DBGPRINTF("Clean shutdown completed, bye\n");
/* dbgClassExit MUST be the last one, because it de-inits the debug system */
dbgClassExit();
@@ -2026,7 +1808,7 @@ static void doexit()
/* set the maximum message size */
-static rsRetVal setMaxMsgSize(void __attribute__((unused)) *pVal, int iNewVal)
+static rsRetVal setMaxMsgSize(void __attribute__((unused)) *pVal, long iNewVal)
{
return glbl.SetMaxLine(iNewVal);
}
@@ -2039,127 +1821,282 @@ static rsRetVal setActionResumeInterval(void __attribute__((unused)) *pVal, int
}
+/* set the processes max number ob files (upon configuration request)
+ * 2009-04-14 rgerhards
+ */
+static rsRetVal setMaxFiles(void __attribute__((unused)) *pVal, int iFiles)
+{
+ struct rlimit maxFiles;
+ char errStr[1024];
+ DEFiRet;
+
+ maxFiles.rlim_cur = iFiles;
+ maxFiles.rlim_max = iFiles;
+
+ if(setrlimit(RLIMIT_NOFILE, &maxFiles) < 0) {
+ /* NOTE: under valgrind, we seem to be unable to extend the size! */
+ rs_strerror_r(errno, errStr, sizeof(errStr));
+ errmsg.LogError(0, RS_RET_ERR_RLIM_NOFILE, "could not set process file limit to %d: %s [kernel max %ld]",
+ iFiles, errStr, (long) maxFiles.rlim_max);
+ ABORT_FINALIZE(RS_RET_ERR_RLIM_NOFILE);
+ }
+ DBGPRINTF("Max number of files set to %d [kernel max %ld].\n", iFiles, (long) maxFiles.rlim_max);
+
+finalize_it:
+ RETiRet;
+}
+
+
/* set the processes umask (upon configuration request) */
static rsRetVal setUmask(void __attribute__((unused)) *pVal, int iUmask)
{
umask(iUmask);
- dbgprintf("umask set to 0%3.3o.\n", iUmask);
+ DBGPRINTF("umask set to 0%3.3o.\n", iUmask);
return RS_RET_OK;
}
-/* helper to freeSelectors(), used with llExecFunc() to flush
- * pending output. -- rgerhards, 2007-08-02
- * We do not need to lock the action object here as the processing
- * queue is already empty and no other threads are running when
- * we call this function. -- rgerhards, 2007-12-12
+/* drop to specified group
+ * if something goes wrong, the function never returns
+ * Note that such an abort can cause damage to on-disk structures, so we should
+ * re-design the "interface" in the long term. -- rgerhards, 2008-11-26
*/
-DEFFUNC_llExecFunc(freeSelectorsActions)
+static void doDropPrivGid(int iGid)
{
- action_t *pAction = (action_t*) pData;
+ int res;
+ uchar szBuf[1024];
- assert(pAction != NULL);
-
- /* flush any pending output */
- if(pAction->f_prevcount) {
- actionWriteToAction(pAction);
+ res = setgroups(0, NULL); /* remove all supplementary group IDs */
+ if(res) {
+ perror("could not remove supplemental group IDs");
+ exit(1);
}
-
- return RS_RET_OK; /* never fails ;) */
+ DBGPRINTF("setgroups(0, NULL): %d\n", res);
+ res = setgid(iGid);
+ if(res) {
+ /* if we can not set the userid, this is fatal, so let's unconditionally abort */
+ perror("could not set requested group id");
+ exit(1);
+ }
+ DBGPRINTF("setgid(%d): %d\n", iGid, res);
+ snprintf((char*)szBuf, sizeof(szBuf)/sizeof(uchar), "rsyslogd's groupid changed to %d", iGid);
+ logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, szBuf, 0);
}
-/* Close all open log files and free selector descriptor array.
+/* drop to specified user
+ * if something goes wrong, the function never returns
+ * Note that such an abort can cause damage to on-disk structures, so we should
+ * re-design the "interface" in the long term. -- rgerhards, 2008-11-19
*/
-static void freeSelectors(void)
+static void doDropPrivUid(int iUid)
{
- selector_t *f;
- selector_t *fPrev;
-
- if(Files != NULL) {
- dbgprintf("Freeing log structures.\n");
-
- for(f = Files ; f != NULL ; f = f->f_next) {
- llExecFunc(&f->llActList, freeSelectorsActions, NULL);
- }
-
- /* actions flushed and ready for destruction - so do that... */
- f = Files;
- while (f != NULL) {
- fPrev = f;
- f = f->f_next;
- selectorDestruct(fPrev);
- }
+ int res;
+ uchar szBuf[1024];
- /* Reflect the deletion of the selectors linked list. */
- Files = NULL;
- bHaveMainQueue = 0;
+ res = setuid(iUid);
+ if(res) {
+ /* if we can not set the userid, this is fatal, so let's unconditionally abort */
+ perror("could not set requested userid");
+ exit(1);
}
+ DBGPRINTF("setuid(%d): %d\n", iUid, res);
+ snprintf((char*)szBuf, sizeof(szBuf)/sizeof(uchar), "rsyslogd's userid changed to %d", iUid);
+ logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, szBuf, 0);
}
-/* helper to dbPrintInitInfo, to print out all actions via
+/* helper to generateConfigDAG, to print out all actions via
* the llExecFunc() facility.
* rgerhards, 2007-08-02
*/
-DEFFUNC_llExecFunc(dbgPrintInitInfoAction)
+struct dag_info {
+ FILE *fp; /* output file */
+ int iActUnit; /* current action unit number */
+ int iAct; /* current action in unit */
+ int bDiscarded; /* message discarded (config error) */
+ };
+DEFFUNC_llExecFunc(generateConfigDAGAction)
{
+ action_t *pAction;
+ uchar *pszModName;
+ uchar *pszVertexName;
+ struct dag_info *pDagInfo;
DEFiRet;
- iRet = actionDbgPrint((action_t*) pData);
- dbgprintf("\n");
+
+ pDagInfo = (struct dag_info*) pParam;
+ pAction = (action_t*) pData;
+
+ pszModName = module.GetStateName(pAction->pMod);
+
+ /* vertex */
+ if(pAction->pszName == NULL) {
+ if(!strcmp((char*)pszModName, "builtin-discard"))
+ pszVertexName = (uchar*)"discard";
+ else
+ pszVertexName = pszModName;
+ } else {
+ pszVertexName = pAction->pszName;
+ }
+
+ fprintf(pDagInfo->fp, "\tact%d_%d\t\t[label=\"%s\"%s%s]\n",
+ pDagInfo->iActUnit, pDagInfo->iAct, pszVertexName,
+ pDagInfo->bDiscarded ? " style=dotted color=red" : "",
+ (pAction->pQueue->qType == QUEUETYPE_DIRECT) ? "" : " shape=hexagon"
+ );
+
+ /* edge */
+ if(pDagInfo->iAct == 0) {
+ } else {
+ fprintf(pDagInfo->fp, "\tact%d_%d -> act%d_%d[%s%s]\n",
+ pDagInfo->iActUnit, pDagInfo->iAct - 1,
+ pDagInfo->iActUnit, pDagInfo->iAct,
+ pDagInfo->bDiscarded ? " style=dotted color=red" : "",
+ pAction->bExecWhenPrevSusp ? " label=\"only if\\nsuspended\"" : "" );
+ }
+
+ /* check for discard */
+ if(!strcmp((char*) pszModName, "builtin-discard")) {
+ fprintf(pDagInfo->fp, "\tact%d_%d\t\t[shape=box]\n",
+ pDagInfo->iActUnit, pDagInfo->iAct);
+ pDagInfo->bDiscarded = 1;
+ }
+
+
+ ++pDagInfo->iAct;
RETiRet;
}
-/* print debug information as part of init(). This pretty much
- * outputs the whole config of rsyslogd. I've moved this code
- * out of init() to clean it somewhat up.
- * rgerhards, 2007-07-31
+
+/* create config DAG
+ * This functions takes a rsyslog config and produces a .dot file for use
+ * with graphviz (http://www.graphviz.org). This is done in an effort to
+ * document, and also potentially troubleshoot, configurations. Plus, I
+ * consider it a nice feature to explain some concepts. Note that the
+ * current version only produces a graph with relatively little information.
+ * This is a foundation that may be later expanded (if it turns out to be
+ * useful enough).
+ * rgerhards, 2009-05-11
*/
-static void dbgPrintInitInfo(void)
+static rsRetVal
+generateConfigDAG(uchar *pszDAGFile)
{
- register selector_t *f;
- int iSelNbr = 1;
- int i;
+ //rule_t *f;
+ FILE *fp;
+ int iActUnit = 1;
+ //int bHasFilter = 0; /* filter associated with this action unit? */
+ //int bHadFilter;
+ //int i;
+ struct dag_info dagInfo;
+ //char *pszFilterName;
+ char szConnectingNode[64];
+ DEFiRet;
+
+ assert(pszDAGFile != NULL);
+
+ if((fp = fopen((char*) pszDAGFile, "w")) == NULL) {
+ logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)
+ "configuraton graph output file could not be opened, none generated", 0);
+ ABORT_FINALIZE(RS_RET_FILENAME_INVALID);
+ }
+
+ dagInfo.fp = fp;
- dbgprintf("\nActive selectors:\n");
- for (f = Files; f != NULL ; f = f->f_next) {
- dbgprintf("Selector %d:\n", iSelNbr++);
- if(f->pCSProgNameComp != NULL)
- dbgprintf("tag: '%s'\n", rsCStrGetSzStrNoNULL(f->pCSProgNameComp));
- if(f->eHostnameCmpMode != HN_NO_COMP)
- dbgprintf("hostname: %s '%s'\n",
- f->eHostnameCmpMode == HN_COMP_MATCH ?
- "only" : "allbut",
- rsCStrGetSzStrNoNULL(f->pCSHostnameComp));
+ /* from here on, we assume writes go well. This here is a really
+ * unimportant utility function and if something goes wrong, it has
+ * almost no effect. So let's not overdo this...
+ */
+ fprintf(fp, "# graph created by rsyslog " VERSION "\n\n"
+ "# use the dot tool from http://www.graphviz.org to visualize!\n"
+ "digraph rsyslogConfig {\n"
+ "\tinputs [shape=tripleoctagon]\n"
+ "\tinputs -> act0_0\n"
+ "\tact0_0 [label=\"main\\nqueue\" shape=hexagon]\n"
+ /*"\tmainq -> act1_0\n"*/
+ );
+ strcpy(szConnectingNode, "act0_0");
+ dagInfo.bDiscarded = 0;
+
+/* TODO: re-enable! */
+#if 0
+ for(f = Files; f != NULL ; f = f->f_next) {
+ /* BSD-Style filters are currently ignored */
+ bHadFilter = bHasFilter;
if(f->f_filter_type == FILTER_PRI) {
+ bHasFilter = 0;
for (i = 0; i <= LOG_NFACILITIES; i++)
- if (f->f_filterData.f_pmask[i] == TABLE_NOPRI)
- dbgprintf(" X ");
- else
- dbgprintf("%2X ", f->f_filterData.f_pmask[i]);
- } else if(f->f_filter_type == FILTER_EXPR) {
- dbgprintf("EXPRESSION-BASED Filter: can currently not be displayed");
+ if (f->f_filterData.f_pmask[i] != 0xff) {
+ bHasFilter = 1;
+ break;
+ }
+ } else {
+ bHasFilter = 1;
+ }
+
+ /* we know we have a filter, so it can be false */
+ switch(f->f_filter_type) {
+ case FILTER_PRI:
+ pszFilterName = "pri filter";
+ break;
+ case FILTER_PROP:
+ pszFilterName = "property filter";
+ break;
+ case FILTER_EXPR:
+ pszFilterName = "script filter";
+ break;
+ }
+
+ /* write action unit node */
+ if(bHasFilter) {
+ fprintf(fp, "\t%s -> act%d_end\t[label=\"%s:\\nfalse\"]\n",
+ szConnectingNode, iActUnit, pszFilterName);
+ fprintf(fp, "\t%s -> act%d_0\t[label=\"%s:\\ntrue\"]\n",
+ szConnectingNode, iActUnit, pszFilterName);
+ fprintf(fp, "\tact%d_end\t\t\t\t[shape=point]\n", iActUnit);
+ snprintf(szConnectingNode, sizeof(szConnectingNode), "act%d_end", iActUnit);
} else {
- dbgprintf("PROPERTY-BASED Filter:\n");
- dbgprintf("\tProperty.: '%s'\n",
- rsCStrGetSzStrNoNULL(f->f_filterData.prop.pCSPropName));
- dbgprintf("\tOperation: ");
- if(f->f_filterData.prop.isNegated)
- dbgprintf("NOT ");
- dbgprintf("'%s'\n", getFIOPName(f->f_filterData.prop.operation));
- dbgprintf("\tValue....: '%s'\n",
- rsCStrGetSzStrNoNULL(f->f_filterData.prop.pCSCompValue));
- dbgprintf("\tAction...: ");
+ fprintf(fp, "\t%s -> act%d_0\t[label=\"no filter\"]\n",
+ szConnectingNode, iActUnit);
+ snprintf(szConnectingNode, sizeof(szConnectingNode), "act%d_0", iActUnit);
}
- dbgprintf("\nActions:\n");
- llExecFunc(&f->llActList, dbgPrintInitInfoAction, NULL); /* actions */
+ /* draw individual nodes */
+ dagInfo.iActUnit = iActUnit;
+ dagInfo.iAct = 0;
+ dagInfo.bDiscarded = 0;
+ llExecFunc(&f->llActList, generateConfigDAGAction, &dagInfo); /* actions */
- dbgprintf("\n");
+ /* finish up */
+ if(bHasFilter && !dagInfo.bDiscarded) {
+ fprintf(fp, "\tact%d_%d -> %s\n",
+ iActUnit, dagInfo.iAct - 1, szConnectingNode);
+ }
+
+ ++iActUnit;
}
- dbgprintf("\n");
+#endif
+
+ fprintf(fp, "\t%s -> act%d_0\n", szConnectingNode, iActUnit);
+ fprintf(fp, "\tact%d_0\t\t[label=discard shape=box]\n"
+ "}\n", iActUnit);
+ fclose(fp);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* print debug information as part of init(). This pretty much
+ * outputs the whole config of rsyslogd. I've moved this code
+ * out of init() to clean it somewhat up.
+ * rgerhards, 2007-07-31
+ */
+static void dbgPrintInitInfo(void)
+{
+ ruleset.DebugPrintAll();
+ DBGPRINTF("\n");
if(bDebugPrintTemplateList)
tplPrintList();
if(bDebugPrintModuleList)
@@ -2169,37 +2106,59 @@ static void dbgPrintInitInfo(void)
if(bDebugPrintCfSysLineHandlerList)
dbgPrintCfSysLineHandlers();
- dbgprintf("Messages with malicious PTR DNS Records are %sdropped.\n",
+ DBGPRINTF("Messages with malicious PTR DNS Records are %sdropped.\n",
glbl.GetDropMalPTRMsgs() ? "" : "not ");
- dbgprintf("Control characters are %sreplaced upon reception.\n",
+ DBGPRINTF("Control characters are %sreplaced upon reception.\n",
bEscapeCCOnRcv? "" : "not ");
if(bEscapeCCOnRcv)
- dbgprintf("Control character escape sequence prefix is '%c'.\n",
+ DBGPRINTF("Control character escape sequence prefix is '%c'.\n",
cCCEscapeChar);
- dbgprintf("Main queue size %d messages.\n", iMainMsgQueueSize);
- dbgprintf("Main queue worker threads: %d, Perists every %d updates.\n",
- iMainMsgQueueNumWorkers, iMainMsgQPersistUpdCnt);
- dbgprintf("Main queue timeouts: shutdown: %d, action completion shutdown: %d, enq: %d\n",
+ DBGPRINTF("Main queue size %d messages.\n", iMainMsgQueueSize);
+ DBGPRINTF("Main queue worker threads: %d, wThread shutdown: %d, Perists every %d updates.\n",
+ iMainMsgQueueNumWorkers, iMainMsgQtoWrkShutdown, iMainMsgQPersistUpdCnt);
+ DBGPRINTF("Main queue timeouts: shutdown: %d, action completion shutdown: %d, enq: %d\n",
iMainMsgQtoQShutdown, iMainMsgQtoActShutdown, iMainMsgQtoEnq);
- dbgprintf("Main queue watermarks: high: %d, low: %d, discard: %d, discard-severity: %d\n",
+ DBGPRINTF("Main queue watermarks: high: %d, low: %d, discard: %d, discard-severity: %d\n",
iMainMsgQHighWtrMark, iMainMsgQLowWtrMark, iMainMsgQDiscardMark, iMainMsgQDiscardSeverity);
- dbgprintf("Main queue save on shutdown %d, max disk space allowed %lld\n",
+ DBGPRINTF("Main queue save on shutdown %d, max disk space allowed %lld\n",
bMainMsgQSaveOnShutdown, iMainMsgQueMaxDiskSpace);
/* TODO: add
iActionRetryCount = 0;
iActionRetryInterval = 30000;
- static int iMainMsgQtoWrkShutdown = 60000;
static int iMainMsgQtoWrkMinMsgs = 100;
static int iMainMsgQbSaveOnShutdown = 1;
iMainMsgQueMaxDiskSpace = 0;
- setQPROP(queueSettoWrkShutdown, "$MainMsgQueueTimeoutWorkerThreadShutdown", 5000);
- setQPROP(queueSetiMinMsgsPerWrkr, "$MainMsgQueueWorkerThreadMinimumMessages", 100);
- setQPROP(queueSetbSaveOnShutdown, "$MainMsgQueueSaveOnShutdown", 1);
+ setQPROP(qqueueSetiMinMsgsPerWrkr, "$MainMsgQueueWorkerThreadMinimumMessages", 100);
+ setQPROP(qqueueSetbSaveOnShutdown, "$MainMsgQueueSaveOnShutdown", 1);
*/
- dbgprintf("Work Directory: '%s'.\n", glbl.GetWorkDir());
+ DBGPRINTF("Work Directory: '%s'.\n", glbl.GetWorkDir());
+}
+
+
+/* Actually run the input modules. This happens after privileges are dropped,
+ * if that is requested.
+ */
+static rsRetVal
+runInputModules(void)
+{
+ modInfo_t *pMod;
+
+ BEGINfunc
+ /* loop through all modules and activate them (brr...) */
+ pMod = module.GetNxtType(NULL, eMOD_IN);
+ while(pMod != NULL) {
+ if(pMod->mod.im.bCanRun) {
+ /* activate here */
+ thrdCreate(pMod->mod.im.runInput, pMod->mod.im.afterRun);
+ }
+ pMod = module.GetNxtType(pMod, eMOD_IN);
+ }
+
+ ENDfunc
+ return RS_RET_OK; /* intentional: we do not care about module errors */
}
@@ -2218,11 +2177,10 @@ startInputModules(void)
/* loop through all modules and activate them (brr...) */
pMod = module.GetNxtType(NULL, eMOD_IN);
while(pMod != NULL) {
- if((iRet = pMod->mod.im.willRun()) == RS_RET_OK) {
- /* activate here */
- thrdCreate(pMod->mod.im.runInput, pMod->mod.im.afterRun);
- } else {
- dbgprintf("module %lx will not run, iRet %d\n", (unsigned long) pMod, iRet);
+ iRet = pMod->mod.im.willRun();
+ pMod->mod.im.bCanRun = (iRet == RS_RET_OK);
+ if(!pMod->mod.im.bCanRun) {
+ DBGPRINTF("module %lx will not run, iRet %d\n", (unsigned long) pMod, iRet);
}
pMod = module.GetNxtType(pMod, eMOD_IN);
}
@@ -2238,15 +2196,16 @@ startInputModules(void)
* else happens. -- rgerhards, 2008-07-28
*/
static rsRetVal
-init(void)
+init()
{
- DEFiRet;
rsRetVal localRet;
int iNbrActions;
int bHadConfigErr = 0;
+ ruleset_t *pRuleset;
char cbuf[BUFSIZ];
char bufStartUpMsg[512];
struct sigaction sigAct;
+ DEFiRet;
thrdTerminateAll(); /* stop all running input threads - TODO: reconsider location! */
@@ -2255,25 +2214,25 @@ init(void)
pDfltProgNameCmp = NULL;
eDfltHostnameCmpMode = HN_NO_COMP;
- dbgprintf("rsyslog %s - called init()\n", VERSION);
+ DBGPRINTF("rsyslog %s - called init()\n", VERSION);
/* delete the message queue, which also flushes all messages left over */
if(pMsgQueue != NULL) {
- dbgprintf("deleting main message queue\n");
- queueDestruct(&pMsgQueue); /* delete pThis here! */
+ DBGPRINTF("deleting main message queue\n");
+ qqueueDestruct(&pMsgQueue); /* delete pThis here! */
pMsgQueue = NULL;
}
/* Close all open log files and free log descriptor array. This also frees
* all output-modules instance data.
*/
- freeSelectors();
+ destructAllActions();
/* Unload all non-static modules */
- dbgprintf("Unloading non-static modules.\n");
+ DBGPRINTF("Unloading non-static modules.\n");
module.UnloadAndDestructAll(eMOD_LINK_DYNAMIC_LOADED);
- dbgprintf("Clearing templates.\n");
+ DBGPRINTF("Clearing templates.\n");
tplDeleteNew();
/* re-setting values to defaults (where applicable) */
@@ -2288,6 +2247,11 @@ init(void)
conf.ReInitConf();
+ /* construct the default ruleset */
+ ruleset.Construct(&pRuleset);
+ ruleset.SetName(pRuleset, UCHAR_CONSTANT("RSYSLOG_DefaultRuleset"));
+ ruleset.ConstructFinalize(pRuleset);
+
/* open the configuration file */
localRet = conf.processConfFile(ConfFile);
CHKiRet(conf.GetNbrActActions(&iNbrActions));
@@ -2309,23 +2273,23 @@ init(void)
* We ignore any errors while doing this - we would be lost anyhow...
*/
errmsg.LogError(0, NO_ERRCODE, "EMERGENCY CONFIGURATION ACTIVATED - fix rsyslog config file!");
- selector_t *f = NULL;
/* note: we previously used _POSIY_TTY_NAME_MAX+1, but this turned out to be
* too low on linux... :-S -- rgerhards, 2008-07-28
*/
char szTTYNameBuf[128];
- conf.cfline((uchar*)"*.ERR\t" _PATH_CONSOLE, &f);
- conf.cfline((uchar*)"syslog.*\t" _PATH_CONSOLE, &f);
- conf.cfline((uchar*)"*.PANIC\t*", &f);
- conf.cfline((uchar*)"syslog.*\troot", &f);
+ rule_t *pRule = NULL; /* initialization to NULL is *vitally* important! */
+ conf.cfline(UCHAR_CONSTANT("*.ERR\t" _PATH_CONSOLE), &pRule);
+ conf.cfline(UCHAR_CONSTANT("syslog.*\t" _PATH_CONSOLE), &pRule);
+ conf.cfline(UCHAR_CONSTANT("*.PANIC\t*"), &pRule);
+ conf.cfline(UCHAR_CONSTANT("syslog.*\troot"), &pRule);
if(ttyname_r(0, szTTYNameBuf, sizeof(szTTYNameBuf)) == 0) {
snprintf(cbuf,sizeof(cbuf), "*.*\t%s", szTTYNameBuf);
- conf.cfline((uchar*)cbuf, &f);
+ conf.cfline((uchar*)cbuf, &pRule);
} else {
- dbgprintf("error %d obtaining controlling terminal, not using that emergency rule\n", errno);
+ DBGPRINTF("error %d obtaining controlling terminal, not using that emergency rule\n", errno);
}
- selectorAddList(f);
+ ruleset.AddRule(ruleset.GetCurrent(), &pRule);
}
legacyOptsHook();
@@ -2361,6 +2325,10 @@ init(void)
}
}
+ /* check if we need to generate a config DAG and, if so, do that */
+ if(pszConfDAGFile != NULL)
+ generateConfigDAG(pszConfDAGFile);
+
/* we are done checking the config - now validate if we should actually run or not.
* If not, terminate. -- rgerhards, 2008-07-25
*/
@@ -2378,13 +2346,13 @@ init(void)
}
/* create message queue */
- CHKiRet_Hdlr(queueConstruct(&pMsgQueue, MainMsgQueType, iMainMsgQueueNumWorkers, iMainMsgQueueSize, msgConsumer)) {
+ CHKiRet_Hdlr(qqueueConstruct(&pMsgQueue, MainMsgQueType, iMainMsgQueueNumWorkers, iMainMsgQueueSize, msgConsumer)) {
/* no queue is fatal, we need to give up in that case... */
fprintf(stderr, "fatal error %d: could not create message queue - rsyslogd can not run!\n", iRet);
exit(1);
}
/* name our main queue object (it's not fatal if it fails...) */
- obj.SetName((obj_t*) pMsgQueue, (uchar*) "main queue");
+ obj.SetName((obj_t*) pMsgQueue, (uchar*) "main Q");
/* ... set some properties ... */
# define setQPROP(func, directive, data) \
@@ -2396,41 +2364,45 @@ init(void)
errmsg.LogError(0, NO_ERRCODE, "Invalid " #directive ", error %d. Ignored, running with default setting", iRet); \
}
- setQPROP(queueSetMaxFileSize, "$MainMsgQueueFileSize", iMainMsgQueMaxFileSize);
- setQPROP(queueSetsizeOnDiskMax, "$MainMsgQueueMaxDiskSpace", iMainMsgQueMaxDiskSpace);
- setQPROPstr(queueSetFilePrefix, "$MainMsgQueueFileName", pszMainMsgQFName);
- setQPROP(queueSetiPersistUpdCnt, "$MainMsgQueueCheckpointInterval", iMainMsgQPersistUpdCnt);
- setQPROP(queueSettoQShutdown, "$MainMsgQueueTimeoutShutdown", iMainMsgQtoQShutdown );
- setQPROP(queueSettoActShutdown, "$MainMsgQueueTimeoutActionCompletion", iMainMsgQtoActShutdown);
- setQPROP(queueSettoWrkShutdown, "$MainMsgQueueWorkerTimeoutThreadShutdown", iMainMsgQtoWrkShutdown);
- setQPROP(queueSettoEnq, "$MainMsgQueueTimeoutEnqueue", iMainMsgQtoEnq);
- setQPROP(queueSetiHighWtrMrk, "$MainMsgQueueHighWaterMark", iMainMsgQHighWtrMark);
- setQPROP(queueSetiLowWtrMrk, "$MainMsgQueueLowWaterMark", iMainMsgQLowWtrMark);
- setQPROP(queueSetiDiscardMrk, "$MainMsgQueueDiscardMark", iMainMsgQDiscardMark);
- setQPROP(queueSetiDiscardSeverity, "$MainMsgQueueDiscardSeverity", iMainMsgQDiscardSeverity);
- setQPROP(queueSetiMinMsgsPerWrkr, "$MainMsgQueueWorkerThreadMinimumMessages", iMainMsgQWrkMinMsgs);
- setQPROP(queueSetbSaveOnShutdown, "$MainMsgQueueSaveOnShutdown", bMainMsgQSaveOnShutdown);
- setQPROP(queueSetiDeqSlowdown, "$MainMsgQueueDequeueSlowdown", iMainMsgQDeqSlowdown);
- setQPROP(queueSetiDeqtWinFromHr, "$MainMsgQueueDequeueTimeBegin", iMainMsgQueueDeqtWinFromHr);
- setQPROP(queueSetiDeqtWinToHr, "$MainMsgQueueDequeueTimeEnd", iMainMsgQueueDeqtWinToHr);
+ setQPROP(qqueueSetMaxFileSize, "$MainMsgQueueFileSize", iMainMsgQueMaxFileSize);
+ setQPROP(qqueueSetsizeOnDiskMax, "$MainMsgQueueMaxDiskSpace", iMainMsgQueMaxDiskSpace);
+ setQPROPstr(qqueueSetFilePrefix, "$MainMsgQueueFileName", pszMainMsgQFName);
+ setQPROP(qqueueSetiPersistUpdCnt, "$MainMsgQueueCheckpointInterval", iMainMsgQPersistUpdCnt);
+ setQPROP(qqueueSetbSyncQueueFiles, "$MainMsgQueueSyncQueueFiles", bMainMsgQSyncQeueFiles);
+ setQPROP(qqueueSettoQShutdown, "$MainMsgQueueTimeoutShutdown", iMainMsgQtoQShutdown );
+ setQPROP(qqueueSettoActShutdown, "$MainMsgQueueTimeoutActionCompletion", iMainMsgQtoActShutdown);
+ setQPROP(qqueueSettoWrkShutdown, "$MainMsgQueueWorkerTimeoutThreadShutdown", iMainMsgQtoWrkShutdown);
+ setQPROP(qqueueSettoEnq, "$MainMsgQueueTimeoutEnqueue", iMainMsgQtoEnq);
+ setQPROP(qqueueSetiHighWtrMrk, "$MainMsgQueueHighWaterMark", iMainMsgQHighWtrMark);
+ setQPROP(qqueueSetiLowWtrMrk, "$MainMsgQueueLowWaterMark", iMainMsgQLowWtrMark);
+ setQPROP(qqueueSetiDiscardMrk, "$MainMsgQueueDiscardMark", iMainMsgQDiscardMark);
+ setQPROP(qqueueSetiDiscardSeverity, "$MainMsgQueueDiscardSeverity", iMainMsgQDiscardSeverity);
+ setQPROP(qqueueSetiMinMsgsPerWrkr, "$MainMsgQueueWorkerThreadMinimumMessages", iMainMsgQWrkMinMsgs);
+ setQPROP(qqueueSetbSaveOnShutdown, "$MainMsgQueueSaveOnShutdown", bMainMsgQSaveOnShutdown);
+ setQPROP(qqueueSetiDeqSlowdown, "$MainMsgQueueDequeueSlowdown", iMainMsgQDeqSlowdown);
+ setQPROP(qqueueSetiDeqtWinFromHr, "$MainMsgQueueDequeueTimeBegin", iMainMsgQueueDeqtWinFromHr);
+ setQPROP(qqueueSetiDeqtWinToHr, "$MainMsgQueueDequeueTimeEnd", iMainMsgQueueDeqtWinToHr);
# undef setQPROP
# undef setQPROPstr
/* ... and finally start the queue! */
- CHKiRet_Hdlr(queueStart(pMsgQueue)) {
+ CHKiRet_Hdlr(qqueueStart(pMsgQueue)) {
/* no queue is fatal, we need to give up in that case... */
fprintf(stderr, "fatal error %d: could not start message queue - rsyslogd can not run!\n", iRet);
exit(1);
}
bHaveMainQueue = (MainMsgQueType == QUEUETYPE_DIRECT) ? 0 : 1;
- dbgprintf("Main processing queue is initialized and running\n");
+ DBGPRINTF("Main processing queue is initialized and running\n");
/* the output part and the queue is now ready to run. So it is a good time
- * to start the inputs. Please note that the net code above should be
+ * to initialize the inputs. Please note that the net code above should be
* shuffled to down here once we have everything in input modules.
* rgerhards, 2007-12-14
+ * NOTE: as of 2009-06-29, the input modules are initialized, but not yet run.
+ * Keep in mind. though, that the outputs already run if the queue was
+ * persisted to disk. -- rgerhards
*/
startInputModules();
@@ -2452,56 +2424,55 @@ init(void)
sigAct.sa_handler = sighup_handler;
sigaction(SIGHUP, &sigAct, NULL);
- dbgprintf(" (re)started.\n");
+ DBGPRINTF(" (re)started.\n");
finalize_it:
RETiRet;
}
-/* add a completely-processed selector (after config line parsing) to
- * the linked list of selectors. 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.
- * 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
+/* Switch the default ruleset (that, what servcies bind to if nothing specific
+ * is specified).
+ * rgerhards, 2009-06-12
*/
-rsRetVal
-selectorAddList(selector_t *f)
+static rsRetVal
+setDefaultRuleset(void __attribute__((unused)) *pVal, uchar *pszName)
{
DEFiRet;
- int iActionCnt;
- static selector_t *nextp = NULL; /* TODO: make this go away (see comment below) */
+ CHKiRet(ruleset.SetDefaultRuleset(pszName));
- if(f != NULL) {
- CHKiRet(llGetNumElts(&f->llActList, &iActionCnt));
- if(iActionCnt == 0) {
- errmsg.LogError(0, NO_ERRCODE, "warning: selector line without actions will be discarded");
- selectorDestruct(f);
- } else {
- /* successfully created an entry */
- dbgprintf("selector line successfully processed\n");
- /* TODO: we should use the linked list class for the selector list, else we need to add globals
- * ... well nextp could be added temporarily...
- * Thanks to varmojfekoj for having the idea to just use "Files" to make this
- * code work. I had actually forgotten to fix the code here before moving to 1.18.0.
- * And, of course, I also did not migrate the selector_t structure to the linked list class.
- * However, that should still be one of the very next things to happen.
- * rgerhards, 2007-08-06
- */
- if(Files == NULL) {
- Files = f;
- } else {
- nextp->f_next = f;
- }
- nextp = f;
- }
+finalize_it:
+ free(pszName); /* no longer needed */
+ RETiRet;
+}
+
+
+/* Switch to either an already existing rule set or start a new one. The
+ * named rule set becomes the new "current" rule set (what means that new
+ * actions are added to it).
+ * rgerhards, 2009-06-12
+ */
+static rsRetVal
+setCurrRuleset(void __attribute__((unused)) *pVal, uchar *pszName)
+{
+ ruleset_t *pRuleset;
+ rsRetVal localRet;
+ DEFiRet;
+
+ localRet = ruleset.SetCurrRuleset(pszName);
+
+ if(localRet == RS_RET_NOT_FOUND) {
+ DBGPRINTF("begin new current rule set '%s'\n", pszName);
+ CHKiRet(ruleset.Construct(&pRuleset));
+ CHKiRet(ruleset.SetName(pRuleset, pszName));
+ CHKiRet(ruleset.ConstructFinalize(pRuleset));
+ } else {
+ ABORT_FINALIZE(localRet);
}
finalize_it:
+ free(pszName); /* no longer needed */
RETiRet;
}
@@ -2515,16 +2486,16 @@ static rsRetVal setMainMsgQueType(void __attribute__((unused)) *pVal, uchar *psz
if (!strcasecmp((char *) pszType, "fixedarray")) {
MainMsgQueType = QUEUETYPE_FIXED_ARRAY;
- dbgprintf("main message queue type set to FIXED_ARRAY\n");
+ DBGPRINTF("main message queue type set to FIXED_ARRAY\n");
} else if (!strcasecmp((char *) pszType, "linkedlist")) {
MainMsgQueType = QUEUETYPE_LINKEDLIST;
- dbgprintf("main message queue type set to LINKEDLIST\n");
+ DBGPRINTF("main message queue type set to LINKEDLIST\n");
} else if (!strcasecmp((char *) pszType, "disk")) {
MainMsgQueType = QUEUETYPE_DISK;
- dbgprintf("main message queue type set to DISK\n");
+ DBGPRINTF("main message queue type set to DISK\n");
} else if (!strcasecmp((char *) pszType, "direct")) {
MainMsgQueType = QUEUETYPE_DIRECT;
- dbgprintf("main message queue type set to DIRECT (no queueing at all)\n");
+ DBGPRINTF("main message queue type set to DIRECT (no queueing at all)\n");
} else {
errmsg.LogError(0, RS_RET_INVALID_PARAMS, "unknown mainmessagequeuetype parameter: %s", (char *) pszType);
iRet = RS_RET_INVALID_PARAMS;
@@ -2539,20 +2510,18 @@ static rsRetVal setMainMsgQueType(void __attribute__((unused)) *pVal, uchar *psz
* The following function is resposible for handling a SIGHUP signal. Since
* we are now doing mallocs/free as part of init we had better not being
* doing this during a signal handler. Instead this function simply sets
- * a flag variable which will tell the main loop to go through a restart.
+ * a flag variable which will tells the main loop to do "the right thing".
*/
void sighup_handler()
{
struct sigaction sigAct;
- restart = 1;
+ bHadHUP = 1;
memset(&sigAct, 0, sizeof (sigAct));
sigemptyset(&sigAct.sa_mask);
sigAct.sa_handler = sighup_handler;
sigaction(SIGHUP, &sigAct, NULL);
-
- return;
}
@@ -2574,6 +2543,47 @@ static void processImInternal(void)
}
+/* helper to doHUP(), this "HUPs" each action. The necessary locking
+ * is done inside the action class and nothing we need to take care of.
+ * rgerhards, 2008-10-22
+ */
+DEFFUNC_llExecFunc(doHUPActions)
+{
+ BEGINfunc
+ actionCallHUPHdlr((action_t*) pData);
+ ENDfunc
+ return RS_RET_OK; /* we ignore errors, we can not do anything either way */
+}
+
+
+/* This function processes a HUP after one has been detected. Note that this
+ * is *NOT* the sighup handler. The signal is recorded by the handler, that record
+ * detected inside the mainloop and then this function is called to do the
+ * real work. -- rgerhards, 2008-10-22
+ */
+static inline void
+doHUP(void)
+{
+ char buf[512];
+
+ snprintf(buf, sizeof(buf) / sizeof(char),
+ " [origin software=\"rsyslogd\" " "swVersion=\"" VERSION
+ "\" x-pid=\"%d\" x-info=\"http://www.rsyslog.com\"] rsyslogd was HUPed, type '%s'.",
+ (int) myPid, glbl.GetHUPisRestart() ? "restart" : "lightweight");
+ errno = 0;
+ logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)buf, 0);
+
+ if(glbl.GetHUPisRestart()) {
+ DBGPRINTF("Received SIGHUP, configured to be restart, reloading rsyslogd.\n");
+ init(); /* main queue is stopped as part of init() */
+ runInputModules();
+ } else {
+ DBGPRINTF("Received SIGHUP, configured to be a non-restart type of HUP - notifying actions.\n");
+ ruleset.IterateAllActions(doHUPActions, NULL);
+ }
+}
+
+
/* This is the main processing loop. It is called after successful initialization.
* When it returns, the syslogd terminates.
* Its sole function is to provide some housekeeping things. The real work is done
@@ -2601,6 +2611,7 @@ mainloop(void)
* but a once-a-day wakeup should be quite acceptable. -- rgerhards, 2008-06-09
*/
tvSelectTimeout.tv_sec = (bReduceRepeatMsgs == 1) ? TIMERINTVL : 86400 /*1 day*/;
+ //tvSelectTimeout.tv_sec = TIMERINTVL; /* TODO: change this back to the above code when we have a better solution for apc */
tvSelectTimeout.tv_usec = 0;
select(1, NULL, NULL, NULL, &tvSelectTimeout);
if(bFinished)
@@ -2630,56 +2641,16 @@ mainloop(void)
if(bReduceRepeatMsgs == 1)
doFlushRptdMsgs();
- if(restart) {
- dbgprintf("\nReceived SIGHUP, reloading rsyslogd.\n");
- /* main queue is stopped as part of init() */
- init();
- restart = 0;
+ if(bHadHUP) {
+ doHUP();
+ bHadHUP = 0;
continue;
}
+ // TODO: remove execScheduled(); /* handle Apc calls (if any) */
}
ENDfunc
}
-/* If user is not root, prints warnings or even exits
- * TODO: check all dynafiles for write permission
- * ... but it is probably better to wait here until we have
- * a module interface - rgerhards, 2007-07-23
- */
-static void checkPermissions()
-{
-#if 0
- /* TODO: this function must either be redone or removed - now with the input modules,
- * there is no such simple check we can do. What we can check, however, is if there is
- * any input module active and terminate, if not. -- rgerhards, 2007-12-26
- */
- /* we are not root */
- if (geteuid() != 0)
- {
- fputs("WARNING: Local messages will not be logged! If you want to log them, run rsyslog as root.\n",stderr);
-#ifdef SYSLOG_INET
- /* udp enabled and port number less than or equal to 1024 */
- if ( AcceptRemote && (atoi(LogPort) <= 1024) )
- fprintf(stderr, "WARNING: Will not listen on UDP port %s. Use port number higher than 1024 or run rsyslog as root!\n", LogPort);
-
- /* tcp enabled and port number less or equal to 1024 */
- if( bEnableTCP && (atoi(TCPLstnPort) <= 1024) )
- fprintf(stderr, "WARNING: Will not listen on TCP port %s. Use port number higher than 1024 or run rsyslog as root!\n", TCPLstnPort);
-
- /* Neither explicit high UDP port nor explicit high TCP port.
- * It is useless to run anymore */
- if( !(AcceptRemote && (atoi(LogPort) > 1024)) && !( bEnableTCP && (atoi(TCPLstnPort) > 1024)) )
- {
-#endif
- fprintf(stderr, "ERROR: Nothing to log, no reason to run. Please run rsyslog as root.\n");
- exit(EXIT_FAILURE);
-#ifdef SYSLOG_INET
- }
-#endif
- }
-#endif
-}
-
/* load build-in modules
* very first version begun on 2007-07-23 by rgerhards
@@ -2688,23 +2659,26 @@ static rsRetVal loadBuildInModules(void)
{
DEFiRet;
- if((iRet = module.doModInit(modInitFile, (uchar*) "builtin-file", NULL)) != RS_RET_OK) {
+ if((iRet = module.doModInit(modInitFile, UCHAR_CONSTANT("builtin-file"), NULL)) != RS_RET_OK) {
+ RETiRet;
+ }
+ if((iRet = module.doModInit(modInitPipe, UCHAR_CONSTANT("builtin-pipe"), NULL)) != RS_RET_OK) {
RETiRet;
}
#ifdef SYSLOG_INET
- if((iRet = module.doModInit(modInitFwd, (uchar*) "builtin-fwd", NULL)) != RS_RET_OK) {
+ if((iRet = module.doModInit(modInitFwd, UCHAR_CONSTANT("builtin-fwd"), NULL)) != RS_RET_OK) {
RETiRet;
}
#endif
- if((iRet = module.doModInit(modInitShell, (uchar*) "builtin-shell", NULL)) != RS_RET_OK) {
+ if((iRet = module.doModInit(modInitShell, UCHAR_CONSTANT("builtin-shell"), NULL)) != RS_RET_OK) {
RETiRet;
}
- if((iRet = module.doModInit(modInitDiscard, (uchar*) "builtin-discard", NULL)) != RS_RET_OK) {
+ if((iRet = module.doModInit(modInitDiscard, UCHAR_CONSTANT("builtin-discard"), NULL)) != RS_RET_OK) {
RETiRet;
}
/* dirty, but this must be for the time being: the usrmsg module must always be
- * loaded as last module. This is because it processes any time of action selector.
+ * loaded as last module. This is because it processes any type of action selector.
* If we load it before other modules, these others will never have a chance of
* working with the config file. We may change that implementation so that a user name
* must start with an alnum, that would definitely help (but would it break backwards
@@ -2712,8 +2686,7 @@ static rsRetVal loadBuildInModules(void)
* User names now must begin with:
* [a-zA-Z0-9_.]
*/
- if((iRet = module.doModInit(modInitUsrMsg, (uchar*) "builtin-usrmsg", NULL)) != RS_RET_OK)
- RETiRet;
+ CHKiRet(module.doModInit(modInitUsrMsg, (uchar*) "builtin-usrmsg", NULL));
/* ok, initialization of the command handler probably does not 100% belong right in
* this space here. However, with the current design, this is actually quite a good
@@ -2723,6 +2696,8 @@ static rsRetVal loadBuildInModules(void)
* This, I think, is the right thing to do. -- rgerhards, 2007-07-31
*/
CHKiRet(regCfSysLineHdlr((uchar *)"actionresumeretrycount", 0, eCmdHdlrInt, NULL, &glbliActionResumeRetryCount, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"defaultruleset", 0, eCmdHdlrGetWord, setDefaultRuleset, NULL, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"ruleset", 0, eCmdHdlrGetWord, setCurrRuleset, NULL, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuefilename", 0, eCmdHdlrGetWord, NULL, &pszMainMsgQFName, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuesize", 0, eCmdHdlrInt, NULL, &iMainMsgQueueSize, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuehighwatermark", 0, eCmdHdlrInt, NULL, &iMainMsgQHighWtrMark, NULL));
@@ -2730,6 +2705,7 @@ static rsRetVal loadBuildInModules(void)
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuediscardmark", 0, eCmdHdlrInt, NULL, &iMainMsgQDiscardMark, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuediscardseverity", 0, eCmdHdlrSeverity, NULL, &iMainMsgQDiscardSeverity, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuecheckpointinterval", 0, eCmdHdlrInt, NULL, &iMainMsgQPersistUpdCnt, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuesyncqueuefiles", 0, eCmdHdlrBinary, NULL, &bMainMsgQSyncQeueFiles, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuetype", 0, eCmdHdlrGetWord, setMainMsgQueType, NULL, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueueworkerthreads", 0, eCmdHdlrInt, NULL, &iMainMsgQueueNumWorkers, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuetimeoutshutdown", 0, eCmdHdlrInt, NULL, &iMainMsgQtoQShutdown, NULL));
@@ -2756,14 +2732,20 @@ static rsRetVal loadBuildInModules(void)
CHKiRet(regCfSysLineHdlr((uchar *)"modload", 0, eCmdHdlrCustomHandler, conf.doModLoad, NULL, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"includeconfig", 0, eCmdHdlrCustomHandler, conf.doIncludeLine, NULL, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"umask", 0, eCmdHdlrFileCreateMode, setUmask, NULL, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"maxopenfiles", 0, eCmdHdlrInt, setMaxFiles, NULL, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"debugprinttemplatelist", 0, eCmdHdlrBinary, NULL, &bDebugPrintTemplateList, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"debugprintmodulelist", 0, eCmdHdlrBinary, NULL, &bDebugPrintModuleList, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"debugprintcfsyslinehandlerlist", 0, eCmdHdlrBinary,
NULL, &bDebugPrintCfSysLineHandlerList, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"moddir", 0, eCmdHdlrGetWord, NULL, &pModDir, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"generateconfiggraph", 0, eCmdHdlrGetWord, NULL, &pszConfDAGFile, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"errormessagestostderr", 0, eCmdHdlrBinary, NULL, &bErrMsgToStderr, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"maxmessagesize", 0, eCmdHdlrSize, setMaxMsgSize, NULL, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"privdroptouser", 0, eCmdHdlrUID, NULL, &uidDropPriv, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"privdroptouserid", 0, eCmdHdlrInt, NULL, &uidDropPriv, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"privdroptogroup", 0, eCmdHdlrGID, NULL, &gidDropPriv, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"privdroptogroupid", 0, eCmdHdlrGID, NULL, &gidDropPriv, NULL));
/* now add other modules handlers (we should work on that to be able to do it in ClassInit(), but so far
* that is not possible). -- rgerhards, 2008-01-28
@@ -2786,7 +2768,7 @@ static void printVersion(void)
#else
printf("\tFEATURE_REGEXP:\t\t\t\tNo\n");
#endif
-#ifndef NOLARGEFILE
+#if defined(_LARGE_FILES) || (defined (_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS >= 64)
printf("\tFEATURE_LARGEFILE:\t\t\tYes\n");
#else
printf("\tFEATURE_LARGEFILE:\t\t\tNo\n");
@@ -2858,20 +2840,53 @@ static rsRetVal mainThread()
CHKiRet(init());
if(Debug && debugging_on) {
- dbgprintf("Debugging enabled, SIGUSR1 to turn off debugging.\n");
+ DBGPRINTF("Debugging enabled, SIGUSR1 to turn off debugging.\n");
}
+
/* Send a signal to the parent so it can terminate.
*/
- if (myPid != ppid)
- kill (ppid, SIGTERM);
+ if(myPid != ppid)
+ kill(ppid, SIGTERM);
+
+
+ /* If instructed to do so, we now drop privileges. Note that this is not 100% secure,
+ * because outputs are already running at this time. However, we can implement
+ * dropping of privileges rather quickly and it will work in many cases. While it is not
+ * the ultimate solution, the current one is still much better than not being able to
+ * drop privileges at all. Doing it correctly, requires a change in architecture, which
+ * we should do over time. TODO -- rgerhards, 2008-11-19
+ */
+ if(gidDropPriv != 0) {
+ doDropPrivGid(gidDropPriv);
+ glbl.SetHUPisRestart(0); /* we can not do restart-type HUPs with dropped privs */
+ }
+
+ if(uidDropPriv != 0) {
+ doDropPrivUid(uidDropPriv);
+ glbl.SetHUPisRestart(0); /* we can not do restart-type HUPs with dropped privs */
+ }
+
+ /* finally let the inputs run... */
+ runInputModules();
/* END OF INTIALIZATION
* ... but keep in mind that we might do a restart and thus init() might
- * be called again. If that happens, we must shut down the worker thread,
- * do the init() and then restart things.
- * rgerhards, 2005-10-24
+ * be called again. -- rgerhards, 2005-10-24
+ */
+ DBGPRINTF("initialization completed, transitioning to regular run mode\n");
+
+ /* close stderr and stdout if they are kept open during a fork. Note that this
+ * may introduce subtle security issues: if we are in a jail, one may break out of
+ * it via these descriptors. But if I close them earlier, error messages will (once
+ * again) not be emitted to the user that starts the daemon. As root jail support
+ * is still in its infancy (and not really done), we currently accept this issue.
+ * rgerhards, 2009-06-29
*/
- dbgprintf("initialization completed, transitioning to regular run mode\n");
+ if(!(Debug || NoFork)) {
+ close(1);
+ close(2);
+ bErrMsgToStderr = 0;
+ }
mainloop();
@@ -2902,22 +2917,26 @@ InitGlobalClasses(void)
CHKiRet(objUse(errmsg, CORE_COMPONENT));
pErrObj = "module";
CHKiRet(objUse(module, CORE_COMPONENT));
- pErrObj = "var";
- CHKiRet(objUse(var, CORE_COMPONENT));
pErrObj = "datetime";
CHKiRet(objUse(datetime, CORE_COMPONENT));
- pErrObj = "vm";
- CHKiRet(objUse(vm, CORE_COMPONENT));
pErrObj = "expr";
CHKiRet(objUse(expr, CORE_COMPONENT));
+ pErrObj = "rule";
+ CHKiRet(objUse(rule, CORE_COMPONENT));
+ pErrObj = "ruleset";
+ CHKiRet(objUse(ruleset, CORE_COMPONENT));
pErrObj = "conf";
CHKiRet(objUse(conf, CORE_COMPONENT));
+ pErrObj = "prop";
+ CHKiRet(objUse(prop, CORE_COMPONENT));
/* intialize some dummy classes that are not part of the runtime */
pErrObj = "action";
CHKiRet(actionClassInit());
pErrObj = "template";
CHKiRet(templateInit());
+ pErrObj = "parser";
+ CHKiRet(parserClassInit());
/* TODO: the dependency on net shall go away! -- rgerhards, 2008-03-07 */
pErrObj = "net";
@@ -2950,33 +2969,15 @@ GlobalClassExit(void)
/* first, release everything we used ourself */
objRelease(net, LM_NET_FILENAME);/* TODO: the dependency on net shall go away! -- rgerhards, 2008-03-07 */
+ objRelease(prop, CORE_COMPONENT);
objRelease(conf, CORE_COMPONENT);
+ objRelease(ruleset, CORE_COMPONENT);
+ objRelease(rule, CORE_COMPONENT);
objRelease(expr, CORE_COMPONENT);
- objRelease(vm, CORE_COMPONENT);
- objRelease(var, CORE_COMPONENT);
+ vmClassExit(); /* this is hack, currently core_modules do not get this automatically called */
objRelease(datetime, CORE_COMPONENT);
/* TODO: implement the rest of the deinit */
-#if 0
- CHKiRet(datetimeClassInit(NULL));
- CHKiRet(msgClassInit(NULL));
- CHKiRet(strmClassInit(NULL));
- CHKiRet(wtiClassInit(NULL));
- CHKiRet(wtpClassInit(NULL));
- CHKiRet(queueClassInit(NULL));
- CHKiRet(vmstkClassInit(NULL));
- CHKiRet(sysvarClassInit(NULL));
- CHKiRet(vmClassInit(NULL));
- CHKiRet(vmopClassInit(NULL));
- CHKiRet(vmprgClassInit(NULL));
- CHKiRet(ctok_tokenClassInit(NULL));
- CHKiRet(ctokClassInit(NULL));
- CHKiRet(exprClassInit(NULL));
-
- /* dummy "classes" */
- CHKiRet(actionClassInit());
- CHKiRet(templateInit());
-#endif
/* dummy "classes */
strExit();
@@ -3070,12 +3071,11 @@ doGlblProcessInit(void)
int i;
DEFiRet;
- checkPermissions();
thrdInit();
if( !(Debug || NoFork) )
{
- dbgprintf("Checking pidfile.\n");
+ DBGPRINTF("Checking pidfile.\n");
if (!check_pid(PidFile))
{
memset(&sigAct, 0, sizeof (sigAct));
@@ -3108,12 +3108,10 @@ doGlblProcessInit(void)
fputs(" Already running.\n", stderr);
exit(1); /* "good" exit, done if syslogd is already running */
}
- } else {
- debugging_on = 1;
}
/* tuck my process id away */
- dbgprintf("Writing pidfile %s.\n", PidFile);
+ DBGPRINTF("Writing pidfile %s.\n", PidFile);
if (!check_pid(PidFile))
{
if (!write_pid(PidFile))
@@ -3173,6 +3171,7 @@ int realMain(int argc, char **argv)
uchar legacyConfLine[80];
uchar *LocalHostName;
uchar *LocalDomain;
+ uchar *LocalFQDNName;
/* first, parse the command line options. We do not carry out any actual work, just
* see what we should do. This relieves us from certain anomalies and we can process
@@ -3188,7 +3187,7 @@ int realMain(int argc, char **argv)
* only when actually neeeded.
* rgerhards, 2008-04-04
*/
- while((ch = getopt(argc, argv, "46a:Ac:def:g:hi:l:m:M:nN:op:qQr::s:t:u:vwx")) != EOF) {
+ while((ch = getopt(argc, argv, "46a:Ac:def:g:hi:l:m:M:nN:op:qQr::s:t:T:u:vwx")) != EOF) {
switch((char)ch) {
case '4':
case '6':
@@ -3206,6 +3205,7 @@ int realMain(int argc, char **argv)
case 'q': /* add hostname if DNS resolving has failed */
case 'Q': /* dont resolve hostnames in ACL to IPs */
case 's':
+ case 'T': /* chroot on startup (primarily for testing) */
case 'u': /* misc user settings */
case 'w': /* disable disallowed host warnings */
case 'x': /* disable dns for remote messages */
@@ -3215,6 +3215,7 @@ int realMain(int argc, char **argv)
iCompatibilityMode = atoi(optarg);
break;
case 'd': /* debug - must be handled now, so that debug is active during init! */
+ debugging_on = 1;
Debug = 1;
break;
case 'e': /* log every message (no repeat message supression) */
@@ -3257,7 +3258,7 @@ int realMain(int argc, char **argv)
if ((argc -= optind))
usage();
- dbgprintf("rsyslogd %s startup, compatibility mode %d, module path '%s'\n",
+ DBGPRINTF("rsyslogd %s startup, compatibility mode %d, module path '%s'\n",
VERSION, iCompatibilityMode, glblModPath == NULL ? "" : (char*)glblModPath);
/* we are done with the initial option parsing and processing. Now we init the system. */
@@ -3274,10 +3275,21 @@ int realMain(int argc, char **argv)
/* doing some core initializations */
+ /* we need to create the inputName property (only once during our lifetime) */
+ CHKiRet(prop.Construct(&pInternalInputName));
+ CHKiRet(prop.SetString(pInternalInputName, UCHAR_CONSTANT("rsyslogd"), sizeof("rsyslgod") - 1));
+ CHKiRet(prop.ConstructFinalize(pInternalInputName));
+
+ CHKiRet(prop.Construct(&pLocalHostIP));
+ CHKiRet(prop.SetString(pLocalHostIP, UCHAR_CONSTANT("127.0.0.1"), sizeof("127.0.0.1") - 1));
+ CHKiRet(prop.ConstructFinalize(pLocalHostIP));
+
/* get our host and domain names - we need to do this early as we may emit
* error log messages, which need the correct hostname. -- rgerhards, 2008-04-04
*/
- net.getLocalHostname(&LocalHostName);
+ net.getLocalHostname(&LocalFQDNName);
+ CHKmalloc(LocalHostName = (uchar*) strdup((char*)LocalFQDNName));
+ glbl.SetLocalFQDNName(LocalFQDNName); /* set the FQDN before we modify it */
if((p = (uchar*)strchr((char*)LocalHostName, '.'))) {
*p++ = '\0';
LocalDomain = p;
@@ -3320,6 +3332,7 @@ int realMain(int argc, char **argv)
*/
glbl.SetLocalHostName(LocalHostName);
glbl.SetLocalDomain(LocalDomain);
+ glbl.GenerateLocalHostNameProperty(); /* must be redone after conf processing, FQDN setting may have changed */
/* initialize the objects */
if((iRet = modInitIminternal()) != RS_RET_OK) {
@@ -3337,7 +3350,7 @@ int realMain(int argc, char **argv)
/* END core initializations - we now come back to carrying out command line options*/
while((iRet = bufOptRemove(&ch, &arg)) == RS_RET_OK) {
- dbgprintf("deque option %c, optarg '%s'\n", ch, arg);
+ DBGPRINTF("deque option %c, optarg '%s'\n", ch, (arg == NULL) ? "" : arg);
switch((char)ch) {
case '4':
glbl.SetDefPFFamily(PF_INET);
@@ -3449,6 +3462,12 @@ int realMain(int argc, char **argv)
} else
fprintf(stderr, "-t option only supported in compatibility modes 0 to 2 - ignored\n");
break;
+ case 'T':/* chroot() immediately at program startup, but only for testing, NOT security yet */
+ if(chroot(arg) != 0) {
+ perror("chroot");
+ exit(1);
+ }
+ break;
case 'u': /* misc user settings */
iHelperUOpt = atoi(arg);
if(iHelperUOpt & 0x01)
@@ -3483,11 +3502,14 @@ int realMain(int argc, char **argv)
/* process compatibility mode settings */
- if(iCompatibilityMode < 3) {
+ if(iCompatibilityMode < 4) {
errmsg.LogError(0, NO_ERRCODE, "WARNING: rsyslogd is running in compatibility mode. Automatically "
"generated config directives may interfer with your rsyslog.conf settings. "
- "We suggest upgrading your config and adding -c3 as the first "
+ "We suggest upgrading your config and adding -c4 as the first "
"rsyslogd option.");
+ }
+
+ if(iCompatibilityMode < 3) {
if(MarkInterval > 0) {
legacyOptsEnq((uchar *) "ModLoad immark");
snprintf((char *) legacyConfLine, sizeof(legacyConfLine), "MarkMessagePeriod %d", MarkInterval);
@@ -3508,6 +3530,9 @@ int realMain(int argc, char **argv)
if(!iConfigVerify)
CHKiRet(doGlblProcessInit());
+ /* re-generate local host name property, as the config may have changed our FQDN settings */
+ glbl.GenerateLocalHostNameProperty();
+
CHKiRet(mainThread());
/* do any de-init's that need to be done AFTER this comment */
@@ -3539,6 +3564,5 @@ int main(int argc, char **argv)
dbgClassInit();
return realMain(argc, argv);
}
-
/* vim:set ai:
*/
diff --git a/tools/syslogd.h b/tools/syslogd.h
index e866a16b..3dfdbe2b 100644
--- a/tools/syslogd.h
+++ b/tools/syslogd.h
@@ -29,64 +29,6 @@
#include "linkedlist.h"
#include "expr.h"
-
-#ifndef _PATH_CONSOLE
-#define _PATH_CONSOLE "/dev/console"
-#endif
-
-
-/* This structure represents the files that will have log
- * copies printed.
- * RGerhards 2004-11-08: Each instance of the filed structure
- * describes what I call an "output channel". This is important
- * to mention as we now allow database connections to be
- * present in the filed structure. If helps immensely, if we
- * think of it as the abstraction of an output channel.
- * rgerhards, 2005-10-26: The structure below provides ample
- * opportunity for non-thread-safety. Each of the variable
- * accesses must be carefully evaluated, many of them probably
- * be guarded by mutexes. But beware of deadlocks...
- * rgerhards, 2007-08-01: as you can see, the structure has shrunk pretty much. I will
- * remove some of the comments some time. It's still the structure that controls much
- * of the processing that goes on in syslogd, but it now has lots of helpers.
- */
-struct filed {
- struct filed *f_next; /* next in linked list */
- /* filter properties */
- enum {
- FILTER_PRI = 0, /* traditional PRI based filer */
- FILTER_PROP = 1, /* extended filter, property based */
- FILTER_EXPR = 2 /* extended filter, expression based */
- } f_filter_type;
- EHostnameCmpMode eHostnameCmpMode;
- cstr_t *pCSHostnameComp; /* hostname to check */
- cstr_t *pCSProgNameComp; /* tag to check or NULL, if not to be checked */
- union {
- u_char f_pmask[LOG_NFACILITIES+1]; /* priority mask */
- struct {
- cstr_t *pCSPropName;
- enum {
- FIOP_NOP = 0, /* do not use - No Operation */
- FIOP_CONTAINS = 1, /* contains string? */
- FIOP_ISEQUAL = 2, /* is (exactly) equal? */
- FIOP_STARTSWITH = 3, /* starts with a string? */
- FIOP_REGEX = 4 /* matches a regular expression? */
- } operation;
- cstr_t *pCSCompValue; /* value to "compare" against */
- char isNegated; /* actually a boolean ;) */
- } prop;
- expr_t *f_expr; /* expression object */
- } f_filterData;
-
- linkedList_t llActList; /* list of configured actions */
-};
-
-
-#include "net.h" /* TODO: remove when you remoe isAllowedSender from here! */
-void untty(void);
-rsRetVal selectorConstruct(selector_t **ppThis);
-rsRetVal selectorDestruct(void *pVal);
-rsRetVal selectorAddList(selector_t *f);
/* the following prototypes should go away once we have an input
* module interface -- rgerhards, 2007-12-12
*/
diff --git a/tools/zpipe.c b/tools/zpipe.c
new file mode 100644
index 00000000..38069425
--- /dev/null
+++ b/tools/zpipe.c
@@ -0,0 +1,262 @@
+/* zpipe.c: example of proper use of zlib's inflate() and deflate()
+ Not copyrighted -- provided to the public domain
+ Version 1.5 11 December 2005 Mark Adler
+ Version 2.0 03 June 2009 Rainer Gerhards */
+
+/* RSYSLOG NOTE:
+ * This file is primarily been used as a testing aid for rsyslog. We do NOT
+ * properly maintain it and it has been brought to our attention that it may
+ * have some security issues. However, we prefer not to remove the file as it
+ * may turn out to be useful for further testing. All users are advised NOT
+ * to base any development on this version here, but rather look for the
+ * original zpipe.c by the authors mentioned above.
+ *
+ * This file is beeing distributed as part of rsyslog, but is just an
+ * add-on. Most importantly, rsyslog's copyright does not apply but
+ * rather the (non-) copyright stated above.
+ */
+
+/* Version history:
+ 1.0 30 Oct 2004 First version
+ 1.1 8 Nov 2004 Add void casting for unused return values
+ Use switch statement for inflate() return values
+ 1.2 9 Nov 2004 Add assertions to document zlib guarantees
+ 1.3 6 Apr 2005 Remove incorrect assertion in inf()
+ 1.4 11 Dec 2005 Add hack to avoid MSDOS end-of-line conversions
+ Avoid some compiler warnings for input and output buffers
+ 2.0 03 Jun 2009 Add hack to support multiple deflate records inside a single
+ file on inflate. This is needed in order to support reading
+ files created by rsyslog's zip output writer.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "zlib.h"
+
+#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
+# include <fcntl.h>
+# include <io.h>
+# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
+#else
+# define SET_BINARY_MODE(file)
+#endif
+
+#define CHUNK 16384
+
+/* Compress from file source to file dest until EOF on source.
+ def() returns Z_OK on success, Z_MEM_ERROR if memory could not be
+ allocated for processing, Z_STREAM_ERROR if an invalid compression
+ level is supplied, Z_VERSION_ERROR if the version of zlib.h and the
+ version of the library linked do not match, or Z_ERRNO if there is
+ an error reading or writing the files. */
+int def(FILE *source, FILE *dest, int level)
+{
+ int ret, flush;
+ unsigned have;
+ z_stream strm;
+ unsigned char in[CHUNK];
+ unsigned char out[CHUNK];
+
+ /* allocate deflate state */
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ ret = deflateInit(&strm, level);
+ if (ret != Z_OK)
+ return ret;
+
+ /* compress until end of file */
+ do {
+ strm.avail_in = fread(in, 1, CHUNK, source);
+ if (ferror(source)) {
+ (void)deflateEnd(&strm);
+ return Z_ERRNO;
+ }
+ flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
+ strm.next_in = in;
+
+ /* run deflate() on input until output buffer not full, finish
+ compression if all of source has been read in */
+ do {
+ strm.avail_out = CHUNK;
+ strm.next_out = out;
+ ret = deflate(&strm, flush); /* no bad return value */
+ assert(ret != Z_STREAM_ERROR); /* state not clobbered */
+ have = CHUNK - strm.avail_out;
+ if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
+ (void)deflateEnd(&strm);
+ return Z_ERRNO;
+ }
+ } while (strm.avail_out == 0);
+ assert(strm.avail_in == 0); /* all input will be used */
+
+ /* done when last data in file processed */
+ } while (flush != Z_FINISH);
+ assert(ret == Z_STREAM_END); /* stream will be complete */
+
+ /* clean up and return */
+ (void)deflateEnd(&strm);
+ return Z_OK;
+}
+
+
+/* initialize stream for deflating (we need this in case of
+ * multiple records.
+ * rgerhards, 2009-06-03
+ */
+int doInflateInit(z_stream *strm)
+{
+ int ret;
+
+ /* allocate inflate state */
+ strm->zalloc = Z_NULL;
+ strm->zfree = Z_NULL;
+ strm->opaque = Z_NULL;
+ strm->avail_in = 0;
+ strm->next_in = Z_NULL;
+ ret = inflateInit(strm);
+ return ret;
+}
+
+
+/* Decompress from file source to file dest until stream ends or EOF.
+ inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be
+ allocated for processing, Z_DATA_ERROR if the deflate data is
+ invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and
+ the version of the library linked do not match, or Z_ERRNO if there
+ is an error reading or writing the files. */
+int inf(FILE *source, FILE *dest)
+{
+ int ret;
+ unsigned have;
+ z_stream strm;
+ unsigned char in[CHUNK];
+ int len;
+ unsigned char *next_in_save;
+ unsigned char out[CHUNK];
+
+ ret = doInflateInit(&strm);
+ if (ret != Z_OK)
+ return ret;
+
+ /* decompress until deflate stream ends or end of file */
+ do {
+ len = fread(in, 1, CHUNK, source);
+ if (ferror(source)) {
+ (void)inflateEnd(&strm);
+ return Z_ERRNO;
+ }
+ if (len == 0) {
+ break;
+ }
+ strm.avail_in = len;
+ strm.next_in = in;
+
+ /* run inflate() on input until output buffer not full */
+ strm.avail_out = CHUNK;
+ strm.next_out = out;
+ do {
+ /* fprintf(stderr, "---inner LOOP---, avail_in %d, avail_out %d Byte 0: %x, 1: %x\n", strm.avail_in, strm.avail_out, *strm.next_in, *(strm.next_in+1));*/
+ do {
+ ret = inflate(&strm, Z_NO_FLUSH);
+ assert(ret != Z_STREAM_ERROR); /* state not clobbered */
+ switch (ret) {
+ case Z_NEED_DICT:
+ ret = Z_DATA_ERROR; /* and fall through */
+ case Z_DATA_ERROR:
+ case Z_MEM_ERROR:
+ (void)inflateEnd(&strm);
+ return ret;
+ }
+ have = CHUNK - strm.avail_out;
+ if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
+ (void)inflateEnd(&strm);
+ return Z_ERRNO;
+ }
+ } while (strm.avail_out == 0);
+ /* handle the case that more than one deflate record is contained
+ * in a single file. -- rgerhards, 2009-06-03
+ */
+ if(ret == Z_STREAM_END) {
+ len -= strm.total_in;
+ if(len > 0) {
+ next_in_save = strm.next_in;
+ (void)inflateEnd(&strm);
+ ret = doInflateInit(&strm);
+ if (ret != Z_OK)
+ return ret;
+ strm.avail_in = len;
+ strm.next_in = next_in_save;
+ strm.avail_out = CHUNK;
+ strm.next_out = out;
+ ret = Z_OK; /* continue outer loop */
+ }
+ }
+ } while (strm.avail_in > 0);
+
+ /* done when inflate() says it's done */
+ } while (ret != Z_STREAM_END);
+
+ /* clean up and return */
+ (void)inflateEnd(&strm);
+ return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
+}
+
+/* report a zlib or i/o error */
+void zerr(int ret)
+{
+ fputs("zpipe: ", stdout);
+ switch (ret) {
+ case Z_ERRNO:
+ if (ferror(stdin))
+ fputs("error reading stdin\n", stdout);
+ if (ferror(stdout))
+ fputs("error writing stdout\n", stdout);
+ break;
+ case Z_STREAM_ERROR:
+ fputs("invalid compression level\n", stdout);
+ break;
+ case Z_DATA_ERROR:
+ fputs("invalid or incomplete deflate data\n", stdout);
+ break;
+ case Z_MEM_ERROR:
+ fputs("out of memory\n", stdout);
+ break;
+ case Z_VERSION_ERROR:
+ fputs("zlib version mismatch!\n", stdout);
+ }
+}
+
+/* compress or decompress from stdin to stdout */
+int main(int argc, char **argv)
+{
+ int ret;
+
+ /* avoid end-of-line conversions */
+ SET_BINARY_MODE(stdin);
+ SET_BINARY_MODE(stdout);
+
+ /* do compression if no arguments */
+ if (argc == 1) {
+ ret = def(stdin, stdout, Z_DEFAULT_COMPRESSION);
+ if (ret != Z_OK)
+ zerr(ret);
+ return ret;
+ }
+
+ /* do decompression if -d specified */
+ else if (argc == 2 && strcmp(argv[1], "-d") == 0) {
+ ret = inf(stdin, stdout);
+ if (ret != Z_OK)
+ zerr(ret);
+ return ret;
+ }
+
+ /* otherwise, report usage */
+ else {
+ fputs("zpipe usage: zpipe [-d] < source > dest\n", stdout);
+ return 1;
+ }
+}