summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--ChangeLog47
-rw-r--r--Makefile.am11
-rw-r--r--action.c11
-rw-r--r--action.h1
-rwxr-xr-xautogen.sh32
-rw-r--r--configure.ac181
-rw-r--r--dirty.h1
-rw-r--r--doc/Makefile.am20
-rw-r--r--doc/dataflow.pngbin0 -> 24601 bytes
-rw-r--r--doc/dev_oplugins.html2
-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/how2help.html116
-rw-r--r--doc/imtcp.html6
-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_generateconfiggraph.html121
-rw-r--r--doc/rsconf1_maxopenfiles.html35
-rw-r--r--doc/rsyslog-vers.pngbin0 -> 162637 bytes
-rw-r--r--doc/rsyslog_conf.html6
-rw-r--r--doc/rsyslog_conf_global.html23
-rw-r--r--doc/rsyslog_conf_modules.html5
-rw-r--r--doc/rsyslog_confgraph_complex.conf108
-rw-r--r--doc/rsyslog_confgraph_complex.pngbin0 -> 143204 bytes
-rw-r--r--doc/rsyslog_confgraph_std.conf79
-rw-r--r--doc/rsyslog_confgraph_std.pngbin0 -> 167756 bytes
-rw-r--r--doc/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--doc/troubleshoot.html18
-rw-r--r--m4/shave.m499
-rw-r--r--plugins/imdiag/imdiag.c361
-rw-r--r--plugins/imfile/imfile.c5
-rw-r--r--plugins/imgssapi/imgssapi.c5
-rw-r--r--plugins/imklog/imklog.c7
-rw-r--r--plugins/imklog/ksym_mod.c42
-rw-r--r--plugins/imklog/ksyms.h4
-rw-r--r--plugins/imtcp/imtcp.c34
-rw-r--r--plugins/imudp/imudp.c5
-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.c572
-rw-r--r--plugins/omoracle/omoracle.h31
-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/Makefile.am1
-rw-r--r--runtime/conf.c1
-rw-r--r--runtime/datetime.c12
-rw-r--r--runtime/datetime.h4
-rw-r--r--runtime/expr.c4
-rw-r--r--runtime/modules.c29
-rw-r--r--runtime/msg.c106
-rw-r--r--runtime/msg.h8
-rw-r--r--runtime/netstrm.c2
-rw-r--r--runtime/netstrm.h1
-rw-r--r--runtime/nsd_gtls.c2
-rw-r--r--runtime/parser.c2
-rw-r--r--runtime/rsyslog.h14
-rw-r--r--runtime/unicode-helper.h54
-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--shave-libtool.in109
-rw-r--r--shave.in102
-rw-r--r--tcps_sess.c95
-rw-r--r--tcps_sess.h19
-rw-r--r--tcpsrv.c184
-rw-r--r--tcpsrv.h21
-rw-r--r--tests/3.rstest4
-rw-r--r--tests/DiagTalker.java43
-rw-r--r--tests/Makefile.am46
-rw-r--r--tests/chkseq.c76
-rwxr-xr-xtests/diag.sh83
-rwxr-xr-xtests/diskqueue.sh18
-rwxr-xr-xtests/fieldtest.sh13
-rw-r--r--tests/getline.c3
-rwxr-xr-xtests/imtcp-multiport.sh38
-rwxr-xr-xtests/inputname.sh20
-rwxr-xr-xtests/killrsyslog.sh7
-rwxr-xr-xtests/manytcp.sh8
-rw-r--r--tests/nettester.c (renamed from tests/udptester.c)188
-rwxr-xr-xtests/omod-if-array.sh15
-rwxr-xr-xtests/parsertest.sh14
-rwxr-xr-xtests/queue-persist-drvr.sh28
-rwxr-xr-xtests/queue-persist.sh11
-rw-r--r--tests/rscript.c1
-rw-r--r--tests/tcpflood.c288
-rw-r--r--tests/testsuites/1.field13
-rw-r--r--tests/testsuites/1.inputname_imtcp_125143
-rw-r--r--tests/testsuites/1.inputname_imtcp_125153
-rw-r--r--tests/testsuites/1.inputname_imtcp_125163
-rw-r--r--tests/testsuites/3.parse13
-rw-r--r--tests/testsuites/diag-common.conf16
-rw-r--r--tests/testsuites/diskqueue.conf16
-rw-r--r--tests/testsuites/field1.conf8
-rw-r--r--tests/testsuites/imtcp-multiport.conf13
-rw-r--r--tests/testsuites/inputname_imtcp.conf19
-rw-r--r--tests/testsuites/manytcp.conf13
-rw-r--r--tests/testsuites/omod-if-array.conf3
-rw-r--r--tests/testsuites/parse1.conf3
-rw-r--r--tests/testsuites/queue-persist.conf23
-rw-r--r--tools/msggen.c2
-rw-r--r--tools/syslogd.c311
129 files changed, 4696 insertions, 670 deletions
diff --git a/.gitignore b/.gitignore
index ea044fbe..bc362e7f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,3 +35,5 @@ log
logfile
debug
core.*
+shave
+shave-libtool
diff --git a/ChangeLog b/ChangeLog
index 3e153d96..931f21f8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,51 @@
---------------------------------------------------------------------------
+Version 4.3.? [DEVEL] (rgerhards), 2009-??-??
+- bugfix: imdiag/imtcp had a race condition
+- improved testbench (now much better code design and reuse)
+---------------------------------------------------------------------------
+Version 4.3.1 [DEVEL] (rgerhards), 2009-05-25
+- added capability to run multiple tcp listeners (on different ports)
+- performance enhancement: imtcp calls parser no longer on input thread
+ but rather inside on of the potentially many main msg queue worker
+ threads (an enhancement scheduled for all input plugins where this is
+ possible)
+- added $GenerateConfigGraph configuration command which can be used
+ to generate nice-looking (and very informative) rsyslog configuration
+ graphs.
+- added $ActionName configuration directive (currently only used for
+ graph generation, but may find other uses)
+- improved doc
+ * added (hopefully) easier to grasp queue explanation
+- improved testbench
+ * added tests for queue disk-only mode (checks disk queue logic)
+- bugfix: light and full delay watermarks had invalid values, badly
+ affecting performance for delayable inputs
+- build system improvements - thanks to Michael Biebl
+- added new testing module imdiag, which enables to talk to the
+ rsyslog core at runtime. The current implementation is only a
+ beginning, but can be expanded over time
+---------------------------------------------------------------------------
+Version 4.3.0 [DEVEL] (rgerhards), 2009-04-17
+- new feature: new output plugin omprog, which permits to start program
+ and feed it (via its stdin) with syslog messages. If the program
+ terminates, it is restarted.
+- improved internal handling of RainerScript functions, building the
+ necessary plumbing to support more functions with decent runtime
+ performance. This is also necessary towards the long-term goal
+ of loadable library modules.
+- added new RainerScript function "tolower"
+- improved testbench
+ * added tests for tcp-based reception
+ * added tcp-load test (1000 connections, 20,000 messages)
+- added $MaxOpenFiles configuration directive
+- bugfix: solved potential memory leak in msg processing, could manifest
+ itself in imtcp
+- bugfix: ompgsql did not detect problems in sql command execution
+ this could cause loss of messages. The handling was correct if the
+ connection broke, but not if there was a problem with statement
+ execution. The most probable case for such a case would be invalid
+ sql inside the template, and this is now much easier to diagnose.
+---------------------------------------------------------------------------
Version 4.1.8 [BETA] (rgerhards), 2009-04-??
- bugfix: light and full delay watermarks had invalid values, badly
affecting performance for delayable inputs
diff --git a/Makefile.am b/Makefile.am
index 97f4aed3..e991f323 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 --enable-imdiag --enable-shave
ACLOCAL_AMFLAGS = -I m4
diff --git a/action.c b/action.c
index 03073153..51620fce 100644
--- a/action.c
+++ b/action.c
@@ -60,6 +60,7 @@ static int glbliActionResumeInterval = 30;
int glbliActionResumeRetryCount = 0; /* how often should suspended actions be retried? */
static int bActionRepMsgHasMsg = 0; /* last messsage repeated... has msg fragment in it */
+static uchar *pszActionName; /* short name for the action */
/* main message queue and its configuration parameters */
static queueType_t ActionQueType = QUEUETYPE_DIRECT; /* type of the main message queue above */
static int iActionQueueSize = 1000; /* size of the main message queue above */
@@ -163,8 +164,7 @@ actionResetQueueParams(void)
glbliActionResumeRetryCount = 0; /* I guess it is smart to reset this one, too */
- if(pszActionQFName != NULL)
- d_free(pszActionQFName);
+ d_free(pszActionQFName);
pszActionQFName = NULL; /* prefix for the main message queue file */
RETiRet;
@@ -191,8 +191,8 @@ rsRetVal actionDestruct(action_t *pThis)
SYNC_OBJ_TOOL_EXIT(pThis);
pthread_mutex_destroy(&pThis->mutActExec);
- if(pThis->ppTpl != NULL)
- d_free(pThis->ppTpl);
+ d_free(pThis->pszName);
+ d_free(pThis->ppTpl);
d_free(pThis);
RETiRet;
@@ -829,6 +829,7 @@ actionAddCfSysLineHdrl(void)
{
DEFiRet;
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionname", 0, eCmdHdlrGetWord, NULL, &pszActionName, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuefilename", 0, eCmdHdlrGetWord, NULL, &pszActionQFName, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuesize", 0, eCmdHdlrInt, NULL, &iActionQueueSize, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuemaxdiskspace", 0, eCmdHdlrSize, NULL, &iActionQueMaxDiskSpace, NULL));
@@ -881,6 +882,8 @@ addAction(action_t **ppAction, modInfo_t *pMod, void *pModData, omodStringReques
CHKiRet(actionConstruct(&pAction)); /* create action object first */
pAction->pMod = pMod;
pAction->pModData = pModData;
+ pAction->pszName = pszActionName;
+ pszActionName = NULL; /* free again! */
pAction->bExecWhenPrevSusp = bActExecWhenPrevSusp;
pAction->iSecsExecOnceInterval = iActExecOnceInterval;
pAction->iExecEveryNthOccur = iActExecEveryNthOccur;
diff --git a/action.h b/action.h
index f2706af6..2a1487a5 100644
--- a/action.h
+++ b/action.h
@@ -72,6 +72,7 @@ struct action_s {
*/
qqueue_t *pQueue; /* action queue */
SYNC_OBJ_TOOL; /* required for mutex support */
+ uchar *pszName; /* action name (for documentation) */
pthread_mutex_t mutActExec; /* mutex to guard actual execution of doAction for single-threaded modules */
};
typedef struct action_s action_t;
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 00000000..daa87a2a
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+(test -f $srcdir/configure.ac) || {
+ echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
+ echo " top-level package directory"
+ exit 1
+}
+
+if test -z "$*"; then
+ echo "**Warning**: I am going to run \`configure' with no arguments."
+ echo "If you wish to pass any to it, please specify them on the"
+ echo \`$0\'" command line."
+ echo
+fi
+
+(cd $srcdir && autoreconf --verbose --force --install) || exit 1
+
+conf_flags="--enable-shave --cache-file=config.cache"
+
+if test x$NOCONFIGURE = x; then
+ echo Running $srcdir/configure $conf_flags "$@" ...
+ $srcdir/configure $conf_flags "$@" \
+ && echo Now type \`make\' to compile. || exit 1
+else
+ echo Skipping configure process.
+fi
+
+
diff --git a/configure.ac b/configure.ac
index 4da2a4ff..7150d211 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.7],[rsyslog@lists.adiscon.com])
+AC_INIT([rsyslog],[4.3.1],[rsyslog@lists.adiscon.com])
AM_INIT_AUTOMAKE
AC_CONFIG_SRCDIR([ChangeLog])
AC_CONFIG_MACRO_DIR([m4])
@@ -144,6 +144,7 @@ AC_ARG_WITH(moddirs,
AM_CONDITIONAL(WITH_MODDIRS, test x$moddirs != x)
AC_SUBST(moddirs)
+
# Large file support
AC_ARG_ENABLE(largefile,
[AS_HELP_STRING([--enable-largefile],[Enable large file support @<:@default=yes@:>@])],
@@ -158,6 +159,7 @@ if test "$enable_largefile" = "no"; then
AC_DEFINE(NOLARGEFILE, 1, [Defined when large file support is disabled.])
fi
+
# Regular expressions
AC_ARG_ENABLE(regexp,
[AS_HELP_STRING([--enable-regexp],[Enable regular expressions support @<:@default=yes@:>@])],
@@ -173,6 +175,7 @@ if test "$enable_regexp" = "yes"; then
AC_DEFINE(FEATURE_REGEXP, 1, [Regular expressions support enabled.])
fi
+
# zlib compression
AC_ARG_ENABLE(zlib,
[AS_HELP_STRING([--enable-zlib],[Enable zlib compression support @<:@default=yes@:>@])],
@@ -193,17 +196,18 @@ if test "$enable_zlib" = "yes"; then
fi
fi
+
#gssapi
AC_ARG_ENABLE(gssapi_krb5,
[AS_HELP_STRING([--enable-gssapi-krb5],[Enable GSSAPI Kerberos 5 support @<:@default=no@:>@])],
[case "${enableval}" in
- yes) want_gssapi_krb5="yes" ;;
- no) want_gssapi_krb5="no" ;;
+ yes) enable_gssapi_krb5="yes" ;;
+ no) enable_gssapi_krb5="no" ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-gssapi-krb5) ;;
esac],
- [want_gssapi_krb5=no]
+ [enable_gssapi_krb5=no]
)
-if test $want_gssapi_krb5 = yes; then
+if test $enable_gssapi_krb5 = yes; then
AC_CHECK_LIB(gssapi_krb5, gss_acquire_cred, [
AC_CHECK_HEADER(gssapi/gssapi.h, [
AC_DEFINE(USE_GSSAPI,,
@@ -213,7 +217,8 @@ if test $want_gssapi_krb5 = yes; then
])
])
fi
-AM_CONDITIONAL(ENABLE_GSSAPI, test x$want_gssapi_krb5 = xyes)
+AM_CONDITIONAL(ENABLE_GSSAPI, test x$enable_gssapi_krb5 = xyes)
+
# multithreading via pthreads
AC_ARG_ENABLE(pthreads,
@@ -255,6 +260,7 @@ if test "x$enable_pthreads" != "xno"; then
)
fi
+
# klog
AC_ARG_ENABLE(klog,
[AS_HELP_STRING([--enable-klog],[Integrated klog functionality @<:@default=yes@:>@])],
@@ -269,6 +275,7 @@ AM_CONDITIONAL(ENABLE_IMKLOG, test x$enable_klog = xyes)
AM_CONDITIONAL(ENABLE_IMKLOG_BSD, test x$os_type = xbsd)
AM_CONDITIONAL(ENABLE_IMKLOG_LINUX, test x$os_type = xlinux)
+
#
# SYSLOG_UNIXAF
#
@@ -289,6 +296,7 @@ AC_ARG_ENABLE([unix],
AC_DEFINE([SYSLOG_UNIXAF], [1], [Description])
])
+
# inet
AC_ARG_ENABLE(inet,
[AS_HELP_STRING([--enable-inet],[Enable networking support @<:@default=yes@:>@])],
@@ -304,6 +312,7 @@ if test "$enable_inet" = "yes"; then
AC_DEFINE(SYSLOG_INET, 1, [network support is integrated.])
fi
+
#
# The following define determines whether the package adheres to the
# file system standard.
@@ -325,6 +334,7 @@ AC_ARG_ENABLE([fsstnd],
AC_DEFINE([FSSTND], [1], [Description])
])
+
# debug
AC_ARG_ENABLE(debug,
[AS_HELP_STRING([--enable-debug],[Enable debug mode @<:@default=no@:>@])],
@@ -342,6 +352,7 @@ if test "$enable_debug" = "no"; then
AC_DEFINE(NDEBUG, 1, [Defined if debug mode is disabled.])
fi
+
# runtime instrumentation
AC_ARG_ENABLE(rtinst,
[AS_HELP_STRING([--enable-rtinst],[Enable runtime instrumentation mode @<:@default=no@:>@])],
@@ -356,6 +367,7 @@ if test "$enable_rtinst" = "yes"; then
AC_DEFINE(RTINST, 1, [Defined if runtime instrumentation mode is enabled.])
fi
+
# valgrind
AC_ARG_ENABLE(valgrind,
[AS_HELP_STRING([--enable-valgrind],[Enable valgrind support settings @<:@default=no@:>@])],
@@ -453,6 +465,40 @@ AC_SUBST(PGSQL_CFLAGS)
AC_SUBST(PGSQL_LIBS)
+# oracle (OCI) support
+AC_ARG_ENABLE(oracle,
+ [AS_HELP_STRING([--enable-oracle],[Enable native Oracle database support @<:@default=no@:>@])],
+ [case "${enableval}" in
+ yes) enable_oracle="yes" ;;
+ no) enable_oracle="no" ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-oracle) ;;
+ esac],
+ [enable_oracle=no]
+)
+if test "x$enable_oracle" = "xyes"; then
+ AC_CHECK_PROG(
+ [HAVE_ORACLE_CONFIG],
+ [oracle-instantclient-config],
+ [yes],,,
+ )
+ if test "x${HAVE_ORACLE_CONFIG}" != "xyes"; then
+ AC_MSG_FAILURE([oracle-instantclient-config not found in PATH])
+ fi
+ AC_CHECK_LIB(
+ [occi],
+ [OCIEnvCreate],
+ [ORACLE_CFLAGS="`oracle-instantclient-config --cflags`"
+ ORACLE_LIBS="`oracle-instantclient-config --libs`"
+ ],
+ [AC_MSG_FAILURE([Oracle (OCI) libraray is missing])],
+ [`oracle-instantclient-config --libs --cflags`]
+ )
+fi
+AM_CONDITIONAL(ENABLE_ORACLE, test x$enable_oracle = xyes)
+AC_SUBST(ORACLE_CFLAGS)
+AC_SUBST(ORACLE_LIBS)
+
+
# libdbi support
AC_ARG_ENABLE(libdbi,
[AS_HELP_STRING([--enable-libdbi],[Enable libdbi database support @<:@default=no@:>@])],
@@ -613,6 +659,7 @@ AM_CONDITIONAL(ENABLE_RELP, test x$enable_relp = xyes)
AC_SUBST(RELP_CFLAGS)
AC_SUBST(RELP_LIBS)
+
# RFC 3195 support
AC_ARG_ENABLE(rfc3195,
[AS_HELP_STRING([--enable-rfc3195],[Enable RFC3195 support @<:@default=no@:>@])],
@@ -631,7 +678,7 @@ AC_SUBST(LIBLOGGING_CFLAGS)
AC_SUBST(LIBLOGGING_LIBS)
-# settings for the file input module;
+# settings for the file input module
AC_ARG_ENABLE(imfile,
[AS_HELP_STRING([--enable-imfile],[file input module enabled @<:@default=no@:>@])],
[case "${enableval}" in
@@ -641,13 +688,35 @@ AC_ARG_ENABLE(imfile,
esac],
[enable_imfile=no]
)
-#
-# you may want to do some library checks here - see snmp, mysql, pgsql modules
-# for samples
-#
AM_CONDITIONAL(ENABLE_IMFILE, test x$enable_imfile = xyes)
+# settings for the omprog output module
+AC_ARG_ENABLE(omprog,
+ [AS_HELP_STRING([--enable-omprog],[Compiles omprog module @<:@default=no@:>@])],
+ [case "${enableval}" in
+ yes) enable_omprog="yes" ;;
+ no) enable_omprog="no" ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-omprog) ;;
+ esac],
+ [enable_omprog=no]
+)
+AM_CONDITIONAL(ENABLE_OMPROG, test x$enable_omprog = xyes)
+
+
+# settings for omstdout
+AC_ARG_ENABLE(omstdout,
+ [AS_HELP_STRING([--enable-omstdout],[Compiles stdout module @<:@default=no@:>@])],
+ [case "${enableval}" in
+ yes) enable_omstdout="yes" ;;
+ no) enable_omstdout="no" ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-omstdout) ;;
+ esac],
+ [enable_omstdout=no]
+)
+AM_CONDITIONAL(ENABLE_OMSTDOUT, test x$enable_omstdout = xyes)
+
+
# settings for the template input module; copy and modify this code
# if you intend to add your own module. Be sure to replace imtemplate
# by the actual name of your module.
@@ -665,7 +734,7 @@ AC_ARG_ENABLE(imtemplate,
# for samples
#
AM_CONDITIONAL(ENABLE_IMTEMPLATE, test x$enable_imtemplate = xyes)
-# end of copy template - be sure to serach for imtemplate to find everything!
+# end of copy template - be sure to search for imtemplate to find everything!
# settings for the template output module; copy and modify this code
@@ -685,29 +754,15 @@ AC_ARG_ENABLE(omtemplate,
# for samples
#
AM_CONDITIONAL(ENABLE_OMTEMPLATE, test x$enable_omtemplate = xyes)
-# end of copy template - be sure to serach for omtemplate to find everything!
+# end of copy template - be sure to search for omtemplate to find everything!
-# settings for omstdout
-# note that "make check" fails, if omstdout is not enabled (thus we enable
-# it by default).
-AC_ARG_ENABLE(omstdout,
- [AS_HELP_STRING([--enable-omstdout],[Compiles stdout template module @<:@default=yes@:>@])],
- [case "${enableval}" in
- yes) enable_omstdout="yes" ;;
- no) enable_omstdout="no" ;;
- *) AC_MSG_ERROR(bad value ${enableval} for --enable-omstdout) ;;
- esac],
- [enable_omstdout=no]
-)
-#
-# you may want to do some library checks here - see snmp, mysql, pgsql modules
-# for samples
-#
-AM_CONDITIONAL(ENABLE_OMSTDOUT, test x$enable_omstdout = xyes)
+SHAVE_INIT
AC_CONFIG_FILES([Makefile \
+ shave \
+ shave-libtool \
runtime/Makefile \
tools/Makefile \
doc/Makefile \
@@ -720,6 +775,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,35 +788,48 @@ AC_CONFIG_FILES([Makefile \
plugins/omlibdbi/Makefile \
plugins/ommail/Makefile \
plugins/omsnmp/Makefile \
+ plugins/omoracle/Makefile \
tests/Makefile])
AC_OUTPUT
echo "****************************************************"
echo "rsyslog will be compiled with the following settings:"
echo
-echo "Multithreading support enabled: $enable_pthreads"
-echo "Klog functionality enabled: $enable_klog ($os_type)"
-echo "Regular expressions support enabled: $enable_regexp"
-echo "Zlib compression support enabled: $enable_zlib"
-echo "MySql support enabled: $enable_mysql"
-echo "libdbi support enabled: $enable_libdbi"
-echo "PostgreSQL support enabled: $enable_pgsql"
-echo "SNMP support enabled: $enable_snmp"
-echo "Mail support enabled: $enable_mail"
-echo "RELP support enabled: $enable_relp"
-echo "imdiag enabled: $enable_imdiag"
-echo "file input module enabled: $enable_imfile"
-echo "input template module will be compiled: $enable_imtemplate"
-echo "output template module will be compiled: $enable_omtemplate"
-echo "omstdout module will be compiled: $enable_omstdout"
-echo "Large file support enabled: $enable_largefile"
-echo "Networking support enabled: $enable_inet"
-echo "GnuTLS network stream driver enabled: $enable_gnutls"
-echo "Enable GSSAPI Kerberos 5 support: $want_gssapi_krb5"
-echo "Debug mode enabled: $enable_debug"
-echo "Runtime Instrumentation enabled: $enable_rtinst"
-echo "Diagnostic tools enabled: $enable_diagtools"
-echo "valgrind support settings enabled: $enable_valgrind"
-echo "rsyslog runtime will be built: $enable_rsyslogrt"
-echo "rsyslogd will be built: $enable_rsyslogd"
-
+echo " Multithreading support enabled: $enable_pthreads"
+echo " Large file support enabled: $enable_largefile"
+echo " Networking support enabled: $enable_inet"
+echo " Regular expressions support enabled: $enable_regexp"
+echo " Zlib compression support enabled: $enable_zlib"
+echo " rsyslog runtime will be built: $enable_rsyslogrt"
+echo " rsyslogd will be built: $enable_rsyslogd"
+echo
+echo "---{ input plugins }---"
+echo " Klog functionality enabled: $enable_klog ($os_type)"
+echo " imdiag enabled: $enable_imdiag"
+echo " file input module enabled: $enable_imfile"
+echo " input template module will be compiled: $enable_imtemplate"
+echo
+echo "---{ output plugins }---"
+echo " Mail support enabled: $enable_mail"
+echo " omprog module will be compiled: $enable_omprog"
+echo " omstdout module will be compiled: $enable_omstdout"
+echo " output template module will be compiled: $enable_omtemplate"
+echo
+echo "---{ database support }---"
+echo " MySql support enabled: $enable_mysql"
+echo " libdbi support enabled: $enable_libdbi"
+echo " PostgreSQL support enabled: $enable_pgsql"
+echo " Oracle (OCI) support enabled: $enable_oracle"
+echo
+echo "---{ protocol support }---"
+echo " GnuTLS network stream driver enabled: $enable_gnutls"
+echo " GSSAPI Kerberos 5 support enabled: $enable_gssapi_krb5"
+echo " RELP support enabled: $enable_relp"
+echo " SNMP support enabled: $enable_snmp"
+echo
+echo "---{ debugging support }---"
+echo " Debug mode enabled: $enable_debug"
+echo " Runtime Instrumentation enabled: $enable_rtinst"
+echo " Diagnostic tools enabled: $enable_diagtools"
+echo " Valgrind support settings enabled: $enable_valgrind"
+echo
diff --git a/dirty.h b/dirty.h
index db9bc31b..6d585753 100644
--- a/dirty.h
+++ b/dirty.h
@@ -32,6 +32,7 @@ rsRetVal logmsgInternal(int iErr, int pri, uchar *msg, int flags);
rsRetVal parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int flags, flowControl_t flowCtlTypeu, uchar *pszInputName, struct syslogTime *stTime, time_t ttGenTime);
int parseRFCSyslogMsg(msg_t *pMsg, int flags);
int parseLegacySyslogMsg(msg_t *pMsg, int flags);
+rsRetVal diagGetMainMsgQSize(int *piSize); /* for imdiag */
/* TODO: the following 2 need to go in conf obj interface... */
rsRetVal cflineParseTemplateName(uchar** pp, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts, uchar *dfltTplName);
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 3015d6b5..0703b8fc 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 \
@@ -77,6 +78,7 @@ html_files = \
rsconf1_filecreatemode.html \
rsconf1_filegroup.html \
rsconf1_fileowner.html \
+ rsconf1_generateconfiggraph.html \
rsconf1_gssforwardservicename.html \
rsconf1_gsslistenservicename.html \
rsconf1_gssmode.html \
@@ -108,6 +110,22 @@ 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 = \
+ rsyslog_confgraph_complex.png\
+ rsyslog_confgraph_std.png \
+ direct_queue0.png \
+ direct_queue1.png \
+ direct_queue2.png \
+ direct_queue3.png \
+ direct_queue_rsyslog.png \
+ direct_queue_rsyslog2.png \
+ direct_queue_directq.png \
+ dataflow.png \
+ queue_analogy_tv.png \
+ gssapi.png \
+ rsyslog-vers.png
+
+EXTRA_DIST = $(html_files) $(grfx_files)
diff --git a/doc/dataflow.png b/doc/dataflow.png
new file mode 100644
index 00000000..fd614d8c
--- /dev/null
+++ b/doc/dataflow.png
Binary files differ
diff --git a/doc/dev_oplugins.html b/doc/dev_oplugins.html
index 5bfc974c..cc2f7f38 100644
--- a/doc/dev_oplugins.html
+++ b/doc/dev_oplugins.html
@@ -47,7 +47,7 @@ copying omtemplate. Then, the basic steps you need to do are:
<li>mv omtemplate.c your-plugin.c
<li>cd ../..
<li>vi Makefile.am configure.ac
-<br>serach for omtemplate, copy and modify (follow comments)
+<br>search for omtemplate, copy and modify (follow comments)
</ul>
<p>Basically, this is all you need to do ... Well, except, of course, coding
your plugin ;). For testing, you need rsyslog's debugging support. Some useful
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/how2help.html b/doc/how2help.html
index 0caa5a3a..4f0bd57a 100644
--- a/doc/how2help.html
+++ b/doc/how2help.html
@@ -1,59 +1,57 @@
-<html>
-<head>
-<title>How you can Help</title>
-</head>
-<body>
-<h2>How you can Help</h2>
-<p><b>You like rsyslog and would like to lend us a helping hand?</b> This page
-tells you how easy it is to help a little bit. You can contribute to the project
-even with a single mouse click! If you could pick a single item from the
-wish list, that would be awfully helpful!</p>
-<p>This is our wish list:</p>
-<ul>
- <li>let others know how great rsyslog is<ul>
- <li>rate us at <a href="http://freshmeat.net/rate/52985/">freshmeat.net</a>
- and <a href="http://www.icewalkers.com/vote.php?ID=2420">icewalkers.com</a></li>
- <li>spread word about rsyslog in forums and newsgroups</li>
- <li>place a link to <a href="http://www.rsyslog.com">www.rsyslog.com</a>
- from your home page</li>
- </ul>
- </li>
- <li>let us know about rsyslog - we are eager for feedback<ul>
- <li>tell us what you like and what you not like - so that we can include
- that into development</li>
- <li>tell us what you use rsyslog for - especially if you have high
- traffic volume or an otherwise &quot;uncommon&quot; deployment. We are looking for
- case studies and experience how rsyslog performs in unusual scenarios.</li>
- <li>allow us to post your thoughts and experiences as a &quot;user story&quot; on
- the web site (so far, none are there ;))</li>
- </ul>
- </li>
- <li>if you know how to create packages (rpm, deb, ...)<ul>
- <li>we would very much appreciate your help with package creation. We know
- that it is important to have good binary packages for a product to
- spread widely. Yet, we do not have the knowledge to do it all ourselves.
- <a href="mailto:rgerhards@adiscon.com">Drop Rainer a note </a>if you
- could help us out.</li>
- </ul>
- </li>
- <li>if you have configured a device for sending syslog data, and that device
- is not in our
- <a href="http://www.monitorware.com/en/syslog-enabled-products/">syslog
- configuration database</a>, you might want to tell us how to configure it.</li>
- <li>if you are a corporate user<ul>
- <li>you might consider <a href="http://www.adiscon.com">Adiscon</a>'s
- commercial <a href="http://www.monitorware.com/">MonitorWare products</a>
- for Windows, e.g. to deliver Windows Event Log data to rsyslogd (sales
- of the commercial products funds the open source development - and they
- also work very well).</li>
- <li>you might be interested in
- <a href="http://www.adiscon.com/Common/en/Products/techsup.php">
- purchasing professional support or add-on development</a> for rsyslog</li>
- </ul>
- </li>
-</ul>
-<p><b>We appreciate your help very much.</b> A big thank you for anything you
-might do!</p>
-
-</body>
-</html>
+<html>
+<head>
+<title>How you can Help</title>
+</head>
+<body>
+<h2>How you can Help</h2>
+<p><b>You like rsyslog and would like to lend us a helping hand?</b> This page
+tells you how easy it is to help a little bit. You can contribute to the project
+even with a single mouse click! If you could pick a single item from the
+wish list, that would be awfully helpful!</p>
+<p>This is our wish list:</p>
+<ul>
+ <li>let others know how great rsyslog is<ul>
+ <li>spread word about rsyslog in forums and newsgroups</li>
+ <li>place a link to <a href="http://www.rsyslog.com">www.rsyslog.com</a>
+ from your home page</li>
+ </ul>
+ </li>
+ <li>let us know about rsyslog - we are eager for feedback<ul>
+ <li>tell us what you like and what you not like - so that we can include
+ that into development</li>
+ <li>tell us what you use rsyslog for - especially if you have high
+ traffic volume or an otherwise &quot;uncommon&quot; deployment. We are looking for
+ case studies and experience how rsyslog performs in unusual scenarios.</li>
+ <li>allow us to post your thoughts and experiences as a &quot;user story&quot; on
+ the web site (so far, none are there ;))</li>
+ </ul>
+ </li>
+ <li>if you know how to create packages (rpm, deb, ...)<ul>
+ <li>we would very much appreciate your help with package creation. We know
+ that it is important to have good binary packages for a product to
+ spread widely. Yet, we do not have the knowledge to do it all ourselves.
+ <a href="mailto:rgerhards@adiscon.com">Drop Rainer a note </a>if you
+ could help us out.</li>
+ </ul>
+ </li>
+ <li>if you have configured a device for sending syslog data, and that device
+ is not in our
+ <a href="http://www.monitorware.com/en/syslog-enabled-products/">syslog
+ configuration database</a>, you might want to tell us how to configure it.</li>
+ <li>if you are a corporate user<ul>
+ <li>you might consider <a href="http://www.adiscon.com">Adiscon</a>'s
+ commercial <a href="http://www.monitorware.com/">MonitorWare products</a>
+ for Windows, e.g. to deliver Windows Event Log data to rsyslogd (sales
+ of the commercial products funds the open source development - and they
+ also work very well).</li>
+ <li>you might be interested in
+ <a href="http://www.adiscon.com/Common/en/Products/techsup.php">
+ purchasing professional support or add-on development</a> for rsyslog</li>
+ </ul>
+ </li>
+</ul>
+<p><b>We appreciate your help very much.</b> A big thank you for anything you
+might do!</p>
+
+</body>
+</html
diff --git a/doc/imtcp.html b/doc/imtcp.html
index 0ee0f96a..9ea7efa1 100644
--- a/doc/imtcp.html
+++ b/doc/imtcp.html
@@ -14,9 +14,10 @@ Encryption can be provided by using <a href="rsyslog_stunnel.html">stunnel</a>
(an alternative is the use
the&nbsp;<a href="imgssapi.html">imgssapi</a>
modul).</p>
-<p>In the future, multiple receivers may be configured by
+<p>Multiple receivers may be configured by
specifying
-$InputTCPServerRun multiple times. This is not currently supported.
+$InputTCPServerRun multiple times. This is available since version 4.3.1, earlier
+versions do NOT support it.
</p>
<p><b>Configuration Directives</b>:</p>
<ul>
@@ -58,7 +59,6 @@ AuthMode and&nbsp; <a href="netstream.html">network stream driver</a>. Permitted
<b>Caveats/Known Bugs:</b>
<ul>
<li>module always binds to all interfaces</li>
-<li>only a single listener can be bound</li>
<li>can not be loaded together with <a href="imgssapi.html">imgssapi</a>
(which includes the functionality of imtcp)</li>
</ul>
diff --git a/doc/manual.html b/doc/manual.html
index 5b9c4b1e..adf89587 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.7 (devel branch) of rsyslog.</b>
+<p><b>This documentation is for version 4.3.1 (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..d7533ad5
--- /dev/null
+++ b/doc/queues_analogy.html
@@ -0,0 +1,259 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head>
+<title>turning lanes and rsyslog queues - an analogy</title></head>
+<body>
+<a href="rsyslog_conf_global.html">back</a>
+
+<h1>Turning Lanes and Rsyslog Queues - an Analogy</h1>
+<p>If there is a single object absolutely vital to understanding the way
+rsyslog works, this object is queues. Queues offer a variety of services,
+including support for multithreading. While there is elaborate in-depth
+documentation on the ins and outs of <a href="queues.html">rsyslog queues</a>,
+some of the concepts are hard to grasp even for experienced people. I think this
+is because rsyslog uses a very high layer of abstraction which includes things
+that look quite unnatural, like queues that do <b>not</b> actually queue...
+<p>With this document, I take a different approach: I will not describe every specific
+detail of queue operation but hope to be able to provide the core idea of how
+queues are used in rsyslog by using an analogy. I will compare the rsyslog data flow
+with real-life traffic flowing at an intersection.
+<p>But first let's set the stage for the rsyslog part. The graphic below describes
+the data flow inside rsyslog:
+<p align="center"><img src="dataflow.png" alt="rsyslog data flow">
+<p>Note that there is a <a href="http://www.rsyslog.com/Article350.phtml">video tutorial</a>
+available on the data flow. It is not perfect, but may aid in understanding this picture.
+<p>For our needs, the important fact to know is that messages enter rsyslog on &quot;the
+left side&quot; (for example, via UDP), are being preprocessed, put into the
+so-called main queue, taken off that queue, be filtered and be placed into one or
+several action queues (depending on filter results). They leave rsyslog on &quot;the
+right side&quot; where output modules (like the file or database writer) consume them.
+<p>So there are always <b>two</b> stages where a message (conceptually) is queued - first
+in the main queue and later on in <i>n</i> action specific queues (with <i>n</i> being the number of
+actions that the message in question needs to be processed by, what is being decided
+by the &quot;Filter Engine&quot;). As such, a message will be in at least two queues
+during its lifetime (with the exception of messages being discarded by the queue itself,
+but for the purpose of this document, we will ignore that possibility).
+<p>Also, it is vitally
+important to understand that <b>each</b> action has a queue sitting in front of it.
+If you have dug into the details of rsyslog configuration, you have probably seen that
+a queue mode can be set for each action. And the default queue mode is the so-called
+&quot;direct mode&quot;, in which &quot;the queue does not actually enqueue data&quot;.
+That sounds silly, but is not. It is an important abstraction that helps keep the code clean.
+<p>To understand this, we first need to look at who is the active component. In our data flow,
+the active part always sits to the left of the object. For example, the &quot;Preprocessor&quot;
+is being called by the inputs and calls itself into the main message queue. That is, the queue
+receiver is called, it is passive. One might think that the &quot;Parser &amp; Filter Engine&quot;
+is an active component that actively pulls messages from the queue. This is wrong! Actually,
+it is the queue that has a pool of worker threads, and these workers pull data from the queue
+and then call the passively waiting Parser and Filter Engine with those messages. So the
+main message queue is the active part, the Parser and Filter Engine is passive.
+<p>Let's now try an analogy analogy for this part: Think about a TV show. The show is produced
+in some TV studio, from there sent (actively) to a radio tower. The radio tower passively
+receives from the studio and then actively sends out a signal, which is passively received
+by your TV set. In our simplified view, we have the following picture:
+<p align="center"><img src="queue_analogy_tv.png" alt="rsyslog queues and TV analogy">
+<p>The lower part of the picture lists the equivalent rsyslog entities, in an abstracted way.
+Every queue has a producer (in the above sample the input) and a consumer (in the above sample the Parser
+and Filter Engine). Their active and passive functions are equivalent to the TV entities
+that are listed on top of the rsyslog entity. For example, a rsyslog consumer can never
+actively initiate reception of a message in the same way a TV set cannot actively
+&quot;initiate&quot; a TV show - both can only &quot;handle&quot; (display or process)
+what is sent to them.
+<p>Now let's look at the action queues: here, the active part, the producer, is the
+Parser and Filter Engine. The passive part is the Action Processor. The later does any
+processing that is necessary to call the output plugin, in particular it processes the template
+to create the plugin calling parameters (either a string or vector of arguments). From the
+action queue's point of view, Action Processor and Output form a single entity. Again, the
+TV set analogy holds. The Output <b>does not</b> actively ask the queue for data, but
+rather passively waits until the queue itself pushes some data to it.
+
+<p>Armed with this knowledge, we can now look at the way action queue modes work. My analogy here
+is a junction, as shown below (note that the colors in the pictures below are <b>not</b> related to
+the colors in the pictures above!):
+<p align="center"><img src="direct_queue0.png">
+<p>This is a very simple real-life traffic case: one road joins another. We look at
+traffic on the straight road, here shown by blue and green arrows. Traffic in the
+opposing direction is shown in blue. Traffic flows without
+any delays as long as nobody takes turns. To be more precise, if the opposing traffic takes
+a (right) turn, traffic still continues to flow without delay. However, if a car in the red traffic
+flow intends to do a (left, then) turn, the situation changes:
+<p align="center"><img src="direct_queue1.png">
+<p>The turning car is represented by the green arrow. It cannot turn unless there is a gap
+in the &quot;blue traffic stream&quot;. And as this car blocks the roadway, the remaining
+traffic (now shown in red, which should indicate the block condition),
+must wait until the &quot;green&quot; car has made its turn. So
+a queue will build up on that lane, waiting for the turn to be completed.
+Note that in the examples below I do not care that much about the properties of the
+opposing traffic. That is, because its structure is not really important for what I intend to
+show. Think about the blue arrow as being a traffic stream that most of the time blocks
+left-turners, but from time to time has a gap that is sufficiently large for a left-turn
+to complete.
+<p>Our road network designers know that this may be unfortunate, and for more important roads
+and junctions, they came up with the concept of turning lanes:
+<p align="center"><img src="direct_queue2.png">
+<p>Now, the car taking the turn can wait in a special area, the turning lane. As such,
+the &quot;straight&quot; traffic is no longer blocked and can flow in parallel to the
+turning lane (indicated by a now-green-again arrow).
+
+<p>However, the turning lane offers only finite space. So if too many cars intend to
+take a left turn, and there is no gap in the &quot;blue&quot; traffic, we end up with
+this well-known situation:
+<p align="center"><img src="direct_queue3.png">
+<p>The turning lane is now filled up, resulting in a tailback of cars intending to
+left turn on the main driving lane. The end result is that &quot;straight&quot; traffic
+is again being blocked, just as in our initial problem case without the turning lane.
+In essence, the turning lane has provided some relief, but only for a limited amount of
+cars. Street system designers now try to weight cost vs. benefit and create (costly)
+turning lanes that are sufficiently large to prevent traffic jams in most, but not all
+cases.
+<p><b>Now let's dig a bit into the mathematical properties of turning lanes.</b> We assume that
+cars all have the same length. So, units of cars, the length is alsways one (which is nice,
+as we don't need to care about that factor any longer ;)). A turning lane has finite capacity of
+<i>n</i> cars. As long as the number of cars wanting to take a turn is less than or eqal
+to <i>n</i>, &quot;straigth traffic&quot; is not blocked (or the other way round, traffic
+is blocked if at least <i>n + 1</i> cars want to take a turn!). We can now find an optimal
+value for <i>n</i>: it is a function of the probability that a car wants to turn
+and the cost of the turning lane
+(as well as the probability there is a gap in the &quot;blue&quot; traffic, but we ignore this
+in our simple sample).
+If we start from some finite upper bound of <i>n</i>, we can decrease
+<i>n</i> to a point where it reaches zero. But let's first look at <i>n = 1</i>, in which case exactly
+one car can wait on the turning lane. More than one car, and the rest of the traffic is blocked.
+Our everyday logic indicates that this is actually the lowest boundary for <i>n</i>.
+<p>In an abstract view, however, <i>n</i> can be zero and that works nicely. There still can be
+<i>n</i> cars at any given time on the turning lane, it just happens that this means there can
+be no car at all on it. And, as usual, if we have at least <i>n + 1</i> cars wanting to turn,
+the main traffic flow is blocked. True, but <i>n + 1 = 0 + 1 = 1</i> so as soon as there is any
+car wanting to take a turn, the main traffic flow is blocked (remember, in all cases, I assume
+no sufficiently large gaps in the opposing traffic).
+<p>This is the situation our everyday perception calls &quot;road without turning lane&quot;.
+In my math model, it is a &quot;road with turning lane of size 0&quot;. The subtle difference
+is important: my math model guarantees that, in an abstract sense, there always is a turning
+lane, it may just be too short. But it exists, even though we don't see it. And now I can
+claim that even in my small home village, all roads have turning lanes, which is rather
+impressive, isn't it? ;)
+<p><b>And now we finally have arrived at rsyslog's queues!</b> Rsyslog action queues exists for
+all actions just like all roads in my village have turning lanes! And as in this real-life sample,
+it may be hard to see the action queues for that reason. In rsyslog, the &quot;direct&quot; queue
+mode is the equivalent to the 0-sized turning lane. And actions queues are the equivalent to turning
+lanes in general, with our real-life <i>n</i> being the maximum queue size.
+The main traffic line (which sometimes is blocked) is the equivalent to the
+main message queue. And the periods without gaps in the opposing traffic are equivalent to
+execution time of an action. In a rough sketch, the rsyslog main and action queues look like in the
+following picture.
+<p align="center"><img src="direct_queue_rsyslog.png">
+<p>We need to read this picture from right to left (otherwise I would need to redo all
+the graphics ;)). In action 3, you see a 0-sized turning lane, aka an action queue in &quot;direct&quot;
+mode. All other queues are run in non-direct modes, but with different sizes greater than 0.
+<p>Let us first use our car analogy:
+Assume we are in a car on the main lane that wants to take turn into the &quot;action 4&quot;
+road. We pass action 1, where a number of cars wait in the turning lane and we pass
+action 2, which has a slightly smaller, but still not filled up turning lane. So we pass that
+without delay, too. Then we come to &quot;action 3&quot;, which has no turning lane. Unfortunately,
+the car in front of us wants to turn left into that road, so it blocks the main lane. So, this time
+we need to wait. An observer standing on the sidewalk may see that while we need to wait, there are
+still some cars in the &quot;action 4&quot; turning lane. As such, even though no new cars can
+arrive on the main lane, cars still turn into the &quot;action 4&quot; lane. In other words,
+an observer standing in &quot;action 4&quot; road is unable to see that traffic on the main lane
+is blocked.
+<p>Now on to rsyslog: Other than in the real-world traffic example, messages in rsyslog
+can - at more or less the
+same time - &quot;take turns&quot; into several roads at once. This is done by duplicating the message
+if the road has a non-zero-sized &quot;turning lane&quot; - or in rsyslog terms a queue that is
+running in any non-direct mode. If so, a deep copy of the message object is made, that placed into
+the action queue and then the initial message proceeds on the &quot;main lane&quot;. The action
+queue then pushes the duplicates through action processing. This is also the reason why a
+discard action inside a non-direct queue does not seem to have an effect. Actually, it discards the
+copy that was just created, but the original message object continues to flow.
+<p>
+In action 1, we have some entries in the action queue, as we have in action 2 (where the queue is
+slightly shorter). As we have seen, new messages pass action one and two almost instantaneously.
+However, when a messages reaches action 3, its flow is blocked. Now, message processing must wait
+for the action to complete. Processing flow in a direct mode queue is something like a U-turn:
+
+<p align="center"><img src="direct_queue_directq.png" alt="message processing in an rsyslog action queue in direct mode">
+<p>The message starts to execute the action and once this is done, processing flow continues.
+In a real-life analogy, this may be the route of a delivery man who needs to drop a parcel
+in a side street before he continues driving on the main route. As a side-note, think of what happens
+with the rest of the delivery route, at least for today, if the delivery truck has a serious accident
+in the side street. The rest of the parcels won't be delivered today, will they? This is exactly how the
+discard action works. It drops the message object inside the action and thus the message will no
+longer be available for further delivery - but as I said, only if the discard is done in a
+direct mode queue (I am stressing this example because it often causes a lot of confusion).
+<p>Back to the overall scenario. We have seen that messages need to wait for action 3 to
+complete. Does this necessarily mean that at the same time no messages can be processed
+in action 4? Well, it depends. As in the real-life scenario, action 4 will continue to
+receive traffic as long as its action queue (&quot;turn lane&quot;) is not drained. In
+our drawing, it is not. So action 4 will be executed while messages still wait for action 3
+to be completed.
+<p>Now look at the overall picture from a slightly different angle:
+<p align="center"><img src="direct_queue_rsyslog2.png" alt="message processing in an rsyslog action queue in direct mode">
+<p>The number of all connected green and red arrows is four - one each for action 1, 2 and 3
+(this one is dotted as action 4 was a special case) and one for the &quot;main lane&quot; as
+well as action 3 (this one contains the sole red arrow). <b>This number is the lower bound for
+the number of threads in rsyslog's output system (&quot;right-hand part&quot; of the main message
+queue)!</b> Each of the connected arrows is a continuous thread and each &quot;turn lane&quot; is
+a place where processing is forked onto a new thread. Also, note that in action 3 the processing
+is carried out on the main thread, but not in the non-direct queue modes.
+<p>I have said this is &quot;the lower bound for the number of threads...&quot;. This is with
+good reason: the main queue may have more than one worker thread (individual action queues
+currently do not support this, but could do in the future - there are good reasons for that, too
+but exploring why would finally take us away from what we intend to see). Note that you
+configure an upper bound for the number of main message queue worker threads. The actual number
+varies depending on a lot of operational variables, most importantly the number of messages
+inside the queue. The number <i>t_m</i> of actually running threads is within the integer-interval
+[0,confLimit] (with confLimit being the operator configured limit, which defaults to 5).
+Output plugins may have more than one thread created by themselves. It is quite unusual for an
+output plugin to create such threads and so I assume we do not have any of these.
+Then, the overall number of threads in rsyslog's filtering and output system is
+<i>t_total = t_m + number of actions in non-direct modes</i>. Add the number of
+inputs configured to that and you have the total number of threads running in rsyslog at
+a given time (assuming again that inputs utilize only one thread per plugin, a not-so-safe
+assumption).
+<p>A quick side-note: I gave the lower bound for <i>t_m</i> as zero, which is somewhat in contrast
+to what I wrote at the begin of the last paragraph. Zero is actually correct, because rsyslog
+stops all worker threads when there is no work to do. This is also true for the action queues.
+So the ultimate lower bound for a rsyslog output system without any work to carry out actually is zero.
+But this bound will never be reached when there is continuous flow of activity. And, if you are
+curios: if the number of workers is zero, the worker wakeup process is actually handled within the
+threading context of the &quot;left-hand-side&quot; (or producer) of the queue. After being
+started, the worker begins to play the active queue component again. All of this, of course,
+can be overridden with configuration directives.
+<p>When looking at the threading model, one can simply add n lanes to the main lane but otherwise
+retain the traffic analogy. This is a very good description of the actual process (think what this
+means to the &quot;turning lanes&quot;; hint: there still is only one per action!).
+<p><b>Let's try to do a warp-up:</b> I have hopefully been able to show that in rsyslog, an action
+queue &quot;sits in front of&quot; each output plugin. Messages are received and flow, from input
+to output, over various stages and two level of queues to the outputs. Actions queues are always
+present, but may not easily be visible when in direct mode (where no actual queuing takes place).
+The "road junction with turning lane" analogy well describes the way - and intent - of the various
+queue levels in rsyslog.
+<p>On the output side, the queue is the active component, <b>not</b> the consumer. As such, the consumer
+cannot ask the queue for anything (like n number of messages) but rather is activated by the queue
+itself. As such, a queue somewhat resembles a &quot;living thing&quot; whereas the outputs are
+just tools that this &quot;living thing&quot; uses.
+<p><b>Note that I left out a couple of subtleties</b>, especially when it comes
+to error handling and terminating
+a queue (you hopefully have now at least a rough idea why I say &quot;terminating <b>a queue</b>&quot;
+and not &quot;terminating an action&quot; - <i>who is the &quot;living thing&quot;?</i>). An action returns
+a status to the queue, but it is the queue that ultimately decides which messages can finally be
+considered processed and which not. Please note that the queue may even cancel an output right in
+the middle of its action. This happens, if configured, if an output needs more than a configured
+maximum processing time and is a guard condition to prevent slow outputs from deferring a rsyslog
+restart for too long. Especially in this case re-queuing and cleanup is not trivial. Also, note that
+I did not discuss disk-assisted queue modes. The basic rules apply, but there are some additional
+constraints, especially in regard to the threading model. Transitioning between actual
+disk-assisted mode and pure-in-memory-mode (which is done automatically when needed) is also far from
+trivial and a real joy for an implementer to work on ;).
+<p>If you have not done so before, it may be worth reading the
+<a href="queues.html">rsyslog queue user's guide,</a> which most importantly lists all
+the knobs you can turn to tweak queue operation.
+<p>[<a href="manual.html">manual index</a>]
+[<a href="rsyslog_conf.html">rsyslog.conf</a>]
+[<a href="http://www.rsyslog.com/">rsyslog site</a>]</p>
+<p><font size="2">This documentation is part of the
+<a href="http://www.rsyslog.com/">rsyslog</a> project.<br>
+Copyright &copy; 2009 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 3 or higher.</font></p>
+</body>
+</html>
diff --git a/doc/rsconf1_generateconfiggraph.html b/doc/rsconf1_generateconfiggraph.html
new file mode 100644
index 00000000..0b18463a
--- /dev/null
+++ b/doc/rsconf1_generateconfiggraph.html
@@ -0,0 +1,121 @@
+<html>
+<head>
+<title>rsyslog.conf file</title>
+</head>
+<body>
+<a href="rsyslog_conf_global.html">back</a>
+
+<h2>$GenerateConfigGraph</h2>
+<p><b>Type:</b> global configuration directive</p>
+<p><b>Default:</b> </p>
+<p><b>Available Since:</b> 4.3.1</p>
+<p><b>Description:</b></p>
+<p>This directive permits to create (hopefully) good-looking visualizations of rsyslogd's
+configuration. It does not affect rsyslog operation. If the directive is specified multiple
+times, all but the last are ignored. If it is specified, a graph is created. This happens
+both during a regular startup as well a config check run. It is recommended to include
+this directive only for documentation purposes and remove it from a production
+configuraton.
+<p>The graph is not drawn by rsyslog itself. Instead, it uses the great open source tool
+<a href="http://www.graphviz.org">Graphviz</a> to do the actual drawing. This has at least
+two advantages:
+<ul>
+<li>the graph drawing support code in rsyslog is extremly slim and without overhead
+<li>the user may change or further annotate the generated file, thus potentially
+improving his documentation
+</ul>
+The drawback, of course, is that you need to run Graphviz once you have generated
+the control file with rsyslog. Fortunately, the process to do so is rather easy:
+<ol>
+<li>add &quot;$GenerateConfigGraph /path/to/file.dot&quot; to rsyslog.conf (from now on, I
+will call the file just file.dot). Optionally, add &quot;$ActionName&quot; statement
+<b>in front of</b> those actions that you like to use friendly names with. If you do
+this, keep the names short.
+<li>run rsyslog at least once (either in regular or configuration check mode)
+<li>remember to remove the $GenerateConfigGraph directive when you no longer need it (or
+comment it out)
+<li>change your working directory to where you place the dot file
+<li>if you would like to edit the rsyslog-generated file, now is the time to do so
+<li>do &quot;dot -Tpng file.dot &gt; file.png&quot;
+<li>remember that you can use &quot;convert -resize 50% file.png resized.png&quot; if
+dot's output is too large (likely) or too small. Resizing can be especially useful if
+you intend to get a rough overview over your configuration.
+</ol>
+After completing these steps, you should have a nice graph of your configuration. Details
+are missing, but that is exactly the point. At the start of the graph is always (at least
+in this version, could be improved) a node called &quot;inputs&quot; in a tripple hexagon
+shape. This represents all inputs active in the system (assuming you have defined some,
+what the current version does not check). Next comes the main queue. It is given in a
+hexagon shape. That shape indicates that a queue is peresent and used to de-couple
+the inbound from the outbound part of the graph. In technical terms, here is a
+threading boundary. Action with &quot;real&quot; queues (other than in direct mode)
+also utilize this shape. For actions, notice that a &quot;hexagon action&quot; creates
+a deep copy of the message. As such, a &quot;discard hexagon action&quot; actually does
+nothing, because it duplicates the message and then discards <b>the duplicate</b>.
+At the end of the diagram, you always see a &quot;discard&quot; action. This indicates
+that rsyslog discards messages which have been run through all available rules.
+<p>Edges are labeled with information about when they are taken. For filters, the type of
+filter, but not any specifics, are given. It is also indicated if no filter is
+applied in the configuration file (by using a &quot;*.*&quot; selector). Edges without
+labels are unconditionally taken. The actions themselfs are labeled with the name of
+the output module that handles them. If provided, the name given via
+&quot;ActionName&quot; is used instead. No further details are provided.
+<p>If there is anything in red, this should draw your attention. In this case, rsyslogd
+has detected something that does not look quite right. A typical example is a discard
+action which is followed by some other actions in an action unit. Even though something
+may be red, it can be valid - rsyslogd's graph generator does not yet check each and
+every speciality, so the configuration may just cover a very uncommon case.
+<p>Now let's look at some examples. The graph below was generated on a fairly standard
+Fedora rsyslog.conf file. It had only the usually commented-out last forwarding action
+activated:
+<p align="center">
+<img src="rsyslog_confgraph_std.png" alt="rsyslog configuration graph for a default fedora rsyslog.conf">
+<p>This is the typical structure for a simple rsyslog configuration. There are a couple of
+actions, each guarded by a filter. Messages run from top to bottom and control branches
+whenever a filter evaluates to true. As there is no discard action, all messages will
+run through all filters and discarded in the system default discard action right after
+all configured actions.
+</p>
+<p>A more complex example can be seen in the next graph. This is a configuration I
+created for testing the graph-creation features, so it contains a little bit of
+everything. However, real-world configurations can look quite complex, too (and I
+wouldn't say this one is very complex):
+<p align="center">
+<img src="rsyslog_confgraph_complex.png">
+</p>
+<p>Here, we have a user-defined discard action. You can immediately see this because
+processing branches after the first &quot;builtin-file&quot; action. Those messages
+where the filter evaluates to true for will never run through the left-hand action
+branch. However, there is also a configuration error present: there are two more
+actions (now shown red) after the discard action. As the message is discarded, these will
+never be executed. Note that the discard branch contains no further filters. This is
+because these actions are all part of the same action unit, which is guarded only by
+an entry filter. The same is present a bit further down at the node labeled
+&quot;write_system_log_2&quot;. This note has one more special feature, that is label
+was set via &quot;ActionName&quot;, thus is does not have standard form (the same
+happened to the node named &quot;Forward&quot; right at the top of the diagram.
+Inside this diagram, the &quot;Forward&quot; node is executed asynchonously on its own
+queue. All others are executed synchronously.
+<p>Configuration graphs are useful for documenting a setup, but are also a great
+<a href="troubleshoot.html">troubleshooting</a> resource. It is important to
+remember that <b>these graphs are generated
+from rsyslogd's in-memory action processing structures</b>. You can not get closer
+to understanding on how rsyslog interpreted its configuration files.
+So if the graph does not look
+what you intended to do, there is probably something worng in rsyslog.conf.
+<p>If something is not working as expected, but you do not spot the error immediately,
+I recommend to generate a graph and zoom it so that you see all of it in one great picture.
+You may not be able to read anything, but the structure should look good to you and
+so you can zoom into those areas that draw your attention.
+<p><b>Sample:</b></p>
+<p><code><b>$DirOwner /path/to/graphfile-file.dot</b></code></p>
+
+<p>[<a href="rsyslog_conf.html">rsyslog.conf overview</a>] [<a href="manual.html">manual
+index</a>] [<a href="http://www.rsyslog.com/">rsyslog site</a>]</p>
+<p><font size="2">This documentation is part of the
+<a href="http://www.rsyslog.com/">rsyslog</a> project.<br>
+Copyright &copy; 2009 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 2 or higher.</font></p>
+</body>
+</html>
diff --git a/doc/rsconf1_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-vers.png b/doc/rsyslog-vers.png
new file mode 100644
index 00000000..e8ec8b81
--- /dev/null
+++ b/doc/rsyslog-vers.png
Binary files differ
diff --git a/doc/rsyslog_conf.html b/doc/rsyslog_conf.html
index 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..fe76912d 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.
@@ -18,6 +18,8 @@ many parameter settings modify queue parameters. If in doubt, use the
default, it is usually well-chosen and applicable in most cases.</p>
<ul>
<li><a href="rsconf1_actionexeconlywhenpreviousissuspended.html">$ActionExecOnlyWhenPreviousIsSuspended</a></li>
+<li>$ActionName &lt;a_single_word&gt; - used primarily for documentation, e.g. when
+generating a configuration graph. Available sice 4.3.1.
<li>$ActionExecOnlyOnceEveryInterval &lt;seconds&gt; -
execute action only if the last execute is at last
&lt;seconds&gt; seconds in the past (more info in <a href="ommail.html">ommail</a>,
@@ -116,6 +118,7 @@ default 60000 (1 minute)]</li>
<li><a href="rsconf1_filecreatemode.html">$FileCreateMode</a></li>
<li><a href="rsconf1_filegroup.html">$FileGroup</a></li>
<li><a href="rsconf1_fileowner.html">$FileOwner</a></li>
+<li><a href="rsconf1_generateconfiggraph.html">$GenerateConfigGraph</a></li>
<li><a href="rsconf1_gssforwardservicename.html">$GssForwardServiceName</a></li>
<li><a href="rsconf1_gsslistenservicename.html">$GssListenServiceName</a></li>
<li><a href="rsconf1_gssmode.html">$GssMode</a></li>
@@ -180,6 +183,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,
@@ -192,7 +196,7 @@ large enough for the whole message. (Introduced with 4.1.5). Once set, it affect
<li><a href="rsconf1_resetconfigvariables.html">$ResetConfigVariables</a></li>
<li><b>$OptimizeForUniprocessor</b> [on/<b>off</b>] - turns on optimizatons which lead to better
performance on uniprocessors. If you run on multicore-machiens, turning this off lessens CPU load. The
-default may change as uniprocessor systems become less common.</li>
+default may change as uniprocessor systems become less common. [available since 4.1.0]</li>
<li>$PreserveFQDN [on/<b>off</b>) - if set to off (legacy default to remain compatible
to sysklogd), the domain part from a name that is within the same domain as the receiving
system is stripped. If set to on, full names are always used.</li>
@@ -214,7 +218,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 +238,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/rsyslog_confgraph_complex.conf b/doc/rsyslog_confgraph_complex.conf
new file mode 100644
index 00000000..3d7ec0a3
--- /dev/null
+++ b/doc/rsyslog_confgraph_complex.conf
@@ -0,0 +1,108 @@
+$DebugPrintTemplateList off
+$DebugPrintCfSysLineHandlerList off
+$DebugPrintModuleList off
+#$ResetConfigVariables
+$ErrorMessagesToStderr off
+$ModLoad /home/rger/proj/rsyslog/plugins/imuxsock/.libs/imuxsock.so
+#$ModLoad /home/rger/proj/rsyslog/plugins/imklog/.libs/imklog
+#$ModLoad /home/rger/proj/rsyslog/plugins/imtcp/.libs/imtcp
+$ModLoad /home/rger/proj/rsyslog/plugins/imtcp/.libs/imtcp
+$ModLoad /home/rger/proj/rsyslog/plugins/imudp/.libs/imudp
+$ModLoad /home/rger/proj/rsyslog/plugins/omstdout/.libs/omstdout
+$ModLoad /home/rger/proj/rsyslog/plugins/omprog/.libs/omprog
+$ModLoad /home/rger/proj/rsyslog/plugins/omtesting/.libs/omtesting
+#$ModLoad /home/rger/proj/rsyslog/plugins/ommail/.libs/ommail
+#
+#
+# PGSQL testing
+$ModLoad /home/rger/proj/rsyslog/plugins/ompgsql/.libs/ompgsql.so
+$template pgfmt,"insert into SystemEvents (Message, Facility, FromHost, Priority, DeviceReportedTime, ReceivedAt, InfoUnitID, SysLogTag) values ('%msg%', %syslogfacility%, '%HOSTNAME%', %syslogpriority%, '%timereported:::date-pgsql%', '%timegenerated:::date-pgsql%', %iut%, '%syslogtag%');",STDSQL
+#$ActionQueueType linkedlist
+#*.* :ompgsql:127.0.0.1,rsyslog,postgres,;pgfmt
+
+#$ActionOMStdoutArrayInterface on
+#*.* :omstdout:
+
+$ActionResumeInterval 4
+$ActionResumeRetryCount 3
+$ActionQueueType LinkedList # run asynchronously
+$ActionName Forward to 172.19.3.9
+*.* @@172.19.3.9:10514
+#*.* :omtesting:randfail
+#*.* :omtesting:always_suspend
+#*.* :omtesting:fail 2 2
+
+#$UDPServerTimeRequery 10
+$UDPServerRun 514
+$inputtcpmaxsessions 2000
+$InputTCPServerRun 12514
+
+#$PrivDropToUser rger
+#$InputTCPServerInputName tcp/514
+#$InputTCPServerAddtlFrameDelimiter 10
+#$InputTCPServerRun 514
+#$AllowedSender UDP,127.0.0.1/32
+#$AllowedSender TCP,127.0.0.1/32
+
+$PreserveFQDN off
+
+#$HUPisRestart on
+
+#$MainMsgQueueType direct
+$MainMsgQueueType linkedlist
+$MainMsgQueueDequeueBatchSize 200
+#$MainMsgQueueWorkerTimeoutThreadShutdown -1
+
+#---- test DA mode
+# set spool locations and switch queue to disk assisted mode
+$WorkDirectory spool
+$MainMsgQueueSize 200 # this *should* trigger moving on to DA mode...
+# note: we must set QueueSize sufficiently high, so that 70% (light delay mark)
+# is high enough above HighWatermark!
+$MainMsgQueueHighWatermark 80
+$MainMsgQueueLowWatermark 40
+$MainMsgQueueFilename mainq
+$MainMsgQueueType linkedlist
+# ucomment, as we now have an issue (finally the test case works ;))
+#$MainMsgQueueDequeueBatchSize 80
+#---- end test DA mode
+
+#$template test,"%timereported:::date-rfc3339%,%timereported:::date-mysql%,%timereported:::date-subseconds%, %timegenerated:::date-mysql%, %timegenerated:::date-subseconds%, msg: %msg%\n"
+#$template db,"re: '%msg:R,ERE,1,FIELD:dsn=([0-9]+\.[0-9]+\.[0-9])--end%', msg: '%msg%'\n"
+#$template db,"re: '%msg:R,ERE,1,ZERO:dsn=([0-9]+\.[0-9]+\.[0-9])--end%', msg: '%msg%'\n"
+#$template DEBUG,"Debug line with all properties:\nFROMHOST: '%FROMHOST%', fromhost-ip: '%fromhost-ip%, HOSTNAME: '%HOSTNAME%', PRI: %PRI%,\nsyslogtag '%syslogtag%', programname: '%programname%', APP-NAME: '%APP-NAME%', PROCID: '%PROCID%', MSGID: '%MSGID%',\nTIMESTAMP: '%TIMESTAMP%', STRUCTURED-DATA: '%STRUCTURED-DATA%',\nmsg: '%msg%'\nescaped msg: '%msg:::drop-cc%'\nrawmsg: '%rawmsg%'\n\n"
+$template csv,"%syslogtag:::csv%,%msg:::upppercase,csv%,%msg%\n"
+*.* -/home/rger/proj/rsyslog/logfile
+kern.* -/home/rger/proj/rsyslog/logfile
+$ActionExecOnlyWhenPreviousIsSuspended on
+& -/tmp/xyz/uuu
+$ActionExecOnlyWhenPreviousIsSuspended off
+& ~
+& -/tmp/xyz/uuu2
+& -/tmp/xyz/uuu3
+
+
+#$template dynfile,"/home/rger/proj/rsyslog/test-%syslogtag%"
+#*.* -?dynfile
+#:msg, ereregex, "test|tast" /home/rger/proj/rsyslog/ere
+#if strlen($syslogtag & strlen($msg)) > 10 then /home/rger/proj/rsyslog/longlog
+#if strlen($msg) > 10 then /home/rger/proj/rsyslog/longlog
+#if tolower($msg) contains 'test' then /home/rger/proj/rsyslog/longlog
+#if $msg contains 'test' then /home/rger/proj/rsyslog/longlog
+
+#$ActionOMProgBinary /home/rger/proj/rsyslog/consumer
+#*.* :omprog:
+
+#$actionresumeretryCount -1
+#$actionResumeInterval 4
+#$template dynfile,"/mnt2/logs/logfile.log"
+#*.* /mnt2/logs/logfile.log
+#if $msg contains 'test' then ?dynfile
+#*.* ?dynfile
+:msg, contains, "test " /tmpo/sdafsdf
+
+$ActionName write_system_log_2
+if $msg == 'test' then /tmpo/sdafsdf2
+& /tmpo/234234
+*.* @@(o,z9)172.19.3.21:10514
+$GenerateConfigGraph /home/rger/proj/rsyslog/rsyslog.dot
diff --git a/doc/rsyslog_confgraph_complex.png b/doc/rsyslog_confgraph_complex.png
new file mode 100644
index 00000000..21c04c57
--- /dev/null
+++ b/doc/rsyslog_confgraph_complex.png
Binary files differ
diff --git a/doc/rsyslog_confgraph_std.conf b/doc/rsyslog_confgraph_std.conf
new file mode 100644
index 00000000..64c9a18a
--- /dev/null
+++ b/doc/rsyslog_confgraph_std.conf
@@ -0,0 +1,79 @@
+#rsyslog v3 config file
+
+# if you experience problems, check
+# http://www.rsyslog.com/troubleshoot for assistance
+
+#### MODULES ####
+
+$ModLoad imuxsock.so # provides support for local system logging (e.g. via logger command)
+$ModLoad imklog.so # provides kernel logging support (previously done by rklogd)
+#$ModLoad immark.so # provides --MARK-- message capability
+
+# Provides UDP syslog reception
+#$ModLoad imudp.so
+#$UDPServerRun 514
+
+# Provides TCP syslog reception
+#$ModLoad imtcp.so
+#$InputTCPServerRun 514
+
+
+#### GLOBAL DIRECTIVES ####
+
+# Use default timestamp format
+$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
+
+# File syncing capability is disabled by default. This feature is usually not required,
+# not useful and an extreme performance hit
+#$ActionFileEnableSync on
+
+
+#### RULES ####
+
+# Log all kernel messages to the console.
+# Logging much else clutters up the screen.
+#kern.* /dev/console
+
+# Log anything (except mail) of level info or higher.
+# Don't log private authentication messages!
+*.info;mail.none;authpriv.none;cron.none /var/log/messages
+
+# The authpriv file has restricted access.
+authpriv.* /var/log/secure
+
+# Log all the mail messages in one place.
+mail.* -/var/log/maillog
+
+
+# Log cron stuff
+cron.* /var/log/cron
+
+# Everybody gets emergency messages
+*.emerg *
+
+# Save news errors of level crit and higher in a special file.
+uucp,news.crit /var/log/spooler
+
+# Save boot messages also to boot.log
+local7.* /var/log/boot.log
+
+
+
+# ### begin forwarding rule ###
+# The statement between the begin ... end define a SINGLE forwarding
+# rule. They belong together, do NOT split them. If you create multiple
+# forwarding rules, duplicate the whole block!
+# Remote Logging (we use TCP for reliable delivery)
+#
+# An on-disk queue is created for this action. If the remote host is
+# down, messages are spooled to disk and sent when it is up again.
+#$WorkDirectory /var/spppl/rsyslog # where to place spool files
+#$ActionQueueFileName fwdRule1 # unique name prefix for spool files
+#$ActionQueueMaxDiskSpace 1g # 1gb space limit (use as much as possible)
+#$ActionQueueSaveOnShutdown on # save messages to disk on shutdown
+$ActionQueueType LinkedList # run asynchronously
+#$ActionResumeRetryCount -1 # infinite retries if host is down
+# remote host is: name/ip:port, e.g. 192.168.0.1:514, port optional
+*.* @@remote-host:514
+# ### end of the forwarding rule ###
+$GenerateConfigGraph /home/rger/proj/rsyslog/rsyslog.dot
diff --git a/doc/rsyslog_confgraph_std.png b/doc/rsyslog_confgraph_std.png
new file mode 100644
index 00000000..655a7f82
--- /dev/null
+++ b/doc/rsyslog_confgraph_std.png
Binary files differ
diff --git a/doc/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..4e8f1a5f 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-05-25.</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.1 [2009-05-25] -
+<a href="http://www.rsyslog.com/Article372.phtml">change log</a> -
+<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-159.phtml">download</a>
<br><b>beta:</b> 3.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.22.0 [2009-04-21] - <a href="http://www.rsyslog.com/Article368.phtml">change log</a> -
+<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-157.phtml">download</a>
-<br><b>v2 stable:</b> 2.0.6 [2008-08-07] - <a href="http://www.rsyslog.com/Article266.phtml">change log</a> -
-<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-125.phtml">download</a>
+<br><b>v2 stable:</b> 2.0.7 [2009-04-14] - <a href="http://www.rsyslog.com/Article362.phtml">change log</a> -
+<a href="http://www.rsyslog.com/Downloads-req-viewdownloaddetails-lid-154.phtml">download</a>
<br>v0 and v1 are deprecated and no longer supported. If you absolutely do not like to
upgrade, you may consider purchasing a
<a href="professional_support.html">commercial rsyslog support package</a>. Just let us point
diff --git a/doc/troubleshoot.html b/doc/troubleshoot.html
index e655c2ef..a8855fd4 100644
--- a/doc/troubleshoot.html
+++ b/doc/troubleshoot.html
@@ -18,7 +18,14 @@ is a rsyslog-doc package, that often needs to be installed separately.
<p><b>Malformed Messages and Message Properties</b>
<p>A common trouble source are <a href="syslog_parsing.html">ill-formed syslog messages</a>, which
lead to to all sorts of interesting problems, including malformed hostnames and dates.
-Read the quoted guide to find relief.
+Read the quoted guide to find relief. A common symptom is that the %HOSTNAME% property is
+used for generating dynafile names, but some glibberish shows up. This is caused by the
+malformed syslog messages, so be sure to read the
+<a href="syslog_parsing.html">guide</a> if you face that problem. Just let me add that the
+common work-around is to use %FROMHOST% or %FROMHOST-IP% instead. These do not take the
+hostname from the message, but rather use the host that sent the message (taken from
+the socket layer). Of course, this does not work over NAT or relay chains, where the
+only cure is to make sure senders emit well-formed messages.
<p><b>Configuration Problems</b>
<p>Rsyslog 3.21.1 and above has been enhanced to support extended configuration checking.
It offers a special command line switch (-N1) that puts it into "config verfication mode".
@@ -28,6 +35,15 @@ mode can be used in parallel to a running instance of rsyslogd.
<p><b><i>/path/to/rsyslogd -f/path/to/config-file -N1</i></b>
<p>You should also specify other options you usually give (like -c3 and whatever else).
Any problems experienced are reported to stderr [aka "your screen" (if not redirected)].
+<p><b>Configuration Graphs</b>
+<p>Starting with rsyslog 4.3.1, the
+&quot;<a href="rsconf1_generateconfiggraph.html">$GenerateConfigGraph</a>&quot;
+command is supported, a very valuable troubleshooting tool. It permits to
+generate a graph of how rsyslogd understood its configuration file. It is assumed that
+many configuration issues can easily be detected just by looking at the configuration graph.
+Full details of how to generate the graphs, and what to look for can be found in the
+&quot;<a href="rsconf1_generateconfiggraph.html">$GenerateConfigGraph</a>&quot;
+manual page.
<p><b>Asking for Help</b>
<p>If you can't find the answer yourself, you should look at these places for
community help.
diff --git a/m4/shave.m4 b/m4/shave.m4
new file mode 100644
index 00000000..e647e579
--- /dev/null
+++ b/m4/shave.m4
@@ -0,0 +1,99 @@
+dnl Make automake/libtool output more friendly to humans
+dnl
+dnl Copyright (c) 2009, Damien Lespiau <damien.lespiau@gmail.com>
+dnl
+dnl Permission is hereby granted, free of charge, to any person
+dnl obtaining a copy of this software and associated documentation
+dnl files (the "Software"), to deal in the Software without
+dnl restriction, including without limitation the rights to use,
+dnl copy, modify, merge, publish, distribute, sublicense, and/or sell
+dnl copies of the Software, and to permit persons to whom the
+dnl Software is furnished to do so, subject to the following
+dnl conditions:
+dnl
+dnl The above copyright notice and this permission notice shall be
+dnl included in all copies or substantial portions of the Software.
+dnl
+dnl THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+dnl EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+dnl OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+dnl NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+dnl HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+dnl WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+dnl FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+dnl OTHER DEALINGS IN THE SOFTWARE.
+dnl
+dnl SHAVE_INIT([shavedir],[default_mode])
+dnl
+dnl shavedir: the directory where the shave scripts are, it defaults to
+dnl $(top_builddir)
+dnl default_mode: (enable|disable) default shave mode. This parameter
+dnl controls shave's behaviour when no option has been
+dnl given to configure. It defaults to disable.
+dnl
+dnl * SHAVE_INIT should be called late in your configure.(ac|in) file (just
+dnl before AC_CONFIG_FILE/AC_OUTPUT is perfect. This macro rewrites CC and
+dnl LIBTOOL, you don't want the configure tests to have these variables
+dnl re-defined.
+dnl * This macro requires GNU make's -s option.
+
+AC_DEFUN([_SHAVE_ARG_ENABLE],
+[
+ AC_ARG_ENABLE([shave],
+ AS_HELP_STRING(
+ [--enable-shave],
+ [use shave to make the build pretty [[default=$1]]]),,
+ [enable_shave=$1]
+ )
+])
+
+AC_DEFUN([SHAVE_INIT],
+[
+ dnl you can tweak the default value of enable_shave
+ m4_if([$2], [enable], [_SHAVE_ARG_ENABLE(yes)], [_SHAVE_ARG_ENABLE(no)])
+
+ if test x"$enable_shave" = xyes; then
+ dnl where can we find the shave scripts?
+ m4_if([$1],,
+ [shavedir="$ac_pwd"],
+ [shavedir="$ac_pwd/$1"])
+ AC_SUBST(shavedir)
+
+ dnl make is now quiet
+ AC_SUBST([MAKEFLAGS], [-s])
+ AC_SUBST([AM_MAKEFLAGS], ['`test -z $V && echo -s`'])
+
+ dnl we need sed
+ AC_CHECK_PROG(SED,sed,sed,false)
+
+ dnl substitute libtool
+ SHAVE_SAVED_LIBTOOL=$LIBTOOL
+ LIBTOOL="${SHELL} ${shavedir}/shave-libtool '${SHAVE_SAVED_LIBTOOL}'"
+ AC_SUBST(LIBTOOL)
+
+ dnl substitute cc/cxx
+ SHAVE_SAVED_CC=$CC
+ SHAVE_SAVED_CXX=$CXX
+ SHAVE_SAVED_FC=$FC
+ SHAVE_SAVED_F77=$F77
+ SHAVE_SAVED_OBJC=$OBJC
+ CC="${SHELL} ${shavedir}/shave cc ${SHAVE_SAVED_CC}"
+ CXX="${SHELL} ${shavedir}/shave cxx ${SHAVE_SAVED_CXX}"
+ FC="${SHELL} ${shavedir}/shave fc ${SHAVE_SAVED_FC}"
+ F77="${SHELL} ${shavedir}/shave f77 ${SHAVE_SAVED_F77}"
+ OBJC="${SHELL} ${shavedir}/shave objc ${SHAVE_SAVED_OBJC}"
+ AC_SUBST(CC)
+ AC_SUBST(CXX)
+ AC_SUBST(FC)
+ AC_SUBST(F77)
+ AC_SUBST(OBJC)
+
+ V=@
+ else
+ V=1
+ fi
+ Q='$(V:1=)'
+ AC_SUBST(V)
+ AC_SUBST(Q)
+])
+
diff --git a/plugins/imdiag/imdiag.c b/plugins/imdiag/imdiag.c
index 77e99236..c700cab7 100644
--- a/plugins/imdiag/imdiag.c
+++ b/plugins/imdiag/imdiag.c
@@ -1,5 +1,3 @@
-#warning "imdiag is NOT supported in this version of rsyslog"
-#if 0
/* imdiag.c
* This is a diagnostics module, primarily meant for troubleshooting
* and information about the runtime state of rsyslog. It is implemented
@@ -9,7 +7,7 @@
*
* File begun on 2008-07-25 by RGerhards
*
- * Copyright 2008 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2008, 2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -28,8 +26,8 @@
*
* A copy of the GPL can be found in the file "COPYING" in this distribution.
*/
-
#include "config.h"
+#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
@@ -45,117 +43,380 @@
#include <fcntl.h>
#endif
#include "rsyslog.h"
-//#include "dirty.h"
+#include "dirty.h"
#include "cfsysline.h"
#include "module-template.h"
+#include "unicode-helper.h"
#include "net.h"
#include "netstrm.h"
#include "errmsg.h"
+#include "tcpsrv.h"
+#include "srUtils.h"
+#include "msg.h"
+#include "datetime.h"
+#include "net.h" /* for permittedPeers, may be removed when this is removed */
MODULE_TYPE_INPUT
/* static data */
DEF_IMOD_STATIC_DATA
+DEFobjCurrIf(tcpsrv)
+DEFobjCurrIf(tcps_sess)
DEFobjCurrIf(net)
DEFobjCurrIf(netstrm)
DEFobjCurrIf(errmsg)
+DEFobjCurrIf(datetime)
/* Module static data */
-netstrms_t *pNS; /**< pointer to network stream subsystem */
-netstrm_t **ppLstn[10]; /**< our netstream listners */
-int iLstnMax = 0; /**< max nbr of listeners currently supported */
+static tcpsrv_t *pOurTcpsrv = NULL; /* our TCP server(listener) TODO: change for multiple instances */
+static permittedPeers_t *pPermPeersRoot = NULL;
/* config settings */
+static int iTCPSessMax = 20; /* max number of sessions */
+static int iStrmDrvrMode = 0; /* mode for stream driver, driver-dependent (0 mostly means plain tcp) */
+static uchar *pszStrmDrvrAuthMode = NULL; /* authentication mode to use */
+static uchar *pszInputName = NULL; /* value for inputname property, NULL is OK and handled by core engine */
+
+
+/* callbacks */
+/* this shall go into a specific ACL module! */
+static int
+isPermittedHost(struct sockaddr __attribute__((unused)) *addr, char __attribute__((unused)) *fromHostFQDN,
+ void __attribute__((unused)) *pUsrSrv, void __attribute__((unused)) *pUsrSess)
+{
+ return 1; /* TODO: implement ACLs ... or via some other way? */
+}
+
+
+static rsRetVal
+doOpenLstnSocks(tcpsrv_t *pSrv)
+{
+ ISOBJ_TYPE_assert(pSrv, tcpsrv);
+ return tcpsrv.create_tcp_socket(pSrv);
+}
+
+
+static rsRetVal
+doRcvData(tcps_sess_t *pSess, char *buf, size_t lenBuf, ssize_t *piLenRcvd)
+{
+ DEFiRet;
+ assert(pSess != NULL);
+ assert(piLenRcvd != NULL);
+
+ *piLenRcvd = lenBuf;
+ CHKiRet(netstrm.Rcv(pSess->pStrm, (uchar*) buf, piLenRcvd));
+finalize_it:
+ RETiRet;
+}
+
+static rsRetVal
+onRegularClose(tcps_sess_t *pSess)
+{
+ DEFiRet;
+ assert(pSess != NULL);
+
+ /* process any incomplete frames left over */
+ tcps_sess.PrepareClose(pSess);
+ /* Session closed */
+ tcps_sess.Close(pSess);
+ RETiRet;
+}
+
+
+static rsRetVal
+onErrClose(tcps_sess_t *pSess)
+{
+ DEFiRet;
+ assert(pSess != NULL);
+
+ tcps_sess.Close(pSess);
+ RETiRet;
+}
+
+/* ------------------------------ end callbacks ------------------------------ */
+
+
+/* get the first word delimited by space from a given string. The pointer is
+ * advanced to after the word. Any leading spaces are discarded. If the
+ * output buffer is too small, parsing ends on buffer full condition.
+ * An empty buffer is returned if there is no more data inside the string.
+ * rgerhards, 2009-05-27
+ */
+#define TO_LOWERCASE 1
+#define NO_MODIFY 0
+static void
+getFirstWord(uchar **ppszSrc, uchar *pszBuf, size_t lenBuf, int options)
+{
+ uchar c;
+ uchar *pszSrc = *ppszSrc;
+
+ while(*pszSrc && *pszSrc == ' ')
+ ++pszSrc; /* skip to first non-space */
+
+ while(*pszSrc && *pszSrc != ' ' && lenBuf > 1) {
+ c = *pszSrc++;
+ if(options & TO_LOWERCASE)
+ c = tolower(c);
+ *pszBuf++ = c;
+ lenBuf--;
+ }
+
+ *pszBuf = '\0';
+ *ppszSrc = pszSrc;
+}
+
+
+/* send a response back to the originator
+ * rgerhards, 2009-05-27
+ */
+static rsRetVal __attribute__((format(printf, 2, 3)))
+sendResponse(tcps_sess_t *pSess, char *fmt, ...)
+{
+ va_list ap;
+ ssize_t len;
+ uchar buf[1024];
+ DEFiRet;
+
+ va_start(ap, fmt);
+ len = vsnprintf((char*)buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ CHKiRet(netstrm.Send(pSess->pStrm, buf, &len));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* actually submit a message to the rsyslog core
+ */
+static rsRetVal
+doInjectMsg(int iNum)
+{
+ uchar szMsg[1024];
+ msg_t *pMsg;
+ struct syslogTime stTime;
+ time_t ttGenTime;
+ DEFiRet;
+
+ snprintf((char*)szMsg, sizeof(szMsg)/sizeof(uchar),
+ "<167>Mar 1 01:00:00 172.20.245.8 tag msgnum:%8.8d:\n", iNum);
+
+ datetime.getCurrTime(&stTime, &ttGenTime);
+ /* we now create our own message object and submit it to the queue */
+ CHKiRet(msgConstructWithTime(&pMsg, &stTime, ttGenTime));
+ CHKmalloc(pMsg->pszRawMsg = ustrdup(szMsg));
+ pMsg->iLenRawMsg = ustrlen(szMsg);
+ MsgSetInputName(pMsg, UCHAR_CONSTANT("imdiag"));
+ MsgSetFlowControlType(pMsg, eFLOWCTL_NO_DELAY);
+ pMsg->msgFlags = NEEDS_PARSING | PARSE_HOSTNAME;
+ pMsg->bParseHOSTNAME = 1;
+ MsgSetRcvFrom(pMsg, UCHAR_CONSTANT("127.0.0.1")); /* TODO: way may use the real sender here... */
+ CHKiRet(MsgSetRcvFromIP(pMsg, UCHAR_CONSTANT("127.0.0.1")));
+ CHKiRet(submitMsg(pMsg));
+
+finalize_it:
+ RETiRet;
+}
-/* add a listen socket to our listen socket array. This is a callback
- * invoked from the netstrm class. -- rgerhards, 2008-04-23
+/* This function injects messages. Command format:
+ * injectmsg <fromnbr> <number-of-messages>
+ * rgerhards, 2009-05-27
*/
static rsRetVal
-addTcpLstn(void *pUsr, netstrm_t *pLstn)
+injectMsg(uchar *pszCmd, tcps_sess_t *pSess)
{
+ uchar wordBuf[1024];
+ int iFrom;
+ int nMsgs;
+ int i;
DEFiRet;
- ISOBJ_TYPE_assert(pLstn, netstrm);
+ /* we do not check errors here! */
+ getFirstWord(&pszCmd, wordBuf, sizeof(wordBuf)/sizeof(uchar), TO_LOWERCASE);
+ iFrom = atoi((char*)wordBuf);
+ getFirstWord(&pszCmd, wordBuf, sizeof(wordBuf)/sizeof(uchar), TO_LOWERCASE);
+ nMsgs = atoi((char*)wordBuf);
- if(iLstnMax >= sizeof(ppLstn)/sizeof(netstrm_t))
- ABORT_FINALIZE(RS_RET_MAX_LSTN_REACHED);
+ for(i = 0 ; i < nMsgs ; ++i) {
+ doInjectMsg(i + iFrom);
+ }
- ppLstn[pThis->iLstnMax] = pLstn;
- ++iLstnMax;
+ CHKiRet(sendResponse(pSess, "messages injected\n"));
finalize_it:
RETiRet;
}
-/* initialize network stream subsystem */
+/* This function waits until the main queue is drained (size = 0)
+ */
static rsRetVal
-initNetstrm(void)
+waitMainQEmpty(tcps_sess_t *pSess)
{
+ int iMsgQueueSize;
DEFiRet;
- /* prepare network stream subsystem */
- CHKiRet(netstrms.Construct(&pNS));
- CHKiRet(netstrms.SetDrvrMode(pNS, 0)); /* always plain text */
- //CHKiRet(netstrms.SetDrvrAuthMode(pThis->pNS, pThis->pszDrvrAuthMode));
- //CHKiRet(netstrms.SetDrvrPermPeers(pThis->pNS, pThis->pPermPeers));
- // TODO: set driver!
- CHKiRet(netstrms.ConstructFinalize(pThis->pNS));
+ CHKiRet(diagGetMainMsgQSize(&iMsgQueueSize));
+ while(iMsgQueueSize > 0) {
+ srSleep(0,2); /* wait a little bit */
+ CHKiRet(diagGetMainMsgQSize(&iMsgQueueSize));
+ }
- /* set up listeners */
- CHKiRet(netstrm.LstnInit(pNS, NULL, addTcpLstn, "127.0.0.1", "44514", 1));
+ CHKiRet(sendResponse(pSess, "mainqueue empty\n"));
finalize_it:
- if(iRet != RS_RET_OK) {
- if(pThis->pNS != NULL)
- netstrms.Destruct(&pThis->pNS);
+ RETiRet;
+}
+
+/* Function to handle received messages. This is our core function!
+ * rgerhards, 2009-05-24
+ */
+static rsRetVal
+OnMsgReceived(tcps_sess_t *pSess, uchar *pRcv, int iLenMsg)
+{
+ int iMsgQueueSize;
+ uchar *pszMsg;
+ uchar cmdBuf[1024];
+ DEFiRet;
+
+ assert(pSess != NULL);
+ assert(pRcv != NULL);
+
+ /* NOTE: pRcv is NOT a C-String but rather an array of characters
+ * WITHOUT a termination \0 char. So we need to convert it to one
+ * before proceeding.
+ */
+ CHKmalloc(pszMsg = malloc(sizeof(uchar) * (iLenMsg + 1)));
+ memcpy(pszMsg, pRcv, iLenMsg);
+ pszMsg[iLenMsg] = '\0';
+
+ getFirstWord(&pszMsg, cmdBuf, sizeof(cmdBuf)/sizeof(uchar), TO_LOWERCASE);
+
+ if(!ustrcmp(cmdBuf, UCHAR_CONSTANT("getmainmsgqueuesize"))) {
+ CHKiRet(diagGetMainMsgQSize(&iMsgQueueSize));
+ CHKiRet(sendResponse(pSess, "%d\n", iMsgQueueSize));
+ } else if(!ustrcmp(cmdBuf, UCHAR_CONSTANT("waitmainqueueempty"))) {
+ CHKiRet(waitMainQEmpty(pSess));
+ } else if(!ustrcmp(cmdBuf, UCHAR_CONSTANT("injectmsg"))) {
+ CHKiRet(injectMsg(pszMsg, pSess));
+ } else {
+ dbgprintf("imdiag unkown command '%s'\n", cmdBuf);
+ CHKiRet(sendResponse(pSess, "unkown command '%s'\n", cmdBuf));
}
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* set permitted peer -- rgerhards, 2008-05-19
+ */
+static rsRetVal
+setPermittedPeer(void __attribute__((unused)) *pVal, uchar *pszID)
+{
+ DEFiRet;
+ CHKiRet(net.AddPermittedPeer(&pPermPeersRoot, pszID));
+ free(pszID); /* no longer needed, but we need to free as of interface def */
+finalize_it:
RETiRet;
}
-/* This function is called to gather input. In our case, it is a bit abused
- * to drive the listener loop for the diagnostics code.
+static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVal)
+{
+ DEFiRet;
+
+ if(pOurTcpsrv == NULL) {
+ CHKiRet(tcpsrv.Construct(&pOurTcpsrv));
+ CHKiRet(tcpsrv.SetSessMax(pOurTcpsrv, iTCPSessMax));
+ CHKiRet(tcpsrv.SetCBIsPermittedHost(pOurTcpsrv, isPermittedHost));
+ CHKiRet(tcpsrv.SetCBRcvData(pOurTcpsrv, doRcvData));
+ CHKiRet(tcpsrv.SetCBOpenLstnSocks(pOurTcpsrv, doOpenLstnSocks));
+ CHKiRet(tcpsrv.SetCBOnRegularClose(pOurTcpsrv, onRegularClose));
+ CHKiRet(tcpsrv.SetCBOnErrClose(pOurTcpsrv, onErrClose));
+ CHKiRet(tcpsrv.SetDrvrMode(pOurTcpsrv, iStrmDrvrMode));
+ CHKiRet(tcpsrv.SetOnMsgReceive(pOurTcpsrv, OnMsgReceived));
+ /* now set optional params, but only if they were actually configured */
+ if(pszStrmDrvrAuthMode != NULL) {
+ CHKiRet(tcpsrv.SetDrvrAuthMode(pOurTcpsrv, pszStrmDrvrAuthMode));
+ }
+ if(pPermPeersRoot != NULL) {
+ CHKiRet(tcpsrv.SetDrvrPermPeers(pOurTcpsrv, pPermPeersRoot));
+ }
+ }
+
+ /* initialized, now add socket */
+ CHKiRet(tcpsrv.SetInputName(pOurTcpsrv, pszInputName == NULL ?
+ UCHAR_CONSTANT("imdiag") : pszInputName));
+ tcpsrv.configureTCPListen(pOurTcpsrv, pNewVal);
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ errmsg.LogError(0, NO_ERRCODE, "error %d trying to add listener", iRet);
+ if(pOurTcpsrv != NULL)
+ tcpsrv.Destruct(&pOurTcpsrv);
+ }
+ RETiRet;
+}
+
+/* This function is called to gather input.
*/
BEGINrunInput
CODESTARTrunInput
+ CHKiRet(tcpsrv.ConstructFinalize(pOurTcpsrv));
+ iRet = tcpsrv.Run(pOurTcpsrv);
+finalize_it:
ENDrunInput
/* initialize and return if will run or not */
BEGINwillRun
CODESTARTwillRun
- iRet = initNetstrm();
+ /* first apply some config settings */
+ if(pOurTcpsrv == NULL)
+ ABORT_FINALIZE(RS_RET_NO_RUN);
+finalize_it:
ENDwillRun
BEGINafterRun
CODESTARTafterRun
/* do cleanup here */
- /* finally close our listen streams */
- for(i = 0 ; i < iLstnMax ; ++i) {
- netstrm.Destruct(ppLstn + i);
- }
-
- /* destruct netstream subsystem */
- netstrms.Destruct(pNS);
ENDafterRun
BEGINmodExit
CODESTARTmodExit
+ if(pOurTcpsrv != NULL)
+ iRet = tcpsrv.Destruct(&pOurTcpsrv);
+
+ if(pPermPeersRoot != NULL) {
+ net.DestructPermittedPeers(&pPermPeersRoot);
+ }
+
/* release objects we used */
objRelease(net, LM_NET_FILENAME);
objRelease(netstrm, LM_NETSTRMS_FILENAME);
+ objRelease(tcps_sess, LM_TCPSRV_FILENAME);
+ objRelease(tcpsrv, LM_TCPSRV_FILENAME);
objRelease(errmsg, CORE_COMPONENT);
+ objRelease(datetime, CORE_COMPONENT);
ENDmodExit
static rsRetVal
resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
+ iTCPSessMax = 200;
+ iStrmDrvrMode = 0;
+ free(pszInputName);
+ pszInputName = NULL;
+ if(pszStrmDrvrAuthMode != NULL) {
+ free(pszStrmDrvrAuthMode);
+ pszStrmDrvrAuthMode = NULL;
+ }
return RS_RET_OK;
}
@@ -175,25 +436,27 @@ CODEmodInit_QueryRegCFSLineHdlr
/* request objects we use */
CHKiRet(objUse(net, LM_NET_FILENAME));
CHKiRet(objUse(netstrm, LM_NETSTRMS_FILENAME));
+ CHKiRet(objUse(tcps_sess, LM_TCPSRV_FILENAME));
+ CHKiRet(objUse(tcpsrv, LM_TCPSRV_FILENAME));
CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
-#if 0
/* register config file handlers */
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverrun", 0, eCmdHdlrGetWord,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("imdiagserverrun"), 0, eCmdHdlrGetWord,
addTCPListener, NULL, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpmaxsessions", 0, eCmdHdlrInt,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("imdiagmaxsessions"), 0, eCmdHdlrInt,
NULL, &iTCPSessMax, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverstreamdrivermode", 0,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("imdiagserverstreamdrivermode"), 0,
eCmdHdlrInt, NULL, &iStrmDrvrMode, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverstreamdriverauthmode", 0,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("imdiagserverstreamdriverauthmode"), 0,
eCmdHdlrGetWord, NULL, &pszStrmDrvrAuthMode, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverstreamdriverpermittedpeer", 0,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("imdiagserverstreamdriverpermittedpeer"), 0,
eCmdHdlrGetWord, setPermittedPeer, NULL, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("imdiagserverinputname"), 0,
+ eCmdHdlrGetWord, NULL, &pszInputName, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("resetconfigvariables"), 1, eCmdHdlrCustomHandler,
resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
-#endif
ENDmodInit
-#endif
/* vim:set ai:
diff --git a/plugins/imfile/imfile.c b/plugins/imfile/imfile.c
index b0211bf6..92fd30c3 100644
--- a/plugins/imfile/imfile.c
+++ b/plugins/imfile/imfile.c
@@ -45,6 +45,7 @@
#include "errmsg.h"
#include "glbl.h"
#include "datetime.h"
+#include "unicode-helper.h"
MODULE_TYPE_INPUT /* must be present for input modules, do not remove */
@@ -94,11 +95,11 @@ static rsRetVal enqLine(fileInfo_t *pInfo, cstr_t *cstrLine)
CHKiRet(msgConstruct(&pMsg));
MsgSetFlowControlType(pMsg, eFLOWCTL_FULL_DELAY);
- MsgSetInputName(pMsg, "imfile");
+ MsgSetInputName(pMsg, UCHAR_CONSTANT("imfile"));
MsgSetUxTradMsg(pMsg, (char*)rsCStrGetSzStr(cstrLine));
MsgSetRawMsg(pMsg, (char*)rsCStrGetSzStr(cstrLine));
MsgSetMSG(pMsg, (char*)rsCStrGetSzStr(cstrLine));
- MsgSetHOSTNAME(pMsg, (char*)glbl.GetLocalHostName());
+ MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName());
MsgSetTAG(pMsg, (char*)pInfo->pszTag);
pMsg->iFacility = LOG_FAC(pInfo->iFacility);
pMsg->iSeverity = LOG_PRI(pInfo->iSeverity);
diff --git a/plugins/imgssapi/imgssapi.c b/plugins/imgssapi/imgssapi.c
index debe935e..d8791880 100644
--- a/plugins/imgssapi/imgssapi.c
+++ b/plugins/imgssapi/imgssapi.c
@@ -9,7 +9,7 @@
* NOTE: read comments in module-template.h to understand how this file
* works!
*
- * Copyright 2007 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007, 2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -249,7 +249,6 @@ onErrClose(tcps_sess_t *pSess)
static rsRetVal
doOpenLstnSocks(tcpsrv_t *pSrv)
{
- int *pRet = NULL;
gsssrv_t *pGSrv;
DEFiRet;
@@ -331,7 +330,7 @@ addGSSListener(void __attribute__((unused)) *pVal, uchar *pNewVal)
CHKiRet(tcpsrv.SetCBOnSessAccept(pOurTcpsrv, onSessAccept));
CHKiRet(tcpsrv.SetCBOnRegularClose(pOurTcpsrv, onRegularClose));
CHKiRet(tcpsrv.SetCBOnErrClose(pOurTcpsrv, onErrClose));
- tcpsrv.configureTCPListen(pOurTcpsrv, (char *) pNewVal);
+ tcpsrv.configureTCPListen(pOurTcpsrv, pNewVal);
CHKiRet(tcpsrv.ConstructFinalize(pOurTcpsrv));
}
diff --git a/plugins/imklog/imklog.c b/plugins/imklog/imklog.c
index 20bc34ab..ecb6c100 100644
--- a/plugins/imklog/imklog.c
+++ b/plugins/imklog/imklog.c
@@ -53,6 +53,7 @@
#include "datetime.h"
#include "imklog.h"
#include "glbl.h"
+#include "unicode-helper.h"
MODULE_TYPE_INPUT
@@ -94,14 +95,14 @@ enqMsg(uchar *msg, uchar* pszTag, int iFacility, int iSeverity)
CHKiRet(msgConstruct(&pMsg));
MsgSetFlowControlType(pMsg, eFLOWCTL_LIGHT_DELAY);
- MsgSetInputName(pMsg, "imklog");
+ MsgSetInputName(pMsg, UCHAR_CONSTANT("imklog"));
MsgSetRawMsg(pMsg, (char*)msg);
MsgSetUxTradMsg(pMsg, (char*)msg);
MsgSetRawMsg(pMsg, (char*)msg);
MsgSetMSG(pMsg, (char*)msg);
- MsgSetRcvFrom(pMsg, (char*)glbl.GetLocalHostName());
+ MsgSetRcvFrom(pMsg, glbl.GetLocalHostName());
MsgSetRcvFromIP(pMsg, (uchar*)"127.0.0.1");
- MsgSetHOSTNAME(pMsg, (char*)glbl.GetLocalHostName());
+ MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName());
MsgSetTAG(pMsg, (char*)pszTag);
pMsg->iFacility = LOG_FAC(iFacility);
pMsg->iSeverity = LOG_PRI(iSeverity);
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/imtcp/imtcp.c b/plugins/imtcp/imtcp.c
index 5a8a62f6..84e660bc 100644
--- a/plugins/imtcp/imtcp.c
+++ b/plugins/imtcp/imtcp.c
@@ -3,7 +3,7 @@
*
* File begun on 2007-12-21 by RGerhards (extracted from syslogd.c)
*
- * Copyright 2007 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007, 2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -56,6 +56,7 @@
#include "dirty.h"
#include "cfsysline.h"
#include "module-template.h"
+#include "unicode-helper.h"
#include "net.h"
#include "netstrm.h"
#include "errmsg.h"
@@ -91,7 +92,7 @@ static int
isPermittedHost(struct sockaddr *addr, char *fromHostFQDN, void __attribute__((unused)) *pUsrSrv,
void __attribute__((unused)) *pUsrSess)
{
- return net.isAllowedSender((uchar*) "TCP", addr, fromHostFQDN);
+ return net.isAllowedSender(UCHAR_CONSTANT("TCP"), addr, fromHostFQDN);
}
@@ -169,7 +170,6 @@ static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVa
CHKiRet(tcpsrv.SetCBOnRegularClose(pOurTcpsrv, onRegularClose));
CHKiRet(tcpsrv.SetCBOnErrClose(pOurTcpsrv, onErrClose));
CHKiRet(tcpsrv.SetDrvrMode(pOurTcpsrv, iStrmDrvrMode));
- CHKiRet(tcpsrv.SetInputName(pOurTcpsrv, pszInputName == NULL ? (uchar*)"imtcp" : pszInputName));
CHKiRet(tcpsrv.SetAddtlFrameDelim(pOurTcpsrv, iAddtlFrameDelim));
/* now set optional params, but only if they were actually configured */
if(pszStrmDrvrAuthMode != NULL) {
@@ -178,11 +178,13 @@ static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVa
if(pPermPeersRoot != NULL) {
CHKiRet(tcpsrv.SetDrvrPermPeers(pOurTcpsrv, pPermPeersRoot));
}
- /* most params set, now start listener */
- tcpsrv.configureTCPListen(pOurTcpsrv, (char *) pNewVal);
- CHKiRet(tcpsrv.ConstructFinalize(pOurTcpsrv));
}
+ /* initialized, now add socket */
+ CHKiRet(tcpsrv.SetInputName(pOurTcpsrv, pszInputName == NULL ?
+ UCHAR_CONSTANT("imtcp") : pszInputName));
+ tcpsrv.configureTCPListen(pOurTcpsrv, pNewVal);
+
finalize_it:
if(iRet != RS_RET_OK) {
errmsg.LogError(0, NO_ERRCODE, "error %d trying to add listener", iRet);
@@ -199,7 +201,9 @@ CODESTARTrunInput
/* TODO: we must be careful to start the listener here. Currently, tcpsrv.c seems to
* do that in ConstructFinalize
*/
+ CHKiRet(tcpsrv.ConstructFinalize(pOurTcpsrv));
iRet = tcpsrv.Run(pOurTcpsrv);
+finalize_it:
ENDrunInput
@@ -217,7 +221,7 @@ ENDwillRun
BEGINafterRun
CODESTARTafterRun
/* do cleanup here */
- net.clearAllowedSenders((uchar*)"TCP");
+ net.clearAllowedSenders(UCHAR_CONSTANT("TCP"));
ENDafterRun
@@ -277,21 +281,21 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(errmsg, CORE_COMPONENT));
/* register config file handlers */
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverrun", 0, eCmdHdlrGetWord,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverrun"), 0, eCmdHdlrGetWord,
addTCPListener, NULL, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpmaxsessions", 0, eCmdHdlrInt,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpmaxsessions"), 0, eCmdHdlrInt,
NULL, &iTCPSessMax, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverstreamdrivermode", 0,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverstreamdrivermode"), 0,
eCmdHdlrInt, NULL, &iStrmDrvrMode, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverstreamdriverauthmode", 0,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverstreamdriverauthmode"), 0,
eCmdHdlrGetWord, NULL, &pszStrmDrvrAuthMode, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverstreamdriverpermittedpeer", 0,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverstreamdriverpermittedpeer"), 0,
eCmdHdlrGetWord, setPermittedPeer, NULL, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserveraddtlframedelimiter", 0, eCmdHdlrInt,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserveraddtlframedelimiter"), 0, eCmdHdlrInt,
NULL, &iAddtlFrameDelim, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverinputname", 0,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverinputname"), 0,
eCmdHdlrGetWord, NULL, &pszInputName, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("resetconfigvariables"), 1, eCmdHdlrCustomHandler,
resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
diff --git a/plugins/imudp/imudp.c b/plugins/imudp/imudp.c
index 773ba7a4..a486b818 100644
--- a/plugins/imudp/imudp.c
+++ b/plugins/imudp/imudp.c
@@ -43,6 +43,7 @@
#include "msg.h"
#include "parser.h"
#include "datetime.h"
+#include "unicode-helper.h"
MODULE_TYPE_INPUT
@@ -219,11 +220,11 @@ processSocket(int fd, struct sockaddr_storage *frominetPrev, int *pbIsPermitted,
CHKmalloc(pMsg->pszRawMsg = malloc(sizeof(uchar)* lenRcvBuf));
memcpy(pMsg->pszRawMsg, pRcvBuf, lenRcvBuf);
pMsg->iLenRawMsg = lenRcvBuf;
- MsgSetInputName(pMsg, "imudp");
+ MsgSetInputName(pMsg, UCHAR_CONSTANT("imudp"));
MsgSetFlowControlType(pMsg, eFLOWCTL_NO_DELAY);
pMsg->msgFlags = NEEDS_PARSING | PARSE_HOSTNAME;
pMsg->bParseHOSTNAME = 1;
- MsgSetRcvFrom(pMsg, (char*)fromHost);
+ MsgSetRcvFrom(pMsg, fromHost);
CHKiRet(MsgSetRcvFromIP(pMsg, fromHostIP));
CHKiRet(submitMsg(pMsg));
}
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..331b7dd4
--- /dev/null
+++ b/plugins/omoracle/omoracle.c
@@ -0,0 +1,572 @@
+/** omoracle.c
+
+ This is an output module feeding directly to an Oracle
+ database. It uses Oracle Call Interface, a propietary module
+ provided by Oracle.
+
+ Selector lines to be used are of this form:
+
+ :omoracle:;TemplateName
+
+ The module gets its configuration via rsyslog $... directives,
+ namely:
+
+ $OmoracleDBUser: user name to log in on the database.
+
+ $OmoracleDBPassword: password to log in on the database.
+
+ $OmoracleDB: connection string (an Oracle easy connect or a db
+ name as specified by tnsnames.ora)
+
+ $OmoracleBatchSize: Number of elements to send to the DB on each
+ transaction.
+
+ $OmoracleBatchItemSize: Number of characters each property may
+ have. Make it as big as the longest value you expect for *any*
+ property in the sentence. For instance, if you expect 5 arguments
+ to the statement, 4 have 10 bytes and the 5th may be up to 3KB,
+ then specify $OmoracleBatchItemSize 3072. Please, remember to
+ leave space to the trailing \0!!
+
+ $OmoracleStatementTemplate: Name of the template containing the
+ statement to be prepared and executed in batches. Please note that
+ Oracle's prepared statements have their placeholders as
+ ':identifier', and this module uses the colon to guess how many
+ placeholders there will be.
+
+ All these directives are mandatory. The dbstring can be an Oracle
+ easystring or a DB name, as present in the tnsnames.ora file.
+
+ The form of the template is just a list of strings you want
+ inserted to the DB, for instance:
+
+ $template TestStmt,"%hostname%%msg%"
+
+ Will provide the arguments to a statement like
+
+ $OmoracleStatement \
+ insert into foo(hostname,message)values(:host,:message)
+
+ Also note that identifiers to placeholders are arbitrarry. You
+ need to define the properties on the template in the correct order
+ you want them passed to the statement!
+
+ This file is licensed under the terms of the GPL version 3 or, at
+ your choice, any later version. Exceptionally (perhaps), you are
+ allowed to link to the Oracle Call Interface in your derived work
+
+ Author: Luis Fernando Muñoz Mejías
+ <Luis.Fernando.Munoz.Mejias@cern.ch>
+
+ This file is part of rsyslog.
+*/
+#include "config.h"
+#include "rsyslog.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <oci.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <time.h>
+#include <assert.h>
+#include <ctype.h>
+#include "dirty.h"
+#include "syslogd-types.h"
+#include "srUtils.h"
+#include "template.h"
+#include "module-template.h"
+#include "errmsg.h"
+#include "cfsysline.h"
+#include "omoracle.h"
+
+MODULE_TYPE_OUTPUT
+
+/** */
+DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+
+/** */
+struct oracle_batch
+{
+ /* Batch size */
+ int size;
+ /* Last element inserted in the buffer. The batch will be
+ * executed when n == size */
+ int n;
+ /* Number of arguments the statement takes */
+ int arguments;
+ /** Maximum size of each parameter */
+ int param_size;
+ /* Parameters to pass to the statement on this transaction */
+ char*** parameters;
+ /* Binding parameters */
+ OCIBind** bindings;
+};
+
+typedef struct _instanceData {
+ /* Environment handler, the base for any OCI work. */
+ OCIEnv* environment;
+ /* Session handler, the actual DB connection object. */
+ OCISession* session;
+ /* Error handler for OCI calls. */
+ OCIError* error;
+ /* Prepared statement. */
+ OCIStmt* statement;
+ /* Service handler. */
+ OCISvcCtx* service;
+ /* Credentials object for the connection. */
+ OCIAuthInfo* authinfo;
+ /* Connection string, kept here for possible retries. */
+ char* connection;
+ /* Statement to be prepared. */
+ char* txt_statement;
+ /* Batch */
+ struct oracle_batch batch;
+} instanceData;
+
+/** Database name, to be filled by the $OmoracleDB directive */
+static char* db_name;
+/** Database user name, to be filled by the $OmoracleDBUser
+ * directive */
+static char* db_user;
+/** Database password, to be filled by the $OmoracleDBPassword */
+static char* db_password;
+/** Batch size. */
+static int batch_size;
+/** Size of each element in the batch. */
+static int batch_item_size;
+/** Statement to prepare and execute */
+static char* db_statement;
+
+/** Generic function for handling errors from OCI.
+
+ It will be called only inside CHECKERR and CHECKENV macros.
+
+ Arguments: handle The error or environment handle to check.
+ htype: OCI_HTYPE_* constant, usually OCI_HTYPE_ERROR or
+ OCI_HTYPE_ENV
+ status: status code to check, usually the return value of an OCI
+ function.
+
+ Returns OCI_SUCCESS on success, OCI_ERROR otherwise.
+*/
+static int oci_errors(void* handle, ub4 htype, sword status)
+{
+ sb4 errcode;
+ unsigned char buf[MAX_BUFSIZE];
+
+ switch (status) {
+ case OCI_SUCCESS:
+ return OCI_SUCCESS;
+ break;
+ case OCI_SUCCESS_WITH_INFO:
+ errmsg.LogError(0, NO_ERRCODE, "OCI SUCCESS - With info\n");
+ break;
+ case OCI_NEED_DATA:
+ errmsg.LogError(0, NO_ERRCODE, "OCI NEEDS MORE DATA\n");
+ break;
+ case OCI_ERROR:
+ dbgprintf ("OCI GENERAL ERROR\n");
+ if (handle) {
+ OCIErrorGet(handle, 1, NULL, &errcode, buf,
+ sizeof buf, htype);
+ errmsg.LogError(0, NO_ERRCODE, "Error message: %s", buf);
+ } else
+ errmsg.LogError(0, NO_ERRCODE, "NULL handle\n"
+ "Unable to extract further "
+ "information");
+ break;
+ case OCI_INVALID_HANDLE:
+ errmsg.LogError(0, NO_ERRCODE, "OCI INVALID HANDLE\n");
+ break;
+ case OCI_STILL_EXECUTING:
+ errmsg.LogError(0, NO_ERRCODE, "Still executing...\n");
+ break;
+ case OCI_CONTINUE:
+ errmsg.LogError(0, NO_ERRCODE, "OCI CONTINUE\n");
+ break;
+ }
+ return OCI_ERROR;
+}
+
+/** Callback for OCIBindDynamic.
+ *
+ * OCI doesn't insert an array of char* by itself (although it can
+ * handle arrays of int), so we must either run in batches of size one
+ * (no way) or bind all parameters with OCI_DATA_AT_EXEC instead of
+ * OCI_DEFAULT, and then give this function as an argument to
+ * OCIBindDynamic so that it is able to handle all strings in a single
+ * server trip.
+ *
+ * See the documentation of OCIBindDynamic
+ * (http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28395/oci16rel003.htm#i444015)
+ * for more details.
+ */
+static int bind_dynamic (char** in, OCIBind __attribute__((unused))* bind,
+ int iter, int __attribute__((unused)) idx,
+ char** out, int* buflen, unsigned char* piece,
+ void** bd)
+{
+ *out = in[iter];
+ *buflen = strlen(*out) + 1;
+ dbgprintf ("omoracle bound line %d, length %d: %s\n", iter, *buflen,
+ *out);
+ *piece = OCI_ONE_PIECE;
+ *bd = NULL;
+ return OCI_CONTINUE;
+}
+
+
+/** Returns the number of bind parameters for the statement given as
+ * an argument. It counts the number of appearances of ':', as in
+ *
+ * insert into foo(bar, baz) values(:bar, :baz)
+ *
+ * while taking in account that string literals must not be parsed. */
+static int count_bind_parameters(char* p)
+{
+ int n = 0;
+ int enable = 1;
+
+ for (; *p; p++)
+ if (enable && *p == BIND_MARK )
+ n++;
+ else if (*p == '\'')
+ enable ^= 1;
+ dbgprintf ("omoracle statement has %d parameters\n", n);
+ return n;
+}
+
+/** Prepares the statement, binding all its positional parameters */
+static int prepare_statement(instanceData* pData)
+{
+ int i;
+ DEFiRet;
+
+ CHECKERR(pData->error,
+ OCIStmtPrepare(pData->statement,
+ pData->error,
+ pData->txt_statement,
+ strlen(pData->txt_statement),
+ OCI_NTV_SYNTAX, OCI_DEFAULT));
+ for (i = 0; i < pData->batch.arguments; i++) {
+ CHECKERR(pData->error,
+ OCIBindByPos(pData->statement,
+ pData->batch.bindings+i,
+ pData->error, i+1, NULL,
+ pData->batch.param_size,
+ SQLT_STR, NULL, NULL, NULL,
+ 0, 0, OCI_DATA_AT_EXEC));
+ CHECKERR(pData->error,
+ OCIBindDynamic(pData->batch.bindings[i],
+ pData->error,
+ pData->batch.parameters[i],
+ bind_dynamic, NULL, NULL));
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Resource allocation */
+BEGINcreateInstance
+ int i, j;
+ struct template* tpl;
+CODESTARTcreateInstance
+
+ ASSERT(pData != NULL);
+
+ CHECKENV(pData->environment,
+ OCIEnvCreate((void*) &(pData->environment), OCI_DEFAULT,
+ NULL, NULL, NULL, NULL, 0, NULL));
+ CHECKENV(pData->environment,
+ OCIHandleAlloc(pData->environment, (void*) &(pData->error),
+ OCI_HTYPE_ERROR, 0, NULL));
+ CHECKENV(pData->environment,
+ OCIHandleAlloc(pData->environment, (void*) &(pData->authinfo),
+ OCI_HTYPE_AUTHINFO, 0, NULL));
+ CHECKENV(pData->environment,
+ OCIHandleAlloc(pData->environment, (void*) &(pData->statement),
+ OCI_HTYPE_STMT, 0, NULL));
+ tpl = tplFind(db_statement, strlen(db_statement));
+ pData->txt_statement = strdup(tpl->pEntryRoot->data.constant.pConstant);
+ CHKmalloc(pData->txt_statement);
+ dbgprintf("omoracle will run stored statement: %s\n",
+ pData->txt_statement);
+
+ pData->batch.n = 0;
+ pData->batch.size = batch_size;
+ pData->batch.param_size = batch_item_size *
+ sizeof ***pData->batch.parameters;
+ pData->batch.arguments = count_bind_parameters(pData->txt_statement);
+
+ /* I know, this can be done with a single malloc() call but this is
+ * easier to read. :) */
+ pData->batch.parameters = calloc(pData->batch.arguments,
+ sizeof *pData->batch.parameters);
+ CHKmalloc(pData->batch.parameters);
+ for (i = 0; i < pData->batch.arguments; i++) {
+ pData->batch.parameters[i] = calloc(pData->batch.size,
+ sizeof **pData->batch.parameters);
+ CHKmalloc(pData->batch.parameters[i]);
+ for (j = 0; j < pData->batch.size; j++) {
+ /* Each entry has at most
+ * pData->batch.param_size bytes because OCI
+ * doesn't like null-terminated strings when
+ * operating with batches, and the maximum
+ * size of each entry must be provided when
+ * binding parameters. pData->batch.param_size
+ * is long enough for usual entries. */
+ pData->batch.parameters[i][j] = malloc(pData->batch.param_size);
+ CHKmalloc(pData->batch.parameters[i][j]);
+ }
+ }
+
+ pData->batch.bindings = calloc(pData->batch.arguments,
+ sizeof *pData->batch.bindings);
+ CHKmalloc(pData->batch.bindings);
+
+finalize_it:
+ENDcreateInstance
+
+/* Inserts all stored statements into the database, releasing any
+ * allocated memory. */
+static int insert_to_db(instanceData* pData)
+{
+ DEFiRet;
+
+ CHECKERR(pData->error,
+ OCIStmtExecute(pData->service,
+ pData->statement,
+ pData->error,
+ pData->batch.n, 0, NULL, NULL,
+ OCI_BATCH_ERRORS));
+
+finalize_it:
+ pData->batch.n = 0;
+ OCITransCommit(pData->service, pData->error, 0);
+ dbgprintf ("omoracle insertion to DB %s\n", iRet == RS_RET_OK ?
+ "succeeded" : "did not succeed");
+ RETiRet;
+}
+
+/** Close the session and free anything allocated by
+ createInstance. */
+BEGINfreeInstance
+ int i, j;
+CODESTARTfreeInstance
+
+/* Before actually releasing our resources, let's try to commit
+ * anything pending so that we don't lose any messages. */
+ insert_to_db(pData);
+ OCISessionRelease(pData->service, pData->error, NULL, 0, OCI_DEFAULT);
+ OCIHandleFree(pData->environment, OCI_HTYPE_ENV);
+ OCIHandleFree(pData->error, OCI_HTYPE_ERROR);
+ OCIHandleFree(pData->service, OCI_HTYPE_SVCCTX);
+ OCIHandleFree(pData->authinfo, OCI_HTYPE_AUTHINFO);
+ OCIHandleFree(pData->statement, OCI_HTYPE_STMT);
+ free(pData->connection);
+ free(pData->txt_statement);
+ for (i = 0; i < pData->batch.arguments; i++) {
+ for (j = 0; j < pData->batch.size; j++)
+ free(pData->batch.parameters[i][j]);
+ free(pData->batch.parameters[i]);
+ }
+ free(pData->batch.parameters);
+ free(pData->batch.bindings);
+ dbgprintf ("omoracle freed all its resources\n");
+
+ENDfreeInstance
+
+BEGINtryResume
+CODESTARTtryResume
+ /* Here usually only a reconnect is done. The rsyslog core will call
+ * this entry point from time to time when the action suspended itself.
+ * Note that the rsyslog core expects that if the plugin suspended itself
+ * the action was not carried out during that invocation. Thus, rsyslog
+ * will call the action with *the same* data item again AFTER a resume
+ * was successful. As such, tryResume should NOT write the failed data
+ * item. If it needs to for some reason, it must delete the item again,
+ * otherwise, it will get duplicated.
+ * This handling inside the rsyslog core is important to be able to
+ * preserve data over rsyslog restarts. With it, the core can ensure that
+ * it queues all not-yet-processed messages without the plugin needing
+ * to take care about that.
+ * So in essence, it is recommended that just a reconnet is tried, but
+ * the last action not restarted. Note that it is not a real problem
+ * (but causes a slight performance degradation) if tryResume returns
+ * successfully but the next call to doAction() immediately returns
+ * RS_RET_SUSPENDED. So it is OK to do the actual restart inside doAction().
+ * ... of course I don't know why Oracle might need a full restart...
+ * rgerhards, 2009-03-26
+ */
+ dbgprintf("omoracle attempting to reconnect to DB server\n");
+ OCISessionRelease(pData->service, pData->error, NULL, 0, OCI_DEFAULT);
+ OCIHandleFree(pData->service, OCI_HTYPE_SVCCTX);
+ CHECKERR(pData->error, OCISessionGet(pData->environment, pData->error,
+ &pData->service, pData->authinfo,
+ pData->connection,
+ strlen(pData->connection), NULL, 0,
+ NULL, NULL, NULL, OCI_DEFAULT));
+ CHKiRet(prepare_statement(pData));
+
+finalize_it:
+ENDtryResume
+
+static rsRetVal startSession(instanceData* pData, char* connection, char* user,
+ char * password)
+{
+ DEFiRet;
+ CHECKERR(pData->error,
+ OCIAttrSet(pData->authinfo, OCI_HTYPE_AUTHINFO, user,
+ strlen(user), OCI_ATTR_USERNAME, pData->error));
+ CHECKERR(pData->error,
+ OCIAttrSet(pData->authinfo, OCI_HTYPE_AUTHINFO, password,
+ strlen(password), OCI_ATTR_PASSWORD, pData->error));
+ CHECKERR(pData->error,
+ OCISessionGet(pData->environment, pData->error,
+ &pData->service, pData->authinfo, connection,
+ strlen(connection), NULL, 0, NULL, NULL, NULL,
+ OCI_DEFAULT));
+finalize_it:
+ if (iRet != RS_RET_OK)
+ errmsg.LogError(0, NO_ERRCODE,
+ "Unable to start Oracle session\n");
+ RETiRet;
+}
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ /* Right now, this module is compatible with nothing. */
+ iRet = RS_RET_INCOMPATIBLE;
+ENDisCompatibleWithFeature
+
+BEGINparseSelectorAct
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(1);
+
+ if (strncmp((char*) p, ":omoracle:", sizeof ":omoracle:" - 1)) {
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+ }
+
+ p += sizeof ":omoracle:" - 1;
+
+ if (*p == '\0' || *p == ',') {
+ errmsg.LogError(0, NO_ERRCODE,
+ "Wrong char processing module arguments: %c\n",
+ *p);
+ ABORT_FINALIZE(RS_RET_INVALID_PARAMS);
+ }
+
+ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0,
+ OMSR_TPL_AS_ARRAY, " StdFmt"));
+ CHKiRet(createInstance(&pData));
+ CHKmalloc(pData->connection = strdup(db_name));
+ CHKiRet(startSession(pData, db_name, db_user, db_password));
+
+ CHKiRet(prepare_statement(pData));
+
+ dbgprintf ("omoracle module got all its resources allocated "
+ "and connected to the DB\n");
+
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+BEGINdoAction
+ int i;
+ char **params = (char**) ppString[0];
+CODESTARTdoAction
+
+ if (pData->batch.n == pData->batch.size) {
+ dbgprintf("omoracle batch size limit hit, sending into DB\n");
+ CHKiRet(insert_to_db(pData));
+ }
+
+ for (i = 0; i < pData->batch.arguments && params[i]; i++) {
+ dbgprintf("batch[%d][%d]=%s\n", i, pData->batch.n, params[i]);
+ strncpy(pData->batch.parameters[i][pData->batch.n], params[i],
+ pData->batch.param_size);
+ CHKmalloc(pData->batch.parameters[i][pData->batch.n]);
+ }
+ pData->batch.n++;
+
+finalize_it:
+ENDdoAction
+
+BEGINmodExit
+CODESTARTmodExit
+ENDmodExit
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ENDdbgPrintInstInfo
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_OMOD_QUERIES
+ENDqueryEtryPt
+
+static rsRetVal
+resetConfigVariables(uchar __attribute__((unused)) *pp,
+ void __attribute__((unused)) *pVal)
+{
+ int n;
+ DEFiRet;
+ if(db_user != NULL)
+ free(db_user);
+ if(db_name != NULL)
+ free(db_name);
+ if (db_password != NULL) {
+ n = strlen(db_password);
+ memset(db_password, 0, n);
+ free(db_password);
+ }
+ if (db_statement != NULL)
+ free(db_statement);
+ db_name = db_user = db_password = db_statement = NULL;
+ batch_size = batch_item_size = 0;
+ RETiRet;
+}
+
+BEGINmodInit()
+ rsRetVal (*supported_options)(unsigned long *pOpts);
+ unsigned long opts;
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION;
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(omsdRegCFSLineHdlr((uchar*) "resetconfigvariables", 1,
+ eCmdHdlrCustomHandler, resetConfigVariables,
+ NULL, STD_LOADABLE_MODULE_ID));
+
+ CHKiRet(omsdRegCFSLineHdlr((uchar*) "omoracledbuser", 0,
+ eCmdHdlrGetWord, NULL, &db_user,
+ STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar*) "omoracledbpassword", 0,
+ eCmdHdlrGetWord, NULL, &db_password,
+ STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar*) "omoracledb", 0,
+ eCmdHdlrGetWord, NULL, &db_name,
+ STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar*) "omoraclebatchsize", 0,
+ eCmdHdlrInt, NULL, &batch_size,
+ STD_LOADABLE_MODULE_ID));
+ CHKiRet(pHostQueryEtryPt((uchar*)"OMSRgetSupportedTplOpts", &supported_options));
+ CHKiRet((*supported_options)(&opts));
+ if (!(opts & OMSR_TPL_AS_ARRAY))
+ ABORT_FINALIZE(RS_RET_RSCORE_TOO_OLD);
+
+ CHKiRet(omsdRegCFSLineHdlr((uchar*) "omoraclestatementtemplate", 0,
+ eCmdHdlrGetWord, NULL,
+ &db_statement, STD_LOADABLE_MODULE_ID));
+
+ CHKiRet(omsdRegCFSLineHdlr((uchar*) "omoraclebatchitemsize", 0,
+ eCmdHdlrInt, NULL,
+ &batch_item_size, STD_LOADABLE_MODULE_ID));
+
+ENDmodInit
diff --git a/plugins/omoracle/omoracle.h b/plugins/omoracle/omoracle.h
new file mode 100644
index 00000000..0ff879b3
--- /dev/null
+++ b/plugins/omoracle/omoracle.h
@@ -0,0 +1,31 @@
+/** Definitions for the Oracle output module.
+
+ This module needs OCI to be installed (on Red Hat-like systems
+ this is usually the oracle-instantclient-devel RPM).
+
+ This file is part of rsyslog.
+
+ This file is licensed under the terms of the GPL version 3 or, at
+ your choice, any later version. Exceptionally (perhaps), you are
+ allowed to link to the Oracle Call Interface in your derived work
+
+ Author: Luis Fernando Muñoz Mejías <Luis.Fernando.Munoz.Mejias@cern.ch>
+*/
+#ifndef __OMORACLEH__
+#define __OMORACLEH__
+
+/** Macros to make error handling easier. */
+
+/** Checks for errors on the OCI handling. */
+#define CHECKERR(handle,status) CHKiRet(oci_errors((handle), \
+ OCI_HTYPE_ERROR, (status)))
+
+/** Checks for errors when handling the environment of OCI. */
+#define CHECKENV(handle,status) CHKiRet(oci_errors((handle), \
+ OCI_HTYPE_ENV, (status)))
+
+enum { MAX_BUFSIZE = 2048 };
+
+#define BIND_MARK ':'
+
+#endif
diff --git a/plugins/omoracle/omoracle.te b/plugins/omoracle/omoracle.te
new file mode 100644
index 00000000..81eb6cf1
--- /dev/null
+++ b/plugins/omoracle/omoracle.te
@@ -0,0 +1,13 @@
+
+module omoracle 1.0;
+
+require {
+ type syslogd_t;
+ type port_t;
+ class process { execstack execmem };
+ class tcp_socket name_connect;
+}
+
+#============= syslogd_t ==============
+allow syslogd_t port_t:tcp_socket name_connect;
+allow syslogd_t self:process { execstack execmem };
diff --git a/plugins/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/Makefile.am b/runtime/Makefile.am
index 2f0a1aa0..bc03c4a7 100644
--- a/runtime/Makefile.am
+++ b/runtime/Makefile.am
@@ -7,6 +7,7 @@ pkglib_LTLIBRARIES =
librsyslog_la_SOURCES = \
rsyslog.c \
rsyslog.h \
+ unicode-helper.h \
atomic.h \
syslogd-types.h \
module-template.h \
diff --git a/runtime/conf.c b/runtime/conf.c
index 55eb9800..7cdcf5ec 100644
--- a/runtime/conf.c
+++ b/runtime/conf.c
@@ -802,7 +802,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/datetime.c b/runtime/datetime.c
index deb66eb4..19e61a0a 100644
--- a/runtime/datetime.c
+++ b/runtime/datetime.c
@@ -150,7 +150,7 @@ static void getCurrTime(struct syslogTime *t, time_t *ttSeconds)
* \retval The number parsed.
*/
-static int srSLMGParseInt32(char** ppsz)
+static int srSLMGParseInt32(uchar** ppsz)
{
int i;
@@ -172,9 +172,9 @@ static int srSLMGParseInt32(char** ppsz)
* could be obtained (restriction added 2008-09-16 by rgerhards).
*/
static rsRetVal
-ParseTIMESTAMP3339(struct syslogTime *pTime, char** ppszTS)
+ParseTIMESTAMP3339(struct syslogTime *pTime, uchar** ppszTS)
{
- char *pszTS = *ppszTS;
+ uchar *pszTS = *ppszTS;
/* variables to temporarily hold time information while we parse */
int year;
int month;
@@ -234,7 +234,7 @@ ParseTIMESTAMP3339(struct syslogTime *pTime, char** ppszTS)
/* Now let's see if we have secfrac */
if(*pszTS == '.') {
- char *pszStart = ++pszTS;
+ uchar *pszStart = ++pszTS;
secfrac = srSLMGParseInt32(&pszTS);
secfracPrecision = (int) (pszTS - pszStart);
} else {
@@ -307,7 +307,7 @@ finalize_it:
* time() call reduction ;).
*/
static rsRetVal
-ParseTIMESTAMP3164(struct syslogTime *pTime, char** ppszTS)
+ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS)
{
/* variables to temporarily hold time information while we parse */
int month;
@@ -317,7 +317,7 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, char** ppszTS)
int minute;
int second;
/* end variables to temporarily hold time information while we parse */
- char *pszTS;
+ uchar *pszTS;
DEFiRet;
assert(ppszTS != NULL);
diff --git a/runtime/datetime.h b/runtime/datetime.h
index 0739588d..efb0a0af 100644
--- a/runtime/datetime.h
+++ b/runtime/datetime.h
@@ -36,8 +36,8 @@ typedef struct datetime_s {
/* interfaces */
BEGINinterface(datetime) /* name must also be changed in ENDinterface macro! */
void (*getCurrTime)(struct syslogTime *t, time_t *ttSeconds);
- rsRetVal (*ParseTIMESTAMP3339)(struct syslogTime *pTime, char** ppszTS);
- rsRetVal (*ParseTIMESTAMP3164)(struct syslogTime *pTime, char** pszTS);
+ rsRetVal (*ParseTIMESTAMP3339)(struct syslogTime *pTime, uchar** ppszTS);
+ rsRetVal (*ParseTIMESTAMP3164)(struct syslogTime *pTime, uchar** pszTS);
int (*formatTimestampToMySQL)(struct syslogTime *ts, char* pDst, size_t iLenDst);
int (*formatTimestampToPgSQL)(struct syslogTime *ts, char *pDst, size_t iLenDst);
int (*formatTimestamp3339)(struct syslogTime *ts, char* pBuf, size_t iLenBuf);
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/modules.c b/runtime/modules.c
index 9fdb48e7..32ae659f 100644
--- a/runtime/modules.c
+++ b/runtime/modules.c
@@ -40,6 +40,7 @@
#include <time.h>
#include <assert.h>
#include <errno.h>
+#include <pthread.h>
#ifdef OS_BSD
# include "libgen.h"
#endif
@@ -61,6 +62,14 @@
DEFobjStaticHelpers
DEFobjCurrIf(errmsg)
+/* we must ensure that only one thread at one time tries to load or unload
+ * modules, otherwise we may see race conditions. This first came up with
+ * imdiag/imtcp, which both use the same stream drivers. Below is the mutex
+ * for that handling.
+ * rgerhards, 2009-05-25
+ */
+static pthread_mutex_t mutLoadUnload;
+
static modInfo_t *pLoadedModules = NULL; /* list of currently-loaded modules */
static modInfo_t *pLoadedModulesLast = NULL; /* tail-pointer */
@@ -479,6 +488,8 @@ modUnlinkAndDestroy(modInfo_t **ppThis)
pThis = *ppThis;
assert(pThis != NULL);
+ pthread_mutex_lock(&mutLoadUnload);
+
/* first check if we are permitted to unload */
if(pThis->eType == eMOD_LIB) {
if(pThis->uRefCnt > 0) {
@@ -513,6 +524,7 @@ modUnlinkAndDestroy(modInfo_t **ppThis)
moduleDestruct(pThis);
finalize_it:
+ pthread_mutex_unlock(&mutLoadUnload);
RETiRet;
}
@@ -587,6 +599,8 @@ Load(uchar *pModName)
assert(pModName != NULL);
dbgprintf("Requested to load module '%s'\n", pModName);
+ pthread_mutex_lock(&mutLoadUnload);
+
iModNameLen = strlen((char *) pModName);
if(iModNameLen > 3 && !strcmp((char *) pModName + iModNameLen - 3, ".so")) {
iModNameLen -= 3;
@@ -696,6 +710,7 @@ Load(uchar *pModName)
}
finalize_it:
+ pthread_mutex_unlock(&mutLoadUnload);
RETiRet;
}
@@ -791,6 +806,15 @@ BEGINObjClassExit(module, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MA
CODESTARTObjClassExit(module)
/* release objects we no longer need */
objRelease(errmsg, CORE_COMPONENT);
+ /* We have a problem in our reference counting, which leads to this function
+ * being called too early. This usually is no problem, but if we destroy
+ * the mutex object, we get into trouble. So rather than finding the root cause,
+ * we do not release the mutex right now and have a very, very slight leak.
+ * We know that otherwise no bad effects happen, so this acceptable for the
+ * time being. -- rgerhards, 2009-05-25
+ *
+ * TODO: add again: pthread_mutex_destroy(&mutLoadUnload);
+ */
# ifdef DEBUG
modUsrPrintAll(); /* debug aid - TODO: integrate with debug.c, at least the settings! */
@@ -833,6 +857,7 @@ ENDobjQueryInterface(module)
*/
BEGINAbstractObjClassInit(module, 1, OBJ_IS_CORE_MODULE) /* class, version - CHANGE class also in END MACRO! */
uchar *pModPath;
+ pthread_mutexattr_t mutAttr;
/* use any module load path specified in the environment */
if((pModPath = (uchar*) getenv("RSYSLOG_MODDIR")) != NULL) {
@@ -850,6 +875,10 @@ BEGINAbstractObjClassInit(module, 1, OBJ_IS_CORE_MODULE) /* class, version - CHA
SetModDir(glblModPath);
}
+ pthread_mutexattr_init(&mutAttr);
+ pthread_mutexattr_settype(&mutAttr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&mutLoadUnload, &mutAttr);
+
/* request objects we use */
CHKiRet(objUse(errmsg, CORE_COMPONENT));
ENDObjClassInit(module)
diff --git a/runtime/msg.c b/runtime/msg.c
index 9165e8d6..dbc3c779 100644
--- a/runtime/msg.c
+++ b/runtime/msg.c
@@ -45,6 +45,7 @@
#include "glbl.h"
#include "regexp.h"
#include "atomic.h"
+#include "unicode-helper.h"
/* static data */
DEFobjStaticHelpers
@@ -341,52 +342,29 @@ CODESTARTobjDestruct(msg)
if(currRefCount == 0)
{
/* DEV Debugging Only! dbgprintf("msgDestruct\t0x%lx, RefCount now 0, doing DESTROY\n", (unsigned long)pThis); */
- if(pThis->pszUxTradMsg != NULL)
- free(pThis->pszUxTradMsg);
- if(pThis->pszRawMsg != NULL)
- free(pThis->pszRawMsg);
- if(pThis->pszTAG != NULL)
- free(pThis->pszTAG);
- if(pThis->pszHOSTNAME != NULL)
- free(pThis->pszHOSTNAME);
- if(pThis->pszInputName != NULL)
- free(pThis->pszInputName);
- if(pThis->pszRcvFrom != NULL)
- free(pThis->pszRcvFrom);
- if(pThis->pszRcvFromIP != NULL)
- free(pThis->pszRcvFromIP);
- if(pThis->pszMSG != NULL)
- free(pThis->pszMSG);
- if(pThis->pszFacility != NULL)
- free(pThis->pszFacility);
- if(pThis->pszFacilityStr != NULL)
- free(pThis->pszFacilityStr);
- if(pThis->pszSeverity != NULL)
- free(pThis->pszSeverity);
- if(pThis->pszSeverityStr != NULL)
- free(pThis->pszSeverityStr);
- if(pThis->pszRcvdAt3164 != NULL)
- free(pThis->pszRcvdAt3164);
- if(pThis->pszRcvdAt3339 != NULL)
- free(pThis->pszRcvdAt3339);
- if(pThis->pszRcvdAt_SecFrac != NULL)
- free(pThis->pszRcvdAt_SecFrac);
- if(pThis->pszRcvdAt_MySQL != NULL)
- free(pThis->pszRcvdAt_MySQL);
- if(pThis->pszRcvdAt_PgSQL != NULL)
- free(pThis->pszRcvdAt_PgSQL);
- if(pThis->pszTIMESTAMP3164 != NULL)
- free(pThis->pszTIMESTAMP3164);
- if(pThis->pszTIMESTAMP3339 != NULL)
- free(pThis->pszTIMESTAMP3339);
- if(pThis->pszTIMESTAMP_SecFrac != NULL)
- free(pThis->pszTIMESTAMP_SecFrac);
- if(pThis->pszTIMESTAMP_MySQL != NULL)
- free(pThis->pszTIMESTAMP_MySQL);
- if(pThis->pszTIMESTAMP_PgSQL != NULL)
- free(pThis->pszTIMESTAMP_PgSQL);
- if(pThis->pszPRI != NULL)
- free(pThis->pszPRI);
+ free(pThis->pszUxTradMsg);
+ free(pThis->pszRawMsg);
+ free(pThis->pszTAG);
+ free(pThis->pszHOSTNAME);
+ free(pThis->pszInputName);
+ free(pThis->pszRcvFrom);
+ free(pThis->pszRcvFromIP);
+ free(pThis->pszMSG);
+ free(pThis->pszFacility);
+ free(pThis->pszFacilityStr);
+ free(pThis->pszSeverity);
+ free(pThis->pszSeverityStr);
+ free(pThis->pszRcvdAt3164);
+ free(pThis->pszRcvdAt3339);
+ free(pThis->pszRcvdAt_SecFrac);
+ free(pThis->pszRcvdAt_MySQL);
+ free(pThis->pszRcvdAt_PgSQL);
+ free(pThis->pszTIMESTAMP3164);
+ free(pThis->pszTIMESTAMP3339);
+ free(pThis->pszTIMESTAMP_SecFrac);
+ free(pThis->pszTIMESTAMP_MySQL);
+ free(pThis->pszTIMESTAMP_PgSQL);
+ free(pThis->pszPRI);
if(pThis->pCSProgName != NULL)
rsCStrDestruct(&pThis->pCSProgName);
if(pThis->pCSStrucData != NULL)
@@ -1306,15 +1284,15 @@ uchar *getInputName(msg_t *pM)
}
-char *getRcvFrom(msg_t *pM)
+uchar *getRcvFrom(msg_t *pM)
{
if(pM == NULL)
- return "";
+ return UCHAR_CONSTANT("");
else
if(pM->pszRcvFrom == NULL)
- return "";
+ return UCHAR_CONSTANT("");
else
- return (char*) pM->pszRcvFrom;
+ return pM->pszRcvFrom;
}
@@ -1488,13 +1466,13 @@ static int getAPPNAMELen(msg_t *pM)
/* rgerhards 2008-09-10: set pszInputName in msg object
*/
-void MsgSetInputName(msg_t *pMsg, char* pszInputName)
+void MsgSetInputName(msg_t *pMsg, uchar* pszInputName)
{
assert(pMsg != NULL);
if(pMsg->pszInputName != NULL)
free(pMsg->pszInputName);
- pMsg->iLenInputName = strlen(pszInputName);
+ pMsg->iLenInputName = ustrlen(pszInputName);
if((pMsg->pszInputName = malloc(pMsg->iLenInputName + 1)) != NULL) {
memcpy(pMsg->pszInputName, pszInputName, pMsg->iLenInputName + 1);
}
@@ -1502,13 +1480,12 @@ void MsgSetInputName(msg_t *pMsg, char* pszInputName)
/* rgerhards 2004-11-16: set pszRcvFrom in msg object
*/
-void MsgSetRcvFrom(msg_t *pMsg, char* pszRcvFrom)
+void MsgSetRcvFrom(msg_t *pMsg, uchar* pszRcvFrom)
{
assert(pMsg != NULL);
- if(pMsg->pszRcvFrom != NULL)
- free(pMsg->pszRcvFrom);
+ free(pMsg->pszRcvFrom);
- pMsg->iLenRcvFrom = strlen(pszRcvFrom);
+ pMsg->iLenRcvFrom = ustrlen(pszRcvFrom);
if((pMsg->pszRcvFrom = malloc(pMsg->iLenRcvFrom + 1)) != NULL) {
memcpy(pMsg->pszRcvFrom, pszRcvFrom, pMsg->iLenRcvFrom + 1);
}
@@ -1559,17 +1536,16 @@ void MsgAssignHOSTNAME(msg_t *pMsg, char *pBuf)
* we need it. The rest of the code already knows how to handle an
* unset HOSTNAME.
*/
-void MsgSetHOSTNAME(msg_t *pMsg, char* pszHOSTNAME)
+void MsgSetHOSTNAME(msg_t *pMsg, uchar* pszHOSTNAME)
{
assert(pMsg != NULL);
- if(pMsg->pszHOSTNAME != NULL)
- free(pMsg->pszHOSTNAME);
+ free(pMsg->pszHOSTNAME);
- pMsg->iLenHOSTNAME = strlen(pszHOSTNAME);
+ pMsg->iLenHOSTNAME = ustrlen(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");
}
@@ -1790,7 +1766,7 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
} else if(!strcmp((char*) pName, "inputname")) {
pRes = (char*) getInputName(pMsg);
} else if(!strcmp((char*) pName, "fromhost")) {
- pRes = getRcvFrom(pMsg);
+ pRes = (char*) getRcvFrom(pMsg);
} else if(!strcmp((char*) pName, "fromhost-ip")) {
pRes = (char*) getRcvFromIP(pMsg);
} else if(!strcmp((char*) pName, "source") || !strcmp((char*) pName, "hostname")) {
@@ -2517,13 +2493,13 @@ rsRetVal MsgSetProperty(msg_t *pThis, var_t *pProp)
} else if(isProp("pszTAG")) {
MsgSetTAG(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
} else if(isProp("pszInputName")) {
- MsgSetInputName(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ MsgSetInputName(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr));
} else if(isProp("pszRcvFromIP")) {
MsgSetRcvFromIP(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr));
} else if(isProp("pszRcvFrom")) {
- MsgSetRcvFrom(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ MsgSetRcvFrom(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr));
} else if(isProp("pszHOSTNAME")) {
- MsgSetHOSTNAME(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ MsgSetHOSTNAME(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr));
} else if(isProp("pCSStrucData")) {
MsgSetStructuredData(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
} else if(isProp("pCSAPPNAME")) {
diff --git a/runtime/msg.h b/runtime/msg.h
index c8350626..a14f6b15 100644
--- a/runtime/msg.h
+++ b/runtime/msg.h
@@ -158,7 +158,7 @@ char *getSeverity(msg_t *pM);
char *getSeverityStr(msg_t *pM);
char *getFacility(msg_t *pM);
char *getFacilityStr(msg_t *pM);
-void MsgSetInputName(msg_t *pMsg, char*);
+void MsgSetInputName(msg_t *pMsg, uchar*);
rsRetVal MsgSetAPPNAME(msg_t *pMsg, char* pszAPPNAME);
char *getAPPNAME(msg_t *pM);
rsRetVal MsgSetPROCID(msg_t *pMsg, char* pszPROCID);
@@ -171,15 +171,15 @@ rsRetVal MsgSetFlowControlType(msg_t *pMsg, flowControl_t eFlowCtl);
char *getTAG(msg_t *pM);
int getHOSTNAMELen(msg_t *pM);
char *getHOSTNAME(msg_t *pM);
-char *getRcvFrom(msg_t *pM);
+uchar *getRcvFrom(msg_t *pM);
rsRetVal MsgSetStructuredData(msg_t *pMsg, char* pszStrucData);
char *getStructuredData(msg_t *pM);
int getProgramNameLen(msg_t *pM);
char *getProgramName(msg_t *pM);
-void MsgSetRcvFrom(msg_t *pMsg, char* pszRcvFrom);
+void MsgSetRcvFrom(msg_t *pMsg, uchar* pszRcvFrom);
rsRetVal MsgSetRcvFromIP(msg_t *pMsg, uchar* pszRcvFromIP);
void MsgAssignHOSTNAME(msg_t *pMsg, char *pBuf);
-void MsgSetHOSTNAME(msg_t *pMsg, char* pszHOSTNAME);
+void MsgSetHOSTNAME(msg_t *pMsg, uchar* pszHOSTNAME);
int MsgSetUxTradMsg(msg_t *pMsg, char* pszUxTradMsg);
void MsgSetMSG(msg_t *pMsg, char* pszMSG);
void MsgSetRawMsg(msg_t *pMsg, char* pszRawMsg);
diff --git a/runtime/netstrm.c b/runtime/netstrm.c
index ffa1c578..05bb25c0 100644
--- a/runtime/netstrm.c
+++ b/runtime/netstrm.c
@@ -17,7 +17,7 @@
* Rainer Gerhards and Adiscon GmbH have agreed to permit using the code
* under the terms of the GNU Lesser General Public License.
*
- * Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007-2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of the rsyslog runtime library.
*
diff --git a/runtime/netstrm.h b/runtime/netstrm.h
index 3ab790e8..b00dd223 100644
--- a/runtime/netstrm.h
+++ b/runtime/netstrm.h
@@ -31,6 +31,7 @@ struct netstrm_s {
BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
nsd_t *pDrvrData; /**< the driver's data elements (at most other places, this is called pNsd) */
nsd_if_t Drvr; /**< our stream driver */
+ void *pUsr; /**< pointer to user-provided data structure */
netstrms_t *pNS; /**< pointer to our netstream subsystem object */
};
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/parser.c b/runtime/parser.c
index b4ab0a3e..212d40f3 100644
--- a/runtime/parser.c
+++ b/runtime/parser.c
@@ -283,7 +283,7 @@ rsRetVal parseMsg(msg_t *pMsg)
MsgSetUxTradMsg(pMsg, (char*) msg);
if(pMsg->bParseHOSTNAME == 0)
- MsgSetHOSTNAME(pMsg, (char*) pMsg->pszRcvFrom);
+ MsgSetHOSTNAME(pMsg, pMsg->pszRcvFrom);
/* rger 2005-11-24 (happy thanksgiving!): we now need to check if we have
* a traditional syslog message or one formatted according to syslog-protocol.
diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h
index a4a8a204..2f15f926 100644
--- a/runtime/rsyslog.h
+++ b/runtime/rsyslog.h
@@ -84,6 +84,11 @@ typedef rsRetVal (*errLogFunc_t)(uchar*); /* this is a trick to store a function
typedef struct permittedPeers_s permittedPeers_t; /* this should go away in the long term -- rgerhards, 2008-05-19 */
typedef struct permittedPeerWildcard_s permittedPeerWildcard_t; /* this should go away in the long term -- rgerhards, 2008-05-19 */
typedef struct tcpsrv_s tcpsrv_t;
+typedef struct tcps_sess_s tcps_sess_t;
+typedef struct vmstk_s vmstk_t;
+typedef rsRetVal (*prsf_t)(struct vmstk_s*, int); /* pointer to a RainerScript function */
+
+typedef struct tcpLstnPortList_s tcpLstnPortList_t; // TODO: rename?
/* some universal 64 bit define... */
typedef long long int64;
@@ -258,7 +263,15 @@ 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) */
RS_RET_NONFATAL_CONFIG_ERR = -2123, /**< non-fatal error during config processing */
+ RS_RET_FILENAME_INVALID = -2140, /**< filename invalid, not found, no access, ... */
/* RainerScript error messages (range 1000.. 1999) */
RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */
@@ -350,6 +363,7 @@ typedef enum rsObjectID rsObjID;
/* The following prototype is convenient, even though it may not be the 100% correct place.. -- rgerhards 2008-01-07 */
void dbgprintf(char *, ...) __attribute__((format(printf, 1, 2)));
+
#include "debug.h"
#include "obj.h"
diff --git a/runtime/unicode-helper.h b/runtime/unicode-helper.h
new file mode 100644
index 00000000..36d76a78
--- /dev/null
+++ b/runtime/unicode-helper.h
@@ -0,0 +1,54 @@
+/* This is the header file for unicode support.
+ *
+ * Currently, this is a dummy module.
+ * The following functions are wrappers which hopefully enable us to move
+ * from 8-bit chars to unicode with relative ease when we finally attack this
+ *
+ * Begun 2009-05-21 RGerhards
+ *
+ * Copyright (C) 2009 by Rainer Gerhards and Adiscon GmbH
+ *
+ * This file is part of the rsyslog runtime library.
+ *
+ * The rsyslog runtime library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The rsyslog runtime library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
+ */
+#ifndef INCLUDED_UNICODE_HELPER_H
+#define INCLUDED_UNICODE_HELPER_H
+
+#include <string.h>
+
+static inline int ustrcmp(uchar *psz1, uchar *psz2)
+{
+ return strcmp((char*) psz1, (char*) psz2);
+}
+
+static inline int ustrlen(uchar *psz)
+{
+ return strlen((char*) psz);
+}
+
+static inline uchar* ustrdup(uchar *psz)
+{
+ return (uchar*) strdup((char*)psz);
+}
+
+
+#define UCHAR_CONSTANT(x) ((uchar*) (x))
+
+#endif /* multi-include protection */
+/* vim:set ai:
+ */
diff --git a/runtime/vm.c b/runtime/vm.c
index 23adea8b..125b0d21 100644
--- a/runtime/vm.c
+++ b/runtime/vm.c
@@ -27,6 +27,7 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
+#include <ctype.h>
#include "rsyslog.h"
#include "obj.h"
@@ -40,6 +41,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.
@@ -331,7 +468,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.
@@ -341,20 +477,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)
@@ -363,6 +488,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! */
@@ -532,10 +740,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
@@ -549,6 +770,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/shave-libtool.in b/shave-libtool.in
new file mode 100644
index 00000000..54ebd690
--- /dev/null
+++ b/shave-libtool.in
@@ -0,0 +1,109 @@
+#!/bin/sh
+#
+# Copyright (c) 2009, Damien Lespiau <damien.lespiau@gmail.com>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following
+# conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+
+# we need sed
+SED=@SED@
+if test -z "$SED" ; then
+SED=sed
+fi
+
+lt_unmangle ()
+{
+ last_result=`echo $1 | $SED -e 's#.libs/##' -e 's#[0-9a-zA-Z_\-\.]*_la-##'`
+}
+
+# the real libtool to use
+LIBTOOL="$1"
+shift
+
+# if 1, don't print anything, the underlaying wrapper will do it
+pass_though=0
+
+# scan the arguments, keep the right ones for libtool, and discover the mode
+preserved_args=
+
+# have we seen the --tag option of libtool in the command line ?
+tag_seen=0
+
+while test "$#" -gt 0; do
+ opt="$1"
+ shift
+
+ case $opt in
+ --mode=*)
+ mode=`echo $opt | $SED -e 's/[-_a-zA-Z0-9]*=//'`
+ preserved_args="$preserved_args $opt"
+ ;;
+ -o)
+ lt_output="$1"
+ preserved_args="$preserved_args $opt"
+ ;;
+ --tag=*)
+ tag_seen=1
+ preserved_args="$preserved_args $opt"
+ ;;
+ *)
+ preserved_args="$preserved_args $opt"
+ ;;
+ esac
+done
+
+case "$mode" in
+compile)
+ # shave will be called and print the actual CC/CXX/LINK line
+ preserved_args="$preserved_args --shave-mode=$mode"
+ pass_though=1
+ ;;
+link)
+ preserved_args="$preserved_args --shave-mode=$mode"
+ Q=" LINK "
+ ;;
+*)
+ # let's u
+ # echo "*** libtool: Unimplemented mode: $mode, fill a bug report"
+ ;;
+esac
+
+lt_unmangle "$lt_output"
+output=$last_result
+
+# automake does not add a --tag switch to its libtool invocation when
+# assembling a .s file and rely on libtool to infer the right action based
+# on the compiler name. As shave is using CC to hook a wrapper, libtool gets
+# confused. Let's detect these cases and add a --tag=CC option.
+tag=""
+if test $tag_seen -eq 0 -a x"$mode" = xcompile; then
+ tag="--tag=CC"
+fi
+
+if test -z $V; then
+ if test $pass_though -eq 0; then
+ echo "$Q$output"
+ fi
+ $LIBTOOL --silent $tag $preserved_args
+else
+ echo $LIBTOOL $tag $preserved_args
+ $LIBTOOL $tag $preserved_args
+fi
diff --git a/shave.in b/shave.in
new file mode 100644
index 00000000..afed42e1
--- /dev/null
+++ b/shave.in
@@ -0,0 +1,102 @@
+#!/bin/sh
+#
+# Copyright (c) 2009, Damien Lespiau <damien.lespiau@gmail.com>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following
+# conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+
+# we need sed
+SED=@SED@
+if test -z "$SED" ; then
+SED=sed
+fi
+
+lt_unmangle ()
+{
+ last_result=`echo $1 | $SED -e 's#.libs/##' -e 's#[0-9a-zA-Z_\-\.]*_la-##'`
+}
+
+# the tool to wrap (cc, cxx, ar, ranlib, ..)
+tool="$1"
+shift
+
+# the reel tool (to call)
+REEL_TOOL="$1"
+shift
+
+pass_through=0
+preserved_args=
+while test "$#" -gt 0; do
+ opt="$1"
+ shift
+
+ case $opt in
+ --shave-mode=*)
+ mode=`echo $opt | $SED -e 's/[-_a-zA-Z0-9]*=//'`
+ ;;
+ -o)
+ lt_output="$1"
+ preserved_args="$preserved_args $opt"
+ ;;
+ *)
+ preserved_args="$preserved_args $opt"
+ ;;
+ esac
+done
+
+# mode=link is handled in the libtool wrapper
+case "$mode,$tool" in
+link,*)
+ pass_through=1
+ ;;
+*,cxx)
+ Q=" CXX "
+ ;;
+*,cc)
+ Q=" CC "
+ ;;
+*,fc)
+ Q=" FC "
+ ;;
+*,f77)
+ Q=" F77 "
+ ;;
+*,objc)
+ Q=" OBJC "
+ ;;
+*,*)
+ # should not happen
+ Q=" CC "
+ ;;
+esac
+
+lt_unmangle "$lt_output"
+output=$last_result
+
+if test -z $V; then
+ if test $pass_through -eq 0; then
+ echo "$Q$output"
+ fi
+ $REEL_TOOL $preserved_args
+else
+ echo $REEL_TOOL $preserved_args
+ $REEL_TOOL $preserved_args
+fi
diff --git a/tcps_sess.c b/tcps_sess.c
index ea525977..62d51f66 100644
--- a/tcps_sess.c
+++ b/tcps_sess.c
@@ -44,6 +44,7 @@
#include "errmsg.h"
#include "netstrm.h"
#include "msg.h"
+#include "datetime.h"
/* static data */
@@ -51,6 +52,7 @@ DEFobjStaticHelpers
DEFobjCurrIf(glbl)
DEFobjCurrIf(errmsg)
DEFobjCurrIf(netstrm)
+DEFobjCurrIf(datetime)
static int iMaxLine; /* maximum size of a single message */
@@ -183,6 +185,18 @@ SetTcpsrv(tcps_sess_t *pThis, tcpsrv_t *pSrv)
}
+/* set our parent listener info*/
+static rsRetVal
+SetLstnInfo(tcps_sess_t *pThis, tcpLstnPortList_t *pLstnInfo)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, tcps_sess);
+ assert(pLstnInfo != NULL);
+ pThis->pLstnInfo = pLstnInfo;
+ RETiRet;
+}
+
+
static rsRetVal
SetUsrP(tcps_sess_t *pThis, void *pUsr)
{
@@ -192,6 +206,67 @@ SetUsrP(tcps_sess_t *pThis, void *pUsr)
}
+static rsRetVal
+SetOnMsgReceive(tcps_sess_t *pThis, rsRetVal (*OnMsgReceive)(tcps_sess_t*, uchar*, int))
+{
+ DEFiRet;
+ pThis->DoSubmitMessage = OnMsgReceive;
+ RETiRet;
+}
+
+
+/* This is a helper for submitting the message to the rsyslog core.
+ * It does some common processing, including resetting the various
+ * state variables to a "processed" state.
+ * Note that this function is also called if we had a buffer overflow
+ * due to a too-long message. So far, there is no indication this
+ * happened and it may be worth thinking about different handling
+ * of this case (what obviously would require a change to this
+ * function or some related code).
+ * rgerhards, 2009-04-23
+ */
+static rsRetVal
+defaultDoSubmitMessage(tcps_sess_t *pThis)
+{
+ msg_t *pMsg;
+ struct syslogTime stTime;
+ time_t ttGenTime;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, tcps_sess);
+
+ if(pThis->DoSubmitMessage != NULL) {
+ pThis->DoSubmitMessage(pThis, pThis->pMsg, pThis->iMsg);
+ FINALIZE;
+ }
+
+ //TODO: if((iTimeRequery == 0) || (iNbrTimeUsed++ % iTimeRequery) == 0) {
+ datetime.getCurrTime(&stTime, &ttGenTime);
+ //}
+ /* we now create our own message object and submit it to the queue */
+ CHKiRet(msgConstructWithTime(&pMsg, &stTime, ttGenTime));
+ /* first trim the buffer to what we have actually received */
+ CHKmalloc(pMsg->pszRawMsg = malloc(sizeof(uchar) * pThis->iMsg));
+ memcpy(pMsg->pszRawMsg, pThis->pMsg, pThis->iMsg);
+ pMsg->iLenRawMsg = pThis->iMsg;
+ MsgSetInputName(pMsg, pThis->pLstnInfo->pszInputName);
+ MsgSetFlowControlType(pMsg, eFLOWCTL_LIGHT_DELAY);
+ pMsg->msgFlags = NEEDS_PARSING | PARSE_HOSTNAME;
+ pMsg->bParseHOSTNAME = 1;
+ MsgSetRcvFrom(pMsg, pThis->fromHost);
+ CHKiRet(MsgSetRcvFromIP(pMsg, pThis->fromHostIP));
+ CHKiRet(submitMsg(pMsg));
+
+finalize_it:
+ /* reset status variables */
+ pThis->bAtStrtOfFram = 1;
+ pThis->iMsg = 0;
+
+ RETiRet;
+}
+
+
+
/* This should be called before a normal (non forced) close
* of a TCP session. This function checks if there is any unprocessed
* message left in the TCP stream. Such a message is probably a
@@ -231,9 +306,7 @@ PrepareClose(tcps_sess_t *pThis)
* this case.
*/
dbgprintf("Extra data at end of stream in legacy syslog/tcp message - processing\n");
- parseAndSubmitMessage(pThis->fromHost, pThis->fromHostIP, pThis->pMsg, pThis->iMsg,
- PARSE_HOSTNAME, eFLOWCTL_LIGHT_DELAY, pThis->pSrv->pszInputName, NULL, 0);
- pThis->bAtStrtOfFram = 1;
+ defaultDoSubmitMessage(pThis);
}
finalize_it:
@@ -314,9 +387,7 @@ processDataRcvd(tcps_sess_t *pThis, char c)
if(pThis->iMsg >= iMaxLine) {
/* emergency, we now need to flush, no matter if we are at end of message or not... */
dbgprintf("error: message received is larger than max msg size, we split it\n");
- parseAndSubmitMessage(pThis->fromHost, pThis->fromHostIP, pThis->pMsg, pThis->iMsg,
- PARSE_HOSTNAME, eFLOWCTL_LIGHT_DELAY, pThis->pSrv->pszInputName, NULL, 0);
- pThis->iMsg = 0;
+ defaultDoSubmitMessage(pThis);
/* we might think if it is better to ignore the rest of the
* message than to treat it as a new one. Maybe this is a good
* candidate for a configuration parameter...
@@ -327,9 +398,7 @@ processDataRcvd(tcps_sess_t *pThis, char c)
if(( (c == '\n')
|| ((pThis->pSrv->addtlFrameDelim != TCPSRV_NO_ADDTL_DELIMITER) && (c == pThis->pSrv->addtlFrameDelim))
) && pThis->eFraming == TCP_FRAMING_OCTET_STUFFING) { /* record delimiter? */
- parseAndSubmitMessage(pThis->fromHost, pThis->fromHostIP, pThis->pMsg, pThis->iMsg,
- PARSE_HOSTNAME, eFLOWCTL_LIGHT_DELAY, pThis->pSrv->pszInputName, NULL, 0);
- pThis->iMsg = 0;
+ defaultDoSubmitMessage(pThis);
pThis->inputState = eAtStrtFram;
} else {
/* IMPORTANT: here we copy the actual frame content to the message - for BOTH framing modes!
@@ -346,9 +415,7 @@ processDataRcvd(tcps_sess_t *pThis, char c)
pThis->iOctetsRemain--;
if(pThis->iOctetsRemain < 1) {
/* we have end of frame! */
- parseAndSubmitMessage(pThis->fromHost, pThis->fromHostIP, pThis->pMsg, pThis->iMsg,
- PARSE_HOSTNAME, eFLOWCTL_LIGHT_DELAY, pThis->pSrv->pszInputName, NULL, 0);
- pThis->iMsg = 0;
+ defaultDoSubmitMessage(pThis);
pThis->inputState = eAtStrtFram;
}
}
@@ -417,10 +484,12 @@ CODESTARTobjQueryInterface(tcps_sess)
pIf->SetUsrP = SetUsrP;
pIf->SetTcpsrv = SetTcpsrv;
+ pIf->SetLstnInfo = SetLstnInfo;
pIf->SetHost = SetHost;
pIf->SetHostIP = SetHostIP;
pIf->SetStrm = SetStrm;
pIf->SetMsgIdx = SetMsgIdx;
+ pIf->SetOnMsgReceive = SetOnMsgReceive;
finalize_it:
ENDobjQueryInterface(tcps_sess)
@@ -433,6 +502,7 @@ CODESTARTObjClassExit(tcps_sess)
/* release objects we no longer need */
objRelease(errmsg, CORE_COMPONENT);
objRelease(netstrm, LM_NETSTRMS_FILENAME);
+ objRelease(datetime, CORE_COMPONENT);
ENDObjClassExit(tcps_sess)
@@ -444,6 +514,7 @@ BEGINObjClassInit(tcps_sess, 1, OBJ_IS_CORE_MODULE) /* class, version - CHANGE c
/* request objects we use */
CHKiRet(objUse(errmsg, CORE_COMPONENT));
CHKiRet(objUse(netstrm, LM_NETSTRMS_FILENAME));
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
CHKiRet(objUse(glbl, CORE_COMPONENT));
iMaxLine = glbl.GetMaxLine(); /* get maximum size we currently support */
diff --git a/tcps_sess.h b/tcps_sess.h
index 576466ff..5e59aaab 100644
--- a/tcps_sess.h
+++ b/tcps_sess.h
@@ -29,9 +29,10 @@
struct tcpsrv_s;
/* the tcps_sess object */
-typedef struct tcps_sess_s {
+struct tcps_sess_s {
BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
- struct tcpsrv_s *pSrv; /* pointer back to my server (e.g. for callbacks) */
+ tcpsrv_t *pSrv; /* pointer back to my server (e.g. for callbacks) */
+ tcpLstnPortList_t *pLstnInfo; /* pointer back to listener info */
netstrm_t *pStrm;
int iMsg; /* index of next char to store in msg */
int bAtStrtOfFram; /* are we at the very beginning of a new frame? */
@@ -45,8 +46,9 @@ typedef struct tcps_sess_s {
uchar *pMsg; /* message (fragment) received */
uchar *fromHost;
uchar *fromHostIP;
- void *pUsr; /* a user-pointer */
-} tcps_sess_t;
+ void *pUsr; /* a user-pointer */
+ rsRetVal (*DoSubmitMessage)(tcps_sess_t*, uchar*, int); /* submit message callback */
+};
/* interfaces */
@@ -60,13 +62,20 @@ BEGINinterface(tcps_sess) /* name must also be changed in ENDinterface macro! */
rsRetVal (*DataRcvd)(tcps_sess_t *pThis, char *pData, size_t iLen);
/* set methods */
rsRetVal (*SetTcpsrv)(tcps_sess_t *pThis, struct tcpsrv_s *pSrv);
+ rsRetVal (*SetLstnInfo)(tcps_sess_t *pThis, tcpLstnPortList_t *pLstnInfo);
rsRetVal (*SetUsrP)(tcps_sess_t*, void*);
rsRetVal (*SetHost)(tcps_sess_t *pThis, uchar*);
rsRetVal (*SetHostIP)(tcps_sess_t *pThis, uchar*);
rsRetVal (*SetStrm)(tcps_sess_t *pThis, netstrm_t*);
rsRetVal (*SetMsgIdx)(tcps_sess_t *pThis, int);
+ rsRetVal (*SetOnMsgReceive)(tcps_sess_t *pThis, rsRetVal (*OnMsgReceive)(tcps_sess_t*, uchar*, int));
ENDinterface(tcps_sess)
-#define tcps_sessCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+#define tcps_sessCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */
+/* interface changes
+ * to version v2, rgerhards, 2009-05-22
+ * - Data structures changed
+ * - SetLstnInfo entry point added
+ */
/* prototypes */
diff --git a/tcpsrv.c b/tcpsrv.c
index b9434398..02eee88e 100644
--- a/tcpsrv.c
+++ b/tcpsrv.c
@@ -17,7 +17,7 @@
*
* File begun on 2007-12-21 by RGerhards (extracted from syslogd.c)
*
- * Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007, 2008, 2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -66,6 +66,7 @@
#include "netstrm.h"
#include "nssel.h"
#include "errmsg.h"
+#include "unicode-helper.h"
MODULE_TYPE_LIB
@@ -85,43 +86,60 @@ DEFobjCurrIf(netstrm)
DEFobjCurrIf(nssel)
-/* configure TCP listener settings. This is called during command
- * line parsing. The argument following -t is supplied as an argument.
- * The format of this argument is
- * "<port-to-use>, <nbr-of-sessions>"
- * Typically, there is no whitespace between port and session number.
- * (but it may be...).
- * NOTE: you can not use dbgprintf() in here - the dbgprintf() system is
- * not yet initilized when this function is called.
- * rgerhards, 2007-06-21
- * The port in cOptarg is handed over to us - the caller MUST NOT free it!
+/* add new listener port to listener port list
+ * rgerhards, 2009-05-21
+ */
+static inline rsRetVal
+addNewLstnPort(tcpsrv_t *pThis, uchar *pszPort)
+{
+ tcpLstnPortList_t *pEntry;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, tcpsrv);
+
+ /* create entry */
+ CHKmalloc(pEntry = malloc(sizeof(tcpLstnPortList_t)));
+ pEntry->pszPort = pszPort;
+ pEntry->pSrv = pThis;
+ CHKmalloc(pEntry->pszInputName = ustrdup(pThis->pszInputName));
+
+ /* and add to list */
+ pEntry->pNext = pThis->pLstnPorts;
+ pThis->pLstnPorts = pEntry;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* configure TCP listener settings.
+ * Note: pszPort is handed over to us - the caller MUST NOT free it!
* rgerhards, 2008-03-20
*/
-static void
-configureTCPListen(tcpsrv_t *pThis, char *cOptarg)
+static rsRetVal
+configureTCPListen(tcpsrv_t *pThis, uchar *pszPort)
{
- register int i;
- register char *pArg = cOptarg;
+ int i;
+ uchar *pPort = pszPort;
+ DEFiRet;
- assert(cOptarg != NULL);
+ assert(pszPort != NULL);
ISOBJ_TYPE_assert(pThis, tcpsrv);
/* extract port */
i = 0;
- while(isdigit((int) *pArg)) {
- i = i * 10 + *pArg++ - '0';
+ while(isdigit((int) *pPort)) {
+ i = i * 10 + *pPort++ - '0';
}
- if(pThis->TCPLstnPort != NULL) {
- free(pThis->TCPLstnPort);
- pThis->TCPLstnPort = NULL;
- }
-
- if( i >= 0 && i <= 65535) {
- pThis->TCPLstnPort = cOptarg;
+ if(i >= 0 && i <= 65535) {
+ CHKiRet(addNewLstnPort(pThis, pszPort));
} else {
- errmsg.LogError(0, NO_ERRCODE, "Invalid TCP listen port %s - changed to 514.\n", cOptarg);
+ errmsg.LogError(0, NO_ERRCODE, "Invalid TCP listen port %s - ignored.\n", pszPort);
}
+
+finalize_it:
+ RETiRet;
}
@@ -202,6 +220,8 @@ TCPSessGetNxtSess(tcpsrv_t *pThis, int iCurr)
static void deinit_tcp_listener(tcpsrv_t *pThis)
{
int i;
+ tcpLstnPortList_t *pEntry;
+ tcpLstnPortList_t *pDel;
ISOBJ_TYPE_assert(pThis, tcpsrv);
@@ -219,8 +239,15 @@ static void deinit_tcp_listener(tcpsrv_t *pThis)
pThis->pSessions = NULL; /* just to make sure... */
}
- if(pThis->TCPLstnPort != NULL)
- free(pThis->TCPLstnPort);
+ /* free list of tcp listen ports */
+ pEntry = pThis->pLstnPorts;
+ while(pEntry != NULL) {
+ free(pEntry->pszPort);
+ free(pEntry->pszInputName);
+ pDel = pEntry;
+ pEntry = pEntry->pNext;
+ free(pDel);
+ }
/* finally close our listen streams */
for(i = 0 ; i < pThis->iLstnMax ; ++i) {
@@ -235,7 +262,8 @@ static void deinit_tcp_listener(tcpsrv_t *pThis)
static rsRetVal
addTcpLstn(void *pUsr, netstrm_t *pLstn)
{
- tcpsrv_t *pThis = (tcpsrv_t*) pUsr;
+ tcpLstnPortList_t *pPortList = (tcpLstnPortList_t *) pUsr;
+ tcpsrv_t *pThis = pPortList->pSrv;
DEFiRet;
ISOBJ_TYPE_assert(pThis, tcpsrv);
@@ -245,6 +273,7 @@ addTcpLstn(void *pUsr, netstrm_t *pLstn)
ABORT_FINALIZE(RS_RET_MAX_LSTN_REACHED);
pThis->ppLstn[pThis->iLstnMax] = pLstn;
+ pThis->ppLstnPort[pThis->iLstnMax] = pPortList;
++pThis->iLstnMax;
finalize_it:
@@ -252,19 +281,20 @@ finalize_it:
}
-/* Initialize TCP sockets (for listener) and listens on them */
-static rsRetVal
-create_tcp_socket(tcpsrv_t *pThis)
+/* Initialize TCP listener socket for a single port
+ * rgerhards, 2009-05-21
+ */
+static inline rsRetVal
+initTCPListener(tcpsrv_t *pThis, tcpLstnPortList_t *pPortEntry)
{
DEFiRet;
uchar *TCPLstnPort;
ISOBJ_TYPE_assert(pThis, tcpsrv);
+ assert(pPortEntry != NULL);
- if(!strcmp((char*)pThis->TCPLstnPort, "0"))
- TCPLstnPort = (uchar*)"514";
- // TODO: we need to enable the caller to set a port (based on who is
- // using this, 514 may be totally unsuitable... --- rgerhards, 2008-04-22
+ if(!ustrcmp(pPortEntry->pszPort, UCHAR_CONSTANT("0")))
+ TCPLstnPort = UCHAR_CONSTANT("514");
/* use default - we can not do service db update, because there is
* no IANA-assignment for syslog/tcp. In the long term, we might
* re-use RFC 3195 port of 601, but that would probably break to
@@ -272,10 +302,31 @@ create_tcp_socket(tcpsrv_t *pThis)
* rgerhards, 2007-06-28
*/
else
- TCPLstnPort = (uchar*)pThis->TCPLstnPort;
+ TCPLstnPort = pPortEntry->pszPort;
/* TODO: add capability to specify local listen address! */
- CHKiRet(netstrm.LstnInit(pThis->pNS, (void*)pThis, addTcpLstn, TCPLstnPort, NULL, pThis->iSessMax));
+ CHKiRet(netstrm.LstnInit(pThis->pNS, (void*)pPortEntry, addTcpLstn, TCPLstnPort, NULL, pThis->iSessMax));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Initialize TCP sockets (for listener) and listens on them */
+static rsRetVal
+create_tcp_socket(tcpsrv_t *pThis)
+{
+ tcpLstnPortList_t *pEntry;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, tcpsrv);
+
+ /* init all configured ports */
+ pEntry = pThis->pLstnPorts;
+ while(pEntry != NULL) {
+ CHKiRet(initTCPListener(pThis, pEntry));
+ pEntry = pEntry->pNext;
+ }
/* OK, we had success. Now it is also time to
* initialize our connections
@@ -297,7 +348,7 @@ finalize_it:
/* Accept new TCP connection; make entry in session table. If there
* is no more space left in the connection table, the new TCP
* connection is immediately dropped.
- * ppSess has a pointer to the newly created session, if it succeds.
+ * ppSess has a pointer to the newly created session, if it succeeds.
* If it does not succeed, no session is created and ppSess is
* undefined. If the user has provided an OnSessAccept Callback,
* this one is executed immediately after creation of the
@@ -305,7 +356,7 @@ finalize_it:
* rgerhards, 2008-03-02
*/
static rsRetVal
-SessAccept(tcpsrv_t *pThis, tcps_sess_t **ppSess, netstrm_t *pStrm)
+SessAccept(tcpsrv_t *pThis, tcpLstnPortList_t *pLstnInfo, tcps_sess_t **ppSess, netstrm_t *pStrm)
{
DEFiRet;
tcps_sess_t *pSess = NULL;
@@ -316,6 +367,7 @@ SessAccept(tcpsrv_t *pThis, tcps_sess_t **ppSess, netstrm_t *pStrm)
uchar *fromHostIP = NULL;
ISOBJ_TYPE_assert(pThis, tcpsrv);
+ assert(pLstnInfo != NULL);
CHKiRet(netstrm.AcceptConnReq(pStrm, &pNewStrm));
@@ -325,13 +377,15 @@ SessAccept(tcpsrv_t *pThis, tcps_sess_t **ppSess, netstrm_t *pStrm)
errno = 0;
errmsg.LogError(0, RS_RET_MAX_SESS_REACHED, "too many tcp sessions - dropping incoming request");
ABORT_FINALIZE(RS_RET_MAX_SESS_REACHED);
- } else {
- /* we found a free spot and can construct our session object */
- CHKiRet(tcps_sess.Construct(&pSess));
- CHKiRet(tcps_sess.SetTcpsrv(pSess, pThis));
}
- /* OK, we have a "good" index... */
+ /* we found a free spot and can construct our session object */
+ CHKiRet(tcps_sess.Construct(&pSess));
+ CHKiRet(tcps_sess.SetTcpsrv(pSess, pThis));
+ CHKiRet(tcps_sess.SetLstnInfo(pSess, pLstnInfo));
+ if(pThis->OnMsgReceive != NULL)
+ CHKiRet(tcps_sess.SetOnMsgReceive(pSess, pThis->OnMsgReceive));
+
/* get the host name */
CHKiRet(netstrm.GetRemoteHName(pNewStrm, &fromHostFQDN));
CHKiRet(netstrm.GetRemoteIP(pNewStrm, &fromHostIP));
@@ -376,12 +430,10 @@ finalize_it:
if(iRet != RS_RET_OK) {
if(pSess != NULL)
tcps_sess.Destruct(&pSess);
- if(fromHostFQDN != NULL)
- free(fromHostFQDN);
- if(fromHostIP != NULL)
- free(fromHostIP);
if(pNewStrm != NULL)
netstrm.Destruct(&pNewStrm);
+ free(fromHostFQDN);
+ free(fromHostIP);
}
RETiRet;
@@ -445,7 +497,7 @@ Run(tcpsrv_t *pThis)
CHKiRet(nssel.IsReady(pSel, pThis->ppLstn[i], NSDSEL_RD, &bIsReady, &nfds));
if(bIsReady) {
dbgprintf("New connect on NSD %p.\n", pThis->ppLstn[i]);
- SessAccept(pThis, &pNewSess, pThis->ppLstn[i]);
+ SessAccept(pThis, pThis->ppLstnPort[i], &pNewSess, pThis->ppLstn[i]);
--nfds; /* indicate we have processed one */
}
}
@@ -514,6 +566,7 @@ finalize_it: /* this is a very special case - this time only we do not exit the
BEGINobjConstruct(tcpsrv) /* be sure to specify the object type also in END macro! */
pThis->iSessMax = TCPSESS_MAX_DEFAULT; /* TODO: useful default ;) */
pThis->addtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER;
+ pThis->OnMsgReceive = NULL;
ENDobjConstruct(tcpsrv)
@@ -536,6 +589,7 @@ tcpsrvConstructFinalize(tcpsrv_t *pThis)
/* set up listeners */
CHKmalloc(pThis->ppLstn = calloc(TCPLSTN_MAX_DEFAULT, sizeof(netstrm_t*)));
+ CHKmalloc(pThis->ppLstnPort = calloc(TCPLSTN_MAX_DEFAULT, sizeof(tcpLstnPortList_t*)));
iRet = pThis->OpenLstnSocks(pThis);
finalize_it:
@@ -557,12 +611,10 @@ CODESTARTobjDestruct(tcpsrv)
if(pThis->pNS != NULL)
netstrms.Destruct(&pThis->pNS);
- if(pThis->pszDrvrAuthMode != NULL)
- free(pThis->pszDrvrAuthMode);
- if(pThis->ppLstn != NULL)
- free(pThis->ppLstn);
- if(pThis->pszInputName != NULL)
- free(pThis->pszInputName);
+ free(pThis->pszDrvrAuthMode);
+ free(pThis->ppLstn);
+ free(pThis->ppLstnPort);
+ free(pThis->pszInputName);
ENDobjDestruct(tcpsrv)
@@ -660,6 +712,16 @@ SetUsrP(tcpsrv_t *pThis, void *pUsr)
RETiRet;
}
+static rsRetVal
+SetOnMsgReceive(tcpsrv_t *pThis, rsRetVal (*OnMsgReceive)(tcps_sess_t*, uchar*, int))
+{
+ DEFiRet;
+ assert(OnMsgReceive != NULL);
+ pThis->OnMsgReceive = OnMsgReceive;
+ RETiRet;
+}
+
+
/* Set additional framing to use (if any) -- rgerhards, 2008-12-10 */
static rsRetVal
@@ -682,9 +744,8 @@ SetInputName(tcpsrv_t *pThis, uchar *name)
if(name == NULL)
pszName = NULL;
else
- CHKmalloc(pszName = (uchar*)strdup((char*)name));
- if(pThis->pszInputName != NULL)
- free(pThis->pszInputName);
+ CHKmalloc(pszName = ustrdup(name));
+ free(pThis->pszInputName);
pThis->pszInputName = pszName;
finalize_it:
RETiRet;
@@ -713,7 +774,7 @@ SetDrvrAuthMode(tcpsrv_t *pThis, uchar *mode)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, tcpsrv);
- CHKmalloc(pThis->pszDrvrAuthMode = (uchar*)strdup((char*)mode));
+ CHKmalloc(pThis->pszDrvrAuthMode = ustrdup(mode));
finalize_it:
RETiRet;
}
@@ -768,7 +829,7 @@ CODESTARTobjQueryInterface(tcpsrv)
pIf->ConstructFinalize = tcpsrvConstructFinalize;
pIf->Destruct = tcpsrvDestruct;
- pIf->SessAccept = SessAccept;
+ //pIf->SessAccept = SessAccept;
pIf->configureTCPListen = configureTCPListen;
pIf->create_tcp_socket = create_tcp_socket;
pIf->Run = Run;
@@ -790,6 +851,7 @@ CODESTARTobjQueryInterface(tcpsrv)
pIf->SetCBOnDestruct = SetCBOnDestruct;
pIf->SetCBOnRegularClose = SetCBOnRegularClose;
pIf->SetCBOnErrClose = SetCBOnErrClose;
+ pIf->SetOnMsgReceive = SetOnMsgReceive;
finalize_it:
ENDobjQueryInterface(tcpsrv)
diff --git a/tcpsrv.h b/tcpsrv.h
index e5ecb865..2d174ce0 100644
--- a/tcpsrv.h
+++ b/tcpsrv.h
@@ -32,6 +32,15 @@ typedef enum ETCPsyslogFramingAnomaly {
frame_CiscoIOS = 2
} eTCPsyslogFramingAnomaly;
+
+/* list of tcp listen ports */
+struct tcpLstnPortList_s {
+ uchar *pszPort; /**< the ports the listener shall listen on */
+ uchar *pszInputName; /**< value to be used as input name */
+ tcpsrv_t *pSrv; /**< pointer to higher-level server instance */
+ tcpLstnPortList_t *pNext; /**< next port or NULL */
+};
+
#define TCPSRV_NO_ADDTL_DELIMITER -1 /* specifies that no additional delimiter is to be used in TCP framing */
/* the tcpsrv object */
@@ -44,8 +53,9 @@ struct tcpsrv_s {
permittedPeers_t *pPermPeers;/**< driver's permitted peers */
int iLstnMax; /**< max nbr of listeners currently supported */
netstrm_t **ppLstn; /**< our netstream listners */
+ tcpLstnPortList_t **ppLstnPort; /**< pointer to relevant listen port description */
int iSessMax; /**< max number of sessions supported */
- char *TCPLstnPort; /**< the port the listener shall listen on */
+ tcpLstnPortList_t *pLstnPorts; /**< head pointer for listen ports */
int addtlFrameDelim; /**< additional frame delimiter for plain TCP syslog framing (e.g. to handle NetScreen) */
tcps_sess_t **pSessions;/**< array of all of our sessions */
void *pUsr; /**< a user-settable pointer (provides extensibility for "derived classes")*/
@@ -61,6 +71,7 @@ struct tcpsrv_s {
rsRetVal (*pOnSessAccept)(tcpsrv_t *, tcps_sess_t*);
rsRetVal (*OnSessConstructFinalize)(void*);
rsRetVal (*pOnSessDestruct)(void*);
+ rsRetVal (*OnMsgReceive)(tcps_sess_t *, uchar *pszMsg, int iLenMsg); /* submit message callback */
};
@@ -70,8 +81,8 @@ BEGINinterface(tcpsrv) /* name must also be changed in ENDinterface macro! */
rsRetVal (*Construct)(tcpsrv_t **ppThis);
rsRetVal (*ConstructFinalize)(tcpsrv_t __attribute__((unused)) *pThis);
rsRetVal (*Destruct)(tcpsrv_t **ppThis);
- void (*configureTCPListen)(tcpsrv_t*, char *cOptarg);
- rsRetVal (*SessAccept)(tcpsrv_t *pThis, tcps_sess_t **ppSess, netstrm_t *pStrm);
+ rsRetVal (*configureTCPListen)(tcpsrv_t*, uchar *pszPort);
+ //rsRetVal (*SessAccept)(tcpsrv_t *pThis, tcpLstnPortList_t*, tcps_sess_t **ppSess, netstrm_t *pStrm);
rsRetVal (*create_tcp_socket)(tcpsrv_t *pThis);
rsRetVal (*Run)(tcpsrv_t *pThis);
/* set methods */
@@ -94,8 +105,10 @@ BEGINinterface(tcpsrv) /* name must also be changed in ENDinterface macro! */
rsRetVal (*SetCBOnSessConstructFinalize)(tcpsrv_t*, rsRetVal (*) (void*));
/* added v5 */
rsRetVal (*SetSessMax)(tcpsrv_t *pThis, int iMaxSess); /* 2009-04-09 */
+ /* added v6 */
+ rsRetVal (*SetOnMsgReceive)(tcpsrv_t *pThis, rsRetVal (*OnMsgReceive)(tcps_sess_t*, uchar*, int)); /* 2009-05-24 */
ENDinterface(tcpsrv)
-#define tcpsrvCURR_IF_VERSION 5 /* increment whenever you change the interface structure! */
+#define tcpsrvCURR_IF_VERSION 6 /* increment whenever you change the interface structure! */
/* change for v4:
* - SetAddtlFrameDelim() added -- rgerhards, 2008-12-10
* - SetInputName() added -- rgerhards, 2008-12-10
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/DiagTalker.java b/tests/DiagTalker.java
new file mode 100644
index 00000000..e33a5867
--- /dev/null
+++ b/tests/DiagTalker.java
@@ -0,0 +1,43 @@
+//package com.rsyslog.diag;
+import java.io.*;
+import java.net.*;
+
+public class DiagTalker {
+ public static void main(String[] args) throws IOException {
+
+ Socket diagSocket = null;
+ PrintWriter out = null;
+ BufferedReader in = null;
+ final String host = "127.0.0.1";
+ final int port = 13500;
+
+ try {
+ diagSocket = new Socket(host, port);
+ out = new PrintWriter(diagSocket.getOutputStream(), true);
+ in = new BufferedReader(new InputStreamReader(
+ diagSocket.getInputStream()));
+ } catch (UnknownHostException e) {
+ System.err.println("can not resolve " + host + "!");
+ System.exit(1);
+ } catch (IOException e) {
+ System.err.println("Couldn't get I/O for "
+ + "the connection to: " + host + ".");
+ System.exit(1);
+ }
+
+ BufferedReader stdIn = new BufferedReader(
+ new InputStreamReader(System.in));
+ String userInput;
+
+ while ((userInput = stdIn.readLine()) != null) {
+ out.println(userInput);
+ System.out.println("imdiag returns: " + in.readLine());
+ }
+
+ out.close();
+ in.close();
+ stdIn.close();
+ diagSocket.close();
+ }
+}
+
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 02c67d81..e93aba10 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,12 +1,17 @@
-<<<<<<< HEAD:tests/Makefile.am
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 manytcp.sh diskqueue.sh imtcp-multiport.sh queue-persist.sh \
validation-run.sh
+if ENABLE_OMSTDOUT
+TESTS += omod-if-array.sh parsertest.sh inputname.sh fieldtest.sh
+endif
+
TESTS_ENVIRONMENT = RSYSLOG_MODDIR='$(abs_top_builddir)'/runtime/.libs/
-DISTCLEANFILES=rsyslog.pid
+DISTCLEANFILES=rsyslog.pid '$(abs_top_builddir)'/DiagTalker.class
test_files = testbench.h runtime-dummy.c
+check_JAVA = DiagTalker.java
+
EXTRA_DIST= 1.rstest 2.rstest 3.rstest err1.rstest \
validation-run.sh \
testsuites/invalid.conf \
@@ -14,7 +19,7 @@ EXTRA_DIST= 1.rstest 2.rstest 3.rstest err1.rstest \
cfg.sh \
cfg1.cfgtest \
cfg1.testin \
- cfg2.cfgtest \
+ cfg2.cfgtest \
cfg2.testin \
cfg3.cfgtest \
cfg3.testin \
@@ -25,21 +30,46 @@ EXTRA_DIST= 1.rstest 2.rstest 3.rstest err1.rstest \
NoExistFile.cfgtest \
testsuites/parse1.conf \
testsuites/1.parse1 \
- testsuites/rfc3164.parse1 \
+ testsuites/rfc3164.parse1 \
testsuites/rfc5424-1.parse1 \
testsuites/rfc5424-2.parse1 \
testsuites/rfc5424-3.parse1 \
testsuites/rfc5424-4.parse1 \
testsuites/omod-if-array.conf \
testsuites/1.omod-if-array \
+ killrsyslog.sh \
parsertest.sh \
+ diskqueue.sh \
+ testsuites/diskqueue.conf \
+ imtcp-multiport.sh \
+ testsuites/imtcp-multiport.conf \
+ manytcp.sh \
+ testsuites/manytcp.conf \
+ fieldtest.sh \
+ testsuites/field1.conf \
+ testsuites/1.field1 \
+ inputname.sh \
+ testsuites/inputname_imtcp.conf \
+ testsuites/1.inputname_imtcp_12514 \
+ testsuites/1.inputname_imtcp_12515 \
+ testsuites/1.inputname_imtcp_12516 \
omod-if-array.sh \
+ diag.sh \
+ testsuites/diag-common.conf \
+ queue-persist.sh \
+ queue-persist-drvr.sh \
+ testsuites/queue-persist.conf \
+ DiagTalker.java \
cfg.sh
ourtail_SOURCES = ourtail.c
+chkseq_SOURCES = chkseq.c
+
+tcpflood_SOURCES = tcpflood.c
+tcpflood_LDADD = $(SOL_LIBS)
-udptester_SOURCES = udptester.c getline.c
-udptester_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/diag.sh b/tests/diag.sh
new file mode 100755
index 00000000..ec2c1190
--- /dev/null
+++ b/tests/diag.sh
@@ -0,0 +1,83 @@
+# this shell script provides commands to the common diag system. It enables
+# test scripts to wait for certain conditions and initiate certain actions.
+# needs support in config file.
+# NOTE: this file should be included with "source diag.sh", as it otherwise is
+# not always able to convey back states to the upper-level test driver
+# begun 2009-05-27 by rgerhards
+# This file is part of the rsyslog project, released under GPLv3
+#set -o xtrace
+#export RSYSLOG_DEBUG="debug nostdout"
+#export RSYSLOG_DEBUGLOG="log"
+case $1 in
+ 'init') $srcdir/killrsyslog.sh # kill rsyslogd if it runs for some reason
+ cp $srcdir/testsuites/diag-common.conf diag-common.conf
+ rm -f rsyslogd.started work-*.conf
+ rm -f work rsyslog.out.log rsyslog.out.log.save # common work files
+ rm -rf test-spool
+ mkdir test-spool
+ ;;
+ 'exit') rm -f rsyslogd.started work-*.conf diag-common.conf
+ rm -f work rsyslog.out.log rsyslog.out.log.save # common work files
+ rm -rf test-spool
+ ;;
+ 'startup') # start rsyslogd with default params. $2 is the config file name to use
+ # returns only after successful startup
+ ../tools/rsyslogd -c4 -u2 -n -irsyslog.pid -M../runtime/.libs:../.libs -f$srcdir/testsuites/$2 &
+ $srcdir/diag.sh wait-startup
+ ;;
+ 'wait-startup') # wait for rsyslogd startup
+ while test ! -f rsyslogd.started; do
+ true
+ done
+ echo "rsyslogd started with pid " `cat rsyslog.pid`
+ ;;
+ 'wait-shutdown') # actually, we wait for rsyslog.pid to be deleted
+ while test -f rsyslog.pid; do
+ true
+ done
+ ;;
+ 'wait-queueempty') # wait for main message queue to be empty
+ echo WaitMainQueueEmpty | java -classpath $abs_top_builddir DiagTalker
+ ;;
+ 'shutdown-when-empty') # shut rsyslogd down when main queue is empty
+ $srcdir/diag.sh wait-queueempty
+ kill `cat rsyslog.pid`
+ # note: we do not wait for the actual termination!
+ ;;
+ 'shutdown-immediate') # shut rsyslogd down without emptying the queue
+ kill `cat rsyslog.pid`
+ # note: we do not wait for the actual termination!
+ ;;
+ 'tcpflood') # do a tcpflood run and check if it worked params are passed to tcpflood
+ ./tcpflood $2 $3 $4 $5 $6 $7 $8
+ if [ "$?" -ne "0" ]; then
+ echo "error during tcpflood! see rsyslog.out.log.save for what was written"
+ cp rsyslog.out.log rsyslog.out.log.save
+ exit 1
+ fi
+ ;;
+ 'injectmsg') # inject messages via our inject interface (imdiag)
+ echo injectmsg $2 $3 $4 $5 | java -classpath $abs_top_builddir DiagTalker
+ # TODO: some return state checking? (does it really make sense here?)
+ ;;
+ 'check-mainq-spool') # check if mainqueue spool files exist, if not abort (we just check .qi)
+ echo There must exist some files now:
+ ls -l test-spool
+ if test ! -f test-spool/mainq.qi; then
+ echo "error: mainq.qi does not exist where expected to do so!"
+ ls -l test-spool
+ exit 1
+ fi
+ ;;
+ 'seq-check') # do the usual sequence check to see if everything was properly received
+ rm -f work
+ sort < rsyslog.out.log > work
+ ./chkseq work $2 $3
+ if [ "$?" -ne "0" ]; then
+ rm -f work rsyslog.out.log
+ echo "sequence error detected"
+ exit 1
+ fi
+ ;;
+ *) echo "invalid argument" $1
+esac
diff --git a/tests/diskqueue.sh b/tests/diskqueue.sh
new file mode 100755
index 00000000..bf1a46fd
--- /dev/null
+++ b/tests/diskqueue.sh
@@ -0,0 +1,18 @@
+# Test for disk-only queue mode
+# This test checks if queue files can be correctly written
+# and read back, but it does not test the transition from
+# memory to disk mode for DA queues.
+# added 2009-04-17 by Rgerhards
+# This file is part of the rsyslog project, released under GPLv3
+# uncomment for debugging support:
+#set -o xtrace
+#export RSYSLOG_DEBUG="debug nostdout"
+#export RSYSLOG_DEBUGLOG="tmp"
+echo testing queue disk-only mode
+source $srcdir/diag.sh init
+source $srcdir/diag.sh startup diskqueue.conf
+# 20000 messages should be enough - the disk test is slow enough ;)
+source $srcdir/diag.sh tcpflood 127.0.0.1 13514 1 20000
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh seq-check 0 19999
+source $srcdir/diag.sh exit
diff --git a/tests/fieldtest.sh b/tests/fieldtest.sh
new file mode 100755
index 00000000..482fa143
--- /dev/null
+++ b/tests/fieldtest.sh
@@ -0,0 +1,13 @@
+echo test fieldtest via udp
+$srcdir/killrsyslog.sh # kill rsyslogd if it runs for some reason
+
+./nettester -tfield1 -iudp
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
+
+echo test fieldtest via tcp
+./nettester -tfield1 -itcp
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
diff --git a/tests/getline.c b/tests/getline.c
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/imtcp-multiport.sh b/tests/imtcp-multiport.sh
new file mode 100755
index 00000000..702f8834
--- /dev/null
+++ b/tests/imtcp-multiport.sh
@@ -0,0 +1,38 @@
+# Test for multiple ports in imtcp
+# This test checks if multiple tcp listener ports are correctly
+# handled by imtcp
+#
+# NOTE: this test must (and can) be enhanced when we merge in the
+# upgraded tcpflood program
+#
+# added 2009-05-22 by Rgerhards
+# This file is part of the rsyslog project, released under GPLv3
+echo testing imtcp multiple listeners
+source $srcdir/diag.sh init
+source $srcdir/diag.sh startup imtcp-multiport.conf
+source $srcdir/diag.sh tcpflood 127.0.0.1 13514 1 10000
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh seq-check 0 9999
+source $srcdir/diag.sh exit
+#
+#
+# ### now complete new cycle with other port ###
+#
+#
+source $srcdir/diag.sh init
+source $srcdir/diag.sh startup imtcp-multiport.conf
+source $srcdir/diag.sh tcpflood 127.0.0.1 13515 1 10000
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh seq-check 0 9999
+source $srcdir/diag.sh exit
+#
+#
+# ### now complete new cycle with other port ###
+#
+#
+source $srcdir/diag.sh init
+source $srcdir/diag.sh startup imtcp-multiport.conf
+source $srcdir/diag.sh tcpflood 127.0.0.1 13516 1 10000
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh seq-check 0 9999
+source $srcdir/diag.sh exit
diff --git a/tests/inputname.sh b/tests/inputname.sh
new file mode 100755
index 00000000..e1a58517
--- /dev/null
+++ b/tests/inputname.sh
@@ -0,0 +1,20 @@
+echo testing $InputTCPServerInputName directive
+$srcdir/killrsyslog.sh # kill rsyslogd if it runs for some reason
+
+echo port 12514
+./nettester -tinputname_imtcp_12514 -cinputname_imtcp -itcp -p12514
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
+
+echo port 12515
+./nettester -tinputname_imtcp_12515 -cinputname_imtcp -itcp -p12515
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
+
+echo port 12516
+./nettester -tinputname_imtcp_12516 -cinputname_imtcp -itcp -p12516
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
diff --git a/tests/killrsyslog.sh b/tests/killrsyslog.sh
new file mode 100755
index 00000000..b1be757b
--- /dev/null
+++ b/tests/killrsyslog.sh
@@ -0,0 +1,7 @@
+#check if rsyslog instance exists and, if so, kill it
+if [ -e "rsyslog.pid" ]
+then
+ echo rsyslog.pid exists, trying to shut down rsyslogd process `cat rsyslog.pid`.
+ kill `cat rsyslog.pid`
+ sleep 1
+fi
diff --git a/tests/manytcp.sh b/tests/manytcp.sh
new file mode 100755
index 00000000..c55eb9c0
--- /dev/null
+++ b/tests/manytcp.sh
@@ -0,0 +1,8 @@
+# test many concurrent tcp connections
+source $srcdir/diag.sh init
+source $srcdir/diag.sh startup manytcp.conf
+# the config file specifies exactly 1100 connections
+source $srcdir/diag.sh tcpflood 127.0.0.1 13514 1000 40000
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh seq-check 0 39999
+source $srcdir/diag.sh exit
diff --git a/tests/udptester.c b/tests/nettester.c
index a3c6658d..566f553b 100644
--- a/tests/udptester.c
+++ b/tests/nettester.c
@@ -43,12 +43,38 @@
#include <unistd.h>
#include <string.h>
#include <glob.h>
+#include <signal.h>
#include <netinet/in.h>
+#include <getopt.h>
#define EXIT_FAILURE 1
+#define INVALID_SOCKET -1
+/* Name of input file, must match $IncludeConfig in test suite .conf files */
+#define NETTEST_INPUT_CONF_FILE "nettest.input.conf" /* name of input file, must match $IncludeConfig in .conf files */
+typedef enum { inputUDP, inputTCP } inputMode_t;
+inputMode_t inputMode = inputTCP; /* input for which tests are to be run */
+static pid_t rsyslogdPid = 0; /* pid of rsyslog instance being tested */
static char *srcdir; /* global $srcdir, set so that we can run outside of "make check" */
-static char *testSuite; /* name of current test suite */
+static char *testSuite = NULL; /* name of current test suite */
+static int iPort = 12514; /* port which shall be used for sending data */
+static char* pszCustomConf = NULL; /* custom config file, use -c conf to specify */
+static int verbose = 0; /* verbose output? -v option */
+
+
+/* provide user-friednly name of input mode
+ */
+static char *inputMode2Str(inputMode_t mode)
+{
+ char *pszMode;
+
+ if(mode == inputUDP)
+ pszMode = "udp";
+ else
+ pszMode = "tcp";
+
+ return pszMode;
+}
void readLine(int fd, char *ln)
@@ -64,6 +90,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(iPort);
+ 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.
*/
@@ -80,7 +159,7 @@ udpSend(char *buf, int lenBuf)
memset((char *) &si_other, 0, sizeof(si_other));
si_other.sin_family = AF_INET;
- si_other.sin_port = htons(12514);
+ si_other.sin_port = htons(iPort);
if(inet_aton("127.0.0.1", &si_other.sin_addr)==0) {
fprintf(stderr, "inet_aton() failed\n");
return(1);
@@ -107,12 +186,17 @@ 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 };
+ /* debug aide...
+ char *newenviron[] = { "RSYSLOG_DEBUG=debug nostdout",
+ "RSYSLOG_DEBUGLOG=tmp", NULL };
+ */
- sprintf(confFile, "-f%s/testsuites/%s.conf", srcdir, configFile);
+ sprintf(confFile, "-f%s/testsuites/%s.conf", srcdir,
+ (pszCustomConf == NULL) ? configFile : pszCustomConf);
newargv[1] = confFile;
if (pipe(pipefd) == -1) {
@@ -173,9 +257,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
@@ -227,12 +316,12 @@ doTests(int fd, char *files)
++iTests;
/* all regular files are run through the test logic. Symlinks don't work. */
if(S_ISREG(fileInfo.st_mode)) { /* config file */
- printf("processing test case '%s' ... ", testFile);
+ if(verbose) printf("processing test case '%s' ... ", testFile);
ret = processTestFile(fd, testFile);
if(ret == 0) {
- printf("successfully completed\n");
+ if(verbose) printf("successfully completed\n");
} else {
- printf("failed!\n");
+ if(verbose) printf("failed!\n");
++iFailed;
}
}
@@ -249,6 +338,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 +359,72 @@ doTests(int fd, char *files)
int main(int argc, char *argv[])
{
int fd;
- pid_t pid;
- int status;
+ int opt;
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");
+ while((opt = getopt(argc, argv, "c:i:p:t:v")) != EOF) {
+ switch((char)opt) {
+ case 'c':
+ pszCustomConf = optarg;
+ break;
+ case 'i':
+ if(!strcmp(optarg, "udp"))
+ inputMode = inputUDP;
+ else if(!strcmp(optarg, "tcp"))
+ inputMode = inputTCP;
+ else {
+ printf("error: unsupported input mode '%s'\n", optarg);
+ exit(1);
+ }
+ break;
+ case 'p':
+ iPort = atoi(optarg);
+ break;
+ case 't':
+ testSuite = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:printf("Invalid call of nettester, invalid option '%c'.\n", opt);
+ printf("Usage: nettester -ttestsuite-name -iudp|tcp [-pport] [-ccustomConfFile] \n");
+ exit(1);
+ }
+ }
+
+ if(testSuite == NULL) {
+ printf("error: no testsuite given, need to specify -t testsuite!\n");
exit(1);
}
- testSuite = argv[1];
+ atexit(doAtExit);
if((srcdir = getenv("srcdir")) == NULL)
srcdir = ".";
- printf("Start of udptester run ($srcdir=%s, testsuite=%s)\n", srcdir, testSuite);
+ if(verbose) printf("Start of nettester run ($srcdir=%s, testsuite=%s, input=%s/%d)\n",
+ srcdir, testSuite, inputMode2Str(inputMode), iPort);
+
+ /* create input config file */
+ if((fp = fopen(NETTEST_INPUT_CONF_FILE, "w")) == NULL) {
+ perror(NETTEST_INPUT_CONF_FILE);
+ printf("error opening input configuration file\n");
+ exit(1);
+ }
+ if(inputMode == inputUDP) {
+ fputs("$ModLoad ../plugins/imudp/.libs/imudp\n", fp);
+ fprintf(fp, "$UDPServerRun %d\n", iPort);
+ } else {
+ fputs("$ModLoad ../plugins/imtcp/.libs/imtcp\n", fp);
+ fprintf(fp, "$InputTCPServerRun %d\n", iPort);
+ }
+ fclose(fp);
- openPipe(argv[1], &pid, &fd);
+ /* start to be tested rsyslogd */
+ openPipe(testSuite, &rsyslogdPid, &fd);
readLine(fd, buf);
/* generate filename */
@@ -285,9 +432,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");
+ if(verbose) printf("End of nettester run (%d).\n", ret);
exit(ret);
}
diff --git a/tests/omod-if-array.sh b/tests/omod-if-array.sh
index cac08928..2c2a8ef3 100755
--- a/tests/omod-if-array.sh
+++ b/tests/omod-if-array.sh
@@ -1 +1,14 @@
-./udptester omod-if-array
+echo test omod-if-array via udp
+$srcdir/killrsyslog.sh # kill rsyslogd if it runs for some reason
+
+./nettester -tomod-if-array -iudp -p4711
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
+
+echo test omod-if-array via tcp
+./nettester -tomod-if-array -itcp
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
+
diff --git a/tests/parsertest.sh b/tests/parsertest.sh
index e7985bb0..afdb9469 100755
--- a/tests/parsertest.sh
+++ b/tests/parsertest.sh
@@ -1 +1,13 @@
-./udptester parse1
+echo test parsertest via udp
+$srcdir/killrsyslog.sh # kill rsyslogd if it runs for some reason
+
+./nettester -tparse1 -iudp
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
+
+echo test parsertest via tcp
+./nettester -tparse1 -itcp
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
diff --git a/tests/queue-persist-drvr.sh b/tests/queue-persist-drvr.sh
new file mode 100755
index 00000000..ea5386a7
--- /dev/null
+++ b/tests/queue-persist-drvr.sh
@@ -0,0 +1,28 @@
+# Test for queue data persisting at shutdown. The
+# plan is to start an instance, emit some data, do a relatively
+# fast shutdown and then re-start the engine to process the
+# remaining data.
+# added 2009-05-27 by Rgerhards
+# This file is part of the rsyslog project, released under GPLv3
+# uncomment for debugging support:
+echo testing memory queue persisting to disk, mode $1
+source $srcdir/diag.sh init
+
+# prepare config
+echo \$MainMsgQueueType $1 > work-queuemode.conf
+echo "*.* :omtesting:sleep 0 1000" > work-delay.conf
+
+# inject 5000 msgs, so that we do not hit the high watermark
+source $srcdir/diag.sh startup queue-persist.conf
+source $srcdir/diag.sh injectmsg 0 5000
+$srcdir/diag.sh shutdown-immediate
+$srcdir/diag.sh wait-shutdown
+source $srcdir/diag.sh check-mainq-spool
+
+# restart engine and have rest processed
+#remove delay
+echo "#" > work-delay.conf
+source $srcdir/diag.sh startup queue-persist.conf
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh seq-check 0 4999
+source $srcdir/diag.sh exit
diff --git a/tests/queue-persist.sh b/tests/queue-persist.sh
new file mode 100755
index 00000000..999655b1
--- /dev/null
+++ b/tests/queue-persist.sh
@@ -0,0 +1,11 @@
+# Test for queue data persisting at shutdown. We use the actual driver
+# to carry out multiple tests with different queue modes
+# added 2009-05-27 by Rgerhards
+# This file is part of the rsyslog project, released under GPLv3
+source $srcdir/queue-persist-drvr.sh LinkedList
+source $srcdir/queue-persist-drvr.sh FixedArray
+# the disk test should not fail, however, the config is extreme and using
+# it more or less is a config error
+source $srcdir/queue-persist-drvr.sh Disk
+# we do not test Direct mode because this absolute can not work in direct mode
+# (maybe we should do a fail-type of test?)
diff --git a/tests/rscript.c b/tests/rscript.c
index 6b232f5f..ce81491c 100644
--- a/tests/rscript.c
+++ b/tests/rscript.c
@@ -24,6 +24,7 @@
*/
#include "config.h"
#include <stdio.h>
+#include <string.h>
#include <glob.h>
#include <sys/stat.h>
diff --git a/tests/tcpflood.c b/tests/tcpflood.c
new file mode 100644
index 00000000..c3c9c871
--- /dev/null
+++ b/tests/tcpflood.c
@@ -0,0 +1,288 @@
+/* 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];
+
+ 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/1.field1 b/tests/testsuites/1.field1
new file mode 100644
index 00000000..54751171
--- /dev/null
+++ b/tests/testsuites/1.field1
@@ -0,0 +1,3 @@
+<167>Mar 6 16:57:54 172.20.245.8 %PIX-7-710005: DROP_url_www.sina.com.cn:IN=eth1 OUT=eth0 SRC=192.168.10.78 DST=61.172.201.194 LEN=1182 TOS=0x00 PREC=0x00 TTL=63 ID=14368 DF PROTO=TCP SPT=33343 DPT=80 WINDOW=92 RES=0x00 ACK PSH URGP=0
+DROP_url_www.sina.com.cn:IN=eth1
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/1.inputname_imtcp_12514 b/tests/testsuites/1.inputname_imtcp_12514
new file mode 100644
index 00000000..178b1724
--- /dev/null
+++ b/tests/testsuites/1.inputname_imtcp_12514
@@ -0,0 +1,3 @@
+<167>Mar 6 16:57:54 172.20.245.8 %PIX-7-710005: MSG
+12514
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/1.inputname_imtcp_12515 b/tests/testsuites/1.inputname_imtcp_12515
new file mode 100644
index 00000000..d616098b
--- /dev/null
+++ b/tests/testsuites/1.inputname_imtcp_12515
@@ -0,0 +1,3 @@
+<167>Mar 6 16:57:54 172.20.245.8 %PIX-7-710005: MSG
+12515
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/1.inputname_imtcp_12516 b/tests/testsuites/1.inputname_imtcp_12516
new file mode 100644
index 00000000..8e6997ce
--- /dev/null
+++ b/tests/testsuites/1.inputname_imtcp_12516
@@ -0,0 +1,3 @@
+<167>Mar 6 16:57:54 172.20.245.8 %PIX-7-710005: MSG
+12516
+#Only the first two lines are important, you may place anything behind them!
diff --git a/tests/testsuites/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/diag-common.conf b/tests/testsuites/diag-common.conf
new file mode 100644
index 00000000..9e9e28fe
--- /dev/null
+++ b/tests/testsuites/diag-common.conf
@@ -0,0 +1,16 @@
+# This is a config include file. It sets up rsyslog so that the
+# diag system can successfully be used. Also, it generates a file
+# "rsyslogd.started" after rsyslogd is initialized. This config file
+# should be included in all tests that intend to use common code for
+# controlling the daemon.
+# NOTE: we assume that rsyslogd's current working directory is
+# ./tests (or the distcheck equivalent), in particlular that this
+# config file resides in the testsuites subdirectory.
+# rgerhards, 2009-05-27
+$ModLoad ../plugins/imdiag/.libs/imdiag
+$IMDiagServerRun 13500
+
+$template startupfile,"rsyslogd.started" # trick to use relative path names!
+:syslogtag, contains, "rsyslogd" ?startupfile
+
+$ErrorMessagesToStderr off
diff --git a/tests/testsuites/diskqueue.conf b/tests/testsuites/diskqueue.conf
new file mode 100644
index 00000000..a992c5a5
--- /dev/null
+++ b/tests/testsuites/diskqueue.conf
@@ -0,0 +1,16 @@
+# Test for queue disk mode (see .sh file for details)
+# rgerhards, 2009-04-17
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+$InputTCPServerRun 13514
+
+# set spool locations and switch queue to disk-only mode
+$WorkDirectory test-spool
+$MainMsgQueueFilename mainq
+$MainMsgQueueType disk
+
+$template outfmt,"%msg:F,58:2%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+:msg, contains, "msgnum:" ?dynfile;outfmt
diff --git a/tests/testsuites/field1.conf b/tests/testsuites/field1.conf
new file mode 100644
index 00000000..1ff833dd
--- /dev/null
+++ b/tests/testsuites/field1.conf
@@ -0,0 +1,8 @@
+$ModLoad ../plugins/omstdout/.libs/omstdout
+$IncludeConfig nettest.input.conf # This picks the to be tested input from the test driver!
+
+$ErrorMessagesToStderr off
+
+# use a special format that we can easily parse in expect
+$template fmt,"%msg:F,32:2%\n"
+*.* :omstdout:;fmt
diff --git a/tests/testsuites/imtcp-multiport.conf b/tests/testsuites/imtcp-multiport.conf
new file mode 100644
index 00000000..ccdc15fb
--- /dev/null
+++ b/tests/testsuites/imtcp-multiport.conf
@@ -0,0 +1,13 @@
+# Test for queue disk mode (see .sh file for details)
+# rgerhards, 2009-05-22
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+$InputTCPServerRun 13514
+$InputTCPServerRun 13515
+$InputTCPServerRun 13516
+
+$template outfmt,"%msg:F,58:2%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+:msg, contains, "msgnum:" ?dynfile;outfmt
diff --git a/tests/testsuites/inputname_imtcp.conf b/tests/testsuites/inputname_imtcp.conf
new file mode 100644
index 00000000..a25eab37
--- /dev/null
+++ b/tests/testsuites/inputname_imtcp.conf
@@ -0,0 +1,19 @@
+# This is a special case, thus we define the inputs ourselfs
+$ModLoad ../plugins/omstdout/.libs/omstdout
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+
+$InputTCPServerInputname 12514
+$InputTCPServerRun 12514
+
+$InputTCPServerInputname 12515
+$InputTCPServerRun 12515
+
+$InputTCPServerInputname 12516
+$InputTCPServerRun 12516
+
+$ErrorMessagesToStderr off
+
+# use a special format that we can easily parse in expect
+$template fmt,"%inputname%\n"
+*.* :omstdout:;fmt
diff --git a/tests/testsuites/manytcp.conf b/tests/testsuites/manytcp.conf
new file mode 100644
index 00000000..eb9db257
--- /dev/null
+++ b/tests/testsuites/manytcp.conf
@@ -0,0 +1,13 @@
+# Test for tcp "flood" testing
+# rgerhards, 2009-04-08
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 10000
+$MaxOpenFiles 2000
+$InputTCPMaxSessions 1100
+$InputTCPServerRun 13514
+
+$template outfmt,"%msg:F,58:2%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+:msg, contains, "msgnum:" ?dynfile;outfmt
diff --git a/tests/testsuites/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/tests/testsuites/queue-persist.conf b/tests/testsuites/queue-persist.conf
new file mode 100644
index 00000000..81ee1be5
--- /dev/null
+++ b/tests/testsuites/queue-persist.conf
@@ -0,0 +1,23 @@
+# Test for persisting messages on shutdown
+# rgerhards, 2009-04-17
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$MainMsgQueueTimeoutShutdown 1
+$MainMsgQueueSaveOnShutdown on
+$InputTCPServerRun 13514
+
+$ModLoad ../plugins/omtesting/.libs/omtesting
+
+$ErrorMessagesToStderr off
+
+# set spool locations and switch queue to disk-only mode
+$WorkDirectory test-spool
+$MainMsgQueueFilename mainq
+$IncludeConfig work-queuemode.conf
+
+$template outfmt,"%msg:F,58:2%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+:msg, contains, "msgnum:" ?dynfile;outfmt
+
+$IncludeConfig work-delay.conf
diff --git a/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 2ff868a9..b43b7a37 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
@@ -136,6 +137,7 @@
#include "datetime.h"
#include "parser.h"
#include "sysvar.h"
+#include "unicode-helper.h"
/* definitions for objects we access */
DEFobjCurrIf(obj)
@@ -283,6 +285,7 @@ static int gidDropPriv = 0; /* group-id to which priveleges should be dropped to
extern int errno;
+static uchar *pszConfDAGFile = NULL; /* name of config DAG file, non-NULL means generate one */
/* main message queue and its configuration parameters */
static qqueue_t *pMsgQueue = NULL; /* the main message queue */
static int iMainMsgQueueSize = 10000; /* size of the main message queue above */
@@ -347,10 +350,8 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
bDebugPrintModuleList = 1;
bEscapeCCOnRcv = 1; /* default is to escape control characters */
bReduceRepeatMsgs = 0;
- if(pszMainMsgQFName != NULL) {
- free(pszMainMsgQFName);
- pszMainMsgQFName = NULL;
- }
+ free(pszMainMsgQFName);
+ pszMainMsgQFName = NULL;
iMainMsgQueueSize = 10000;
iMainMsgQHighWtrMark = 8000;
iMainMsgQLowWtrMark = 2000;
@@ -409,6 +410,26 @@ static int usage(void)
}
+/* ------------------------------ some support functions for imdiag ------------------------------ *
+ * This is a bit dirty, but the only way to do it, at least with reasonable effort.
+ * rgerhards, 2009-05-25
+ */
+
+/* return back the approximate current number of messages in the main message queue
+ */
+rsRetVal
+diagGetMainMsgQSize(int *piSize)
+{
+ DEFiRet;
+ assert(piSize != NULL);
+ *piSize = pMsgQueue->iQueueSize;
+ RETiRet;
+}
+
+
+/* ------------------------------ end support functions for imdiag ------------------------------ */
+
+
/* function to destruct a selector_t object
* rgerhards, 2007-08-01
*/
@@ -618,7 +639,7 @@ static inline rsRetVal printline(uchar *hname, uchar *hnameIP, uchar *msg, int f
CHKiRet(msgConstructWithTime(&pMsg, stTime, ttGenTime));
}
if(pszInputName != NULL)
- MsgSetInputName(pMsg, (char*) pszInputName);
+ MsgSetInputName(pMsg, pszInputName);
MsgSetFlowControlType(pMsg, flowCtlType);
MsgSetRawMsg(pMsg, (char*)msg);
@@ -647,8 +668,8 @@ static inline rsRetVal printline(uchar *hname, uchar *hnameIP, uchar *msg, int f
* being the local host). rgerhards 2004-11-16
*/
if((pMsg->msgFlags & PARSE_HOSTNAME) == 0)
- MsgSetHOSTNAME(pMsg, (char*)hname);
- MsgSetRcvFrom(pMsg, (char*)hname);
+ MsgSetHOSTNAME(pMsg, hname);
+ MsgSetRcvFrom(pMsg, hname);
CHKiRet(MsgSetRcvFromIP(pMsg, hnameIP));
/* rgerhards 2004-11-19: well, well... we've now seen that we
@@ -926,12 +947,12 @@ logmsgInternal(int iErr, int pri, uchar *msg, int flags)
DEFiRet;
CHKiRet(msgConstruct(&pMsg));
- MsgSetInputName(pMsg, "rsyslogd");
+ MsgSetInputName(pMsg, UCHAR_CONSTANT("rsyslogd"));
MsgSetUxTradMsg(pMsg, (char*)msg);
MsgSetRawMsg(pMsg, (char*)msg);
- MsgSetHOSTNAME(pMsg, (char*)glbl.GetLocalHostName());
- MsgSetRcvFrom(pMsg, (char*)glbl.GetLocalHostName());
- MsgSetRcvFromIP(pMsg, (uchar*)"127.0.0.1");
+ MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName());
+ MsgSetRcvFrom(pMsg, glbl.GetLocalHostName());
+ MsgSetRcvFromIP(pMsg, UCHAR_CONSTANT("127.0.0.1"));
/* check if we have an error code associated and, if so,
* adjust the tag. -- r5gerhards, 2008-06-27
*/
@@ -1231,9 +1252,9 @@ msgConsumer(void __attribute__((unused)) *notNeeded, void *pUsr)
* SP-terminated or any other error occurs.
* rger, 2005-11-24
*/
-static int parseRFCField(char **pp2parse, char *pResult)
+static int parseRFCField(uchar **pp2parse, uchar *pResult)
{
- char *p2parse;
+ uchar *p2parse;
int iRet = 0;
assert(pp2parse != NULL);
@@ -1269,9 +1290,9 @@ static int parseRFCField(char **pp2parse, char *pResult)
* SP-terminated or any other error occurs.
* rger, 2005-11-24
*/
-static int parseRFCStructuredData(char **pp2parse, char *pResult)
+static int parseRFCStructuredData(uchar **pp2parse, uchar *pResult)
{
- char *p2parse;
+ uchar *p2parse;
int bCont = 1;
int iRet = 0;
@@ -1343,14 +1364,14 @@ static int parseRFCStructuredData(char **pp2parse, char *pResult)
*/
int parseRFCSyslogMsg(msg_t *pMsg, int flags)
{
- char *p2parse;
- char *pBuf;
+ uchar *p2parse;
+ uchar *pBuf;
int bContParse = 1;
BEGINfunc
assert(pMsg != NULL);
assert(pMsg->pszUxTradMsg != NULL);
- p2parse = (char*) pMsg->pszUxTradMsg;
+ p2parse = pMsg->pszUxTradMsg;
/* do a sanity check on the version and eat it */
assert(p2parse[0] == '1' && p2parse[1] == ' ');
@@ -1361,7 +1382,7 @@ int parseRFCSyslogMsg(msg_t *pMsg, int flags)
* message, so we can not run into any troubles. I think this is
* more wise then to use individual buffers.
*/
- if((pBuf = malloc(sizeof(char)* strlen(p2parse) + 1)) == NULL)
+ if((pBuf = malloc(sizeof(uchar) * ustrlen(p2parse) + 1)) == NULL)
return 1;
/* IMPORTANT NOTE:
@@ -1396,29 +1417,29 @@ int parseRFCSyslogMsg(msg_t *pMsg, int flags)
/* APP-NAME */
if(bContParse) {
parseRFCField(&p2parse, pBuf);
- MsgSetAPPNAME(pMsg, pBuf);
+ MsgSetAPPNAME(pMsg, (char*)pBuf);
}
/* PROCID */
if(bContParse) {
parseRFCField(&p2parse, pBuf);
- MsgSetPROCID(pMsg, pBuf);
+ MsgSetPROCID(pMsg, (char*)pBuf);
}
/* MSGID */
if(bContParse) {
parseRFCField(&p2parse, pBuf);
- MsgSetMSGID(pMsg, pBuf);
+ MsgSetMSGID(pMsg, (char*)pBuf);
}
/* STRUCTURED-DATA */
if(bContParse) {
parseRFCStructuredData(&p2parse, pBuf);
- MsgSetStructuredData(pMsg, pBuf);
+ MsgSetStructuredData(pMsg, (char*)pBuf);
}
/* MSG */
- MsgSetMSG(pMsg, p2parse);
+ MsgSetMSG(pMsg, (char*)p2parse);
free(pBuf);
ENDfunc
@@ -1441,7 +1462,7 @@ int parseRFCSyslogMsg(msg_t *pMsg, int flags)
*/
int parseLegacySyslogMsg(msg_t *pMsg, int flags)
{
- char *p2parse;
+ uchar *p2parse;
char *pBuf;
char *pWork;
cstr_t *pStrB;
@@ -1451,7 +1472,7 @@ int parseLegacySyslogMsg(msg_t *pMsg, int flags)
assert(pMsg != NULL);
assert(pMsg->pszUxTradMsg != NULL);
- p2parse = (char*) pMsg->pszUxTradMsg;
+ p2parse = pMsg->pszUxTradMsg;
/* Check to see if msg contains a timestamp. We start by assuming
* that the message timestamp is the time of reciption (which we
@@ -1506,7 +1527,7 @@ int parseLegacySyslogMsg(msg_t *pMsg, int flags)
/* the memory allocated is far too much in most cases. But on the plus side,
* it is quite fast... - rgerhards, 2007-09-20
*/
- if((pBuf = malloc(sizeof(char)* (strlen(p2parse) +1))) == NULL)
+ if((pBuf = malloc(sizeof(char)* (ustrlen(p2parse) +1))) == NULL)
return 1;
pWork = pBuf;
/* this is the actual parsing loop */
@@ -1612,7 +1633,7 @@ int parseLegacySyslogMsg(msg_t *pMsg, int flags)
}
/* The rest is the actual MSG */
- MsgSetMSG(pMsg, p2parse);
+ MsgSetMSG(pMsg, (char*)p2parse);
ENDfunc
return 0; /* all ok */
@@ -1922,10 +1943,10 @@ static void doDie(int sig)
# define MSG1 "DoDie called.\n"
# define MSG2 "DoDie called 5 times - unconditional exit\n"
static int iRetries = 0; /* debug aid */
- if(Debug || NoFork)
+ if(Debug)
write(1, MSG1, sizeof(MSG1) - 1);
if(iRetries++ == 4) {
- if(Debug || NoFork)
+ if(Debug)
write(1, MSG2, sizeof(MSG2) - 1);
abort();
}
@@ -1943,10 +1964,9 @@ static void doDie(int sig)
static void
freeAllDynMemForTermination(void)
{
- if(pszMainMsgQFName != NULL)
- free(pszMainMsgQFName);
- if(pModDir != NULL)
- free(pModDir);
+ free(pszMainMsgQFName);
+ free(pModDir);
+ free(pszConfDAGFile);
}
@@ -2078,6 +2098,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)
{
@@ -2188,6 +2234,184 @@ static void freeSelectors(void)
}
+/* helper to generateConfigDAG, to print out all actions via
+ * the llExecFunc() facility.
+ * rgerhards, 2007-08-02
+ */
+struct dag_info {
+ FILE *fp; /* output file */
+ int iActUnit; /* current action unit number */
+ int iAct; /* current action in unit */
+ int bDiscarded; /* message discarded (config error) */
+ };
+DEFFUNC_llExecFunc(generateConfigDAGAction)
+{
+ action_t *pAction;
+ uchar *pszModName;
+ uchar *pszVertexName;
+ struct dag_info *pDagInfo;
+ DEFiRet;
+
+ pDagInfo = (struct dag_info*) pParam;
+ pAction = (action_t*) pData;
+
+ pszModName = module.GetStateName(pAction->pMod);
+
+ /* vertex */
+ if(pAction->pszName == NULL) {
+ if(!strcmp((char*)pszModName, "builtin-discard"))
+ pszVertexName = (uchar*)"discard";
+ else
+ pszVertexName = pszModName;
+ } else {
+ pszVertexName = pAction->pszName;
+ }
+
+ fprintf(pDagInfo->fp, "\tact%d_%d\t\t[label=\"%s\"%s%s]\n",
+ pDagInfo->iActUnit, pDagInfo->iAct, pszVertexName,
+ pDagInfo->bDiscarded ? " style=dotted color=red" : "",
+ (pAction->pQueue->qType == QUEUETYPE_DIRECT) ? "" : " shape=hexagon"
+ );
+
+ /* edge */
+ if(pDagInfo->iAct == 0) {
+ } else {
+ fprintf(pDagInfo->fp, "\tact%d_%d -> act%d_%d[%s%s]\n",
+ pDagInfo->iActUnit, pDagInfo->iAct - 1,
+ pDagInfo->iActUnit, pDagInfo->iAct,
+ pDagInfo->bDiscarded ? " style=dotted color=red" : "",
+ pAction->bExecWhenPrevSusp ? " label=\"only if\\nsuspended\"" : "" );
+ }
+
+ /* check for discard */
+ if(!strcmp((char*) pszModName, "builtin-discard")) {
+ fprintf(pDagInfo->fp, "\tact%d_%d\t\t[shape=box]\n",
+ pDagInfo->iActUnit, pDagInfo->iAct);
+ pDagInfo->bDiscarded = 1;
+ }
+
+
+ ++pDagInfo->iAct;
+
+ RETiRet;
+}
+
+
+/* create config DAG
+ * This functions takes a rsyslog config and produces a .dot file for use
+ * with graphviz (http://www.graphviz.org). This is done in an effort to
+ * document, and also potentially troubleshoot, configurations. Plus, I
+ * consider it a nice feature to explain some concepts. Note that the
+ * current version only produces a graph with relatively little information.
+ * This is a foundation that may be later expanded (if it turns out to be
+ * useful enough).
+ * rgerhards, 2009-05-11
+ */
+static rsRetVal
+generateConfigDAG(uchar *pszDAGFile)
+{
+ selector_t *f;
+ FILE *fp;
+ int iActUnit = 1;
+ int bHasFilter = 0; /* filter associated with this action unit? */
+ int bHadFilter;
+ int i;
+ struct dag_info dagInfo;
+ char *pszFilterName;
+ char szConnectingNode[64];
+ DEFiRet;
+
+ assert(pszDAGFile != NULL);
+
+ if((fp = fopen((char*) pszDAGFile, "w")) == NULL) {
+ logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)
+ "configuraton graph output file could not be opened, none generated", 0);
+ ABORT_FINALIZE(RS_RET_FILENAME_INVALID);
+ }
+
+ dagInfo.fp = fp;
+
+ /* from here on, we assume writes go well. This here is a really
+ * unimportant utility function and if something goes wrong, it has
+ * almost no effect. So let's not overdo this...
+ */
+ fprintf(fp, "# graph created by rsyslog " VERSION "\n\n"
+ "# use the dot tool from http://www.graphviz.org to visualize!\n"
+ "digraph rsyslogConfig {\n"
+ "\tinputs [shape=tripleoctagon]\n"
+ "\tinputs -> act0_0\n"
+ "\tact0_0 [label=\"main\\nqueue\" shape=hexagon]\n"
+ /*"\tmainq -> act1_0\n"*/
+ );
+ strcpy(szConnectingNode, "act0_0");
+ dagInfo.bDiscarded = 0;
+
+ for(f = Files; f != NULL ; f = f->f_next) {
+ /* BSD-Style filters are currently ignored */
+ bHadFilter = bHasFilter;
+ if(f->f_filter_type == FILTER_PRI) {
+ bHasFilter = 0;
+ for (i = 0; i <= LOG_NFACILITIES; i++)
+ if (f->f_filterData.f_pmask[i] != 0xff) {
+ bHasFilter = 1;
+ break;
+ }
+ } else {
+ bHasFilter = 1;
+ }
+
+ /* we know we have a filter, so it can be false */
+ switch(f->f_filter_type) {
+ case FILTER_PRI:
+ pszFilterName = "pri filter";
+ break;
+ case FILTER_PROP:
+ pszFilterName = "property filter";
+ break;
+ case FILTER_EXPR:
+ pszFilterName = "script filter";
+ break;
+ }
+
+ /* write action unit node */
+ if(bHasFilter) {
+ fprintf(fp, "\t%s -> act%d_end\t[label=\"%s:\\nfalse\"]\n",
+ szConnectingNode, iActUnit, pszFilterName);
+ fprintf(fp, "\t%s -> act%d_0\t[label=\"%s:\\ntrue\"]\n",
+ szConnectingNode, iActUnit, pszFilterName);
+ fprintf(fp, "\tact%d_end\t\t\t\t[shape=point]\n", iActUnit);
+ snprintf(szConnectingNode, sizeof(szConnectingNode), "act%d_end", iActUnit);
+ } else {
+ fprintf(fp, "\t%s -> act%d_0\t[label=\"no filter\"]\n",
+ szConnectingNode, iActUnit);
+ snprintf(szConnectingNode, sizeof(szConnectingNode), "act%d_0", iActUnit);
+ }
+
+ /* draw individual nodes */
+ dagInfo.iActUnit = iActUnit;
+ dagInfo.iAct = 0;
+ dagInfo.bDiscarded = 0;
+ llExecFunc(&f->llActList, generateConfigDAGAction, &dagInfo); /* actions */
+
+ /* finish up */
+ if(bHasFilter && !dagInfo.bDiscarded) {
+ fprintf(fp, "\tact%d_%d -> %s\n",
+ iActUnit, dagInfo.iAct - 1, szConnectingNode);
+ }
+
+ ++iActUnit;
+ }
+
+ fprintf(fp, "\t%s -> act%d_0\n", szConnectingNode, iActUnit);
+ fprintf(fp, "\tact%d_0\t\t[label=discard shape=box]\n"
+ "}\n", iActUnit);
+ fclose(fp);
+
+finalize_it:
+ RETiRet;
+}
+
+
/* helper to dbPrintInitInfo, to print out all actions via
* the llExecFunc() facility.
* rgerhards, 2007-08-02
@@ -2201,6 +2425,7 @@ DEFFUNC_llExecFunc(dbgPrintInitInfoAction)
RETiRet;
}
+
/* print debug information as part of init(). This pretty much
* outputs the whole config of rsyslogd. I've moved this code
* out of init() to clean it somewhat up.
@@ -2208,7 +2433,7 @@ DEFFUNC_llExecFunc(dbgPrintInitInfoAction)
*/
static void dbgPrintInitInfo(void)
{
- register selector_t *f;
+ selector_t *f;
int iSelNbr = 1;
int i;
@@ -2448,6 +2673,10 @@ init(void)
}
}
+ /* check if we need to generate a config DAG and, if so, do that */
+ if(pszConfDAGFile != NULL)
+ generateConfigDAG(pszConfDAGFile);
+
/* we are done checking the config - now validate if we should actually run or not.
* If not, terminate. -- rgerhards, 2008-07-25
*/
@@ -2460,7 +2689,6 @@ init(void)
}
/* switch the message object to threaded operation, if necessary */
-/* TODO:XXX: I think we must do this also if we have action queues! -- rgerhards, 2009-01-26 */
if(MainMsgQueType == QUEUETYPE_DIRECT || iMainMsgQueueNumWorkers > 1) {
MsgEnableThreadSafety();
}
@@ -2883,11 +3111,13 @@ static rsRetVal loadBuildInModules(void)
CHKiRet(regCfSysLineHdlr((uchar *)"modload", 0, eCmdHdlrCustomHandler, conf.doModLoad, NULL, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"includeconfig", 0, eCmdHdlrCustomHandler, conf.doIncludeLine, NULL, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"umask", 0, eCmdHdlrFileCreateMode, setUmask, NULL, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"maxopenfiles", 0, eCmdHdlrInt, setMaxFiles, NULL, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"debugprinttemplatelist", 0, eCmdHdlrBinary, NULL, &bDebugPrintTemplateList, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"debugprintmodulelist", 0, eCmdHdlrBinary, NULL, &bDebugPrintModuleList, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"debugprintcfsyslinehandlerlist", 0, eCmdHdlrBinary,
NULL, &bDebugPrintCfSysLineHandlerList, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"moddir", 0, eCmdHdlrGetWord, NULL, &pModDir, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"generateconfiggraph", 0, eCmdHdlrGetWord, NULL, &pszConfDAGFile, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"errormessagestostderr", 0, eCmdHdlrBinary, NULL, &bErrMsgToStderr, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"maxmessagesize", 0, eCmdHdlrSize, setMaxMsgSize, NULL, NULL));
@@ -3104,6 +3334,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);
@@ -3603,18 +3834,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);