summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog27
-rw-r--r--Makefile.am11
-rw-r--r--configure.ac54
-rw-r--r--doc/Makefile.am17
-rw-r--r--doc/dataflow.pngbin0 -> 24601 bytes
-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/features.html1
-rw-r--r--doc/manual.html2
-rw-r--r--doc/modules.html5
-rw-r--r--doc/omoracle.html78
-rw-r--r--doc/queue_analogy_tv.pngbin0 -> 18730 bytes
-rw-r--r--doc/queues.html14
-rw-r--r--doc/queues_analogy.html259
-rw-r--r--doc/rsconf1_maxopenfiles.html35
-rw-r--r--doc/rsyslog_conf.html6
-rw-r--r--doc/rsyslog_conf_global.html18
-rw-r--r--doc/rsyslog_conf_modules.html5
-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/status.html16
-rw-r--r--plugins/im3195/im3195.c3
-rw-r--r--plugins/imgssapi/imgssapi.c1
-rw-r--r--plugins/imklog/ksym_mod.c42
-rw-r--r--plugins/imklog/ksyms.h4
-rw-r--r--plugins/omdtn/Makefile.am8
-rw-r--r--plugins/omdtn/omdtn.c130
-rw-r--r--plugins/omgssapi/omgssapi.c2
-rw-r--r--plugins/omoracle/Makefile.am8
-rw-r--r--plugins/omoracle/omoracle.c569
-rw-r--r--plugins/omoracle/omoracle.h25
-rw-r--r--plugins/omoracle/omoracle.te13
-rw-r--r--plugins/omprog/Makefile.am8
-rw-r--r--plugins/omprog/omprog.c357
-rw-r--r--plugins/omstdout/omstdout.c2
-rw-r--r--runtime/conf.c1
-rw-r--r--runtime/expr.c4
-rw-r--r--runtime/msg.c2
-rw-r--r--runtime/nsd_gtls.c2
-rw-r--r--runtime/rsyslog.h9
-rw-r--r--runtime/vm.c252
-rw-r--r--runtime/vm.h5
-rw-r--r--runtime/vmop.c53
-rw-r--r--runtime/vmop.h11
-rw-r--r--runtime/vmprg.c28
-rw-r--r--runtime/vmprg.h4
-rw-r--r--runtime/vmstk.h4
-rw-r--r--tests/3.rstest4
-rw-r--r--tests/Makefile.am16
-rw-r--r--tests/chkseq.c76
-rwxr-xr-xtests/diskqueue.sh31
-rw-r--r--tests/getline.c3
-rwxr-xr-xtests/manytcp.sh21
-rw-r--r--tests/nettester.c (renamed from tests/udptester.c)131
-rwxr-xr-xtests/omod-if-array.sh13
-rwxr-xr-xtests/parsertest.sh12
-rw-r--r--tests/tcpflood.c290
-rw-r--r--tests/testsuites/3.parse13
-rw-r--r--tests/testsuites/diskqueue.conf16
-rw-r--r--tests/testsuites/manytcp.conf13
-rw-r--r--tests/testsuites/omod-if-array.conf3
-rw-r--r--tests/testsuites/parse1.conf3
-rw-r--r--tools/msggen.c2
-rw-r--r--tools/syslogd.c37
76 files changed, 2501 insertions, 268 deletions
diff --git a/ChangeLog b/ChangeLog
index fa0c5763..ffb31415 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,31 @@
---------------------------------------------------------------------------
+Version 4.3.1 [DEVEL] (rgerhards), 2009-04-??
+- improved doc
+ * added (hopefully) easier to grasp queue explanation
+- improved testbench
+ * added tests for queue disk-only mode (checks disk queue logic)
+---------------------------------------------------------------------------
+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.1.7 [BETA] (rgerhards), 2009-04-??
- bugfix: $InputTCPMaxSessions config directive was accepted, but not
honored. This resulted in a fixed upper limit of 200 connections.
diff --git a/Makefile.am b/Makefile.am
index 97f4aed3..a5cf879c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -108,19 +108,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
+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
ACLOCAL_AMFLAGS = -I m4
diff --git a/configure.ac b/configure.ac
index 32b27483..6daf3f5a 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],[4.1.6],[rsyslog@lists.adiscon.com])
+AC_INIT([rsyslog],[4.3.0],[rsyslog@lists.adiscon.com])
AM_INIT_AUTOMAKE
AC_CONFIG_SRCDIR([ChangeLog])
AC_CONFIG_MACRO_DIR([m4])
@@ -452,7 +452,39 @@ AM_CONDITIONAL(ENABLE_PGSQL, test x$enable_pgsql = xyes)
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@:>@])],
@@ -668,6 +700,20 @@ 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 template 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)
+# end of omprog
+
+
# 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.
@@ -720,6 +766,7 @@ AC_CONFIG_FILES([Makefile \
plugins/imklog/Makefile \
plugins/imtemplate/Makefile \
plugins/omtemplate/Makefile \
+ plugins/omprog/Makefile \
plugins/omstdout/Makefile \
plugins/imfile/Makefile \
plugins/imrelp/Makefile \
@@ -732,6 +779,7 @@ AC_CONFIG_FILES([Makefile \
plugins/omlibdbi/Makefile \
plugins/ommail/Makefile \
plugins/omsnmp/Makefile \
+ plugins/omoracle/Makefile \
tests/Makefile])
AC_OUTPUT
@@ -745,6 +793,7 @@ 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 "Oracle (OCI) support enabled: $enable_oracle"
echo "SNMP support enabled: $enable_snmp"
echo "Mail support enabled: $enable_mail"
echo "RELP support enabled: $enable_relp"
@@ -752,6 +801,7 @@ echo "imdiag enabled: $enable_imdiag"
echo "file input module enabled: $enable_imfile"
echo "input template module will be compiled: $enable_imtemplate"
echo "output template module will be compiled: $enable_omtemplate"
+echo "omprog module will be compiled: $enable_omprog"
echo "omstdout module will be compiled: $enable_omstdout"
echo "Large file support enabled: $enable_largefile"
echo "Networking support enabled: $enable_inet"
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 3015d6b5..4d9d94ff 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -33,6 +33,7 @@ html_files = \
dev_queue.html \
omsnmp.html \
ommysql.html \
+ omoracle.html \
omlibdbi.html \
imfile.html \
imtcp.html \
@@ -108,6 +109,20 @@ html_files = \
rsyslog_conf_output.html \
rsyslog_conf_templates.html \
rsyslog_conf_nomatch.html \
+ queues_analogy.html \
src/classes.dia
-EXTRA_DIST = $(html_files)
+grfx_files = \
+ 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/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/features.html b/doc/features.html
index 17a995bf..626ff65d 100644
--- a/doc/features.html
+++ b/doc/features.html
@@ -127,7 +127,6 @@ community. Plus, it can be financially attractive: just think about how much les
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>Finalize the DTN "planetary Internet" space ship mode output plugin
<li>port it to more *nix variants (eg AIX and HP UX) - this
needs volunteers with access to those machines and knowledge </li>
<li>pcre filtering - maybe (depending on feedback)&nbsp; -
diff --git a/doc/manual.html b/doc/manual.html
index 51271701..ae90f780 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -19,7 +19,7 @@ rsyslog support</a> available directly from the source!</p>
<p><b>Please visit the <a href="http://www.rsyslog.com/sponsors">rsyslog sponsor's page</a>
to honor the project sponsors or become one yourself!</b> We are very grateful for any help towards the
project goals.</p>
-<p><b>This documentation is for version 4.1.6 (devel branch) of rsyslog.</b>
+<p><b>This documentation is for version 4.3.0 (devel branch) of rsyslog.</b>
Visit the <i> <a href="http://www.rsyslog.com/doc-status.html">rsyslog status page</a></i></b> to obtain current
version information and project status.
</p><p><b>If you like rsyslog, you might
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/omoracle.html b/doc/omoracle.html
new file mode 100644
index 00000000..40f6360f
--- /dev/null
+++ b/doc/omoracle.html
@@ -0,0 +1,78 @@
+<!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>[<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/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 41c5865f..4a9509a0 100644
--- a/doc/queues.html
+++ b/doc/queues.html
@@ -1,6 +1,5 @@
<!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>
@@ -10,6 +9,13 @@
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
@@ -18,7 +24,7 @@ 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>&nbsp;Currently, queues are used for the main message queue and for the
+<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
@@ -354,8 +360,8 @@ save.</p>
[<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
+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>
+version 3 or higher.</font></p>
</body></html>
diff --git a/doc/queues_analogy.html b/doc/queues_analogy.html
new file mode 100644
index 00000000..1584c66d
--- /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 exeception 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 anlogy 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 initate reception of a message in the same way a TV set can not 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 (eiter 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
+rater 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 intend 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 can not 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 (remeber, in all cases, I assume
+no sufficently large gaps in the opposing trafic).
+<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 4&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 acton 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 continous 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 continous 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 configuraton 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 queueing takes place).
+The "road junktion 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
+can not 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 defering a rsyslog
+restart for too long. Especially in this case re-queueing and cleanup is not trivial. Also, note that
+I did not discuss disk-assisted queue modes. The basic rules apply, but there are some additonal
+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 implementor 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_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/rsyslog_conf.html b/doc/rsyslog_conf.html
index 852d95b5..6990c6bd 100644
--- a/doc/rsyslog_conf.html
+++ b/doc/rsyslog_conf.html
@@ -26,7 +26,7 @@ Lines can be continued by specifying a backslash ("\") as the last
character of the line. There is a hard-coded maximum line length of 4K.
If you need lines larger than that, you need to change compile-time
settings inside rsyslog and recompile.
-<h2><a href="rsyslog_conf_global.html">Global Directives</a></h2>
+<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
@@ -74,9 +74,9 @@ such features is available in rsyslogd, only.</p>
[<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
+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>
+version 3 or higher.</font></p>
</body>
</html>
>
diff --git a/doc/rsyslog_conf_global.html b/doc/rsyslog_conf_global.html
index d011bd2b..3e33f0da 100644
--- a/doc/rsyslog_conf_global.html
+++ b/doc/rsyslog_conf_global.html
@@ -1,14 +1,14 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html><head><title>Global Directives - rsyslog.conf</title></head>
+<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>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>Please note that not all directives here are actually global. Some affect
-only the next action. This documentation will be changed soon.
+<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.
@@ -180,6 +180,7 @@ instead of UDP (plain TCP syslog, RELP). This resolves the UDP stack size restri
<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>$RepeatedMsgContainsOriginalMsg</b> [on/<b>off</b>] - "last message repeated n times" messages, if generated,
@@ -214,7 +215,6 @@ the value, the less precise the timestamp.
<li><a href="droppriv.html">$PrivDropToGroupID</a></li>
<li><a href="droppriv.html">$PrivDropToUser</a></li>
<li><a href="droppriv.html">$PrivDropToUserID</a></li>
-</ul>
<li><a href="rsconf1_umask.html">$UMASK</a></li>
</ul>
<p><b>Where &lt;size_nbr&gt; is specified above,</b>
@@ -235,7 +235,7 @@ point of view, "1,,0.0.,.,0" also has the value 1000. </p>
<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>
+version 3 or higher.</font></p>
</body>
</html>
diff --git a/doc/rsyslog_conf_modules.html b/doc/rsyslog_conf_modules.html
index a281d9e7..df9abeea 100644
--- a/doc/rsyslog_conf_modules.html
+++ b/doc/rsyslog_conf_modules.html
@@ -19,6 +19,7 @@ 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>
<li><a href="imfile.html">imfile</a>
-&nbsp; input module for text files</li>
<li><a href="imrelp.html">imrelp</a> - RELP
@@ -44,9 +45,9 @@ only available if it has been loaded (using $ModLoad).</p>
[<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
+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>
+version 3 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/status.html b/doc/status.html
index dae94884..01a9cf8f 100644
--- a/doc/status.html
+++ b/doc/status.html
@@ -2,22 +2,22 @@
<html><head><title>rsyslog status page</title></head>
<body>
<h2>rsyslog status page</h2>
-<p>This page reflects the status as of 2009-04-03.</p>
+<p>This page reflects the status as of 2009-04-17.</p>
<h2>Current Releases</h2>
-<p><b>development:</b> 4.1.5 [2009-03-11] -
-<a href="http://www.rsyslog.com/Article349.phtml">change log</a> -
-<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-150.phtml">download</a>
+<p><b>development:</b> 4.3.0 [2009-04-17] -
+<a href="http://www.rsyslog.com/Article366.phtml">change log</a> -
+<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-156.phtml">download</a>
<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.20.3 [2009-04-02] - <a href="http://www.rsyslog.com/Article356.phtml">change log</a> -
-<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-151.phtml">download</a>
+<p><b>v3 stable:</b> 3.20.6 [2009-04-16] - <a href="http://www.rsyslog.com/Article364.phtml">change log</a> -
+<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-155.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
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/imgssapi/imgssapi.c b/plugins/imgssapi/imgssapi.c
index debe935e..b9d7dfe3 100644
--- a/plugins/imgssapi/imgssapi.c
+++ b/plugins/imgssapi/imgssapi.c
@@ -249,7 +249,6 @@ onErrClose(tcps_sess_t *pSess)
static rsRetVal
doOpenLstnSocks(tcpsrv_t *pSrv)
{
- int *pRet = NULL;
gsssrv_t *pGSrv;
DEFiRet;
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/omdtn/Makefile.am b/plugins/omdtn/Makefile.am
deleted file mode 100644
index afb57476..00000000
--- a/plugins/omdtn/Makefile.am
+++ /dev/null
@@ -1,8 +0,0 @@
-pkglib_LTLIBRARIES = omdtn.la
-
-omdtn_la_SOURCES = omdtn.c
-omdtn_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS)
-omdtn_la_LDFLAGS = -module -avoid-version
-omdtn_la_LIBADD =
-
-EXTRA_DIST =
diff --git a/plugins/omdtn/omdtn.c b/plugins/omdtn/omdtn.c
deleted file mode 100644
index 761bde79..00000000
--- a/plugins/omdtn/omdtn.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/* omdtn.c
- * This is the plugin for rsyslog use in the interplanetary Internet,
- * especially useful for rsyslog in space ships of all kinds.
- * The core idea was introduced in early 2009 and considered
- * doable.
- *
- * Note that this has not yet been tested for robustness but needs
- * to prior to placing it on top of a rocket.
- *
- * 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 "dirty.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
-
-typedef struct _instanceData {
-} instanceData;
-
-BEGINcreateInstance
-CODESTARTcreateInstance
-ENDcreateInstance
-
-
-BEGINisCompatibleWithFeature
-CODESTARTisCompatibleWithFeature
- if(eFeat == sFEATURERepeatedMsgReduction)
- iRet = RS_RET_OK;
-ENDisCompatibleWithFeature
-
-
-BEGINfreeInstance
-CODESTARTfreeInstance
-ENDfreeInstance
-
-
-BEGINdbgPrintInstInfo
-CODESTARTdbgPrintInstInfo
-ENDdbgPrintInstInfo
-
-
-BEGINtryResume
-CODESTARTtryResume
-ENDtryResume
-
-BEGINdoAction
-CODESTARTdoAction
- write(1, (char*)ppString[0], strlen((char*)ppString[0]));
-ENDdoAction
-
-
-BEGINparseSelectorAct
-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;
- CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, 0, (uchar*) "RSYSLOG_FileFormat"));
-CODE_STD_FINALIZERparseSelectorAct
-ENDparseSelectorAct
-
-
-BEGINmodExit
-CODESTARTmodExit
-ENDmodExit
-
-
-BEGINqueryEtryPt
-CODESTARTqueryEtryPt
-CODEqueryEtryPt_STD_OMOD_QUERIES
-ENDqueryEtryPt
-
-
-BEGINmodInit()
-CODESTARTmodInit
- *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
-CODEmodInit_QueryRegCFSLineHdlr
-ENDmodInit
-
-/* vi:set ai:
- */
diff --git a/plugins/omgssapi/omgssapi.c b/plugins/omgssapi/omgssapi.c
index e0cc8af6..361f657f 100644
--- a/plugins/omgssapi/omgssapi.c
+++ b/plugins/omgssapi/omgssapi.c
@@ -444,7 +444,7 @@ CODESTARTdoAction
/* error! */
dbgprintf("error forwarding via tcp, suspending\n");
pData->eDestState = eDestFORW_SUSP;
- iRet = RS_RET_SUSPENDED;
+ ABORT_FINALIZE(RS_RET_SUSPENDED);
}
break;
}
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..71cc8e1f
--- /dev/null
+++ b/plugins/omoracle/omoracle.c
@@ -0,0 +1,569 @@
+/** 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.
+
+ $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!
+
+ 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;
+ /* 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;
+/** Statement to prepare and execute */
+static char* db_statement;
+/** Whether or not the core supports the newer array interface. The
+ * module is able to work in both modes, but the newer is the
+ * recommended one for performance reasons. */
+static int array_passing;
+
+/** 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,
+ MAX_BUFSIZE *
+ sizeof ***pData->batch.parameters,
+ 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;
+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));
+ pData->txt_statement = strdup(db_statement);
+ 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.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 MAX_BUFSIZE 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. MAX_BUFSIZE is long enough for
+ * usual entries. */
+ pData->batch.parameters[i][j] = calloc(MAX_BUFSIZE,
+ sizeof ***pData->batch.parameters);
+ 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_DEFAULT));
+
+ CHECKERR(pData->error,
+ OCITransCommit(pData->service, pData->error, 0));
+
+ pData->batch.n = 0;
+finalize_it:
+ 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. */
+ dbgprintf ("***** OMORACLE ***** At isCompatibleWithFeature\n");
+ 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],
+ MAX_BUFSIZE);
+ 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;
+ RETiRet;
+}
+
+/** As I don't find any handler that reads an entire line, I write my
+ * own. */
+static int get_db_statement(char** line, char** stmt)
+{
+ DEFiRet;
+
+ while (isspace(**line))
+ (*line)++;
+ dbgprintf ("Config line: %s\n", *line);
+ *stmt = strdup(*line);
+ CHKmalloc(*stmt);
+ dbgprintf ("Statement: %s\n", *stmt);
+finalize_it:
+ 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 (!(array_passing = opts & OMSR_TPL_AS_ARRAY))
+ ABORT_FINALIZE(RS_RET_RSCORE_TOO_OLD);
+
+ CHKiRet(omsdRegCFSLineHdlr((uchar*) "omoraclestatement", 0,
+ eCmdHdlrCustomHandler, get_db_statement,
+ &db_statement, STD_LOADABLE_MODULE_ID));
+
+ENDmodInit
diff --git a/plugins/omoracle/omoracle.h b/plugins/omoracle/omoracle.h
new file mode 100644
index 00000000..92bcacab
--- /dev/null
+++ b/plugins/omoracle/omoracle.h
@@ -0,0 +1,25 @@
+/** 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).
+
+ 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/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..2a078a6d
--- /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 "dirty.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/omstdout/omstdout.c b/plugins/omstdout/omstdout.c
index 7c63b5c4..181895a4 100644
--- a/plugins/omstdout/omstdout.c
+++ b/plugins/omstdout/omstdout.c
@@ -110,7 +110,7 @@ CODESTARTdoAction
if(iParam > 0)
szBuf[iBuf++] = ','; /* all but first need a delimiter */
iParamVal = 0;
- while(szParams[iParam][iParamVal] != '\0' && iBuf < sizeof(szBuf)) {
+ while(szParams[iParam][iParamVal] != '\0' && iBuf < (int) sizeof(szBuf)) {
szBuf[iBuf++] = szParams[iParam][iParamVal++];
}
++iParam;
diff --git a/runtime/conf.c b/runtime/conf.c
index ede15cc7..27ab8bb4 100644
--- a/runtime/conf.c
+++ b/runtime/conf.c
@@ -796,7 +796,6 @@ dbgprintf("calling expression parser, pp %p ('%s')\n", *pline, *pline);
/* debug support - print vmprg after construction (uncomment to use) */
/* vmprgDebugPrint(f->f_filterData.f_expr->pVmprg); */
- 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
diff --git a/runtime/expr.c b/runtime/expr.c
index 38ed1c68..e449d1c7 100644
--- a/runtime/expr.c
+++ b/runtime/expr.c
@@ -142,7 +142,9 @@ terminal(expr_t *pThis, ctok_t *tok)
* we have all relevant information)
*/
CHKiRet(ctok_token.UnlinkVar(pToken, &pVar));
- CHKiRet(vmprg.AddVarOperation(pThis->pVmprg, opcode_FUNC_CALL, pVar)); /* add to program */
+ 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");
diff --git a/runtime/msg.c b/runtime/msg.c
index 9165e8d6..9d5f3838 100644
--- a/runtime/msg.c
+++ b/runtime/msg.c
@@ -1569,7 +1569,7 @@ void MsgSetHOSTNAME(msg_t *pMsg, char* 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");
+ DBGPRINTF("Could not allocate memory in MsgSetHOSTNAME()\n");
}
diff --git a/runtime/nsd_gtls.c b/runtime/nsd_gtls.c
index 3a79a015..5786e191 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 */
diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h
index 032d8c04..026fbbed 100644
--- a/runtime/rsyslog.h
+++ b/runtime/rsyslog.h
@@ -84,6 +84,8 @@ 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 vmstk_s vmstk_t;
+typedef rsRetVal (*prsf_t)(struct vmstk_s*, int); /* pointer to a RainerScript function */
/* some universal 64 bit define... */
typedef long long int64;
@@ -258,6 +260,13 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth
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) */
/* RainerScript error messages (range 1000.. 1999) */
RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */
diff --git a/runtime/vm.c b/runtime/vm.c
index a25476c2..41d3e483 100644
--- a/runtime/vm.c
+++ b/runtime/vm.c
@@ -26,6 +26,7 @@
#include "config.h"
#include <stdlib.h>
#include <assert.h>
+#include <ctype.h>
#include "rsyslog.h"
#include "obj.h"
@@ -39,6 +40,142 @@ 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));
+ pEntry->rsf = rsf;
+ pEntry->pNext = funcRegRoot;
+ funcRegRoot = pEntry;
+
+finalize_it:
+ 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;
+ rsCStrDestruct(&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.
@@ -330,7 +467,6 @@ 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.
@@ -340,20 +476,9 @@ ENDop(PUSHSYSVAR)
*/
BEGINop(FUNC_CALL) /* remember to set the instruction also in the ENDop macro! */
var_t *numOperands;
- var_t *operand1;
- int iStrlen;
CODESTARTop(FUNC_CALL)
vmstk.PopNumber(pThis->pStk, &numOperands);
- if(numOperands->val.num != 1)
- ABORT_FINALIZE(RS_RET_INVLD_NBR_ARGUMENTS);
- vmstk.PopString(pThis->pStk, &operand1); /* guess there's just one ;) */
- if(!rsCStrSzStrCmp(pOp->operand.pVar->val.pStr, (uchar*) "strlen", 6)) { /* only one supported so far ;) */
-RUNLOG_VAR("%s", rsCStrGetSzStr(operand1->val.pStr));
- iStrlen = strlen((char*) rsCStrGetSzStr(operand1->val.pStr));
-RUNLOG_VAR("%d", iStrlen);
- } else
- ABORT_FINALIZE(RS_RET_INVLD_FUNC);
- PUSHRESULTop(operand1, iStrlen); // TODO: dummy, FIXME
+ CHKiRet((*pOp->operand.rsf)(pThis->pStk, numOperands->val.num));
var.Destruct(&numOperands); /* no longer needed */
finalize_it:
ENDop(FUNC_CALL)
@@ -362,6 +487,89 @@ 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(rsCStrConstruct(&pcstr));
+ vmstk.PopString(pStk, &operand1);
+ pSrc = rsCStrGetSzStr(operand1->val.pStr);
+ iStrlen = strlen((char*)pSrc);
+ while(iStrlen--) {
+ CHKiRet(rsCStrAppendChar(pcstr, tolower(*pSrc++)));
+ }
+
+ /* Store result and cleanup */
+ CHKiRet(rsCStrFinish(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! */
@@ -531,10 +739,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
@@ -548,6 +769,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 a343481e..3e001d27 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,8 +63,10 @@ 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->operand.pVar != NULL)
- var.Destruct(&pThis->operand.pVar);
+ if(pThis->opcode != opcode_FUNC_CALL) {
+ if(pThis->operand.pVar != NULL)
+ var.Destruct(&pThis->operand.pVar);
+ }
ENDobjDestruct(vmop)
@@ -72,13 +76,19 @@ BEGINobjDebugPrint(vmop) /* be sure to specify the object type also in END and C
cstr_t *pStrVar;
CODESTARTobjDebugPrint(vmop)
vmopOpcode2Str(pThis, &pOpcodeName);
- CHKiRet(rsCStrConstruct(&pStrVar));
- CHKiRet(rsCStrFinish(&pStrVar));
- if(pThis->operand.pVar != NULL) {
- CHKiRet(var.Obj2Str(pThis->operand.pVar, pStrVar));
+ 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(rsCStrFinish(&pStrVar));
dbgoprint((obj_t*) pThis, "%.12s\t%s\n", pOpcodeName, rsCStrGetSzStrNoNULL(pStrVar));
- rsCStrDestruct(&pStrVar);
+ if(pThis->opcode != opcode_FUNC_CALL)
+ rsCStrDestruct(&pStrVar);
finalize_it:
ENDobjDebugPrint(vmop)
@@ -98,6 +108,7 @@ static rsRetVal
Obj2Str(vmop_t *pThis, cstr_t *pstrPrg)
{
uchar *pOpcodeName;
+ cstr_t *pcsFuncName;
uchar szBuf[2048];
size_t lenBuf;
DEFiRet;
@@ -107,8 +118,13 @@ 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));
+ 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(rsCStrAppendChar(pstrPrg, '\n'));
finalize_it:
@@ -116,6 +132,23 @@ finalize_it:
}
+/* 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
*/
@@ -248,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;
@@ -263,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 938b08fd..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 */
@@ -96,7 +97,8 @@ typedef struct vmop_s {
BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
opcode_t opcode;
union {
- var_t *pVar; /* for function call, this is the name (string) of function to be called */
+ var_t *pVar;
+ 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;
@@ -112,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 75915025..07757b98 100644
--- a/runtime/vmprg.c
+++ b/runtime/vmprg.c
@@ -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/tests/3.rstest b/tests/3.rstest
index 93cb941a..e75d9754 100644
--- a/tests/3.rstest
+++ b/tests/3.rstest
@@ -7,10 +7,10 @@ out:
00000000: push_msgvar msg[cstr]
00000001: push_const abc[cstr]
00000002: push_const 1[nbr]
-00000003: func_call strlen[cstr]
+00000003: func_call strlen
00000004: strconcat
00000005: push_const 1[nbr]
-00000006: func_call strlen[cstr]
+00000006: func_call strlen
00000007: push_const 20[nbr]
00000008: push_const 30[nbr]
00000009: add
diff --git a/tests/Makefile.am b/tests/Makefile.am
index ab1c5a62..0f4cbce1 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,6 +1,6 @@
TESTRUNS = rt_init rscript
-check_PROGRAMS = $(TESTRUNS) ourtail udptester
-TESTS = $(TESTRUNS) cfg.sh parsertest.sh omod-if-array.sh
+check_PROGRAMS = $(TESTRUNS) ourtail nettester tcpflood chkseq
+TESTS = $(TESTRUNS) cfg.sh parsertest.sh omod-if-array.sh manytcp.sh diskqueue.sh
TESTS_ENVIRONMENT = RSYSLOG_MODDIR='$(abs_top_builddir)'/runtime/.libs/
DISTCLEANFILES=rsyslog.pid
test_files = testbench.h runtime-dummy.c
@@ -27,13 +27,21 @@ EXTRA_DIST= 1.rstest 2.rstest 3.rstest err1.rstest \
testsuites/omod-if-array.conf \
testsuites/1.omod-if-array \
parsertest.sh \
+ diskqueue.sh \
+ testsuites/diskqueue.conf \
+ manytcp.sh \
+ testsuites/manytcp.conf \
omod-if-array.sh \
cfg.sh
ourtail_SOURCES = ourtail.c
+chkseq_SOURCES = chkseq.c
-udptester_SOURCES = udptester.c getline.c
-udptester_LDADD = $(SOL_LIBS)
+tcpflood_SOURCES = tcpflood.c
+tcpflood_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)
diff --git a/tests/chkseq.c b/tests/chkseq.c
new file mode 100644
index 00000000..3203c250
--- /dev/null
+++ b/tests/chkseq.c
@@ -0,0 +1,76 @@
+/* Checks if a file consists of line of strictly monotonically
+ * increasing numbers. An expected start and end number may
+ * be set.
+ *
+ * Params
+ * argv[1] file to check
+ * argv[2] start number
+ * argv[3] end number
+ *
+ * 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>
+
+int main(int argc, char *argv[])
+{
+ FILE *fp;
+ int val;
+ int i;
+ int ret = 0;
+ int start, end;
+
+ if(argc != 4) {
+ printf("Invalid call of chkseq\n");
+ printf("Usage: chkseq file start end\n");
+ exit(1);
+ }
+
+ start = atoi(argv[2]);
+ end = atoi(argv[3]);
+
+ if(start > end) {
+ printf("start must be less than or equal end!\n");
+ exit(1);
+ }
+
+ /* read file */
+ fp = fopen(argv[1], "r");
+ if(fp == NULL) {
+ perror(argv[1]);
+ exit(1);
+ }
+
+ for(i = start ; i < end ; ++i) {
+ if(fscanf(fp, "%d\n", &val) != 1) {
+ printf("scanf error in index i=%d\n", i);
+ exit(1);
+ }
+ if(val != i) {
+ printf("read value %d, but expected value %d\n", val, i);
+ exit(1);
+ }
+ }
+
+ exit(ret);
+}
diff --git a/tests/diskqueue.sh b/tests/diskqueue.sh
new file mode 100755
index 00000000..6384eb64
--- /dev/null
+++ b/tests/diskqueue.sh
@@ -0,0 +1,31 @@
+# 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
+echo testing queue disk-only mode
+rm -rf test-spool
+mkdir test-spool
+rm -f work rsyslog.out.log rsyslog.out.log.save # work files
+../tools/rsyslogd -c4 -u2 -n -irsyslog.pid -M../runtime/.libs:../.libs -f$srcdir/testsuites/diskqueue.conf &
+sleep 1
+echo "rsyslogd started with pid " `cat rsyslog.pid`
+# 20000 messages should be enough - the disk test is slow enough ;)
+./tcpflood 127.0.0.1 13514 1 20000
+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
+fi
+sleep 4 # we need this so that rsyslogd can receive all outstanding messages
+kill `cat rsyslog.pid`
+rm -f work
+sort < rsyslog.out.log > work
+./chkseq work 0 19999
+if [ "$?" -ne "0" ]; then
+ # rm -f work rsyslog.out.log
+ echo "sequence error detected"
+ exit 1
+fi
+rm -f work rsyslog.out.log
+rm -rf test-spool
diff --git a/tests/getline.c b/tests/getline.c
index 10de2ffe..617d1b0e 100644
--- a/tests/getline.c
+++ b/tests/getline.c
@@ -23,7 +23,8 @@
*/
#include "config.h"
#include <stdio.h>
-#include <malloc.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.
diff --git a/tests/manytcp.sh b/tests/manytcp.sh
new file mode 100755
index 00000000..d9b2e9a0
--- /dev/null
+++ b/tests/manytcp.sh
@@ -0,0 +1,21 @@
+rm -f work rsyslog.out.log rsyslog.out.log.save # work files
+../tools/rsyslogd -c4 -u2 -n -irsyslog.pid -M../runtime/.libs:../.libs -f$srcdir/testsuites/manytcp.conf &
+sleep 1
+echo "rsyslogd started with pid " `cat rsyslog.pid`
+# the config file specifies exactly 1100 connections
+./tcpflood 127.0.0.1 13514 1000 40000
+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
+fi
+sleep 5 # we need this so that rsyslogd can receive all outstanding messages
+kill `cat rsyslog.pid`
+rm -f work
+sort < rsyslog.out.log > work
+./chkseq work 0 39999
+if [ "$?" -ne "0" ]; then
+ rm -f work rsyslog.out.log
+ echo "sequence error detected"
+ exit 1
+fi
+rm -f work rsyslog.out.log
diff --git a/tests/udptester.c b/tests/nettester.c
index a3c6658d..37183ac9 100644
--- a/tests/udptester.c
+++ b/tests/nettester.c
@@ -43,10 +43,16 @@
#include <unistd.h>
#include <string.h>
#include <glob.h>
+#include <signal.h>
#include <netinet/in.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 */
+static enum { inputUDP, inputTCP } inputMode; /* 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; /* name of current test suite */
@@ -64,6 +70,59 @@ void readLine(int fd, char *ln)
}
+/* 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;
+}
+
+
/* send a message via UDP
* returns 0 if ok, something else otherwise.
*/
@@ -107,7 +166,7 @@ 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", NULL };
+ "-M../runtime/.libs:../.libs", NULL };
char confFile[1024];
char *newenviron[] = { NULL };
@@ -173,9 +232,14 @@ processTestFile(int fd, char *pszFileName)
testdata[strlen(testdata)-1] = '\0'; /* remove \n */
- /* now we have the test data to send */
- if(udpSend(testdata, strlen(testdata)) != 0)
- return(2);
+ /* now we have the test data to send (we could use function pointers here...) */
+ 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
@@ -249,6 +313,18 @@ doTests(int fd, char *files)
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
@@ -258,26 +334,54 @@ doTests(int fd, char *files)
int main(int argc, char *argv[])
{
int fd;
- pid_t pid;
- int status;
int ret = 0;
+ FILE *fp;
char buf[4096];
char testcases[4096];
- if(argc != 2) {
- printf("Invalid call of udptester\n");
- printf("Usage: udptester testsuite-name\n");
+ if(argc != 3) {
+ printf("Invalid call of nettester\n");
+ printf("Usage: nettester testsuite-name input\n");
+ printf(" input = udp|tcp\n");
exit(1);
}
+
+ atexit(doAtExit);
testSuite = argv[1];
+ if(!strcmp(argv[2], "udp"))
+ inputMode = inputUDP;
+ else if(!strcmp(argv[2], "tcp"))
+ inputMode = inputTCP;
+ else {
+ printf("error: unsupported input mode '%s'\n", argv[2]);
+ exit(1);
+ }
+
if((srcdir = getenv("srcdir")) == NULL)
srcdir = ".";
- printf("Start of udptester run ($srcdir=%s, testsuite=%s)\n", srcdir, testSuite);
+ printf("Start of nettester run ($srcdir=%s, testsuite=%s, input=%s)\n",
+ srcdir, testSuite, argv[2]);
+
+ /* 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);
+ fputs("$UDPServerRun 12514\n", fp);
+ } else {
+ fputs("$ModLoad ../plugins/imtcp/.libs/imtcp\n", fp);
+ fputs("$InputTCPServerRun 13514\n", fp);
+ }
+ fclose(fp);
- openPipe(argv[1], &pid, &fd);
+ /* start to be tested rsyslogd */
+ openPipe(argv[1], &rsyslogdPid, &fd);
readLine(fd, buf);
/* generate filename */
@@ -285,9 +389,6 @@ int main(int argc, char *argv[])
if(doTests(fd, testcases) != 0)
ret = 1;
- /* cleanup */
- kill(pid, SIGTERM);
- waitpid(pid, &status, 0); /* wait until instance terminates */
- printf("End of udptester run.\n");
+ printf("End of nettester run (%d).\n", ret);
exit(ret);
}
diff --git a/tests/omod-if-array.sh b/tests/omod-if-array.sh
index cac08928..fd845b4d 100755
--- a/tests/omod-if-array.sh
+++ b/tests/omod-if-array.sh
@@ -1 +1,12 @@
-./udptester omod-if-array
+echo test omod-if-array via udp
+./nettester omod-if-array udp
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
+
+echo test omod-if-array via tcp
+./nettester omod-if-array tcp
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
+
diff --git a/tests/parsertest.sh b/tests/parsertest.sh
index e7985bb0..a6b7d45c 100755
--- a/tests/parsertest.sh
+++ b/tests/parsertest.sh
@@ -1 +1,11 @@
-./udptester parse1
+echo test parsertest via udp
+./nettester parse1 udp
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
+
+echo test parsertest via tcp
+./nettester parse1 tcp
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
diff --git a/tests/tcpflood.c b/tests/tcpflood.c
new file mode 100644
index 00000000..8dbc201b
--- /dev/null
+++ b/tests/tcpflood.c
@@ -0,0 +1,290 @@
+/* Opens a large number of tcp connections and sends
+ * messages over them. This is used for stress-testing.
+ *
+ * Params
+ * argv[1] target address
+ * argv[2] target port
+ * argv[3] number of connections
+ * argv[4] number of messages to send (connection is random)
+ *
+ * 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 <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
+#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 */
+
+static char *targetIP;
+static int targetPort;
+static int numMsgsToSend; /* number of messages to send */
+static int numConnections; /* number of connections to create */
+static int *sockArray; /* array of sockets to use */
+
+
+/* open a single tcp connection
+ */
+int openConn(int *fd)
+{
+ int sock;
+ struct sockaddr_in addr;
+
+ 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(targetPort);
+ if(inet_aton(targetIP, &addr.sin_addr)==0) {
+ fprintf(stderr, "inet_aton() failed\n");
+ return(1);
+ }
+ if(connect(sock, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
+ perror("connect()");
+ fprintf(stderr, "connect() failed\n");
+ return(1);
+ }
+
+ *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;
+
+ write(1, " open connections", sizeof(" open connections")-1);
+ sockArray = calloc(numConnections, sizeof(int));
+ for(i = 0 ; i < numConnections ; ++i) {
+ if(i % 10 == 0) {
+ 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;
+ char msgBuf[128];
+ size_t lenMsg;
+
+ write(1, " close connections", sizeof(" close connections")-1);
+ for(i = 0 ; i < numConnections ; ++i) {
+ if(i % 10 == 0) {
+ 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);
+
+}
+
+
+/* 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;
+ int socknum;
+ int lenBuf;
+ int lenSend;
+ char buf[2048];
+ char msgBuf[128];
+ size_t lenMsg;
+
+ srand(time(NULL)); /* seed is good enough for our needs */
+
+ printf("Sending %d messages.\n", numMsgsToSend);
+ printf("\r%5.5d messages sent", 0);
+ for(i = 0 ; i < numMsgsToSend ; ++i) {
+ if(i < numConnections)
+ socknum = i;
+ else if(i >= numMsgsToSend - numConnections)
+ socknum = i - (numMsgsToSend - numConnections);
+ else
+ socknum = rand() % numConnections;
+ lenBuf = sprintf(buf, "<167>Mar 1 01:00:00 172.20.245.8 tag msgnum:%8.8d:\n", i);
+ 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\n", socknum, i);
+ fflush(stderr);
+ return(1);
+ }
+ if(i % 100 == 0) {
+ printf("\r%5.5d", i);
+ }
+ }
+ printf("\r%5.5d messages sent\n", i);
+
+ 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 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 ret = 0;
+ struct sigaction sigAct;
+ static char buf[1024];
+
+ /* 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(argc != 5) {
+ printf("Invalid call of tcpflood\n");
+ printf("Usage: tcpflood target-host target-port num-connections num-messages\n");
+ exit(1);
+ }
+
+ targetIP = argv[1];
+ targetPort = atoi(argv[2]);
+ numConnections = atoi(argv[3]);
+ numMsgsToSend = atoi(argv[4]);
+
+ if(openConnections() != 0) {
+ printf("error opening connections\n");
+ exit(1);
+ }
+
+ if(sendMessages() != 0) {
+ printf("error sending messages\n");
+ exit(1);
+ }
+
+ //closeConnections();
+ printf("End of tcpflood Run\n");
+
+ exit(ret);
+}
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/diskqueue.conf b/tests/testsuites/diskqueue.conf
new file mode 100644
index 00000000..8851a459
--- /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
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+$InputTCPServerRun 13514
+
+$ErrorMessagesToStderr off
+
+# 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/manytcp.conf b/tests/testsuites/manytcp.conf
new file mode 100644
index 00000000..8175732e
--- /dev/null
+++ b/tests/testsuites/manytcp.conf
@@ -0,0 +1,13 @@
+# Test for tcp "flood" testing
+# rgerhards, 2009-04-08
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+$MaxOpenFiles 2000
+$InputTCPMaxSessions 1100
+$InputTCPServerRun 13514
+
+$ErrorMessagesToStderr off
+
+$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/omod-if-array.conf b/tests/testsuites/omod-if-array.conf
index e6c05a52..d88db166 100644
--- a/tests/testsuites/omod-if-array.conf
+++ b/tests/testsuites/omod-if-array.conf
@@ -3,8 +3,7 @@
# the testbench, so we do not need to focus on that)
# rgerhards, 2009-04-03
$ModLoad ../plugins/omstdout/.libs/omstdout
-$ModLoad ../plugins/imudp/.libs/imudp
-$UDPServerRun 12514
+$IncludeConfig nettest.input.conf # This picks the to be tested input from the test driver!
$ActionOMStdoutArrayInterface on
$ErrorMessagesToStderr off
diff --git a/tests/testsuites/parse1.conf b/tests/testsuites/parse1.conf
index 0fb7d16d..947a05a8 100644
--- a/tests/testsuites/parse1.conf
+++ b/tests/testsuites/parse1.conf
@@ -1,6 +1,5 @@
$ModLoad ../plugins/omstdout/.libs/omstdout
-$ModLoad ../plugins/imudp/.libs/imudp
-$UDPServerRun 12514
+$IncludeConfig nettest.input.conf # This picks the to be tested input from the test driver!
$ErrorMessagesToStderr off
diff --git a/tools/msggen.c b/tools/msggen.c
index 7990a3c8..06244c18 100644
--- a/tools/msggen.c
+++ b/tools/msggen.c
@@ -24,7 +24,7 @@
#include <stdio.h>
#include <syslog.h>
-int main(int argc, char *argv[])
+int main(int __attribute__((unused)) argc, char __attribute__((unused)) *argv[])
{
int i;
diff --git a/tools/syslogd.c b/tools/syslogd.c
index a40b34dd..8c86c12e 100644
--- a/tools/syslogd.c
+++ b/tools/syslogd.c
@@ -87,6 +87,7 @@
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/file.h>
+#include <sys/resource.h>
#include <grp.h>
#if HAVE_SYS_TIMESPEC_H
@@ -2073,6 +2074,32 @@ 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)
{
@@ -2870,6 +2897,7 @@ 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,
@@ -3091,6 +3119,7 @@ GlobalClassExit(void)
objRelease(net, LM_NET_FILENAME);/* TODO: the dependency on net shall go away! -- rgerhards, 2008-03-07 */
objRelease(conf, CORE_COMPONENT);
objRelease(expr, CORE_COMPONENT);
+ vmClassExit(); /* this is hack, currently core_modules do not get this automatically called */
objRelease(vm, CORE_COMPONENT);
objRelease(var, CORE_COMPONENT);
objRelease(datetime, CORE_COMPONENT);
@@ -3590,18 +3619,10 @@ int realMain(int argc, char **argv)
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 */
-{
-char buf[1024];
-getcwd(buf, 1024);
-printf("pwd: '%s'\n", buf);
-printf("chroot to '%s'\n", arg);
if(chroot(arg) != 0) {
perror("chroot");
exit(1);
}
-getcwd(buf, 1024);
-printf("pwd: '%s'\n", buf);
-}
break;
case 'u': /* misc user settings */
iHelperUOpt = atoi(arg);