diff options
128 files changed, 4696 insertions, 667 deletions
@@ -35,3 +35,5 @@ log logfile debug core.* +shave +shave-libtool @@ -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 @@ -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; @@ -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 @@ -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 Binary files differnew file mode 100644 index 00000000..fd614d8c --- /dev/null +++ b/doc/dataflow.png 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 Binary files differnew file mode 100644 index 00000000..6d1b373f --- /dev/null +++ b/doc/direct_queue0.png diff --git a/doc/direct_queue1.png b/doc/direct_queue1.png Binary files differnew file mode 100644 index 00000000..503f8151 --- /dev/null +++ b/doc/direct_queue1.png diff --git a/doc/direct_queue2.png b/doc/direct_queue2.png Binary files differnew file mode 100644 index 00000000..cbb99af8 --- /dev/null +++ b/doc/direct_queue2.png diff --git a/doc/direct_queue3.png b/doc/direct_queue3.png Binary files differnew file mode 100644 index 00000000..cc49299f --- /dev/null +++ b/doc/direct_queue3.png diff --git a/doc/direct_queue_directq.png b/doc/direct_queue_directq.png Binary files differnew file mode 100644 index 00000000..c5d8769d --- /dev/null +++ b/doc/direct_queue_directq.png diff --git a/doc/direct_queue_rsyslog.png b/doc/direct_queue_rsyslog.png Binary files differnew file mode 100644 index 00000000..6150222d --- /dev/null +++ b/doc/direct_queue_rsyslog.png diff --git a/doc/direct_queue_rsyslog2.png b/doc/direct_queue_rsyslog2.png Binary files differnew file mode 100644 index 00000000..807b064d --- /dev/null +++ b/doc/direct_queue_rsyslog2.png 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) - 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 "uncommon" 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 "user story" 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 "uncommon" 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 "user story" 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 <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 <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: omoracle</b></p> +<p><b>Author: </b>Luis Fernando Muñoz Mejías <Luis.Fernando.Munoz.Mejias@cern.ch></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 © 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 Binary files differnew file mode 100644 index 00000000..fedcb558 --- /dev/null +++ b/doc/queue_analogy_tv.png 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> 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 © 2008 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and +Copyright © 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 "the +left side" (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 "the +right side" 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 "Filter Engine"). 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 +"direct mode", in which "the queue does not actually enqueue data". +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 "Preprocessor" +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 "Parser & Filter Engine" +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 +"initiate" a TV show - both can only "handle" (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 "blue traffic stream". And as this car blocks the roadway, the remaining +traffic (now shown in red, which should indicate the block condition), +must wait until the "green" 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 "straight" 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 "blue" 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 "straight" 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>, "straigth traffic" 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 "blue" 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 "road without turning lane". +In my math model, it is a "road with turning lane of size 0". 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 "direct" 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 "direct" +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 "action 4" +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 "action 3", 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 "action 4" turning lane. As such, even though no new cars can +arrive on the main lane, cars still turn into the "action 4" lane. In other words, +an observer standing in "action 4" 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 - "take turns" into several roads at once. This is done by duplicating the message +if the road has a non-zero-sized "turning lane" - 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 "main lane". 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 ("turn lane") 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 "main lane" 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 ("right-hand part" of the main message +queue)!</b> Each of the connected arrows is a continuous thread and each "turn lane" 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 "the lower bound for the number of threads...". 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 "left-hand-side" (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 "turning lanes"; 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 "sits in front of" 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 "living thing" whereas the outputs are +just tools that this "living thing" 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 "terminating <b>a queue</b>" +and not "terminating an action" - <i>who is the "living thing"?</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 © 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 "$GenerateConfigGraph /path/to/file.dot" to rsyslog.conf (from now on, I +will call the file just file.dot). Optionally, add "$ActionName" 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 "dot -Tpng file.dot > file.png" +<li>remember that you can use "convert -resize 50% file.png resized.png" 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 "inputs" 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 "real" queues (other than in direct mode) +also utilize this shape. For actions, notice that a "hexagon action" creates +a deep copy of the message. As such, a "discard hexagon action" 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 "discard" 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 "*.*" 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 +"ActionName" 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 "builtin-file" 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 +"write_system_log_2". This note has one more special feature, that is label +was set via "ActionName", thus is does not have standard form (the same +happened to the node named "Forward" right at the top of the diagram. +Inside this diagram, the "Forward" 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 © 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 © 2009 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and +<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL +version 3 or higher.</font></p> +</body> +</html> diff --git a/doc/rsyslog_conf.html b/doc/rsyslog_conf.html index 852d95b5..6990c6bd 100644 --- a/doc/rsyslog_conf.html +++ b/doc/rsyslog_conf.html @@ -26,7 +26,7 @@ Lines can be continued by specifying a backslash ("\") as the last character of the line. There is a hard-coded maximum line length of 4K. If you need lines larger than that, you need to change compile-time settings inside rsyslog and recompile. -<h2><a href="rsyslog_conf_global.html">Global Directives</a></h2> +<h2><a href="rsyslog_conf_global.html">Configuration Directives</a></h2> <h2>Basic Structure</h2> <p>Rsyslog supports standard sysklogd's configuration file format and extends it. So in general, you can take a "normal" syslog.conf and @@ -74,9 +74,9 @@ such features is available in rsyslogd, only.</p> [<a href="http://www.rsyslog.com/">rsyslog site</a>]</p> <p><font size="2">This documentation is part of the <a href="http://www.rsyslog.com/">rsyslog</a> project.<br> -Copyright © 2008 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and +Copyright © 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 <a_single_word> - used primarily for documentation, e.g. when +generating a configuration graph. Available sice 4.3.1. <li>$ActionExecOnlyOnceEveryInterval <seconds> - execute action only if the last execute is at last <seconds> 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 <size_nbr> 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 © 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> - 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 © 2008 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and +Copyright © 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 Binary files differnew file mode 100644 index 00000000..21c04c57 --- /dev/null +++ b/doc/rsyslog_confgraph_complex.png 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 Binary files differnew file mode 100644 index 00000000..655a7f82 --- /dev/null +++ b/doc/rsyslog_confgraph_std.png diff --git a/doc/src/dataflow.dia b/doc/src/dataflow.dia Binary files differnew file mode 100644 index 00000000..3875fc61 --- /dev/null +++ b/doc/src/dataflow.dia diff --git a/doc/src/direct_queue0.dia b/doc/src/direct_queue0.dia Binary files differnew file mode 100644 index 00000000..4446619b --- /dev/null +++ b/doc/src/direct_queue0.dia diff --git a/doc/src/direct_queue1.dia b/doc/src/direct_queue1.dia Binary files differnew file mode 100644 index 00000000..7a64ea09 --- /dev/null +++ b/doc/src/direct_queue1.dia diff --git a/doc/src/direct_queue2.dia b/doc/src/direct_queue2.dia Binary files differnew file mode 100644 index 00000000..b0c394c0 --- /dev/null +++ b/doc/src/direct_queue2.dia diff --git a/doc/src/direct_queue3.dia b/doc/src/direct_queue3.dia Binary files differnew file mode 100644 index 00000000..bc477b25 --- /dev/null +++ b/doc/src/direct_queue3.dia diff --git a/doc/src/direct_queue_directq.dia b/doc/src/direct_queue_directq.dia Binary files differnew file mode 100644 index 00000000..37fdb44c --- /dev/null +++ b/doc/src/direct_queue_directq.dia diff --git a/doc/src/direct_queue_rsyslog.dia b/doc/src/direct_queue_rsyslog.dia Binary files differnew file mode 100644 index 00000000..9a030117 --- /dev/null +++ b/doc/src/direct_queue_rsyslog.dia diff --git a/doc/src/direct_queue_rsyslog2.dia b/doc/src/direct_queue_rsyslog2.dia Binary files differnew file mode 100644 index 00000000..c596f39f --- /dev/null +++ b/doc/src/direct_queue_rsyslog2.dia diff --git a/doc/src/queue_analogy_tv.dia b/doc/src/queue_analogy_tv.dia Binary files differnew file mode 100644 index 00000000..00fbdeb5 --- /dev/null +++ b/doc/src/queue_analogy_tv.dia 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 +"<a href="rsconf1_generateconfiggraph.html">$GenerateConfigGraph</a>" +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 +"<a href="rsconf1_generateconfiggraph.html">$GenerateConfigGraph</a>" +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 3cd2dcf8..c700cab7 100644 --- a/plugins/imdiag/imdiag.c +++ b/plugins/imdiag/imdiag.c @@ -7,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. * @@ -26,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> @@ -43,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 ------------------------------ */ -/* add a listen socket to our listen socket array. This is a callback - * invoked from the netstrm class. -- rgerhards, 2008-04-23 + +/* get the first word delimited by space from a given string. The pointer is + * advanced to after the word. Any leading spaces are discarded. If the + * output buffer is too small, parsing ends on buffer full condition. + * An empty buffer is returned if there is no more data inside the string. + * rgerhards, 2009-05-27 + */ +#define TO_LOWERCASE 1 +#define NO_MODIFY 0 +static void +getFirstWord(uchar **ppszSrc, uchar *pszBuf, size_t lenBuf, int options) +{ + uchar c; + uchar *pszSrc = *ppszSrc; + + while(*pszSrc && *pszSrc == ' ') + ++pszSrc; /* skip to first non-space */ + + while(*pszSrc && *pszSrc != ' ' && lenBuf > 1) { + c = *pszSrc++; + if(options & TO_LOWERCASE) + c = tolower(c); + *pszBuf++ = c; + lenBuf--; + } + + *pszBuf = '\0'; + *ppszSrc = pszSrc; +} + + +/* send a response back to the originator + * rgerhards, 2009-05-27 + */ +static rsRetVal __attribute__((format(printf, 2, 3))) +sendResponse(tcps_sess_t *pSess, char *fmt, ...) +{ + va_list ap; + ssize_t len; + uchar buf[1024]; + DEFiRet; + + va_start(ap, fmt); + len = vsnprintf((char*)buf, sizeof(buf), fmt, ap); + va_end(ap); + CHKiRet(netstrm.Send(pSess->pStrm, buf, &len)); + +finalize_it: + RETiRet; +} + + +/* actually submit a message to the rsyslog core */ static rsRetVal -addTcpLstn(void *pUsr, netstrm_t *pLstn) +doInjectMsg(int iNum) { + uchar szMsg[1024]; + msg_t *pMsg; + struct syslogTime stTime; + time_t ttGenTime; DEFiRet; - ISOBJ_TYPE_assert(pLstn, netstrm); + snprintf((char*)szMsg, sizeof(szMsg)/sizeof(uchar), + "<167>Mar 1 01:00:00 172.20.245.8 tag msgnum:%8.8d:\n", iNum); + + datetime.getCurrTime(&stTime, &ttGenTime); + /* we now create our own message object and submit it to the queue */ + CHKiRet(msgConstructWithTime(&pMsg, &stTime, ttGenTime)); + 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)); - if(iLstnMax >= sizeof(ppLstn)/sizeof(netstrm_t)) - ABORT_FINALIZE(RS_RET_MAX_LSTN_REACHED); +finalize_it: + RETiRet; +} + + +/* This function injects messages. Command format: + * injectmsg <fromnbr> <number-of-messages> + * rgerhards, 2009-05-27 + */ +static rsRetVal +injectMsg(uchar *pszCmd, tcps_sess_t *pSess) +{ + uchar wordBuf[1024]; + int iFrom; + int nMsgs; + int i; + DEFiRet; - ppLstn[pThis->iLstnMax] = pLstn; - ++iLstnMax; + /* we do not check errors here! */ + getFirstWord(&pszCmd, wordBuf, sizeof(wordBuf)/sizeof(uchar), TO_LOWERCASE); + iFrom = atoi((char*)wordBuf); + getFirstWord(&pszCmd, wordBuf, sizeof(wordBuf)/sizeof(uchar), TO_LOWERCASE); + nMsgs = atoi((char*)wordBuf); + + for(i = 0 ; i < nMsgs ; ++i) { + doInjectMsg(i + iFrom); + } + + CHKiRet(sendResponse(pSess, "messages injected\n")); finalize_it: RETiRet; } -/* initialize network stream subsystem */ +/* This function waits until the main queue is drained (size = 0) + */ static rsRetVal -initNetstrm(void) +waitMainQEmpty(tcps_sess_t *pSess) { + int iMsgQueueSize; DEFiRet; - /* prepare network stream subsystem */ - CHKiRet(netstrms.Construct(&pNS)); - CHKiRet(netstrms.SetDrvrMode(pNS, 0)); /* always plain text */ - //CHKiRet(netstrms.SetDrvrAuthMode(pThis->pNS, pThis->pszDrvrAuthMode)); - //CHKiRet(netstrms.SetDrvrPermPeers(pThis->pNS, pThis->pPermPeers)); - // TODO: set driver! - CHKiRet(netstrms.ConstructFinalize(pThis->pNS)); + CHKiRet(diagGetMainMsgQSize(&iMsgQueueSize)); + while(iMsgQueueSize > 0) { + srSleep(0,2); /* wait a little bit */ + CHKiRet(diagGetMainMsgQSize(&iMsgQueueSize)); + } - /* set up listeners */ - CHKiRet(netstrm.LstnInit(pNS, NULL, addTcpLstn, "127.0.0.1", "44514", 1)); + CHKiRet(sendResponse(pSess, "mainqueue empty\n")); finalize_it: - if(iRet != RS_RET_OK) { - if(pThis->pNS != NULL) - netstrms.Destruct(&pThis->pNS); + RETiRet; +} + +/* Function to handle received messages. This is our core function! + * rgerhards, 2009-05-24 + */ +static rsRetVal +OnMsgReceived(tcps_sess_t *pSess, uchar *pRcv, int iLenMsg) +{ + int iMsgQueueSize; + uchar *pszMsg; + uchar cmdBuf[1024]; + DEFiRet; + + assert(pSess != NULL); + assert(pRcv != NULL); + + /* NOTE: pRcv is NOT a C-String but rather an array of characters + * WITHOUT a termination \0 char. So we need to convert it to one + * before proceeding. + */ + CHKmalloc(pszMsg = malloc(sizeof(uchar) * (iLenMsg + 1))); + memcpy(pszMsg, pRcv, iLenMsg); + pszMsg[iLenMsg] = '\0'; + + getFirstWord(&pszMsg, cmdBuf, sizeof(cmdBuf)/sizeof(uchar), TO_LOWERCASE); + + if(!ustrcmp(cmdBuf, UCHAR_CONSTANT("getmainmsgqueuesize"))) { + CHKiRet(diagGetMainMsgQSize(&iMsgQueueSize)); + CHKiRet(sendResponse(pSess, "%d\n", iMsgQueueSize)); + } else if(!ustrcmp(cmdBuf, UCHAR_CONSTANT("waitmainqueueempty"))) { + CHKiRet(waitMainQEmpty(pSess)); + } else if(!ustrcmp(cmdBuf, UCHAR_CONSTANT("injectmsg"))) { + CHKiRet(injectMsg(pszMsg, pSess)); + } else { + dbgprintf("imdiag unkown command '%s'\n", cmdBuf); + CHKiRet(sendResponse(pSess, "unkown command '%s'\n", cmdBuf)); } + +finalize_it: RETiRet; } -/* This function is called to gather input. In our case, it is a bit abused - * to drive the listener loop for the diagnostics code. +/* 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; +} + + +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; } @@ -173,23 +436,26 @@ 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 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 37716f88..602a5eb1 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 032d8c04..77d845fd 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,6 +263,14 @@ 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_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) */ @@ -349,6 +362,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 */ @@ -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) @@ -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 5884df7e..69c24ae6 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; @@ -1338,14 +1359,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] == ' '); @@ -1356,7 +1377,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: @@ -1391,29 +1412,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 @@ -1436,7 +1457,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; @@ -1446,7 +1467,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 @@ -1501,7 +1522,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 */ @@ -1607,7 +1628,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 */ @@ -1917,10 +1938,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(); } @@ -1938,10 +1959,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); } @@ -2073,6 +2093,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) { @@ -2183,6 +2229,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 @@ -2196,6 +2420,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. @@ -2203,7 +2428,7 @@ DEFFUNC_llExecFunc(dbgPrintInitInfoAction) */ static void dbgPrintInitInfo(void) { - register selector_t *f; + selector_t *f; int iSelNbr = 1; int i; @@ -2443,6 +2668,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 */ @@ -2455,7 +2684,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(); } @@ -2878,11 +3106,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)); @@ -3099,6 +3329,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); @@ -3598,18 +3829,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); |