summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog34
-rw-r--r--Makefile.am8
-rw-r--r--action.c27
-rw-r--r--action.h4
-rw-r--r--configure.ac89
-rw-r--r--doc/Makefile.am4
-rw-r--r--doc/dev_oplugins.html5
-rw-r--r--doc/imuxsock.html2
-rw-r--r--doc/manual.html2
-rw-r--r--doc/omfile.html167
-rw-r--r--doc/omfwd.html118
-rw-r--r--doc/ompipe.html62
-rw-r--r--doc/omsnmp.html148
-rw-r--r--doc/omusrmsg.html64
-rw-r--r--doc/omuxsock.html2
-rw-r--r--doc/property_replacer.html256
-rw-r--r--doc/rsyslog_conf_global.html48
-rw-r--r--doc/rsyslog_conf_modules.html4
-rw-r--r--doc/rsyslog_conf_templates.html59
-rw-r--r--doc/v6compatibility.html19
-rw-r--r--grammar/grammar.y19
-rw-r--r--grammar/lexer.l11
-rw-r--r--grammar/rainerscript.c221
-rw-r--r--grammar/rainerscript.h27
-rw-r--r--plugins/imklog/imklog.c143
-rw-r--r--plugins/imklog/imklog.h9
-rw-r--r--plugins/immark/immark.c70
-rw-r--r--plugins/impstats/impstats.c135
-rw-r--r--plugins/imrelp/imrelp.c39
-rw-r--r--plugins/imtcp/imtcp.c8
-rw-r--r--plugins/imudp/imudp.c157
-rw-r--r--plugins/imuxsock/imuxsock.c359
-rw-r--r--plugins/imzmq3/Makefile.am8
-rw-r--r--plugins/imzmq3/README24
-rw-r--r--plugins/imzmq3/imzmq3.c657
-rw-r--r--plugins/omhdfs/omhdfs.c2
-rw-r--r--plugins/ommongodb/README23
-rw-r--r--plugins/ommongodb/ommongodb.c206
-rw-r--r--plugins/omudpspoof/omudpspoof.c143
-rw-r--r--plugins/omuxsock/omuxsock.c132
-rw-r--r--plugins/omzmq3/Makefile.am8
-rw-r--r--plugins/omzmq3/README25
-rw-r--r--plugins/omzmq3/omzmq3.c462
-rw-r--r--runtime/cfsysline.c35
-rw-r--r--runtime/cfsysline.h2
-rw-r--r--runtime/conf.c9
-rw-r--r--runtime/debug.h9
-rw-r--r--runtime/glbl.c2
-rw-r--r--runtime/module-template.h32
-rw-r--r--runtime/modules.c109
-rw-r--r--runtime/modules.h20
-rw-r--r--runtime/msg.c73
-rw-r--r--runtime/msg.h2
-rw-r--r--runtime/obj.c2
-rw-r--r--runtime/objomsr.c3
-rw-r--r--runtime/objomsr.h7
-rw-r--r--runtime/parser.c6
-rw-r--r--runtime/queue.c12
-rw-r--r--runtime/rsconf.c66
-rw-r--r--runtime/rsconf.h2
-rw-r--r--runtime/rsyslog.h6
-rw-r--r--runtime/rule.c22
-rw-r--r--runtime/ruleset.c15
-rw-r--r--runtime/statsobj.c12
-rw-r--r--runtime/statsobj.h3
-rw-r--r--runtime/wti.c16
-rw-r--r--tcps_sess.c10
-rw-r--r--tcpsrv.c15
-rw-r--r--template.c650
-rw-r--r--template.h17
-rw-r--r--tools/Makefile.am2
-rw-r--r--tools/omfile.c147
-rw-r--r--tools/omfwd.c135
-rw-r--r--tools/ompipe.c102
-rw-r--r--tools/pidfile.c6
-rw-r--r--tools/pmrfc3164.c4
-rw-r--r--tools/syslogd.c38
77 files changed, 5039 insertions, 562 deletions
diff --git a/ChangeLog b/ChangeLog
index a7a06dbe..061aca78 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,34 @@
---------------------------------------------------------------------------
-<<<<<<< HEAD
+Version 6.5.1 [devel] 2012-08-??
+- added pure JSON output plugin parameter passing mode
+- ommongodb now supports templates
+- bugfix: imtcp could abort on exit due to invalid free()
+---------------------------------------------------------------------------
+Version 6.5.0 [devel] 2012-08-28
+- imrelp now supports non-cancel thread termination
+ (but now requires at least librelp 1.0.1)
+- implemented freeCnf() module interface
+ This was actually not present in older versions, even though some modules
+ already used it. The implementation was now done, and not in 6.3/6.4
+ because the resulting memory leak was ultra-slim and the new interface
+ handling has some potential to seriously break things. Not the kind of
+ thing you want to add in late beta state, if avoidable.
+- added --enable-debugless configure option for very high demanding envs
+ This actually at compile time disables a lot of debug code, resulting
+ in some speedup (but serious loss of debugging capabilities)
+- added new 0mq plugins (via czmq lib)
+ Thanks to David Kelly for contributing these modules
+- bugfix: omhdfs did no longer compile
+- bugfix: SystemLogSocketAnnotate did not work correctly
+ Thanks to Miloslav Trmač for the patch
+- $SystemLogParseTrusted config file option
+ Thanks to Milan Bartos for the patch
+- added template config directive
+- added new uuid message property
+ Thanks to Jérôme Renard for the idea and patches.
+ Note: patches were released under ASL 2.0, see
+ http://bugzilla.adiscon.com/show_bug.cgi?id=353
+---------------------------------------------------------------------------
Version 6.4.1 [V6-STABLE] 2012-09-06
- bugfix: multiple main queues with same queue file name were not detected
This lead to queue file corruption. While the root cause is a config
@@ -552,11 +581,10 @@ expected that interfaces, even new ones, break during the initial
- added imptcp, a simplified, Linux-specific and potentielly fast
syslog plain tcp input plugin (NOT supporting TLS!)
[ported from v4]
-=======
+---------------------------------------------------------------------------
Version 5.10.1 [V5-STABLE], 2012-0?-??
- bugfix: remove invalid socket option call from imuxsock
Thanks to Cristian Ionescu-Idbohrn and Jonny Törnbom
->>>>>>> 2d538f14e2bae1407a3fb22c4a5244247dda1e4b
---------------------------------------------------------------------------
Version 5.10.0 [V5-STABLE], 2012-08-23
diff --git a/Makefile.am b/Makefile.am
index 03a78cce..95b432c9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -160,6 +160,14 @@ if ENABLE_OMHIREDIS
SUBDIRS += plugins/omhiredis
endif
+if ENABLE_OMZMQ3
+SUBDIRS += plugins/omzmq3
+endif
+
+if ENABLE_IMZMQ3
+SUBDIRS += plugins/imzmq3
+endif
+
if ENABLE_OMUXSOCK
SUBDIRS += plugins/omuxsock
endif
diff --git a/action.c b/action.c
index bf016363..8f8371f7 100644
--- a/action.c
+++ b/action.c
@@ -98,6 +98,7 @@
#include <strings.h>
#include <time.h>
#include <errno.h>
+#include <json/json.h>
#include "dirty.h"
#include "template.h"
@@ -262,7 +263,7 @@ actionResetQueueParams(void)
cs.bActionQSyncQeueFiles = 0;
cs.iActionQtoQShutdown = 0; /* queue shutdown */
cs.iActionQtoActShutdown = 1000; /* action shutdown (in phase 2) */
- cs.iActionQtoEnq = 2000; /* timeout for queue enque */
+ cs.iActionQtoEnq = 50; /* timeout for queue enque */
cs.iActionQtoWrkShutdown = 60000; /* timeout for worker thread shutdown */
cs.iActionQWrkMinMsgs = 100; /* minimum messages per worker needed to start a new one */
cs.bActionQSaveOnShutdown = 1; /* save queue on shutdown (when DA enabled)? */
@@ -803,6 +804,7 @@ static rsRetVal prepareDoActionParams(action_t *pAction, batch_obj_t *pElem)
{
int i;
msg_t *pMsg;
+ struct json_object *json;
DEFiRet;
ASSERT(pAction != NULL);
@@ -823,6 +825,10 @@ static rsRetVal prepareDoActionParams(action_t *pAction, batch_obj_t *pElem)
case ACT_MSG_PASSING:
pElem->staticActParams[i] = (void*) pMsg;
break;
+ case ACT_JSON_PASSING:
+ CHKiRet(tplToJSON(pAction->ppTpl[i], pMsg, &json));
+ pElem->staticActParams[i] = (void*) json;
+ break;
default:dbgprintf("software bug/error: unknown pAction->eParamPassing %d in prepareDoActionParams\n",
(int) pAction->eParamPassing);
assert(0); /* software bug if this happens! */
@@ -885,6 +891,13 @@ static rsRetVal releaseBatch(action_t *pAction, batch_t *pBatch)
((uchar**)pElem->staticActParams)[j] = NULL;
}
break;
+ case ACT_JSON_PASSING:
+ for(j = 0 ; j < pAction->iNumTpls ; ++j) {
+ json_object_put((struct json_object*)
+ pElem->staticActParams[j]);
+ pElem->staticActParams[j] = NULL;
+ }
+ break;
}
}
}
@@ -1094,16 +1107,6 @@ finalize_it:
RETiRet;
}
-/* debug aid */
-static void displayBatchState(batch_t *pBatch)
-{
- int i;
- for(i = 0 ; i < pBatch->nElem ; ++i) {
- dbgprintf("XXXXX: displayBatchState2 %p[%d]: %d\n", pBatch, i, pBatch->pElem[i].state);
- }
-}
-
-
/* submit a batch for actual action processing.
* The first nElem elements are processed. This function calls itself
* recursively if it needs to handle errors.
@@ -1933,6 +1936,8 @@ addAction(action_t **ppAction, modInfo_t *pMod, void *pModData,
pAction->eParamPassing = ACT_ARRAY_PASSING;
} else if(iTplOpts & OMSR_TPL_AS_MSG) {
pAction->eParamPassing = ACT_MSG_PASSING;
+ } else if(iTplOpts & OMSR_TPL_AS_JSON) {
+ pAction->eParamPassing = ACT_JSON_PASSING;
} else {
pAction->eParamPassing = ACT_STRING_PASSING;
}
diff --git a/action.h b/action.h
index 5f142b00..0e704ddf 100644
--- a/action.h
+++ b/action.h
@@ -74,7 +74,8 @@ struct action_s {
int f_repeatcount; /* number of "repeated" msgs */
rsRetVal (*submitToActQ)(action_t *, batch_t *);/* function submit message to action queue */
rsRetVal (*qConstruct)(struct queue_s *pThis);
- enum { ACT_STRING_PASSING = 0, ACT_ARRAY_PASSING = 1, ACT_MSG_PASSING }
+ enum { ACT_STRING_PASSING = 0, ACT_ARRAY_PASSING = 1, ACT_MSG_PASSING = 2,
+ ACT_JSON_PASSING = 3}
eParamPassing; /* mode of parameter passing to action */
int iNumTpls; /* number of array entries for template element below */
struct template **ppTpl;/* array of template to use - strings must be passed to doAction
@@ -109,5 +110,6 @@ rsRetVal actionClassInit(void);
rsRetVal addAction(action_t **ppAction, modInfo_t *pMod, void *pModData, omodStringRequest_t *pOMSR, struct cnfparamvals *actParams, struct cnfparamvals *queueParams, int bSuspended);
rsRetVal activateActions(void);
rsRetVal actionNewInst(struct nvlst *lst, action_t **ppAction);
+rsRetVal actionProcessCnf(struct cnfobj *o);
#endif /* #ifndef ACTION_H_INCLUDED */
diff --git a/configure.ac b/configure.ac
index 5d419b12..1d7aec58 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],[6.4.1],[rsyslog@lists.adiscon.com])
+AC_INIT([rsyslog],[6.5.0],[rsyslog@lists.adiscon.com])
AM_INIT_AUTOMAKE
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -34,6 +34,9 @@ PKG_PROG_PKG_CONFIG
# modules we require
PKG_CHECK_MODULES(LIBESTR, libestr >= 0.1.2)
PKG_CHECK_MODULES(LIBEE, libee >= 0.4.0)
+PKG_CHECK_MODULES([JSON_C], [json])
+AC_SUBST([JSON_CFLAGS])
+AC_SUBST([JSON_LIBS])
case "${host}" in
*-*-linux*)
@@ -445,6 +448,22 @@ if test "$enable_rtinst" = "yes"; then
fi
+# total debugless: highest performance, but no way at all to enable debug
+# logging
+AC_ARG_ENABLE(debugless,
+ [AS_HELP_STRING([--enable-debugless],[Enable runtime instrumentation mode @<:@default=no@:>@])],
+ [case "${enableval}" in
+ yes) enable_debugless="yes" ;;
+ no) enable_debugless="no" ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-debugless) ;;
+ esac],
+ [enable_debugless="no"]
+)
+if test "$enable_debugless" = "yes"; then
+ AC_DEFINE(DEBUGLESS, 1, [Defined if debugless mode is enabled.])
+fi
+
+
# valgrind
AC_ARG_ENABLE(valgrind,
[AS_HELP_STRING([--enable-valgrind],[Enable valgrind support settings @<:@default=no@:>@])],
@@ -658,6 +677,21 @@ AM_CONDITIONAL(ENABLE_OMLIBDBI, test x$enable_libdbi = xyes)
AC_SUBST(LIBDBI_CFLAGS)
AC_SUBST(LIBDBI_LIBS)
+# libuuid support
+AC_CHECK_HEADERS(
+ [uuid/uuid.h],,
+ [AC_MSG_FAILURE([libuuid is missing])]
+)
+AC_CHECK_LIB(
+ [uuid],
+ [uuid_generate],
+ [LIBUUID_CFLAGS=""
+ LIBUUID_LIBS="-luuid"
+ ],
+ [AC_MSG_FAILURE([libuuid library is missing])]
+)
+AC_SUBST(LIBUUID_CFLAGS)
+AC_SUBST(LIBUUID_LIBS)
# SNMP support
AC_ARG_ENABLE(snmp,
@@ -757,8 +791,8 @@ if test "x$enable_rsyslogrt" = "xyes"; then
#??CNF_LIBS="\$(top_builddir)/grammar/libgrammar.la"
fi
AM_CONDITIONAL(ENABLE_RSYSLOGRT, test x$enable_rsyslogrt = xyes)
-RSRT_CFLAGS="\$(RSRT_CFLAGS1) \$(LIBESTR_CFLAGS)"
-RSRT_LIBS="\$(RSRT_LIBS1) \$(LIBESTR_LIBS)"
+RSRT_CFLAGS="\$(RSRT_CFLAGS1) \$(LIBESTR_CFLAGS) \$(JSON_C_FLAGS)"
+RSRT_LIBS="\$(RSRT_LIBS1) \$(LIBESTR_LIBS) \$(JSON_C_LIBS)"
AC_SUBST(RSRT_CFLAGS1)
AC_SUBST(RSRT_LIBS1)
AC_SUBST(RSRT_CFLAGS)
@@ -904,7 +938,7 @@ AC_ARG_ENABLE(relp,
[enable_relp=no]
)
if test "x$enable_relp" = "xyes"; then
- PKG_CHECK_MODULES(RELP, relp >= 0.1.1)
+ PKG_CHECK_MODULES(RELP, relp >= 1.0.1)
fi
AM_CONDITIONAL(ENABLE_RELP, test x$enable_relp = xyes)
AC_SUBST(RELP_CFLAGS)
@@ -1245,10 +1279,6 @@ AC_ARG_ENABLE(ommongodb,
esac],
[enable_ommongodb=no]
)
-#
-# you may want to do some library checks here - see snmp, mysql, pgsql modules
-# for samples
-#
if test "x$enable_ommongodb" = "xyes"; then
PKG_CHECK_MODULES(LIBMONGO_CLIENT, libmongo-client >= 0.1.4)
AC_SUBST(LIBMONGO_CLIENT_CFLAGS)
@@ -1257,6 +1287,44 @@ fi
AM_CONDITIONAL(ENABLE_OMMONGODB, test x$enable_ommongodb = xyes)
# end of mongodb code
+# BEGIN ZMQ3 INPUT SUPPORT
+AC_ARG_ENABLE(imzmq3,
+ [AS_HELP_STRING([--enable-imzmq3],[Compiles imzmq3 output module @<:@default=no@:>@])],
+ [case "${enableval}" in
+ yes) enable_imzmq3="yes" ;;
+ no) enable_imzmq3="no" ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-imzmq3) ;;
+ esac],
+ [enable_imzmq3=no]
+)
+if test "x$enable_imzmq3" = "xyes"; then
+ PKG_CHECK_MODULES(CZMQ, libczmq >= 1.1.0)
+ AC_SUBST(CZMQ_CFLAGS)
+ AC_SUBST(CZMQ_LIBS)
+fi
+AM_CONDITIONAL(ENABLE_IMZMQ3, test x$enable_imzmq3 = xyes)
+
+# END ZMQ3 INPUT SUPPORT
+
+# BEGIN ZMQ3 OUTPUT SUPPORT
+AC_ARG_ENABLE(omzmq3,
+ [AS_HELP_STRING([--enable-omzmq3],[Compiles omzmq3 output module @<:@default=no@:>@])],
+ [case "${enableval}" in
+ yes) enable_omzmq3="yes" ;;
+ no) enable_omzmq3="no" ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-omzmq3) ;;
+ esac],
+ [enable_omzmq3=no]
+)
+if test "x$enable_omzmq3" = "xyes"; then
+ PKG_CHECK_MODULES(CZMQ, libczmq >= 1.1.0)
+ AC_SUBST(CZMQ_CFLAGS)
+ AC_SUBST(CZMQ_LIBS)
+fi
+AM_CONDITIONAL(ENABLE_OMZMQ3, test x$enable_omzmq3 = xyes)
+
+# END ZMQ3 SUPPORT
+
# HIREDIS SUPPORT
AC_ARG_ENABLE(omhiredis,
@@ -1307,6 +1375,7 @@ AC_CONFIG_FILES([Makefile \
plugins/impstats/Makefile \
plugins/imrelp/Makefile \
plugins/imdiag/Makefile \
+ plugins/imzmq3/Makefile \
plugins/omtesting/Makefile \
plugins/omgssapi/Makefile \
plugins/ommysql/Makefile \
@@ -1319,6 +1388,7 @@ AC_CONFIG_FILES([Makefile \
plugins/omudpspoof/Makefile \
plugins/ommongodb/Makefile \
plugins/omhiredis/Makefile \
+ plugins/omzmq3/Makefile \
plugins/mmnormalize/Makefile \
plugins/mmjsonparse/Makefile \
plugins/mmaudit/Makefile \
@@ -1351,6 +1421,7 @@ echo " imdiag enabled: $enable_imdiag"
echo " file input module enabled: $enable_imfile"
echo " Solaris input module enabled: $enable_imsolaris"
echo " periodic statistics module enabled: $enable_impstats"
+echo " imzmq3 input module enabled: $enable_imzmq3"
echo
echo "---{ output plugins }---"
echo " Mail support enabled: $enable_mail"
@@ -1361,6 +1432,7 @@ echo " omelasticsearch module will be compiled: $enable_elasticsearch"
echo " omruleset module will be compiled: $enable_omruleset"
echo " omudpspoof module will be compiled: $enable_omudpspoof"
echo " omuxsock module will be compiled: $enable_omuxsock"
+echo " omzmq3 module will be compiled: $enable_omzmq3"
echo
echo "---{ parser modules }---"
echo " pmrfc3164sd module will be compiled: $enable_pmrfc3164sd"
@@ -1398,6 +1470,7 @@ echo " Extended Testbench enabled: $enable_extended_tests"
echo " MySQL Tests enabled: $enable_mysql_tests"
echo " Debug mode enabled: $enable_debug"
echo " Runtime Instrumentation enabled: $enable_rtinst"
+echo " (total) debugless mode enabled: $enable_debugless"
echo " Diagnostic tools enabled: $enable_diagtools"
echo " Enhanced memory checking enabled: $enable_memcheck"
echo " Valgrind support settings enabled: $enable_valgrind"
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 04fda6b3..cc1a3209 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -31,6 +31,10 @@ html_files = \
version_naming.html \
contributors.html \
dev_queue.html \
+ ompipe.html \
+ omfwd.html \
+ omfile.html \
+ omusrmsg.html \
omstdout.html \
omudpspoof.html \
omruleset.html \
diff --git a/doc/dev_oplugins.html b/doc/dev_oplugins.html
index 63c186a3..b33b67f9 100644
--- a/doc/dev_oplugins.html
+++ b/doc/dev_oplugins.html
@@ -143,6 +143,11 @@ omstdout, you can see how a plugin may deal with the situation.
array-passing capability not blindly be used.</b> In such cases, we can not guard the
plugin from segfaulting and if the plugin (as currently always) is run within
rsyslog's process space, that results in a segfault for rsyslog. So do not do this.
+<p>Another possible mode is OMSR_TPL_AS_JSON, where instead of the template
+a json-c memory object tree is passed to the module. The module can extract data
+via json-c API calls. It MUST NOT modify the provided structure. This mode is
+primarily aimed at plugins that need to process tree-like data, as found
+for example in MongoDB or ElasticSearch.
<h3>Batching of Messages</h3>
<p>Starting with rsyslog 4.3.x, batching of output messages is supported. Previously, only
a single-message interface was supported.
diff --git a/doc/imuxsock.html b/doc/imuxsock.html
index 34a696d9..19f9cf51 100644
--- a/doc/imuxsock.html
+++ b/doc/imuxsock.html
@@ -104,6 +104,8 @@ to the system log socket.</li>
be obtained from the log socket itself. If so, the TAG part of the message is rewritten.
It is recommended to turn this option on, but the default is "off" to keep compatible
with earlier versions of rsyslog. This option was introduced in 5.7.0.</li>
+<li><b>$SystemLogParseTrusted</b> [on/<b>off</b>] - specifies if Trusted Properties shall be parsed
+and saved into libee event structure. This option needs $SystemLogSocketAnnotate to be on.</li>
<li><b>$SystemLogRateLimitInterval</b> [number] - specifies the rate-limiting
interval in seconds. Default value is 5 seconds. Set it to 0 to turn rate limiting off.
</li>
diff --git a/doc/manual.html b/doc/manual.html
index de0319f8..9a8644c9 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 6.4.1 (stable branch) of rsyslog.</b>
+<p><b>This documentation is for version 6.5.0 (devel branch) of rsyslog.</b>
Visit the <i><a href="http://www.rsyslog.com/status">rsyslog status page</a></i></b>
to obtain current version information and project status.
</p><p><b>If you like rsyslog, you might
diff --git a/doc/omfile.html b/doc/omfile.html
new file mode 100644
index 00000000..23ecc034
--- /dev/null
+++ b/doc/omfile.html
@@ -0,0 +1,167 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head>
+<meta http-equiv="Content-Language" content="en">
+<title>File Output Module</title></head>
+
+<body>
+<a href="rsyslog_conf_modules.html">back</a>
+
+<h1>File Output Module</h1>
+<p><b>Module Name:&nbsp;&nbsp;&nbsp; omfile</b></p>
+<p><b>Author: </b>Rainer Gerhards &lt;rgergards@adiscon.com&gt;</p>
+<p><b>Description</b>:</p>
+<p>The omfile plug-in provides the core functionality of writing messages to files residing inside the local file system (which may actually be remote if methods like NFS are used). Both files named with static names as well files with names based on message content are supported by this module. It is a built-in module that does not need to be loaded. </p>
+<p>&nbsp;</p>
+
+<p><b>Global Configuration Directives</b>:</p>
+<ul>
+ <li><strong>Template </strong>[templateName]<br>
+ sets a new default template for file actions.<br></li>
+
+</ul>
+<p>&nbsp;</p>
+<p><b>Action specific Configuration Directives</b>:</p>
+<ul>
+ <li><strong>DynaFileCacheSize </strong>(not mandatory, default will be used)<br>
+ Defines a template to be used for the output. <br></li><br>
+
+ <li><strong>ZipLevel </strong>0..9 [default 0]<br>
+ if greater 0, turns on gzip compression of the output file. The higher the number, the better the compression, but also the more CPU is required for zipping.<br></li><br>
+
+ <li><strong>FlushInterval </strong>(not mandatory, default will be used)<br>
+ Defines a template to be used for the output. <br></li><br>
+
+ <li><strong>ASyncWriting </strong>on/off [default off]<br>
+ if turned on, the files will be written in asynchronous mode via a separate thread. In that case, double buffers will be used so that one buffer can be filled while the other buffer is being written. Note that in order to enable FlushInterval, AsyncWriting must be set to "on". Otherwise, the flush interval will be ignored. Also note that when FlushOnTXEnd is "on" but AsyncWriting is off, output will only be written when the buffer is full. This may take several hours, or even require a rsyslog shutdown. However, a buffer flush can be forced in that case by sending rsyslogd a HUP signal. <br></li><br>
+
+ <li><strong>FlushOnTXEnd </strong>on/off [default on]<br>
+ Omfile has the capability to write output using a buffered writer. Disk writes are only done when the buffer is full. So if an error happens during that write, data is potentially lost. In cases where this is unacceptable, set FlushOnTXEnd to on. Then, data is written at the end of each transaction (for pre-v5 this means after each log message) and the usual error recovery thus can handle write errors without data loss. Note that this option severely reduces the effect of zip compression and should be switched to off for that use case. Note that the default -on- is primarily an aid to preserve the traditional syslogd behaviour.<br></li><br>
+
+ <li><strong>IOBufferSize </strong>&lt;size_nbr&gt;, default 4k<br>
+ size of the buffer used to writing output data. The larger the buffer, the potentially better performance is. The default of 4k is quite conservative, it is useful to go up to 64k, and 128K if you used gzip compression (then, even higher sizes may make sense)<br></li><br>
+
+ <li><strong>DirOwner </strong><br>
+ Set the file owner for directories newly created. Please note that this setting does not affect the owner of directories already existing. The parameter is a user name, for which the userid is obtained by rsyslogd during startup processing. Interim changes to the user mapping are not detected.<br></li><br>
+
+ <li><strong>DirGroup </strong><br>
+ Set the group for directories newly created. Please note that this setting does not affect the group of directories already existing. The parameter is a group name, for which the groupid is obtained by rsyslogd on during startup processing. Interim changes to the user mapping are not detected.<br></li><br>
+
+ <li><strong>FileOwner </strong><br>
+ Set the file owner for dynaFiles newly created. Please note that this setting does not affect the owner of files already existing. The parameter is a user name, for which the userid is obtained by rsyslogd during startup processing. Interim changes to the user mapping are not detected.<br></li><br>
+
+ <li><strong>FileGroup </strong><br>
+ Set the group for dynaFiles newly created. Please note that this setting does not affect the group of files already existing. The parameter is a group name, for which the groupid is obtained by rsyslogd during startup processing. Interim changes to the user mapping are not detected.<br></li><br>
+
+ <li><strong>DirCreateMode </strong>[defaul 0700]<br>
+ This is the same as $FileCreateMode, but for directories automatically generated.<br></li><br>
+
+ <li><strong>FileCreateMode </strong>[default 0644]<br>
+ The FileCreateMode directive allows to specify the creation mode with which rsyslogd creates new files. If not specified, the value 0644 is used (which retains backward-compatibility with earlier releases). The value given must always be a 4-digit octal number, with the initial digit being zero. <br>Please note that the actual permission depend on rsyslogd's process umask. If in doubt, use "$umask 0000" right at the beginning of the configuration file to remove any restrictions. <br>FileCreateMode may be specified multiple times. If so, it specifies the creation mode for all selector lines that follow until the next $FileCreateMode directive. Order of lines is vitally important.<br></li><br>
+
+ <li><strong>FailOnCHOwnFailure </strong>on/off [default on]<br>
+ This option modifies behaviour of dynaFile creation. If different owners or groups are specified for new files or directories and rsyslogd fails to set these new owners or groups, it will log an error and NOT write to the file in question if that option is set to "on". If it is set to "off", the error will be ignored and processing continues. Keep in mind, that the files in this case may be (in)accessible by people who should not have permission. The default is "on".<br></li><br>
+
+ <li><strong>CreateDirs </strong>on/off [default on]<br>
+ create directories on an as-needed basis<br></li><br>
+
+ <li><strong>Sync </strong>on/off [default off]<br>
+ enables file syncing capability of omfile.<br></li><br>
+
+ <li><strong>File </strong><br>
+ If the file already exists, new data is appended to it. Existing data is not truncated. If the file does not already exist, it is created. Files are kept open as long as rsyslogd is active. This conflicts with external log file rotation. In order to close a file after rotation, send rsyslogd a HUP signal after the file has been rotated away. <br></li><br>
+
+ <li><strong>DynaFile </strong><br>
+ For each message, the file name is generated based on the given template. Then, this file is opened. As with the ``file'' property, data is appended if the file already exists. If the file does not exist, a new file is created. A cache of recent files is kept. Note that this cache can consume quite some memory (especially if large buffer sizes are used). Files are kept open as long as they stay inside the cache. Currently, files are only evicted from the cache when there is need to do so (due to insufficient cache size). To force-close (and evict) a dynafile from cache, send a HUP signal to rsyslogd. <br></li><br>
+
+ <li><strong>Template </strong>[templateName]<br>
+ sets a new default template for file actions.<br></li><br>
+
+</ul>
+<p><b>Caveats/Known Bugs:</b></p><ul><li>None.</li></ul>
+<p><b>Sample:</b></p>
+<p>The following command writes all syslog messages into a file.</p>
+<textarea rows="5" cols="60">Module (path="builtin:omfile")
+*.* action(type="omfile"
+DirCreateMode="0700"
+FileCreateMode="0644"
+File="/var/log/messages")
+</textarea>
+
+<br><br>
+
+<p><b>Legacy Configuration Directives</b>:</p>
+<ul>
+ <li><strong>$DynaFileCacheSize </strong>(not mandatory, default will be used)<br>
+ Defines a template to be used for the output. <br></li><br>
+
+ <li><strong>$OMFileZipLevel </strong>0..9 [default 0]<br>
+ if greater 0, turns on gzip compression of the output file. The higher the number, the better the compression, but also the more CPU is required for zipping.<br></li><br>
+
+ <li><strong>$OMFileFlushInterval </strong>(not mandatory, default will be used)<br>
+ Defines a template to be used for the output. <br></li><br>
+
+ <li><strong>$OMFileASyncWriting </strong>on/off [default off]<br>
+ if turned on, the files will be written in asynchronous mode via a separate thread. In that case, double buffers will be used so that one buffer can be filled while the other buffer is being written. Note that in order to enable FlushInterval, AsyncWriting must be set to "on". Otherwise, the flush interval will be ignored. Also note that when FlushOnTXEnd is "on" but AsyncWriting is off, output will only be written when the buffer is full. This may take several hours, or even require a rsyslog shutdown. However, a buffer flush can be forced in that case by sending rsyslogd a HUP signal. <br></li><br>
+
+ <li><strong>$OMFileFlushOnTXEnd </strong>on/off [default on]<br>
+ Omfile has the capability to write output using a buffered writer. Disk writes are only done when the buffer is full. So if an error happens during that write, data is potentially lost. In cases where this is unacceptable, set FlushOnTXEnd to on. Then, data is written at the end of each transaction (for pre-v5 this means after each log message) and the usual error recovery thus can handle write errors without data loss. Note that this option severely reduces the effect of zip compression and should be switched to off for that use case. Note that the default -on- is primarily an aid to preserve the traditional syslogd behaviour.<br></li><br>
+
+ <li><strong>$OMFileIOBufferSize </strong>&lt;size_nbr&gt;, default 4k<br>
+ size of the buffer used to writing output data. The larger the buffer, the potentially better performance is. The default of 4k is quite conservative, it is useful to go up to 64k, and 128K if you used gzip compression (then, even higher sizes may make sense)<br></li><br>
+
+ <li><strong>$DirOwner </strong><br>
+ Set the file owner for directories newly created. Please note that this setting does not affect the owner of directories already existing. The parameter is a user name, for which the userid is obtained by rsyslogd during startup processing. Interim changes to the user mapping are not detected.<br></li><br>
+
+ <li><strong>$DirGroup </strong><br>
+ Set the group for directories newly created. Please note that this setting does not affect the group of directories already existing. The parameter is a group name, for which the groupid is obtained by rsyslogd on during startup processing. Interim changes to the user mapping are not detected.<br></li><br>
+
+ <li><strong>$FileOwner </strong><br>
+ Set the file owner for dynaFiles newly created. Please note that this setting does not affect the owner of files already existing. The parameter is a user name, for which the userid is obtained by rsyslogd during startup processing. Interim changes to the user mapping are not detected.<br></li><br>
+
+ <li><strong>$FileGroup </strong><br>
+ Set the group for dynaFiles newly created. Please note that this setting does not affect the group of files already existing. The parameter is a group name, for which the groupid is obtained by rsyslogd during startup processing. Interim changes to the user mapping are not detected.<br></li><br>
+
+ <li><strong>$DirCreateMode </strong>[defaul 0700]<br>
+ This is the same as $FileCreateMode, but for directories automatically generated.<br></li><br>
+
+ <li><strong>$FileCreateMode </strong>[default 0644]<br>
+ The FileCreateMode directive allows to specify the creation mode with which rsyslogd creates new files. If not specified, the value 0644 is used (which retains backward-compatibility with earlier releases). The value given must always be a 4-digit octal number, with the initial digit being zero. <br>Please note that the actual permission depend on rsyslogd's process umask. If in doubt, use "$umask 0000" right at the beginning of the configuration file to remove any restrictions. <br>FileCreateMode may be specified multiple times. If so, it specifies the creation mode for all selector lines that follow until the next $FileCreateMode directive. Order of lines is vitally important.<br></li><br>
+
+ <li><strong>$FailOnCHOwnFailure </strong>on/off [default on]<br>
+ This option modifies behaviour of dynaFile creation. If different owners or groups are specified for new files or directories and rsyslogd fails to set these new owners or groups, it will log an error and NOT write to the file in question if that option is set to "on". If it is set to "off", the error will be ignored and processing continues. Keep in mind, that the files in this case may be (in)accessible by people who should not have permission. The default is "on".<br></li><br>
+
+ <li><strong>$F$OMFileForceCHOwn </strong><br>
+ force ownership change for all files<br></li><br>
+
+ <li><strong>$CreateDirs </strong>on/off [default on]<br>
+ create directories on an as-needed basis<br></li><br>
+
+ <li><strong>$ActionFileEnableSync </strong>on/off [default off]<br>
+ enables file syncing capability of omfile.<br></li><br>
+
+ <li><strong>$ActionFileDefaultTemplate </strong>[templateName]<br>
+ sets a new default template for file actions.<br></li><br>
+
+ <li><strong>$ResetConfigVariables </strong><br>
+ Resets all configuration variables to their default value. Any settings made will not be applied to configuration lines following the $ResetConfigVariables. This is a good method to make sure no side-effects exists from previous directives. This directive has no parameters.<br></li><br>
+
+</ul>
+
+<p><b>Legacy Sample:</b></p>
+<p>The following command writes all syslog messages into a file.</p>
+<textarea rows="5" cols="60">$ModLoad omfile
+$DirCreateMode 0700
+$FileCreateMode 0644
+*.* /var/log/messages
+</textarea>
+
+
+<p>[<a href="rsyslog_conf.html">rsyslog.conf overview</a>] [<a href="manual.html">manual
+index</a>] [<a href="http://www.rsyslog.com/">rsyslog site</a>]</p>
+<p><font size="2">This documentation is part of the
+<a href="http://www.rsyslog.com/">rsyslog</a> project.<br>
+Copyright © 2008 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 3 or higher.</font></p>
+
+</body></html>
diff --git a/doc/omfwd.html b/doc/omfwd.html
new file mode 100644
index 00000000..5599ae39
--- /dev/null
+++ b/doc/omfwd.html
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head>
+<meta http-equiv="Content-Language" content="en">
+<title>Forwarding Output Module</title></head>
+
+<body>
+<a href="rsyslog_conf_modules.html">back</a>
+
+<h1>Forwarding Output Module</h1>
+<p><b>Module Name:&nbsp;&nbsp;&nbsp; omfwd</b></p>
+<p><b>Author: </b>Rainer Gerhards &lt;rgergards@adiscon.com&gt;</p>
+<p><b>Description</b>:</p>
+<p>The omfwd plug-in provides the core functionality of traditional message forwarding via UDP and plain TCP. It is a built-in module that does not need to be loaded. </p>
+<p>&nbsp;</p>
+
+<p><b>Global Configuration Directives</b>:</p>
+<ul>
+ <li><strong>Template </strong>[templateName]<br>
+ sets a new default template for file actions.<br></li>
+
+</ul>
+<p>&nbsp;</p>
+<p><b>Action specific Configuration Directives</b>:</p>
+<ul>
+ <li><strong>Target </strong>string<br>
+ Name or IP-Address of the system that shall receive messages. Any resolvable name is fine. <br></li><br>
+
+ <li><strong>Port </strong>[Default 514]<br>
+ Name or numerical value of port to use when connecting to target. <br></li><br>
+
+ <li><strong>Protocol </strong>udp/tcp [default udp]<br>
+ Type of protocol to use for forwarding. Note that ``tcp'' means both legacy plain tcp syslog as well as RFC5425-based TCL-encrypted syslog. Which one is selected depends on the protocol drivers set before the action commend. Note that as of 6.3.6, there is no way to specify this within the action itself. <br></li><br>
+
+ <li><strong>TCP_Framing </strong>``traditional'' or ``octet-counted'' [default traditional]<br>
+ Framing-Mode to be for forwarding. This affects only TCP-based protocols. It is ignored for UDP. In protocol engineering, ``framing'' means how multiple messages over the same connection are separated. Usually, this is transparent to users. Unfortunately, the early syslog protocol evolved, and so there are cases where users need to specify the framing. The traditional framing is nontransparent. With it, messages are end when a LF (aka ``line break'', ``return'') is encountered, and the next message starts immediately after the LF. If multi-line messages are received, these are essentially broken up into multiple message, usually with all but the first message segment being incorrectly formatted. The octet-counting framing solves this issue. With it, each message is prefixed with the actual message length, so that a receivers knows exactly where the message ends. Multi-line messages cause no problem here. This mode is very close to the method described in RFC5425 for TLS-enabled syslog. Unfortunately, only few syslogd implementations support octet-counted framing. As such, the traditional framing is set as default, even though it has defects. If it is known that the receiver supports octet-counted framing, it is suggested to use that framing mode. <br></li><br>
+
+ <li><strong>ZipLevel </strong>0..9 [default 0]<br>
+ Compression level for messages. Rsyslog implements a proprietary capability to zip transmitted messages. Note that compression happens on a message-per-message basis. As such, there is a performance gain only for larger messages. Before compressing a message, rsyslog checks if there is some gain by compression. If so, the message is sent compressed. If not, it is sent uncompressed. As such, it is totally valid that compressed and uncompressed messages are intermixed within a conversation. <br>The compression level is specified via the usual factor of 0 to 9, with 9 being the strongest compression (taking up most processing time) and 0 being no compression at all (taking up no extra processing time). <br></li><br>
+
+ <li><strong>RebindInterval </strong>integer<br>
+ Permits to specify an interval at which the current connection is broken and re-established. This setting is primarily an aid to load balancers. After the configured number of messages has been transmitted, the current connection is terminated and a new one started. Note that this setting applies to both TCP and UDP traffic. For UDP, the new ``connection'' uses a different source port (ports are cycled and not reused too frequently). This usually is perceived as a ``new connection'' by load balancers, which in turn forward messages to another physical target system. <br></li><br>
+
+ <li><strong>StreamDriver </strong>string<br>
+ Set the file owner for directories newly created. Please note that this setting does not affect the owner of directories already existing. The parameter is a user name, for which the userid is obtained by rsyslogd during startup processing. Interim changes to the user mapping are not detected.<br></li><br>
+
+ <li><strong>StreamDriverMode </strong>integer [default 0]<br>
+ mode to use with the stream driver (driver-specific)<br></li><br>
+
+ <li><strong>StreamDriverAuthMode </strong>string<br>
+ authentication mode to use with the stream driver. Note that this directive requires TLS netstream drivers. For all others, it will be ignored. (driver-specific).<br></li><br>
+
+ <li><strong>StreamDriverPermittedPeers </strong>string<br>
+ accepted fingerprint (SHA1) or name of remote peer. Note that this directive requires TLS netstream drivers. For all others, it will be ignored. (driver-specific)<br></li><br>
+
+ <li><strong>ResendLastMSGOnReconnect </strong>on/off<br>
+ Permits to resend the last message when a connection is reconnected. This setting affects TCP-based syslog, only. It is most useful for traditional, plain TCP syslog. Using this protocol, it is not always possible to know which messages were successfully transmitted to the receiver when a connection breaks. In many cases, the last message sent is lost. By switching this setting to "yes", rsyslog will always retransmit the last message when a connection is reestablished. This reduces potential message loss, but comes at the price that some messages may be duplicated (what usually is more acceptable). <br></li><br>
+
+</ul>
+<p><b>Caveats/Known Bugs:</b></p><ul><li>None.</li></ul>
+<p><b>Sample:</b></p>
+<p>The following command sends all syslog messages to a remote server via TCP port 10514.</p>
+<textarea rows="5" cols="60">Module (path="builtin:omfwd")
+*.* action(type="omfwd"
+Target="192.168.2.11"
+Port="10514"
+Protocol="tcp"
+)
+</textarea>
+
+<br><br>
+
+<p><b>Legacy Configuration Directives</b>:</p>
+<ul>
+ <li><strong>$ActionForwardDefaultTemplateName </strong>string [templatename]<br>
+ sets a new default template for UDP and plain TCP forwarding action<br></li><br>
+
+ <li><strong>$ActionSendTCPRebindInterval </strong>integer<br>
+ instructs the TCP send action to close and re-open the connection to the remote host every nbr of messages sent. Zero, the default, means that no such processing is done. This directive is useful for use with load-balancers. Note that there is some performance overhead associated with it, so it is advisable to not too often "rebind" the connection (what "too often" actually means depends on your configuration, a rule of thumb is that it should be not be much more often than once per second).<br></li><br>
+
+ <li><strong>$ActionSendUDPRebindInterval </strong>integer<br>
+ instructs the UDP send action to rebind the send socket every nbr of messages sent. Zero, the default, means that no rebind is done. This directive is useful for use with load-balancers.<br></li><br>
+
+ <li><strong>$ActionSendStreamDriver </strong>&lt;driver basename&gt;<br>
+ just like $DefaultNetstreamDriver, but for the specific action <br></li><br>
+
+ <li><strong>$ActionSendStreamDriverMode </strong>&lt;mode&gt; [default 0]<br>
+ mode to use with the stream driver (driver-specific)<br></li><br>
+
+ <li><strong>$ActionSendStreamDriverAuthMode </strong>&lt;mode&gt;<br>
+ authentication mode to use with the stream driver. Note that this directive requires TLS netstream drivers. For all others, it will be ignored. (driver-specific))<br></li><br>
+
+ <li><strong>$ActionSendStreamDriverPermittedPeers </strong>&lt;ID&gt;<br>
+ accepted fingerprint (SHA1) or name of remote peer. Note that this directive requires TLS netstream drivers. For all others, it will be ignored. (driver-specific) <br></li><br>
+
+ <li><strong>$ActionSendResendLastMsgOnReconnect </strong>on/off [default off]<br>
+ specifies if the last message is to be resend when a connecition breaks and has been reconnected. May increase reliability, but comes at the risk of message duplication. <br></li><br>
+
+ <li><strong>$ResetConfigVariables </strong><br>
+ Resets all configuration variables to their default value. Any settings made will not be applied to configuration lines following the $ResetConfigVariables. This is a good method to make sure no side-effects exists from previous directives. This directive has no parameters.<br></li><br>
+
+</ul>
+
+<p><b>Legacy Sample:</b></p>
+<p>The following command sends all syslog messages to a remote server via TCP port 10514.</p>
+<textarea rows="5" cols="60">$ModLoad omfwd
+*.* @@192.168.2.11:10514
+</textarea>
+
+
+<p>[<a href="rsyslog_conf.html">rsyslog.conf overview</a>] [<a href="manual.html">manual
+index</a>] [<a href="http://www.rsyslog.com/">rsyslog site</a>]</p>
+<p><font size="2">This documentation is part of the
+<a href="http://www.rsyslog.com/">rsyslog</a> project.<br>
+Copyright © 2008 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 3 or higher.</font></p>
+
+</body></html>
diff --git a/doc/ompipe.html b/doc/ompipe.html
new file mode 100644
index 00000000..49915b78
--- /dev/null
+++ b/doc/ompipe.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head>
+<meta http-equiv="Content-Language" content="en">
+<title>Pipe Output Module</title></head>
+
+<body>
+<a href="rsyslog_conf_modules.html">back</a>
+
+<h1>Pipe Output Module</h1>
+<p><b>Module Name:&nbsp;&nbsp;&nbsp; omfwd</b></p>
+<p><b>Author: </b>Rainer Gerhards &lt;rgergards@adiscon.com&gt;</p>
+<p><b>Description</b>:</p>
+<p>The ompipe plug-in provides the core functionality for logging output to named pipes (fifos). It is a built-in module that does not need to be loaded. </p>
+<p>&nbsp;</p>
+
+<p><b>Global Configuration Directives</b>:</p>
+<ul>
+ <li><strong>Template </strong>[templateName]<br>
+ sets a new default template for file actions.<br></li>
+
+</ul>
+<p>&nbsp;</p>
+<p><b>Action specific Configuration Directives</b>:</p>
+<ul>
+ <li><strong>Pipe </strong>string<br>
+ A fifo or named pipe can be used as a destination for log messages.<br></li><br>
+
+
+
+</ul>
+<p><b>Caveats/Known Bugs:</b></p><ul><li>None.</li></ul>
+<p><b>Sample:</b></p>
+<p>The following command sends all syslog messages to a remote server via TCP port 10514.</p>
+<textarea rows="5" cols="60">Module (path="builtin:ompipe")
+*.* action(type="ompipe"
+Pipe="NameofPipe"
+)
+</textarea>
+
+<br><br>
+
+<p><b>Legacy Configuration Directives</b>:</p>
+<p>rsyslog has support for logging output to named pipes (fifos). A fifo or named pipe can be used as a destination for log messages by prepending a pipe symbol ("|'') to the name of the file. This is handy for debugging. Note that the fifo must be created with the mkfifo(1) command before rsyslogd is started.
+
+</p>
+
+<p><b>Legacy Sample:</b></p>
+<p>The following command sends all syslog messages to a remote server via TCP port 10514.</p>
+<textarea rows="5" cols="60">$ModLoad ompipe
+*.* |/var/log/pipe
+</textarea>
+
+
+<p>[<a href="rsyslog_conf.html">rsyslog.conf overview</a>] [<a href="manual.html">manual
+index</a>] [<a href="http://www.rsyslog.com/">rsyslog site</a>]</p>
+<p><font size="2">This documentation is part of the
+<a href="http://www.rsyslog.com/">rsyslog</a> project.<br>
+Copyright © 2008 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 3 or higher.</font></p>
+
+</body></html>
diff --git a/doc/omsnmp.html b/doc/omsnmp.html
index b38a594f..202bb5bb 100644
--- a/doc/omsnmp.html
+++ b/doc/omsnmp.html
@@ -22,6 +22,153 @@ developer (headers) package installed. </p>
<p>&nbsp;</p>
<p><b>Configuration Directives</b>:</p>
<ul>
+ <li><strong>transport </strong>(This parameter is optional, the
+ default value is "udp")<br>
+ <br>
+ Defines the transport type you wish to use. Technically we can support all
+ transport types which are supported by NET-SNMP. <br>
+ To name a few possible values: <br>
+ <br>
+ udp, tcp, udp6, tcp6, icmp, icmp6 ...<br>
+ <br>
+ Example: <strong>transport udp<br>
+ </strong></li>
+ <li><strong>server</strong><br>
+ <br>
+ This can be a hostname or ip address, and is our snmp target host. This
+ parameter is required, if the snmptarget is not defined, nothing will be
+ send. <br>
+ <br>
+ Example: <strong>server server.domain.xxx</strong><br>
+ </li>
+ <li><strong>port </strong>(This parameter is optional, the
+ default value is "162")<br>
+ <br>
+ The port which will be used, common values are port 162 or 161. <br>
+ <br>
+ Example: <strong>port 162</strong><br>
+ </li>
+ <li><strong>version </strong>(This parameter is optional, the
+ default value is "1")<br>
+ <br>
+ There can only be two choices for this parameter for now. <br>
+ 0 means SNMPv1 will be used.<br>
+ 1 means SNMPv2c will be used. <br>
+ Any other value will default to 1. <br>
+ <br>
+ Example: <strong>version 1</strong><br>
+ </li>
+ <li><strong>community </strong>(This parameter is optional, the
+ default value is "public")<br>
+ <br>
+ This sets the used SNMP Community.<br>
+ <br>
+ Example:<strong> community public<br>
+ </strong><br>
+ </li>
+ <li><strong>trapoid </strong>(This parameter is
+ optional, the default value is "1.3.6.1.4.1.19406.1.2.1&quot; which means
+ &quot;ADISCON-MONITORWARE-MIB::syslogtrap&quot;)<br>
+ This configuration parameter is used for <strong>SNMPv2</strong> only.<br>
+ <br>
+ This is the OID which defines the trap-type, or notifcation-type rsyslog
+ uses to send the trap. <br>
+ In order to decode this OID, you will need to have the
+ ADISCON-MONITORWARE-MIB and ADISCON-MIB mibs installed on the receiver side. Downloads of these mib files
+ can be found here: <br>
+ <a href="http://www.adiscon.org/download/ADISCON-MIB.txt">
+ http://www.adiscon.org/download/ADISCON-MIB.txt</a><br>
+ <a href="http://www.adiscon.org/download/ADISCON-MONITORWARE-MIB.txt">
+ http://www.adiscon.org/download/ADISCON-MONITORWARE-MIB.txt</a><br>
+ <br>
+ Thanks to the net-snmp
+ mailinglist for the help and the recommendations ;).<br>
+ <br>
+ Example: <strong>trapoid 1.3.6.1.4.1.19406.1.2.1<br>
+ </strong>If you have this MIBS installed, you can also configured with the
+ OID Name: <strong>trapoid ADISCON-MONITORWARE-MIB::syslogtrap<br>
+ </strong>
+ </li>
+ <li><strong>messageoid </strong>(This parameter is
+ optional, the default value is "1.3.6.1.4.1.19406.1.1.2.1&quot; which means
+ &quot;ADISCON-MONITORWARE-MIB::syslogMsg&quot;)<br>
+ <br>
+ This OID will be used as a variable, type &quot;OCTET STRING&quot;. This variable will
+ contain up to 255 characters of the original syslog message including syslog header. It is recommend to
+ use the default OID. <br>
+ In order to decode this OID, you will need to have the
+ ADISCON-MONITORWARE-MIB and ADISCON-MIB mibs installed on the receiver side.
+ To download these custom mibs, see the description of <strong>$actionsnmptrapoid.
+ </strong><br>
+ <br>
+ Example: <strong>messageoid 1.3.6.1.4.1.19406.1.1.2.1<br>
+ </strong>If you have this MIBS installed, you can also configured with the
+ OID Name: <strong>messageoid
+ ADISCON-MONITORWARE-MIB::syslogMsg<br>
+ </strong><br>
+ </li>
+ <li><strong>enterpriseoid </strong>(This parameter is optional,
+ the default value is "1.3.6.1.4.1.3.1.1" which means "enterprises.cmu.1.1")<br>
+ <br>
+ Customize this value if needed. I recommend to use the default value unless
+ you require to use a different OID. <br>
+ This configuration parameter is used for <strong>SNMPv1</strong> only. It
+ has no effect if <strong>SNMPv2</strong> is used. <br>
+ <br>
+ Example: <strong>enterpriseoid 1.3.6.1.4.1.3.1.1 <br>
+ </strong><br>
+ </li>
+ <li><strong>specifictype </strong>(This parameter is optional,
+ the default value is "0")<strong> </strong><br>
+ <br>
+ This is the specific trap number. This configuration parameter is used for
+ <strong>SNMPv1</strong> only. It has no effect if <strong>SNMPv2</strong> is
+ used. <br>
+ <br>
+ Example: <strong>specifictype 0<br>
+ </strong><br>
+ </li>
+ <li><strong>traptype</strong> (This parameter is optional, the
+ default value is "6" which means SNMP_TRAP_ENTERPRISESPECIFIC) <br>
+ <br>
+ There are only 7 Possible trap types defined which can be used here. These
+ trap types are: <br>
+ 0 = SNMP_TRAP_COLDSTART<br>
+ 1 = SNMP_TRAP_WARMSTART<br>
+ 2 = SNMP_TRAP_LINKDOWN<br>
+ 3 = SNMP_TRAP_LINKUP<br>
+ 4 = SNMP_TRAP_AUTHFAIL<br>
+ 5 = SNMP_TRAP_EGPNEIGHBORLOSS<br>
+ 6 = SNMP_TRAP_ENTERPRISESPECIFIC<br>
+ <br>
+ Any other value will default to 6 automatically. This configuration
+ parameter is used for <strong>SNMPv1</strong> only. It has no effect if
+ <strong>SNMPv2</strong> is used. <br>
+ <br>
+ Example: <strong>traptype 6</strong><br>
+ </li>
+ <li><strong>template </strong>[templateName]<strong> </strong><br>
+ <br>
+ sets a new default template for file actions.
+ </li>
+</ul>
+<p>&nbsp;</p>
+<p><b>Caveats/Known Bugs:</b></p><ul><li>In order to decode the custom OIDs, you
+ will need to have the adiscon mibs installed. </li></ul>
+<p><b>Sample:</b></p>
+<p>The following commands send every message as a snmp trap.</p>
+<textarea rows="10" cols="60">Module (path="omsnmp")
+*.* action( type="omsnmp"
+transport="udp"
+target="localhost"
+targetport="162"
+version="1"
+community="public")
+
+</textarea>
+
+<p><b>Legacy Configuration Directives</b>:</p>
+<ul>
<li><strong>$actionsnmptransport </strong>(This parameter is optional, the
default value is "udp")<br>
<br>
@@ -164,6 +311,7 @@ $actionsnmpcommunity public
*.* :omsnmp:
</textarea>
+
<p>[<a href="rsyslog_conf.html">rsyslog.conf overview</a>] [<a href="manual.html">manual
index</a>] [<a href="http://www.rsyslog.com/">rsyslog site</a>]</p>
<p><font size="2">This documentation is part of the
diff --git a/doc/omusrmsg.html b/doc/omusrmsg.html
new file mode 100644
index 00000000..eccfef2d
--- /dev/null
+++ b/doc/omusrmsg.html
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head>
+<meta http-equiv="Content-Language" content="en">
+<title>User Message Output Module</title></head>
+
+<body>
+<a href="rsyslog_conf_modules.html">back</a>
+
+<h1>User Message Output Module</h1>
+<p><b>Module Name:&nbsp;&nbsp;&nbsp; omusrmsg</b></p>
+<p><b>Author: </b>Rainer Gerhards &lt;rgergards@adiscon.com&gt;</p>
+<p><b>Description</b>:</p>
+<p>The omusrmsg plug-in provides the core functionality for logging output to a logged on user. It is a built-in module that does not need to be loaded. </p>
+<p>&nbsp;</p>
+
+<p><b>Global Configuration Directives</b>:</p>
+<ul>
+ <li><strong>Template </strong>[templateName]<br>
+ sets a new default template for file actions.<br></li>
+
+</ul>
+<p>&nbsp;</p>
+<p><b>Action specific Configuration Directives</b>:</p>
+<ul>
+ <li><strong>Users </strong>string<br>
+ Must be a valid user name or root.<br></li><br>
+
+
+
+</ul>
+<p><b>Caveats/Known Bugs:</b></p><ul><li>None.</li></ul>
+<p><b>Sample:</b></p>
+<p>The following command sends all critical syslog messages to a user and to root.</p>
+<textarea rows="5" cols="60">Module (path="builtin:omusrmsg")
+*.=crit action(type="omusrmsg"
+Users="ExampleUser"
+Users="root"
+)
+</textarea>
+
+<br><br>
+
+<p><b>Legacy Configuration Directives</b>:</p>
+<p>
+ No specific configuration directives available. See configuration sample below for details on using the plugin.
+</p>
+
+<p><b>Legacy Sample:</b></p>
+<p>The following command sends all critical syslog messages to a user and to root.</p>
+<textarea rows="5" cols="60">$ModLoad omusrmsg
+*.=crit :omusrmsg:exampleuser
+& root
+</textarea>
+
+
+<p>[<a href="rsyslog_conf.html">rsyslog.conf overview</a>] [<a href="manual.html">manual
+index</a>] [<a href="http://www.rsyslog.com/">rsyslog site</a>]</p>
+<p><font size="2">This documentation is part of the
+<a href="http://www.rsyslog.com/">rsyslog</a> project.<br>
+Copyright © 2008 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL
+version 3 or higher.</font></p>
+
+</body></html>
diff --git a/doc/omuxsock.html b/doc/omuxsock.html
index 5fa569eb..a1c09228 100644
--- a/doc/omuxsock.html
+++ b/doc/omuxsock.html
@@ -29,7 +29,7 @@ actions and each of them should use the same template.</li>
<p><b>Sample:</b></p>
<p>The following sample writes all messages to the "/tmp/socksample" socket.
</p>
-<textarea rows="4" cols="80">$ModLoad omucsock
+<textarea rows="4" cols="80">$ModLoad omuxsock
$OMUxSockSocket /tmp/socksample
*.* :omuxsock:
</textarea>
diff --git a/doc/property_replacer.html b/doc/property_replacer.html
index 86a07474..dc09d33c 100644
--- a/doc/property_replacer.html
+++ b/doc/property_replacer.html
@@ -236,7 +236,7 @@ or none if no rule with this field did match.
<p>Properties starting with a $-sign are so-called system
properties. These do NOT stem from the message but are rather
internally-generated.</p>
-<h2>Character Positions</h2>
+<h2>Legacy Character Positions</h2>
<p><b><code>FromChar</code></b> and <b><code>toChar</code></b>
are used to build substrings. They specify the offset within the string
that should be copied. Offset counting starts at 1, so if you need to
@@ -304,15 +304,6 @@ fields in the property is requested. The field number must be placed in
the "ToChar" parameter. An example where the 3rd field (delimited by
TAB) from the msg property is extracted is as follows: "%msg:F:3%". The
same example with semicolon as delimiter is "%msg:F,59:3%".</p>
-<p>The use of fields does not permit to select substrings, what is rather
-unfortunate. To solve this issue, starting with 6.3.9, fromPos and toPos
-can be specified for strings as well. However, the syntax is quite ugly, but
-it was the only way to integrate this functonality into the already-existing
-system. To do so, use ",fromPos" and ",toPos" during field extraction.
-Let's assume you want to extract the substring from position 5 to 9 in the previous
-example. Then, the syntax is as follows: "%msg:F,59,5:3,9%". As you can see,
-"F,59" means field-mode, with semicolon delimiter and ",5" means starting
-at position 5. Then "3,9" means field 3 and string extraction to position 9.
<p>Please note that the special characters "F" and "R" are
case-sensitive. Only upper case works, lower case will return an error.
There are no white spaces permitted inside the sequence (that will lead
@@ -342,6 +333,243 @@ It is modeled after perl compatible regular expressions.
<h2>Property Options</h2>
<b><code>property options</code></b> are
+case-insensitive. They are available as of version 6.5.0.
+Currently, the following options are defined:
+<p></p>
+<table>
+<tbody>
+<tr>
+<td><b>Name</b></td>
+<td>New format. Name of the template / property / constant.</td>
+</tr>
+<tr>
+<td><b>Outname</b></td>
+<td>This field permits to specify a field name for structured-data emitting property replacer options.
+It is most useful to set, for example, the name for JSON-based fields (like used in ommngodb). For
+text-based modules, it is simply ignored.
+If not specified, the original property name is used, with the exception of properties starting with
+"$!", where that prefix is removed. Note that unnamaned constants are NOT forwarded to output modules
+that expect structure (like ommnogodb). To pass constants, an outname must be set.
+</tr>
+<tr>
+<td><b>CaseConversion</b></td>
+<td>New format. Additional values below.</td>
+</tr>
+<tr>
+<td>upper</td>
+<td>convert property to lowercase only</td>
+</tr>
+<tr>
+<td>lower</td>
+<td>convert property text to uppercase only</td>
+</tr>
+<tr>
+<td><b>DateFormat</b></td>
+<td>New format, additional parameter is needed. See below.</td>
+</tr>
+<tr>
+<td>mysql</td>
+<td>format as mysql date</td>
+</tr>
+<tr>
+<td>pgsql</td>
+<td>format as pgsql date</td>
+</tr>
+<tr>
+<td>rfc3164</td>
+<td>format as RFC 3164 date</td>
+</tr>
+<tr>
+<tr>
+<td valign="top">rfc3164-buggyday</td>
+<td>similar to date-rfc3164, but emulates a common coding error: RFC 3164 demands
+that a space is written for single-digit days. With this option, a zero is
+written instead. This format seems to be used by syslog-ng and the
+date-rfc3164-buggyday option can be used in migration scenarios where otherwise
+lots of scripts would need to be adjusted. It is recommended <i>not</i> to use this
+option when forwarding to remote hosts - they may treat the date as invalid
+(especially when parsing strictly according to RFC 3164).</td>
+<br><i>This feature was introduced in rsyslog 4.6.2 and v4 versions above and
+5.5.3 and all versions above.</i>
+</tr>
+<tr>
+<td>rfc3339</td>
+<td>format as RFC 3339 date</td>
+</tr>
+<tr>
+<td>unixtimestamp</td>
+<td>format as unix timestamp (seconds since epoch)</td>
+</tr>
+<tr>
+<td>subseconds</td>
+<td>just the subseconds of a timestamp (always 0 for a low precision timestamp)</td>
+</tr>
+<tr>
+<td><b>ControlCharacters</b></td>
+<td>Option values for how to process control characters</td>
+</tr>
+<tr>
+<td valign="top">escape</td>
+<td>replace control characters (ASCII value 127 and values
+less then 32) with an escape sequence. The sequnce is
+"#&lt;charval&gt;" where charval is the 3-digit decimal value
+of the control character. For example, a tabulator would be replaced by
+"#009".<br>
+Note: using this option requires that <a href="rsconf1_escapecontrolcharactersonreceive.html">$EscapeControlCharactersOnReceive</a>
+is set to off.</td>
+</tr>
+<tr>
+<td valign="top">space</td>
+<td>replace control characters by spaces<br>
+Note: using this option requires that <a href="rsconf1_escapecontrolcharactersonreceive.html">$EscapeControlCharactersOnReceive</a>
+is set to off.</td>
+</tr>
+<tr>
+<td valign="top">drop</td>
+<td>drop control characters - the resulting string will
+neither contain control characters, escape sequences nor any other
+replacement character like space.<br>
+Note: using this option requires that <a href="rsconf1_escapecontrolcharactersonreceive.html">$EscapeControlCharactersOnReceive</a>
+is set to off.</td>
+</tr>
+<tr>
+<td><b>SecurePath</b></td>
+<td>Option values for securing path templates.</td>
+</tr>
+<tr>
+<td valign="top">drop</td>
+<td>Drops slashes inside the field (e.g. "a/b" becomes "ab").
+Useful for secure pathname generation (with dynafiles).
+</td>
+</tr>
+<tr>
+<td valign="top">replace</td>
+<td>Replace slashes inside the field by an underscore. (e.g. "a/b" becomes "a_b").
+Useful for secure pathname generation (with dynafiles).
+</td>
+</tr>
+<tr>
+<td><b>Format</b></td>
+<td>Option values for the general output format.</td>
+</tr>
+<tr>
+<td>json</td>
+<td>encode the value so that it can be used inside a JSON field. This means
+that several characters (according to the JSON spec) are being escaped, for
+example US-ASCII LF is replaced by "\n".
+The json option cannot be used together with either jsonf or csv options.
+</td>
+</tr>
+<tr>
+<td>jsonf</td>
+<td><i>(available in 6.3.9+)</i>
+This signifies that the property should be expressed as a json <b>f</b>ield.
+That means not only the property is written, but rather a complete json field in
+the format<br>
+"fieldname"="value"</b>
+where "filedname" is the assigend field name (or the property name if none was assigned)
+and value is the end result of property replacer operation. Note that value supports
+all property replacer options, like substrings, case converson and the like.
+Values are properly json-escaped. However, field names are (currently) not. It is
+expected that proper field names are configured.
+The jsonf option cannot be used together with either json or csv options.
+</td>
+</tr>
+<tr>
+<td valign="top">csv</td>
+<td>formats the resulting field (after all modifications) in CSV format
+as specified in <a href="http://www.ietf.org/rfc/rfc4180.txt">RFC 4180</a>.
+Rsyslog will always use double quotes. Note that in order to have full CSV-formatted
+text, you need to define a proper template. An example is this one:
+<br>$template csvline,"%syslogtag:::csv%,%msg:::csv%"
+<br>Most importantly, you need to provide the commas between the fields
+inside the template.
+The csv option cannot be used together with either json or jsonf options.
+<br><i>This feature was introduced in rsyslog 4.1.6.</i>
+</td>
+</tr>
+<tr>
+<td><b>droplastlf</b></td>
+<td>The last LF in the message (if any), is dropped.
+Especially useful for PIX.</td>
+</tr>
+<tr>
+<td valign="top"><b>spifno1stsp</b></td>
+<td>This option looks scary and should probably not be used by a user. For any field
+given, it returns either a single space character or no character at all. Field content
+is never returned. A space is returned if (and only if) the first character of the
+field's content is NOT a space. This option is kind of a hack to solve a problem rooted
+in RFC 3164: 3164 specifies no delimiter between the syslog tag sequence and the actual
+message text. Almost all implementation in fact delemit the two by a space. As of
+RFC 3164, this space is part of the message text itself. This leads to a problem when
+building the message (e.g. when writing to disk or forwarding). Should a delimiting
+space be included if the message does not start with one? If not, the tag is immediately
+followed by another non-space character, which can lead some log parsers to misinterpret
+what is the tag and what the message. The problem finally surfaced when the klog module
+was restructured and the tag correctly written. It exists with other message sources,
+too. The solution was the introduction of this special property replacer option. Now,
+the default template can contain a conditional space, which exists only if the
+message does not start with one. While this does not solve all issues, it should
+work good enough in the far majority of all cases. If you read this text and have
+no idea of what it is talking about - relax: this is a good indication you will never
+need this option. Simply forget about it ;)
+</td>
+</tr>
+<tr>
+<td></td>
+<td></td>
+</tr>
+<tr>
+<td><b>New character position</b></td>
+<td>In addition to the above mentioned Character Positions in the legacy format,
+positions can be determined by specifying the correct options for the properties.
+Again, this is mostly for using the list format.</td>
+</tr>
+<tr>
+<td>position.From</td>
+<td>Character position in the property to start from.</td>
+</tr>
+<tr>
+<td>position.To</td>
+<td>Character position that determines the end for extraction. If the value is "$"
+then the end of the string will be used.</td>
+</tr>
+<tr>
+<td>field.Number</td>
+<td>The number of the field, which should be used for the search operation with Regex.</td>
+</tr>
+<tr>
+<td>field.Delimiter</td>
+<td>The Character that should delimit a field. Example: ",". Everything in a
+property until this character is considered a field.</td>
+</tr>
+<tr>
+<td>regex.Expression</td>
+<td>Value to be compared to property.</td>
+</tr>
+<tr>
+<td>regex.Type</td>
+<td>Values BRE or ERE</td>
+</tr>
+<tr>
+<td>regex.NoMatchMode</td>
+<td>DFLT, BLANK, ZERO, FIELD</td>
+</tr>
+<tr>
+<td>regex.Match</td>
+<td>Match to use.</td>
+</tr>
+<tr>
+<td>regex.Submatch</td>
+<td>Submatch to use. Values 0-9 whereas 0 = All</td>
+</tr>
+</tbody>
+</table>
+
+
+
+<h2>Legacy Property Options</h2>
+<b><code>property options</code></b> are
case-insensitive. Currently, the following options are defined:
<p></p>
<table>
@@ -368,7 +596,7 @@ The json option cannot be used together with either jsonf or csv options.
This signifies that the property should be expressed as a json <b>f</b>ield.
That means not only the property is written, but rather a complete json field in
the format<br>
-"fieldname":"value"</b>
+"fieldname"="value"</b>
where "filedname" is the assigend field name (or the property name if none was assigned)
and value is the end result of property replacer operation. Note that value supports
all property replacer options, like substrings, case converson and the like.
@@ -486,6 +714,12 @@ Useful for secure pathname generation (with dynafiles).
Useful for secure pathname generation (with dynafiles).
</td>
</tr>
+<tr>
+<td><b>mandatory-field</b></td>
+<td>In templates that are used for building field lists (in particular, ommongodb), include
+this field, even if it is empty (or NULL). If not set, the field will be removed from
+the output field set if empty. The latter is the default case.
+</tr>
</tbody>
</table>
<p>To use multiple options, simply place them one after each other with a comma delmimiting
diff --git a/doc/rsyslog_conf_global.html b/doc/rsyslog_conf_global.html
index 6c20f4c2..a4d760eb 100644
--- a/doc/rsyslog_conf_global.html
+++ b/doc/rsyslog_conf_global.html
@@ -52,10 +52,10 @@ This directive will timeout previous messages seen if they are older
than 20 minutes. In the example above, the count would now be always 1
and consequently no rule would ever be triggered.
-<li>$ActionFileDefaultTemplate [templateName] - sets a new default template for file actions</li>
-<li>$ActionFileEnableSync [on/<span style="font-weight: bold;">off</span>] - enables file
+<li><a href="omfile.html">$ActionFileDefaultTemplate</a> [templateName] - sets a new default template for file actions</li>
+<li><a href="omfile.html">$ActionFileEnableSync</a> [on/<span style="font-weight: bold;">off</span>] - enables file
syncing capability of omfile</li>
-<li>$ActionForwardDefaultTemplate [templateName] - sets a new
+<li><a href="omfwd.html">$ActionForwardDefaultTemplate</a> [templateName] - sets a new
default template for UDP and plain TCP forwarding action</li>
<li>$ActionGSSForwardDefaultTemplate [templateName] - sets a
new default template for GSS-API forwarding action</li>
@@ -93,23 +93,23 @@ default 60000 (1 minute)]</li>
<li>$ActionQueueWorkerThreadMinumumMessages &lt;number&gt;, default 100</li>
<li><a href="rsconf1_actionresumeinterval.html">$ActionResumeInterval</a></li>
<li>$ActionResumeRetryCount &lt;number&gt; [default 0, -1 means eternal]</li>
-<li>$ActionSendResendLastMsgOnReconnect &lt;[on/<b>off</b>]&gt; specifies if the last message is to be resend when a connecition breaks and has been reconnected. May increase reliability, but comes at the risk of message duplication.
-<li>$ActionSendStreamDriver &lt;driver basename&gt; just like $DefaultNetstreamDriver, but for the specific action</li>
-<li>$ActionSendStreamDriverMode &lt;mode&gt;, default 0, mode to use with the stream driver (driver-specific)</li>
-<li>$ActionSendStreamDriverAuthMode &lt;mode&gt;,&nbsp; authentication mode to use with the stream driver. Note that this directive requires TLS
+<li><a href="omfwd.html">$ActionSendResendLastMsgOnReconnect</a> &lt;[on/<b>off</b>]&gt; specifies if the last message is to be resend when a connecition breaks and has been reconnected. May increase reliability, but comes at the risk of message duplication.
+<li><a href="omfwd.html">$ActionSendStreamDriver</a> &lt;driver basename&gt; just like $DefaultNetstreamDriver, but for the specific action</li>
+<li><a href="omfwd.html">$ActionSendStreamDriverMode</a> &lt;mode&gt;, default 0, mode to use with the stream driver (driver-specific)</li>
+<li><a href="omfwd.html">$ActionSendStreamDriverAuthMode</a> &lt;mode&gt;,&nbsp; authentication mode to use with the stream driver. Note that this directive requires TLS
netstream drivers. For all others, it will be ignored.
(driver-specific)</li>
-<li>$ActionSendStreamDriverPermittedPeer &lt;ID&gt;,&nbsp; accepted fingerprint (SHA1) or name of remote peer. Note that this directive requires TLS
+<li><a href="omfwd.html">$ActionSendStreamDriverPermittedPeer</a> &lt;ID&gt;,&nbsp; accepted fingerprint (SHA1) or name of remote peer. Note that this directive requires TLS
netstream drivers. For all others, it will be ignored.
(driver-specific) -<span style="font-weight: bold;"> directive may go away</span>!</li>
-<li><b>$ActionSendTCPRebindInterval</b> nbr</a>- [available since 4.5.1] - instructs the TCP send
+<li><a href="omfwd.html"><b>$ActionSendTCPRebindInterval</b> nbr</a>- [available since 4.5.1] - instructs the TCP send
action to close and re-open the connection to the remote host every nbr of messages sent.
Zero, the default, means that no such processing is done. This directive is useful for
use with load-balancers. Note that there is some performance overhead associated with it,
so it is advisable to not too often &quot;rebind&quot; the connection (what
&quot;too often&quot; actually means depends on your configuration, a rule of thumb is
that it should be not be much more often than once per second).</li>
-<li><b>$ActionSendUDPRebindInterval</b> nbr</a>- [available since 4.3.2] - instructs the UDP send
+<li><a href="omfwd.html"><b>$ActionSendUDPRebindInterval</b> nbr</a>- [available since 4.3.2] - instructs the UDP send
action to rebind the send socket every nbr of messages sent. Zero, the default, means
that no rebind is done. This directive is useful for use with load-balancers.</li>
<li><b>$ActionWriteAllMarkMessages</b> [on/<b>off</b>]- [available since 5.1.5] - normally, mark messages
@@ -133,22 +133,22 @@ functionality.
the provided <i>name</i> (the default default ruleset is named
&quot;RSYSLOG_DefaultRuleset&quot;). It is advised to also read
our paper on <a href="multi_ruleset.html">using multiple rule sets in rsyslog</a>.</li>
-<li><b>$CreateDirs</b> [<b>on</b>/off] - create directories on an as-needed basis</li>
-<li><a href="rsconf1_dircreatemode.html">$DirCreateMode</a></li>
-<li><a href="rsconf1_dirgroup.html">$DirGroup</a></li>
-<li><a href="rsconf1_dirowner.html">$DirOwner</a></li>
+<li><a href="omfile.html"><b>$CreateDirs</b></a> [<b>on</b>/off] - create directories on an as-needed basis</li>
+<li><a href="omfile.html">$DirCreateMode</a></li>
+<li><a href="omfile.html">$DirGroup</a></li>
+<li><a href="omfile.html">$DirOwner</a></li>
<li><a href="rsconf1_dropmsgswithmaliciousdnsptrrecords.html">$DropMsgsWithMaliciousDnsPTRRecords</a></li>
<li><a href="rsconf1_droptrailinglfonreception.html">$DropTrailingLFOnReception</a></li>
-<li><a href="rsconf1_dynafilecachesize.html">$DynaFileCacheSize</a></li>
+<li><a href="omfile.html">$DynaFileCacheSize</a></li>
<li><a href="rsconf1_escape8bitcharsonreceive.html">$Escape8BitCharactersOnReceive</a></li>
<li><a href="rsconf1_escapecontrolcharactersonreceive.html">$EscapeControlCharactersOnReceive</a></li>
<li><b>$EscapeControlCharactersOnReceive</b> [<b>on</b>|off] - escape USASCII HT character</li>
<li>$SpaceLFOnReceive [on/<b>off</b>] - instructs rsyslogd to replace LF with spaces during message reception (sysklogd compatibility aid)</li>
<li>$ErrorMessagesToStderr [<b>on</b>|off] - direct rsyslogd error message to stderr (in addition to other targets)</li>
-<li><a href="rsconf1_failonchownfailure.html">$FailOnChownFailure</a></li>
-<li><a href="rsconf1_filecreatemode.html">$FileCreateMode</a></li>
-<li><a href="rsconf1_filegroup.html">$FileGroup</a></li>
-<li><a href="rsconf1_fileowner.html">$FileOwner</a></li>
+<li><a href="omfile.html">$FailOnChownFailure</a></li>
+<li><a href="omfile.html">$FileCreateMode</a></li>
+<li><a href="omfile.html">$FileGroup</a></li>
+<li><a href="omfile.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>
@@ -224,7 +224,7 @@ supported in order to be compliant to the upcoming new syslog RFC series.
<li><a href="rsconf1_maxopenfiles.html">$MaxOpenFiles</a></li>
<li><a href="rsconf1_moddir.html">$ModDir</a></li>
<li><a href="rsconf1_modload.html">$ModLoad</a></li>
-<li><b>$OMFileAsyncWriting</b> [on/<b>off</b>], if turned on, the files will be written
+<li><a href="omfile.html"><b>$OMFileAsyncWriting</b></a> [on/<b>off</b>], if turned on, the files will be written
in asynchronous mode via a separate thread. In that case, double buffers will be used so
that one buffer can be filled while the other buffer is being written. Note that in order
to enable $OMFileFlushInterval, $OMFileAsyncWriting must be set to "on". Otherwise, the flush
@@ -232,11 +232,11 @@ interval will be ignored. Also note that when $OMFileFlushOnTXEnd is "on" but
$OMFileAsyncWriting is off, output will only be written when the buffer is full. This may take
several hours, or even require a rsyslog shutdown. However, a buffer flush can be forced
in that case by sending rsyslogd a HUP signal.
-<li><b>$OMFileZipLevel</b> 0..9 [default 0] - if greater 0, turns on gzip compression
+<li><a href="omfile.html"><b>$OMFileZipLevel</b></a> 0..9 [default 0] - if greater 0, turns on gzip compression
of the output file. The higher the number, the better the compression, but also the
more CPU is required for zipping.</li>
-<li><b>$OMFileIOBufferSize</b> &lt;size_nbr&gt;, default 4k, size of the buffer used to writing output data. The larger the buffer, the potentially better performance is. The default of 4k is quite conservative, it is useful to go up to 64k, and 128K if you used gzip compression (then, even higher sizes may make sense)</li>
-<li><b>$OMFileFlushOnTXEnd</b> &lt;[<b>on</b>/off]&gt;, default on. Omfile has the
+<li><a href="omfile.html"><b>$OMFileIOBufferSize</b></a> &lt;size_nbr&gt;, default 4k, size of the buffer used to writing output data. The larger the buffer, the potentially better performance is. The default of 4k is quite conservative, it is useful to go up to 64k, and 128K if you used gzip compression (then, even higher sizes may make sense)</li>
+<li><a href="omfile.html"><b>$OMFileFlushOnTXEnd</b></a> &lt;[<b>on</b>/off]&gt;, default on. Omfile has the
capability to
write output using a buffered writer. Disk writes are only done when the buffer is
full. So if an error happens during that write, data is potentially lost. In cases where
@@ -246,7 +246,7 @@ error recovery thus can handle write errors without data loss. Note that this op
severely reduces the effect of zip compression and should be switched to off
for that use case. Note that the default -on- is primarily an aid to preserve
the traditional syslogd behaviour.</li>
-<li><a href="rsconf1_omfileforcechown.html">$omfileForceChown</a> - force ownership change for all files</li>
+<li><a href="omfile.html">$omfileForceChown</a> - force ownership change for all files</li>
<li><b>$RepeatedMsgContainsOriginalMsg</b> [on/<b>off</b>] - "last message repeated n times" messages, if generated,
have a different format that contains the message that is being repeated.
Note that only the first "n" characters are included, with n to be at least 80 characters, most
diff --git a/doc/rsyslog_conf_modules.html b/doc/rsyslog_conf_modules.html
index 650e20ad..cbd60faf 100644
--- a/doc/rsyslog_conf_modules.html
+++ b/doc/rsyslog_conf_modules.html
@@ -51,6 +51,10 @@ to message generators.
<p>Output modules process messages. With them, message formats can be transformed
and messages be transmitted to various different targets.
<ul>
+<li><a href="omfile.html">omfile</a> - file output module</li>
+<li><a href="omfwd.html">omfwd</a> - syslog forwarding output module</li>
+<li><a href="ompipe.html">ompipe</a> - named pipe output module</li>
+<li><a href="omusrmsg.html">omusrmsg</a> - user message output module</li>
<li><a href="omsnmp.html">omsnmp</a> - SNMP trap output module</li>
<li><a href="omstdout.html">omtdout</a> - stdout output module (mainly a test tool)</li>
<li><a href="omrelp.html">omrelp</a> - RELP output module</li>
diff --git a/doc/rsyslog_conf_templates.html b/doc/rsyslog_conf_templates.html
index bd0b3253..b97f6609 100644
--- a/doc/rsyslog_conf_templates.html
+++ b/doc/rsyslog_conf_templates.html
@@ -33,26 +33,31 @@ string generator module, you need to know how to call it. Each such module has a
which you need to know (look it up in the module doc or ask the developer). Let's assume
that "mystrgen" is the module name. Then you can define a template for that strgen
in the following way:
+
+<blockquote><code>template(name="MyTemplateName" type="plugin" string="mystrgen")</code></blockquote>
+<p>Legacy example:</p>
<blockquote><code>$template MyTemplateName,=mystrgen</code></blockquote>
(Of course, you must have first loaded the module via $ModLoad).
-<p>The important part is the equal sign: it tells the rsyslog config parser that
+<p>The important part is the equal sign in the legacy format: it tells the rsyslog config parser that
no string follows but a strgen module name.
<p>There are no additional parameters but the module name supported. This is because
there is no way to customize anything inside such a "template" other than by
modifying the code of the string generator.
<p>So for most use cases, string-generator module based templates are <b>not</b>
-the route to take. Usually, us use <b>string based templates</b> instead.
+the route to take. Usually, we use <b>string based templates</b> instead.
This is what the rest of the documentation now talks about.
<p>A template consists of a template directive, a name, the
actual template text and optional options. A sample is:</p>
+<blockquote><code>template(name="MyTemplateName" type="string" string="Example: Text %property% some more text\n" options)</code></blockquote>
+<p>Legacy example:</p>
<blockquote><code>$template MyTemplateName,"\7Text
%property% some more text\n",&lt;options&gt;</code></blockquote>
-<p>The "$template" is the template directive. It tells rsyslog
+<p>The "template" (legacy: $template) is the template directive. It tells rsyslog
that this line contains a template. "MyTemplateName" is the template
name. All
-other config lines refer to this name. The text within quotes is the
+other config lines refer to this name. The text within "string" is the
actual template text. The backslash is an escape character, much as it
is in C. It does all these "cool" things. For example, \7 rings the
bell (this is an ASCII value), \n is a new line. C programmers and perl
@@ -69,24 +74,30 @@ on this is below, on some lines of the property replacer.<br>
<br>
The &lt;options&gt; part is optional. It carries options
influencing the template as whole. See details below. Be sure NOT to
-mistake template options with property options - the later ones are
+mistake template options with property options - the latter ones are
processed by the property replacer and apply to a SINGLE property, only
(and not the whole template).<br>
<br>
Template options are case-insensitive. Currently defined are: </p>
-<p><b>sql</b> - format the string suitable for a SQL
+<p><b>option.sql</b> - format the string suitable for a SQL
statement in MySQL format. This will replace single quotes ("'") and
the backslash character by their backslash-escaped counterpart ("\'"
and "\\") inside each field. Please note that in MySQL configuration,
the <code class="literal">NO_BACKSLASH_ESCAPES</code>
mode must be turned off for this format to work (this is the default).</p>
-<p><b>stdsql</b> - format the string suitable for a
+<p><b>option.stdsql</b> - format the string suitable for a
SQL statement that is to be sent to a standards-compliant sql server.
This will replace single quotes ("'") by two single quotes ("''")
inside each field. You must use stdsql together with MySQL if in MySQL
configuration the
<code class="literal">NO_BACKSLASH_ESCAPES</code> is
turned on.</p>
+<p><b>option.json</b> - format the string suitable for a
+json statement.
+This will replace single quotes ("'") by two single quotes ("''")
+inside each field.</p>
+<p>At no time, multiple template option should be used. This can cause
+unpredictable behaviour and is against all logic.</p>
<p>Either the <b>sql</b> or <b>stdsql</b>&nbsp;
option <b>must</b> be specified when a template is used
for writing to a database, otherwise injection might occur. Please note
@@ -120,11 +131,41 @@ $template TraditionalFormat,"%timegenerated% %HOSTNAME% %syslogtag%%msg%\n"<br>
<br>
Properties can be accessed by the <a href="property_replacer.html">property
replacer</a> (see there for details).</p>
-<p><b>Please note that templates can also by
+<p>Templates can be used in the form of a <b>list</b> as well. This has been
+introduced with <b>6.5.0</b> The list consists of two parts which are either
+a <b>constant</b> or a <b>property</b>. The constants
+are taking the part of "text" that you usually enter in string-based templates.
+The properties stay variable, as they are a substitute for different values of a
+certain type. This type of template is extremely useful for complicated cases,
+as it helps you to easily keep an overview over the template. Though, it has
+the disadvantage of needing more effort to create it.</p>
+<br>Config example:
+<br><blockquote><code>template(name="MyTemplate" type="list" option.json="off") {
+ <br>constant(value="Test: ")
+ <br>property(name="msg" outname="mymessage")
+ <br>constant(value=" --!!!-- ")
+ <br>property(name="timereported" dateFormat="rfc3339" caseConversion="lower")
+ <br>constant(value="\n")
+ <br>}
+</code></blockquote>
+<p>First, the general template option will be defined. The values of the template
+itself get defined in the curly brackets. As it can be seen, we have constants
+and properties in exchange. Whereas constants will be filled with a value and probably
+some options, properties do direct to a property and the options that could be needed
+additional format definitions.</p>
+<p>We suggest to use separate lines for all constants and properties. This
+helps to keep a good overview over the different parts of the template.
+Though, writing it in a single line will work, it is much harder to debug
+if anything goes wrong with the template. </p>
+
+
+<p><b>Please note that templates can also be
used to generate selector lines with dynamic file names.</b> For
example, if you would like to split syslog messages from different
hosts to different files (one per host), you can define the following
template:</p>
+<blockquote><code>template (name="DynFile" type="string" string="/var/log/system-%HOSTNAME%.log")</code></blockquote>
+<p>Legacy example:</p>
<blockquote><code>$template
DynFile,"/var/log/system-%HOSTNAME%.log"</code></blockquote>
<p>This template can then be used when defining an output
@@ -169,7 +210,7 @@ out, but this may happen.</li>
is meant to be written to a log file. Do <b>not</b> use for production or remote
forwarding.</li>
</ul>
-<h3>String-based Template Samples</h3>
+<h3>Legacy String-based Template Samples</h3>
<p>This section provides some sample of what the default formats would
look as a text-based template. Hopefully, their description is self-explanatory.
Note that each $Template statement is on a <b>single</b> line, but probably broken
diff --git a/doc/v6compatibility.html b/doc/v6compatibility.html
index 1f830854..eec0784b 100644
--- a/doc/v6compatibility.html
+++ b/doc/v6compatibility.html
@@ -112,6 +112,15 @@ to spot why things went wrong (and if at all).
<p>Due to their positive effect on performance and comparatively low overhead,
default batch sizes have been increased. Starting with 6.3.4, the action queues
have a default batch size of 128 messages.
+<h2>Default action queue enqueue timeout</h2>
+<p>This timeout previously was 2seconds, and has been reduced to 50ms (starting with 6.5.0). This change
+was made as a long timeout will caused delays in the associated main queue, something
+that was quite unexpected to users. Now, this can still happen, but the effect is much
+less harsh (but still considerable on a busy system). Also, 50ms should be fairly enough
+for most output sources, except when they are really broken (like network disconnect). If
+they are really broken, even a 2second timeout does not help, so we hopefully get the best
+of both worlds with the new timeout. A specific timeout can of course still be configured,
+it is just the timeout that changed.
<h2>outchannels</h2>
<p>Outchannels are a to-be-removed feature of rsyslog, at least as far as the config
syntax is concerned. Nevertheless, v6 still supports it, but a new syntax is required
@@ -127,6 +136,16 @@ Note that this syntax is available starting with rsyslog v4. It is important to
mind that future versions of rsyslog will require different syntax and/or drop outchannel support
completely. So if at all possible, avoid using this feature. If you must use it, be prepared for
future changes and watch announcements very carefully.
+<h2>ompipe default template</h2>
+<p>Starting with 6.5.0, ompipe does no longer use the omfile default template.
+Instead, the default template must be set via the module load statement.
+An example is
+<blockquote><code>
+module(load="builtin:ompipe" template="myDefaultTemplate")
+</code> </blockquote>
+<p>For obvious reasons, the default template must be defined somewhere in
+the config file, otherwise errors will happen during the config load
+phase.
<h2>omusrmsg</h2>
<p>The omusrmsg module is used to send messages to users. In legacy-legacy
config format (that is the very old sysklogd style), it was suffucient to use
diff --git a/grammar/grammar.y b/grammar/grammar.y
index 402b1a57..29ff02fe 100644
--- a/grammar/grammar.y
+++ b/grammar/grammar.y
@@ -49,6 +49,7 @@ extern int yyerror(char*);
enum cnfobjType objType;
struct cnfobj *obj;
struct nvlst *nvlst;
+ struct objlst *objlst;
struct cnfactlst *actlst;
struct cnfexpr *expr;
struct cnfrule *rule;
@@ -63,8 +64,12 @@ extern int yyerror(char*);
%token ENDOBJ
%token <s> CFSYSLINE
%token BEGIN_ACTION
+%token BEGIN_PROPERTY
+%token BEGIN_CONSTANT
+%token BEGIN_TPL
%token STOP
%token <s> LEGACY_ACTION
+%token <s> LEGACY_RULESET
%token <s> PRIFILT
%token <s> PROPFILT
%token <s> BSD_TAG_SELECTOR
@@ -89,7 +94,8 @@ extern int yyerror(char*);
%token CMP_STARTSWITHI
%type <nvlst> nv nvlst
-%type <obj> obj
+%type <obj> obj property constant
+%type <objlst> propconst
%type <actlst> actlst
%type <actlst> act
%type <s> cfsysline
@@ -124,10 +130,21 @@ conf: /* empty (to end recursion) */
| conf obj { cnfDoObj($2); }
| conf rule { cnfDoRule($2); }
| conf cfsysline { cnfDoCfsysline($2); }
+ | conf LEGACY_RULESET { cnfDoCfsysline($2); }
| conf BSD_TAG_SELECTOR { cnfDoBSDTag($2); }
| conf BSD_HOST_SELECTOR { cnfDoBSDHost($2); }
obj: BEGINOBJ nvlst ENDOBJ { $$ = cnfobjNew($1, $2); }
| BEGIN_ACTION nvlst ENDOBJ { $$ = cnfobjNew(CNFOBJ_ACTION, $2); }
+ | BEGIN_TPL nvlst ENDOBJ { $$ = cnfobjNew(CNFOBJ_TPL, $2); }
+ | BEGIN_TPL nvlst ENDOBJ '{' propconst '}'
+ { $$ = cnfobjNew(CNFOBJ_TPL, $2);
+ $$->subobjs = $5;
+ }
+propconst: { $$ = NULL; }
+ | propconst property { $$ = objlstAdd($1, $2); }
+ | propconst constant { $$ = objlstAdd($1, $2); }
+property: BEGIN_PROPERTY nvlst ENDOBJ { $$ = cnfobjNew(CNFOBJ_PROPERTY, $2); }
+constant: BEGIN_CONSTANT nvlst ENDOBJ { $$ = cnfobjNew(CNFOBJ_CONSTANT, $2); }
cfsysline: CFSYSLINE { $$ = $1; }
nvlst: { $$ = NULL; }
| nvlst nv { $2->next = $1; $$ = $2; }
diff --git a/grammar/lexer.l b/grammar/lexer.l
index e688ffce..0faacf2e 100644
--- a/grammar/lexer.l
+++ b/grammar/lexer.l
@@ -9,7 +9,7 @@
* cases. So while we hope that cfsysline support can be dropped some time in
* the future, we will probably keep these useful constructs.
*
- * Copyright 2011 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2011-2012 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of the rsyslog runtime library.
*
@@ -151,6 +151,12 @@ int fileno(FILE *stream);
BEGIN INITIAL; }
"global"[ \n\t]*"(" { yylval.objType = CNFOBJ_GLOBAL;
BEGIN INOBJ; return BEGINOBJ; }
+"template"[ \n\t]*"(" { yylval.objType = CNFOBJ_TPL;
+ BEGIN INOBJ; return BEGIN_TPL; }
+"property"[ \n\t]*"(" { yylval.objType = CNFOBJ_PROPERTY;
+ BEGIN INOBJ; return BEGIN_PROPERTY; }
+"constant"[ \n\t]*"(" { yylval.objType = CNFOBJ_CONSTANT;
+ BEGIN INOBJ; return BEGIN_CONSTANT; }
"input"[ \n\t]*"(" { yylval.objType = CNFOBJ_INPUT;
BEGIN INOBJ; return BEGINOBJ; }
"module"[ \n\t]*"(" { yylval.objType = CNFOBJ_MODULE;
@@ -186,6 +192,9 @@ int fileno(FILE *stream);
if(!strncasecmp(yytext, "$includeconfig ", 14)) {
yyless(14);
BEGIN INCL;
+ } else if(!strncasecmp(yytext, "$ruleset ", 9)) {
+ yylval.s = strdup(yytext);
+ return LEGACY_RULESET;
} else {
yylval.s = strdup(yytext);
return CFSYSLINE;
diff --git a/grammar/rainerscript.c b/grammar/rainerscript.c
index a5cc10c2..33630a76 100644
--- a/grammar/rainerscript.c
+++ b/grammar/rainerscript.c
@@ -41,6 +41,11 @@
#include "grammar.h"
#include "queue.h"
#include "srUtils.h"
+#include "regexp.h"
+#include "obj.h"
+
+DEFobjCurrIf(obj)
+DEFobjCurrIf(regexp)
void
readConfFile(FILE *fp, es_str_t **str)
@@ -93,6 +98,62 @@ readConfFile(FILE *fp, es_str_t **str)
es_addChar(str, '\0');
}
+struct objlst*
+objlstNew(struct cnfobj *o)
+{
+ struct objlst *lst;
+
+ if((lst = malloc(sizeof(struct objlst))) != NULL) {
+ lst->next = NULL;
+ lst->obj = o;
+ }
+dbgprintf("AAAA: creating new objlst\n");
+cnfobjPrint(o);
+
+ return lst;
+}
+
+/* add object to end of object list, always returns pointer to root object */
+struct objlst*
+objlstAdd(struct objlst *root, struct cnfobj *o)
+{
+ struct objlst *l;
+ struct objlst *newl;
+
+ newl = objlstNew(o);
+ if(root == 0) {
+ root = newl;
+ } else { /* find last, linear search ok, as only during config phase */
+ for(l = root ; l->next != NULL ; l = l->next)
+ ;
+ l->next = newl;
+ }
+ return root;
+}
+
+void
+objlstDestruct(struct objlst *lst)
+{
+ struct objlst *toDel;
+
+ while(lst != NULL) {
+ toDel = lst;
+ lst = lst->next;
+ // TODO: delete object
+ free(toDel);
+ }
+}
+
+void
+objlstPrint(struct objlst *lst)
+{
+ dbgprintf("objlst %p:\n", lst);
+ while(lst != NULL) {
+ cnfobjPrint(lst->obj);
+ lst = lst->next;
+ }
+}
+
struct nvlst*
nvlstNew(es_str_t *name, es_str_t *value)
{
@@ -576,6 +637,7 @@ cnfobjNew(enum cnfobjType objType, struct nvlst *lst)
nvlstChkDupes(lst);
o->objType = objType;
o->nvlst = lst;
+ o->subobjs = NULL;
}
return o;
@@ -677,7 +739,6 @@ cnfactlstReverse(struct cnfactlst *actlst)
prev = NULL;
while(actlst != NULL) {
- //dbgprintf("reversing: %s\n", actlst->data.legActLine);
curr = actlst;
actlst = actlst->next;
curr->syslines = cnfcfsyslinelstReverse(curr->syslines);
@@ -749,8 +810,7 @@ var2Number(struct var *r, int *bSuccess)
return n;
}
-/* ensure that retval is a string; if string is no number,
- * emit error message and set number to 0.
+/* ensure that retval is a string
*/
static inline es_str_t *
var2String(struct var *r, int *bMustFree)
@@ -774,6 +834,7 @@ doFuncCall(struct cnffunc *func, struct var *ret, void* usrptr)
int bMustFree;
es_str_t *estr;
char *str;
+ int retval;
struct var r[CNFFUNC_MAX_ARGS];
dbgprintf("rainerscript: executing function id %d\n", func->fID);
@@ -816,6 +877,7 @@ doFuncCall(struct cnffunc *func, struct var *ret, void* usrptr)
es_tolower(estr);
ret->datatype = 'S';
ret->d.estr = estr;
+ if(r[0].datatype == 'S') es_deleteStr(r[0].d.estr);
break;
case CNFFUNC_CSTR:
cnfexprEval(func->expr[0], &r[0], usrptr);
@@ -824,6 +886,7 @@ doFuncCall(struct cnffunc *func, struct var *ret, void* usrptr)
estr = es_strdup(estr);
ret->datatype = 'S';
ret->d.estr = estr;
+ if(r[0].datatype == 'S') es_deleteStr(r[0].d.estr);
break;
case CNFFUNC_CNUM:
if(func->expr[0]->nodetype == 'N') {
@@ -838,6 +901,24 @@ doFuncCall(struct cnffunc *func, struct var *ret, void* usrptr)
}
ret->datatype = 'N';
break;
+ case CNFFUNC_RE_MATCH:
+ cnfexprEval(func->expr[0], &r[0], usrptr);
+ estr = var2String(&r[0], &bMustFree);
+ str = es_str2cstr(estr, NULL);
+ retval = regexp.regexec(func->funcdata, str, 0, NULL, 0);
+ if(retval == 0)
+ ret->d.n = 1;
+ else {
+ ret->d.n = 0;
+ if(retval != REG_NOMATCH) {
+ DBGPRINTF("re_match: regexec returned error %d\n", retval);
+ }
+ }
+ ret->datatype = 'N';
+ if(bMustFree) es_deleteStr(estr);
+ free(str);
+ if(r[0].datatype == 'S') es_deleteStr(r[0].d.estr);
+ break;
default:
if(Debug) {
fname = es_str2cstr(func->fname, NULL);
@@ -891,7 +972,7 @@ cnfexprEval(struct cnfexpr *expr, struct var *ret, void* usrptr)
int bMustFree, bMustFree2;
long long n_r, n_l;
- //dbgprintf("eval expr %p, type '%c'(%u)\n", expr, expr->nodetype, expr->nodetype);
+ dbgprintf("eval expr %p, type '%c'(%u)\n", expr, expr->nodetype, expr->nodetype);
switch(expr->nodetype) {
/* note: comparison operations are extremely similar. The code can be copyied, only
* places flagged with "CMP" need to be changed.
@@ -1199,6 +1280,78 @@ cnfexprEval(struct cnfexpr *expr, struct var *ret, void* usrptr)
}
}
+//---------------------------------------------------------
+
+static inline void
+cnffuncDestruct(struct cnffunc *func)
+{
+ unsigned short i;
+
+ for(i = 0 ; i < func->nParams ; ++i) {
+ cnfexprDestruct(func->expr[i]);
+ }
+ /* some functions require special destruction */
+ switch(func->fID) {
+ case CNFFUNC_RE_MATCH:
+ regexp.regfree(func->funcdata);
+ free(func->funcdata);
+ free(func->fname);
+ break;
+ default:break;
+ }
+}
+
+/* Destruct an expression and all sub-expressions contained in it.
+ */
+void
+cnfexprDestruct(struct cnfexpr *expr)
+{
+
+ dbgprintf("cnfexprDestruct expr %p, type '%c'(%u)\n", expr, expr->nodetype, expr->nodetype);
+ switch(expr->nodetype) {
+ case CMP_NE:
+ case CMP_EQ:
+ case CMP_LE:
+ case CMP_GE:
+ case CMP_LT:
+ case CMP_GT:
+ case CMP_STARTSWITH:
+ case CMP_STARTSWITHI:
+ case CMP_CONTAINS:
+ case CMP_CONTAINSI:
+ case OR:
+ case AND:
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '%': /* binary */
+ cnfexprDestruct(expr->l);
+ cnfexprDestruct(expr->r);
+ break;
+ case NOT:
+ case 'M': /* unary */
+ cnfexprDestruct(expr->r);
+ break;
+ case 'N':
+ break;
+ case 'S':
+ es_deleteStr(((struct cnfstringval*)expr)->estr);
+ break;
+ case 'V':
+ free(((struct cnfvar*)expr)->name);
+ break;
+ case 'F':
+ cnffuncDestruct((struct cnffunc*)expr);
+ break;
+ default:break;
+ }
+ free(expr);
+}
+
+//---- END
+
+
/* Evaluate an expression as a bool. This is added because expressions are
* mostly used inside filters, and so this function is quite common and
* important.
@@ -1488,11 +1641,53 @@ funcName2ID(es_str_t *fname, unsigned short nParams)
return CNFFUNC_INVALID;
}
return CNFFUNC_CNUM;
+ } else if(!es_strbufcmp(fname, (unsigned char*)"re_match", sizeof("re_match") - 1)) {
+ if(nParams != 2) {
+ parser_errmsg("number of parameters for re_match() must be two "
+ "but is %d.", nParams);
+ return CNFFUNC_INVALID;
+ }
+ return CNFFUNC_RE_MATCH;
} else {
return CNFFUNC_INVALID;
}
}
+
+static inline rsRetVal
+initFunc_re_match(struct cnffunc *func)
+{
+ rsRetVal localRet;
+ char *regex = NULL;
+ regex_t *re;
+ DEFiRet;
+
+ func->funcdata = NULL;
+ if(func->expr[1]->nodetype != 'S') {
+ parser_errmsg("param 2 of re_match() must be a constant string");
+ FINALIZE;
+ }
+
+ CHKmalloc(re = malloc(sizeof(regex_t)));
+ func->funcdata = re;
+
+ regex = es_str2cstr(((struct cnfstringval*) func->expr[1])->estr, NULL);
+
+ if((localRet = objUse(regexp, LM_REGEXP_FILENAME)) == RS_RET_OK) {
+ if(regexp.regcomp(re, (char*) regex, REG_EXTENDED) != 0) {
+ parser_errmsg("cannot compile regex '%s'", regex);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ } else { /* regexp object could not be loaded */
+ parser_errmsg("could not load regex support - regex ignored");
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+finalize_it:
+ free(regex);
+ RETiRet;
+}
+
struct cnffunc *
cnffuncNew(es_str_t *fname, struct cnffparamlst* paramlst)
{
@@ -1519,6 +1714,14 @@ cnffuncNew(es_str_t *fname, struct cnffparamlst* paramlst)
param = param->next;
free(toDel);
}
+ /* some functions require special initialization */
+ switch(func->fID) {
+ case CNFFUNC_RE_MATCH:
+ /* need to compile the regexp in param 2, so this MUST be a constant */
+ initFunc_re_match(func);
+ break;
+ default:break;
+ }
}
return func;
}
@@ -1616,3 +1819,13 @@ cstrPrint(char *text, es_str_t *estr)
dbgprintf("%s%s", text, str);
free(str);
}
+
+/* init must be called once before any parsing of the script files start */
+rsRetVal
+initRainerscript(void)
+{
+ DEFiRet;
+ CHKiRet(objGetObjInterface(&obj));
+finalize_it:
+ RETiRet;
+}
diff --git a/grammar/rainerscript.h b/grammar/rainerscript.h
index e11ae62f..4c625cd8 100644
--- a/grammar/rainerscript.h
+++ b/grammar/rainerscript.h
@@ -16,6 +16,9 @@ enum cnfobjType {
CNFOBJ_GLOBAL,
CNFOBJ_INPUT,
CNFOBJ_MODULE,
+ CNFOBJ_TPL,
+ CNFOBJ_PROPERTY,
+ CNFOBJ_CONSTANT,
CNFOBJ_INVALID = 0
};
@@ -35,6 +38,15 @@ cnfobjType2str(enum cnfobjType ot)
case CNFOBJ_MODULE:
return "module";
break;
+ case CNFOBJ_TPL:
+ return "template";
+ break;
+ case CNFOBJ_PROPERTY:
+ return "property";
+ break;
+ case CNFOBJ_CONSTANT:
+ return "constant";
+ break;
default:return "error: invalid cnfobjType";
}
}
@@ -60,6 +72,12 @@ struct var {
struct cnfobj {
enum cnfobjType objType;
struct nvlst *nvlst;
+ struct objlst *subobjs;
+};
+
+struct objlst {
+ struct objlst *next;
+ struct cnfobj *obj;
};
struct nvlst {
@@ -163,7 +181,8 @@ enum cnffuncid {
CNFFUNC_GETENV,
CNFFUNC_TOLOWER,
CNFFUNC_CSTR,
- CNFFUNC_CNUM
+ CNFFUNC_CNUM,
+ CNFFUNC_RE_MATCH
};
struct cnffunc {
@@ -171,6 +190,7 @@ struct cnffunc {
es_str_t *fname;
unsigned short nParams;
enum cnffuncid fID; /* function ID for built-ins, 0 means use name */
+ void *funcdata; /* global data for function-specific use (e.g. compiled regex) */
struct cnfexpr *expr[];
};
@@ -216,6 +236,9 @@ struct cnfparamvals { /* the values we obtained for param descr. */
int cnfParseBuffer(char *buf, unsigned lenBuf);
void readConfFile(FILE *fp, es_str_t **str);
+struct objlst* objlstNew(struct cnfobj *obj);
+void objlstDestruct(struct objlst *lst);
+void objlstPrint(struct objlst *lst);
struct nvlst* nvlstNew(es_str_t *name, es_str_t *value);
void nvlstDestruct(struct nvlst *lst);
void nvlstPrint(struct nvlst *lst);
@@ -233,6 +256,7 @@ struct cnfexpr* cnfexprNew(unsigned nodetype, struct cnfexpr *l, struct cnfexpr
void cnfexprPrint(struct cnfexpr *expr, int indent);
void cnfexprEval(struct cnfexpr *expr, struct var *ret, void *pusr);
int cnfexprEvalBool(struct cnfexpr *expr, void *usrptr);
+void cnfexprDestruct(struct cnfexpr *expr);
struct cnfnumval* cnfnumvalNew(long long val);
struct cnfstringval* cnfstringvalNew(es_str_t *estr);
struct cnfrule * cnfruleNew(enum cnfFiltType filttype, struct cnfactlst *actlst);
@@ -249,6 +273,7 @@ void cnfparamsPrint(struct cnfparamblk *params, struct cnfparamvals *vals);
void varDelete(struct var *v);
void cnfparamvalsDestruct(struct cnfparamvals *paramvals, struct cnfparamblk *blk);
void cnfcfsyslinelstDestruct(struct cnfcfsyslinelst *cfslst);
+rsRetVal initRainerscript(void);
/* debug helper */
void cstrPrint(char *text, es_str_t *estr);
diff --git a/plugins/imklog/imklog.c b/plugins/imklog/imklog.c
index f476c5ff..93323707 100644
--- a/plugins/imklog/imklog.c
+++ b/plugins/imklog/imklog.c
@@ -59,6 +59,7 @@
#include "net.h"
#include "glbl.h"
#include "prop.h"
+#include "errmsg.h"
#include "unicode-helper.h"
MODULE_TYPE_INPUT
@@ -71,22 +72,34 @@ DEFobjCurrIf(datetime)
DEFobjCurrIf(glbl)
DEFobjCurrIf(prop)
DEFobjCurrIf(net)
+DEFobjCurrIf(errmsg)
/* config settings */
typedef struct configSettings_s {
- int dbgPrintSymbols; /* this one is extern so the helpers can access it! */
- int symbols_twice;
- int use_syscall;
- int symbol_lookup; /* on recent kernels > 2.6, the kernel does this */
int bPermitNonKernel; /* permit logging of messages not having LOG_KERN facility */
int iFacilIntMsg; /* the facility to use for internal messages (set by driver) */
uchar *pszPath;
- int console_log_level;
+ int console_log_level; /* still used for BSD */
} configSettings_t;
static configSettings_t cs;
static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current load process */
+static int bLegacyCnfModGlobalsPermitted;/* are legacy module-global config parameters permitted? */
+
+/* module-global parameters */
+static struct cnfparamdescr modpdescr[] = {
+ { "logpath", eCmdHdlrGetWord, 0 },
+ { "permitnonkernelfacility", eCmdHdlrBinary, 0 },
+ { "consoleloglevel", eCmdHdlrInt, 0 },
+ { "internalmsgfacility", eCmdHdlrFacility, 0 }
+};
+static struct cnfparamblk modpblk =
+ { CNFPARAMBLK_VERSION,
+ sizeof(modpdescr)/sizeof(struct cnfparamdescr),
+ modpdescr
+ };
+
static prop_t *pInputName = NULL; /* there is only one global inputName for all messages generated by this module */
@@ -96,10 +109,6 @@ static prop_t *pLocalHostIP = NULL; /* a pseudo-constant propterty for 127.0.0.1
static inline void
initConfigSettings(void)
{
- cs.dbgPrintSymbols = 0;
- cs.symbols_twice = 0;
- cs.use_syscall = 0;
- cs.symbol_lookup = 0;
cs.bPermitNonKernel = 0;
cs.console_log_level = -1;
cs.pszPath = NULL;
@@ -276,28 +285,77 @@ BEGINbeginCnfLoad
CODESTARTbeginCnfLoad
loadModConf = pModConf;
pModConf->pConf = pConf;
+ /* init our settings */
+ pModConf->pszPath = NULL;
+ pModConf->bPermitNonKernel = 0;
+ pModConf->console_log_level = -1;
+ pModConf->iFacilIntMsg = klogFacilIntMsg();
+ loadModConf->configSetViaV2Method = 0;
+ bLegacyCnfModGlobalsPermitted = 1;
/* init legacy config vars */
initConfigSettings();
ENDbeginCnfLoad
+BEGINsetModCnf
+ struct cnfparamvals *pvals = NULL;
+ int i;
+CODESTARTsetModCnf
+ pvals = nvlstGetParams(lst, &modpblk, NULL);
+ if(pvals == NULL) {
+ errmsg.LogError(0, RS_RET_MISSING_CNFPARAMS, "error processing module "
+ "config parameters [module(...)]");
+ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+ }
+
+ if(Debug) {
+ dbgprintf("module (global) param blk for imklog:\n");
+ cnfparamsPrint(&modpblk, pvals);
+ }
+
+ for(i = 0 ; i < modpblk.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(modpblk.descr[i].name, "logpath")) {
+ loadModConf->pszPath = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(modpblk.descr[i].name, "permitnonkernelfacility")) {
+ loadModConf->bPermitNonKernel = (int) pvals[i].val.d.n;
+ } else if(!strcmp(modpblk.descr[i].name, "consoleloglevel")) {
+ loadModConf->console_log_level= (int) pvals[i].val.d.n;
+ } else if(!strcmp(modpblk.descr[i].name, "internalmsgfacility")) {
+ loadModConf->iFacilIntMsg = (int) pvals[i].val.d.n;
+ } else {
+ dbgprintf("imklog: program error, non-handled "
+ "param '%s' in beginCnfLoad\n", modpblk.descr[i].name);
+ }
+ }
+
+ /* disable legacy module-global config directives */
+ bLegacyCnfModGlobalsPermitted = 0;
+ loadModConf->configSetViaV2Method = 1;
+
+finalize_it:
+ if(pvals != NULL)
+ cnfparamvalsDestruct(pvals, &modpblk);
+ENDsetModCnf
+
+
BEGINendCnfLoad
CODESTARTendCnfLoad
- /* persist module-specific settings from legacy config system */
- loadModConf->dbgPrintSymbols = cs.dbgPrintSymbols;
- loadModConf->symbols_twice = cs.symbols_twice;
- loadModConf->use_syscall = cs.use_syscall;
- loadModConf->bPermitNonKernel = cs.bPermitNonKernel;
- loadModConf->iFacilIntMsg = cs.iFacilIntMsg;
- loadModConf->console_log_level = cs.console_log_level;
- if((cs.pszPath == NULL) || (cs.pszPath[0] == '\0')) {
- loadModConf->pszPath = NULL;
- if(cs.pszPath != NULL)
- free(cs.pszPath);
- } else {
- loadModConf->pszPath = cs.pszPath;
+ if(!loadModConf->configSetViaV2Method) {
+ /* persist module-specific settings from legacy config system */
+ loadModConf->bPermitNonKernel = cs.bPermitNonKernel;
+ loadModConf->iFacilIntMsg = cs.iFacilIntMsg;
+ loadModConf->console_log_level = cs.console_log_level;
+ if((cs.pszPath == NULL) || (cs.pszPath[0] == '\0')) {
+ loadModConf->pszPath = NULL;
+ if(cs.pszPath != NULL)
+ free(cs.pszPath);
+ } else {
+ loadModConf->pszPath = cs.pszPath;
+ }
+ cs.pszPath = NULL;
}
- cs.pszPath = NULL;
loadModConf = NULL; /* done loading */
ENDendCnfLoad
@@ -348,6 +406,7 @@ CODESTARTmodExit
objRelease(net, CORE_COMPONENT);
objRelease(datetime, CORE_COMPONENT);
objRelease(prop, CORE_COMPONENT);
+ objRelease(errmsg, CORE_COMPONENT);
ENDmodExit
@@ -355,15 +414,12 @@ BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
CODEqueryEtryPt_STD_CONF2_QUERIES
+CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES
CODEqueryEtryPt_STD_CONF2_PREPRIVDROP_QUERIES
ENDqueryEtryPt
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
{
- cs.dbgPrintSymbols = 0;
- cs.symbols_twice = 0;
- cs.use_syscall = 0;
- cs.symbol_lookup = 0;
cs.bPermitNonKernel = 0;
if(cs.pszPath != NULL) {
free(cs.pszPath);
@@ -381,6 +437,7 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(glbl, CORE_COMPONENT));
CHKiRet(objUse(prop, CORE_COMPONENT));
CHKiRet(objUse(net, CORE_COMPONENT));
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
/* we need to create the inputName property (only once during our lifetime) */
CHKiRet(prop.CreateStringProp(&pInputName, UCHAR_CONSTANT("imklog"), sizeof("imklog") - 1));
@@ -389,22 +446,22 @@ CODEmodInit_QueryRegCFSLineHdlr
/* init legacy config settings */
initConfigSettings();
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"debugprintkernelsymbols", 0, eCmdHdlrBinary,
- NULL, &cs.dbgPrintSymbols, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogpath", 0, eCmdHdlrGetWord,
- NULL, &cs.pszPath, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogsymbollookup", 0, eCmdHdlrBinary,
- NULL, &cs.symbol_lookup, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogsymbolstwice", 0, eCmdHdlrBinary,
- NULL, &cs.symbols_twice, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogusesyscallinterface", 0, eCmdHdlrBinary,
- NULL, &cs.use_syscall, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogpermitnonkernelfacility", 0, eCmdHdlrBinary,
- NULL, &cs.bPermitNonKernel, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogconsoleloglevel", 0, eCmdHdlrInt,
- NULL, &cs.console_log_level, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"kloginternalmsgfacility", 0, eCmdHdlrFacility,
- NULL, &cs.iFacilIntMsg, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"debugprintkernelsymbols", 0, eCmdHdlrGoneAway,
+ NULL, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"klogpath", 0, eCmdHdlrGetWord,
+ NULL, &cs.pszPath, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogsymbollookup", 0, eCmdHdlrGoneAway,
+ NULL, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogsymbolstwice", 0, eCmdHdlrGoneAway,
+ NULL, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogusesyscallinterface", 0, eCmdHdlrGoneAway,
+ NULL, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"klogpermitnonkernelfacility", 0, eCmdHdlrBinary,
+ NULL, &cs.bPermitNonKernel, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"klogconsoleloglevel", 0, eCmdHdlrInt,
+ NULL, &cs.console_log_level, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"kloginternalmsgfacility", 0, eCmdHdlrFacility,
+ NULL, &cs.iFacilIntMsg, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
diff --git a/plugins/imklog/imklog.h b/plugins/imklog/imklog.h
index 795dd68c..acfb50ab 100644
--- a/plugins/imklog/imklog.h
+++ b/plugins/imklog/imklog.h
@@ -31,15 +31,12 @@
/* we need to have the modConf type present in all submodules */
struct modConfData_s {
- int dbgPrintSymbols;
- int symbols_twice;
- int use_syscall;
- int symbol_lookup;
- int bPermitNonKernel;
+ rsconf_t *pConf;
int iFacilIntMsg;
uchar *pszPath;
int console_log_level;
- rsconf_t *pConf;
+ sbool bPermitNonKernel;
+ sbool configSetViaV2Method;
};
/* interface to "drivers"
diff --git a/plugins/immark/immark.c b/plugins/immark/immark.c
index 273af021..0e946c0b 100644
--- a/plugins/immark/immark.c
+++ b/plugins/immark/immark.c
@@ -58,9 +58,26 @@ DEFobjCurrIf(errmsg)
static int iMarkMessagePeriod = DEFAULT_MARK_PERIOD;
struct modConfData_s {
+ rsconf_t *pConf; /* our overall config object */
int iMarkMessagePeriod;
+ sbool configSetViaV2Method;
};
+/* module-global parameters */
+static struct cnfparamdescr modpdescr[] = {
+ { "interval", eCmdHdlrInt, 0 }
+};
+static struct cnfparamblk modpblk =
+ { CNFPARAMBLK_VERSION,
+ sizeof(modpdescr)/sizeof(struct cnfparamdescr),
+ modpdescr
+ };
+
+
+static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
+static int bLegacyCnfModGlobalsPermitted;/* are legacy module-global config parameters permitted? */
+
+
BEGINisCompatibleWithFeature
CODESTARTisCompatibleWithFeature
if(eFeat == sFEATURENonCancelInputTermination)
@@ -75,12 +92,57 @@ ENDafterRun
BEGINbeginCnfLoad
CODESTARTbeginCnfLoad
+ loadModConf = pModConf;
+ pModConf->pConf = pConf;
+ /* init our settings */
+ pModConf->iMarkMessagePeriod = DEFAULT_MARK_PERIOD;
+ loadModConf->configSetViaV2Method = 0;
+ bLegacyCnfModGlobalsPermitted = 1;
ENDbeginCnfLoad
+BEGINsetModCnf
+ struct cnfparamvals *pvals = NULL;
+ int i;
+CODESTARTsetModCnf
+ pvals = nvlstGetParams(lst, &modpblk, NULL);
+ if(pvals == NULL) {
+ errmsg.LogError(0, RS_RET_MISSING_CNFPARAMS, "error processing module "
+ "config parameters [module(...)]");
+ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+ }
+
+ if(Debug) {
+ dbgprintf("module (global) param blk for imuxsock:\n");
+ cnfparamsPrint(&modpblk, pvals);
+ }
+
+ for(i = 0 ; i < modpblk.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(modpblk.descr[i].name, "interval")) {
+ loadModConf->iMarkMessagePeriod = (int) pvals[i].val.d.n;
+ } else {
+ dbgprintf("imuxsock: program error, non-handled "
+ "param '%s' in beginCnfLoad\n", modpblk.descr[i].name);
+ }
+ }
+
+ /* disable legacy module-global config directives */
+ bLegacyCnfModGlobalsPermitted = 0;
+ loadModConf->configSetViaV2Method = 1;
+
+finalize_it:
+ if(pvals != NULL)
+ cnfparamvalsDestruct(pvals, &modpblk);
+ENDsetModCnf
+
+
BEGINendCnfLoad
CODESTARTendCnfLoad
- pModConf->iMarkMessagePeriod = iMarkMessagePeriod;
+ if(!loadModConf->configSetViaV2Method) {
+ pModConf->iMarkMessagePeriod = iMarkMessagePeriod;
+ }
ENDendCnfLoad
@@ -97,6 +159,7 @@ ENDcheckCnf
BEGINactivateCnf
CODESTARTactivateCnf
MarkInterval = pModConf->iMarkMessagePeriod;
+ DBGPRINTF("immark set MarkInterval to %d\n", MarkInterval);
ENDactivateCnf
@@ -150,6 +213,7 @@ BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
CODEqueryEtryPt_STD_CONF2_QUERIES
+CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES
CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
ENDqueryEtryPt
@@ -167,8 +231,8 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(errmsg, CORE_COMPONENT));
/* legacy config handlers */
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"markmessageperiod", 0, eCmdHdlrInt, NULL,
- &iMarkMessagePeriod, STD_LOADABLE_MODULE_ID));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"markmessageperiod", 0, eCmdHdlrInt, NULL,
+ &iMarkMessagePeriod, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
diff --git a/plugins/impstats/impstats.c b/plugins/impstats/impstats.c
index 4fec8e70..62599969 100644
--- a/plugins/impstats/impstats.c
+++ b/plugins/impstats/impstats.c
@@ -59,6 +59,7 @@ typedef struct configSettings_s {
int iFacility;
int iSeverity;
int bJSON;
+ int bCEE;
} configSettings_t;
struct modConfData_s {
@@ -67,15 +68,39 @@ struct modConfData_s {
int iFacility;
int iSeverity;
statsFmtType_t statsFmt;
+ sbool configSetViaV2Method;
};
static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current load process */
-
static configSettings_t cs;
-
+static int bLegacyCnfModGlobalsPermitted;/* are legacy module-global config parameters permitted? */
static prop_t *pInputName = NULL;
+/* module-global parameters */
+static struct cnfparamdescr modpdescr[] = {
+ { "interval", eCmdHdlrInt, 0 },
+ { "facility", eCmdHdlrInt, 0 },
+ { "severity", eCmdHdlrInt, 0 },
+ { "format", eCmdHdlrGetWord, 0 }
+};
+static struct cnfparamblk modpblk =
+ { CNFPARAMBLK_VERSION,
+ sizeof(modpdescr)/sizeof(struct cnfparamdescr),
+ modpdescr
+ };
+
+BEGINmodExit
+CODESTARTmodExit
+ prop.Destruct(&pInputName);
+ /* release objects we used */
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(prop, CORE_COMPONENT);
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(statsobj, CORE_COMPONENT);
+ENDmodExit
+
+
BEGINisCompatibleWithFeature
CODESTARTisCompatibleWithFeature
if(eFeat == sFEATURENonCancelInputTermination)
@@ -89,6 +114,7 @@ initConfigSettings(void)
cs.iFacility = DEFAULT_FACILITY;
cs.iSeverity = DEFAULT_SEVERITY;
cs.bJSON = 0;
+ cs.bCEE = 0;
}
@@ -146,18 +172,87 @@ BEGINbeginCnfLoad
CODESTARTbeginCnfLoad
loadModConf = pModConf;
pModConf->pConf = pConf;
+ /* init our settings */
+ loadModConf->configSetViaV2Method = 0;
+ loadModConf->iStatsInterval = DEFAULT_STATS_PERIOD;
+ loadModConf->iFacility = DEFAULT_FACILITY;
+ loadModConf->iSeverity = DEFAULT_SEVERITY;
+ loadModConf->statsFmt = statsFmt_Legacy;
+ bLegacyCnfModGlobalsPermitted = 1;
/* init legacy config vars */
initConfigSettings();
ENDbeginCnfLoad
+BEGINsetModCnf
+ struct cnfparamvals *pvals = NULL;
+ char *mode;
+ int i;
+CODESTARTsetModCnf
+ pvals = nvlstGetParams(lst, &modpblk, NULL);
+ if(pvals == NULL) {
+ errmsg.LogError(0, RS_RET_MISSING_CNFPARAMS, "error processing module "
+ "config parameters [module(...)]");
+ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+ }
+
+ if(Debug) {
+ dbgprintf("module (global) param blk for impstats:\n");
+ cnfparamsPrint(&modpblk, pvals);
+ }
+
+ for(i = 0 ; i < modpblk.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(modpblk.descr[i].name, "interval")) {
+ loadModConf->iStatsInterval = (int) pvals[i].val.d.n;
+ } else if(!strcmp(modpblk.descr[i].name, "facility")) {
+ loadModConf->iFacility = (int) pvals[i].val.d.n;
+ } else if(!strcmp(modpblk.descr[i].name, "severity")) {
+ loadModConf->iSeverity = (int) pvals[i].val.d.n;
+ } else if(!strcmp(modpblk.descr[i].name, "format")) {
+ mode = es_str2cstr(pvals[i].val.d.estr, NULL);
+ if(!strcasecmp(mode, "json")) {
+ loadModConf->statsFmt = statsFmt_JSON;
+ } else if(!strcasecmp(mode, "cee")) {
+ loadModConf->statsFmt = statsFmt_CEE;
+ } else if(!strcasecmp(mode, "legacy")) {
+ loadModConf->statsFmt = statsFmt_Legacy;
+ } else {
+ errmsg.LogError(0, RS_RET_ERR, "impstats: invalid format %s",
+ mode);
+ }
+ free(mode);
+ } else {
+ dbgprintf("impstats: program error, non-handled "
+ "param '%s' in beginCnfLoad\n", modpblk.descr[i].name);
+ }
+ }
+
+ loadModConf->configSetViaV2Method = 1;
+ bLegacyCnfModGlobalsPermitted = 0;
+
+finalize_it:
+ if(pvals != NULL)
+ cnfparamvalsDestruct(pvals, &modpblk);
+ENDsetModCnf
+
+
BEGINendCnfLoad
CODESTARTendCnfLoad
- /* persist module-specific settings from legacy config system */
- loadModConf->iStatsInterval = cs.iStatsInterval;
- loadModConf->iFacility = cs.iFacility;
- loadModConf->iSeverity = cs.iSeverity;
- loadModConf->statsFmt = cs.bJSON ? statsFmt_JSON : statsFmt_Legacy;
+ if(!loadModConf->configSetViaV2Method) {
+ /* persist module-specific settings from legacy config system */
+ loadModConf->iStatsInterval = cs.iStatsInterval;
+ loadModConf->iFacility = cs.iFacility;
+ loadModConf->iSeverity = cs.iSeverity;
+ if (cs.bCEE == 1) {
+ loadModConf->statsFmt = statsFmt_CEE;
+ } else if (cs.bJSON == 1) {
+ loadModConf->statsFmt = statsFmt_JSON;
+ } else {
+ loadModConf->statsFmt = statsFmt_Legacy;
+ }
+ }
ENDendCnfLoad
@@ -165,7 +260,7 @@ BEGINcheckCnf
CODESTARTcheckCnf
if(pModConf->iStatsInterval == 0) {
errmsg.LogError(0, NO_ERRCODE, "impstats: stats interval zero not permitted, using "
- "defaul of %d seconds", DEFAULT_STATS_PERIOD);
+ "default of %d seconds", DEFAULT_STATS_PERIOD);
pModConf->iStatsInterval = DEFAULT_STATS_PERIOD;
}
ENDcheckCnf
@@ -217,22 +312,11 @@ CODESTARTafterRun
ENDafterRun
-BEGINmodExit
-CODESTARTmodExit
- prop.Destruct(&pInputName);
- /* release objects we used */
- objRelease(glbl, CORE_COMPONENT);
- objRelease(prop, CORE_COMPONENT);
- objRelease(errmsg, CORE_COMPONENT);
- objRelease(statsobj, CORE_COMPONENT);
-ENDmodExit
-
-
-
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
CODEqueryEtryPt_STD_CONF2_QUERIES
+CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES
CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
ENDqueryEtryPt
@@ -254,11 +338,12 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(errmsg, CORE_COMPONENT));
CHKiRet(objUse(statsobj, CORE_COMPONENT));
/* the pstatsinverval is an alias to support a previous screwed-up syntax... */
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"pstatsinterval", 0, eCmdHdlrInt, NULL, &cs.iStatsInterval, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"pstatinterval", 0, eCmdHdlrInt, NULL, &cs.iStatsInterval, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"pstatfacility", 0, eCmdHdlrInt, NULL, &cs.iFacility, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"pstatseverity", 0, eCmdHdlrInt, NULL, &cs.iSeverity, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"pstatjson", 0, eCmdHdlrBinary, NULL, &cs.bJSON, STD_LOADABLE_MODULE_ID));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"pstatsinterval", 0, eCmdHdlrInt, NULL, &cs.iStatsInterval, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"pstatinterval", 0, eCmdHdlrInt, NULL, &cs.iStatsInterval, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"pstatfacility", 0, eCmdHdlrInt, NULL, &cs.iFacility, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"pstatseverity", 0, eCmdHdlrInt, NULL, &cs.iSeverity, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"pstatjson", 0, eCmdHdlrBinary, NULL, &cs.bJSON, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"pstatcee", 0, eCmdHdlrBinary, NULL, &cs.bCEE, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
CHKiRet(prop.Construct(&pInputName));
diff --git a/plugins/imrelp/imrelp.c b/plugins/imrelp/imrelp.c
index 99fabd18..f6040b21 100644
--- a/plugins/imrelp/imrelp.c
+++ b/plugins/imrelp/imrelp.c
@@ -22,7 +22,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
#include "config.h"
#include <stdlib.h>
#include <assert.h>
@@ -35,6 +34,7 @@
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include <signal.h>
#include <librelp.h>
#include "rsyslog.h"
#include "dirty.h"
@@ -236,13 +236,39 @@ BEGINfreeCnf
CODESTARTfreeCnf
ENDfreeCnf
+/* This is used to terminate the plugin. Note that the signal handler blocks
+ * other activity on the thread. As such, it is safe to request the stop. When
+ * we terminate, relpEngine is called, and it's select() loop interrupted. But
+ * only *after this function is done*. So we do not have a race!
+ */
+static void
+doSIGTTIN(int __attribute__((unused)) sig)
+{
+ DBGPRINTF("imrelp: termination requested via SIGTTIN - telling RELP engine\n");
+ relpEngineSetStop(pRelpEngine);
+}
+
+
/* This function is called to gather input.
*/
BEGINrunInput
+ sigset_t sigSet;
+ struct sigaction sigAct;
CODESTARTrunInput
- /* TODO: we must be careful to start the listener here. Currently, tcpsrv.c seems to
- * do that in ConstructFinalize
+ /* we want to support non-cancel input termination. To do so, we must signal librelp
+ * when to stop. As we run on the same thread, we need to register as SIGTTIN handler,
+ * which will be used to put the terminating condition into librelp.
*/
+ sigfillset(&sigSet);
+ pthread_sigmask(SIG_BLOCK, &sigSet, NULL);
+ sigemptyset(&sigSet);
+ sigaddset(&sigSet, SIGTTIN);
+ pthread_sigmask(SIG_UNBLOCK, &sigSet, NULL);
+ memset(&sigAct, 0, sizeof (sigAct));
+ sigemptyset(&sigAct.sa_mask);
+ sigAct.sa_handler = doSIGTTIN;
+ sigaction(SIGTTIN, &sigAct, NULL);
+
iRet = relpEngineRun(pRelpEngine);
ENDrunInput
@@ -284,12 +310,19 @@ resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unus
}
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURENonCancelInputTermination)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
CODEqueryEtryPt_STD_CONF2_QUERIES
CODEqueryEtryPt_STD_CONF2_PREPRIVDROP_QUERIES
+CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
ENDqueryEtryPt
diff --git a/plugins/imtcp/imtcp.c b/plugins/imtcp/imtcp.c
index 33404fee..f021307b 100644
--- a/plugins/imtcp/imtcp.c
+++ b/plugins/imtcp/imtcp.c
@@ -366,7 +366,15 @@ ENDactivateCnf
BEGINfreeCnf
+ instanceConf_t *inst, *del;
CODESTARTfreeCnf
+ for(inst = pModConf->root ; inst != NULL ; ) {
+ free(inst->pszBindPort);
+ free(inst->pszInputName);
+ del = inst;
+ inst = inst->next;
+ free(del);
+ }
ENDfreeCnf
/* This function is called to gather input.
diff --git a/plugins/imudp/imudp.c b/plugins/imudp/imudp.c
index 8ab3b9be..9c92ddde 100644
--- a/plugins/imudp/imudp.c
+++ b/plugins/imudp/imudp.c
@@ -6,7 +6,7 @@
*
* File begun on 2007-12-21 by RGerhards (extracted from syslogd.c)
*
- * Copyright 2007-2011 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007-2012 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -80,6 +80,7 @@ static struct lstn_s {
STATSCOUNTER_DEF(ctrSubmit, mutCtrSubmit)
} *lcnfRoot = NULL, *lcnfLast = NULL;
+static int bLegacyCnfModGlobalsPermitted;/* are legacy module-global config parameters permitted? */
static int bDoACLCheck; /* are ACL checks neeed? Cached once immediately before listener startup */
static int iMaxLine; /* maximum UDP message size supported */
static time_t ttLastDiscard = 0; /* timestamp when a message from a non-permitted sender was last discarded
@@ -118,10 +119,23 @@ struct modConfData_s {
int iSchedPolicy; /* scheduling policy as SCHED_xxx */
int iSchedPrio; /* scheduling priority */
int iTimeRequery; /* how often is time to be queried inside tight recv loop? 0=always */
+ sbool configSetViaV2Method;
};
static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current load process */
+/* module-global parameters */
+static struct cnfparamdescr modpdescr[] = {
+ { "schedulingpolicy", eCmdHdlrGetWord, 0 },
+ { "schedulingpriority", eCmdHdlrInt, 0 },
+ { "timerequery", eCmdHdlrInt, 0 }
+};
+static struct cnfparamblk modpblk =
+ { CNFPARAMBLK_VERSION,
+ sizeof(modpdescr)/sizeof(struct cnfparamdescr),
+ modpdescr
+ };
+
#include "im-helper.h" /* must be included AFTER the type definitions! */
@@ -185,52 +199,6 @@ addListner(instanceConf_t *inst)
/* check which address to bind to. We could do this more compact, but have not
* done so in order to make the code more readable. -- rgerhards, 2007-12-27
*/
-#if 0 //<<<<<<< HEAD
-
- DBGPRINTF("imudp: trying to open port at %s:%s.\n",
- (inst->pszBindAddr == NULL) ? (uchar*)"*" : inst->pszBindAddr, inst->pszBindPort);
-
- newSocks = net.create_udp_socket(inst->pszBindAddr, inst->pszBindPort, 1);
- if(newSocks != NULL) {
- /* we now need to add the new sockets to the existing set */
- if(udpLstnSocks == NULL) {
- /* esay, we can just replace it */
- udpLstnSocks = newSocks;
- CHKmalloc(udpRulesets = (ruleset_t**) MALLOC(sizeof(ruleset_t*) * (newSocks[0] + 1)));
- for(iDst = 1 ; iDst <= newSocks[0] ; ++iDst)
- udpRulesets[iDst] = inst->pBindRuleset;
- } else {
- /* we need to add them */
- tmpSocks = (int*) MALLOC(sizeof(int) * (1 + newSocks[0] + udpLstnSocks[0]));
- tmpRulesets = (ruleset_t**) MALLOC(sizeof(ruleset_t*) * (1 + newSocks[0] + udpLstnSocks[0]));
- if(tmpSocks == NULL || tmpRulesets == NULL) {
- DBGPRINTF("out of memory trying to allocate udp listen socket array\n");
- /* in this case, we discard the new sockets but continue with what we
- * already have
- */
- free(newSocks);
- free(tmpSocks);
- free(tmpRulesets);
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- } else {
- /* ready to copy */
- iDst = 1;
- for(iSrc = 1 ; iSrc <= udpLstnSocks[0] ; ++iSrc, ++iDst) {
- tmpSocks[iDst] = udpLstnSocks[iSrc];
- tmpRulesets[iDst] = udpRulesets[iSrc];
- }
- for(iSrc = 1 ; iSrc <= newSocks[0] ; ++iSrc, ++iDst) {
- tmpSocks[iDst] = newSocks[iSrc];
- tmpRulesets[iDst] = inst->pBindRuleset;
- }
- tmpSocks[0] = udpLstnSocks[0] + newSocks[0];
- free(newSocks);
- free(udpLstnSocks);
- udpLstnSocks = tmpSocks;
- free(udpRulesets);
- udpRulesets = tmpRulesets;
- }
-#else //=======
if(inst->pszBindAddr == NULL)
bindAddr = NULL;
else if(inst->pszBindAddr[0] == '*' && inst->pszBindAddr[1] == '\0')
@@ -270,7 +238,6 @@ addListner(instanceConf_t *inst)
else {
lcnfLast->next = newlcnfinfo;
lcnfLast = newlcnfinfo;
-#endif //>>>>>>> ef34821a2737799f48c3032b9616418e4f7fa34f
}
}
}
@@ -672,6 +639,12 @@ BEGINbeginCnfLoad
CODESTARTbeginCnfLoad
loadModConf = pModConf;
pModConf->pConf = pConf;
+ /* init our settings */
+ loadModConf->configSetViaV2Method = 0;
+ loadModConf->iTimeRequery = TIME_REQUERY_DFLT;
+ loadModConf->iSchedPrio = SCHED_PRIO_UNSET;
+ loadModConf->pszSchedPolicy = NULL;
+ bLegacyCnfModGlobalsPermitted = 1;
/* init legacy config vars */
cs.pszBindRuleset = NULL;
cs.pszSchedPolicy = NULL;
@@ -681,21 +654,57 @@ CODESTARTbeginCnfLoad
ENDbeginCnfLoad
+BEGINsetModCnf
+ struct cnfparamvals *pvals = NULL;
+ int i;
+CODESTARTsetModCnf
+ pvals = nvlstGetParams(lst, &modpblk, NULL);
+ if(pvals == NULL) {
+ errmsg.LogError(0, RS_RET_MISSING_CNFPARAMS, "error processing module "
+ "config parameters [module(...)]");
+ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+ }
+
+ if(Debug) {
+ dbgprintf("module (global) param blk for impstats:\n");
+ cnfparamsPrint(&modpblk, pvals);
+ }
+
+ for(i = 0 ; i < modpblk.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(modpblk.descr[i].name, "timerequery")) {
+ loadModConf->iTimeRequery = (int) pvals[i].val.d.n;
+ } else if(!strcmp(modpblk.descr[i].name, "schedulingpriority")) {
+ loadModConf->iSchedPrio = (int) pvals[i].val.d.n;
+ } else if(!strcmp(modpblk.descr[i].name, "schedulingpolicy")) {
+ loadModConf->pszSchedPolicy = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else {
+ dbgprintf("impstats: program error, non-handled "
+ "param '%s' in beginCnfLoad\n", modpblk.descr[i].name);
+ }
+ }
+
+ /* remove all of our legacy handlers, as they can not used in addition
+ * the the new-style config method.
+ */
+ bLegacyCnfModGlobalsPermitted = 0;
+ loadModConf->configSetViaV2Method = 1;
+
+finalize_it:
+ if(pvals != NULL)
+ cnfparamvalsDestruct(pvals, &modpblk);
+ENDsetModCnf
+
BEGINendCnfLoad
CODESTARTendCnfLoad
- /* persist module-specific settings from legacy config system
- * TODO: when we add the new config system, we must decide on priority
- * already-set module options should not be overwritable by the legacy
- * system (though this is debatable and should at least trigger an error
- * message if the equivalent legacy option is selected as well)
- * rgerhards, 2011-05-04
- */
- loadModConf->iSchedPrio = cs.iSchedPrio;
- loadModConf->iTimeRequery = cs.iTimeRequery;
- if((cs.pszSchedPolicy == NULL) || (cs.pszSchedPolicy[0] == '\0')) {
- loadModConf->pszSchedPolicy = NULL;
- } else {
- CHKmalloc(loadModConf->pszSchedPolicy = ustrdup(cs.pszSchedPolicy));
+ if(!loadModConf->configSetViaV2Method) {
+ /* persist module-specific settings from legacy config system */
+ loadModConf->iSchedPrio = cs.iSchedPrio;
+ loadModConf->iTimeRequery = cs.iTimeRequery;
+ if((cs.pszSchedPolicy != NULL) && (cs.pszSchedPolicy[0] != '\0')) {
+ CHKmalloc(loadModConf->pszSchedPolicy = ustrdup(cs.pszSchedPolicy));
+ }
}
finalize_it:
@@ -751,7 +760,16 @@ ENDactivateCnf
BEGINfreeCnf
+ instanceConf_t *inst, *del;
CODESTARTfreeCnf
+ for(inst = pModConf->root ; inst != NULL ; ) {
+ free(inst->pszBindPort);
+ free(inst->pszBindAddr);
+ free(inst->pBindRuleset);
+ del = inst;
+ inst = inst->next;
+ free(del);
+ }
ENDfreeCnf
/* This function is called to gather input.
@@ -819,6 +837,7 @@ BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
CODEqueryEtryPt_STD_CONF2_QUERIES
+CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES
CODEqueryEtryPt_STD_CONF2_PREPRIVDROP_QUERIES
CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
ENDqueryEtryPt
@@ -867,12 +886,16 @@ CODEmodInit_QueryRegCFSLineHdlr
addInstance, NULL, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"udpserveraddress", 0, eCmdHdlrGetWord,
NULL, &cs.pszBindAddr, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"imudpschedulingpolicy", 0, eCmdHdlrGetWord,
- NULL, &cs.pszSchedPolicy, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"imudpschedulingpriority", 0, eCmdHdlrInt,
- NULL, &cs.iSchedPrio, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"udpservertimerequery", 0, eCmdHdlrInt,
- NULL, &cs.iTimeRequery, STD_LOADABLE_MODULE_ID));
+ /* module-global config params - will be disabled in configs that are loaded
+ * via module(...).
+ */
+ CHKiRet(regCfSysLineHdlr2((uchar *)"imudpschedulingpolicy", 0, eCmdHdlrGetWord,
+ NULL, &cs.pszSchedPolicy, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"imudpschedulingpriority", 0, eCmdHdlrInt,
+ NULL, &cs.iSchedPrio, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"udpservertimerequery", 0, eCmdHdlrInt,
+ NULL, &cs.iTimeRequery, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
+
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
diff --git a/plugins/imuxsock/imuxsock.c b/plugins/imuxsock/imuxsock.c
index 38934e3a..eb3011b2 100644
--- a/plugins/imuxsock/imuxsock.c
+++ b/plugins/imuxsock/imuxsock.c
@@ -6,7 +6,7 @@
*
* File begun on 2007-12-20 by RGerhards (extracted from syslogd.c)
*
- * Copyright 2007-2011 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007-2012 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -149,6 +149,7 @@ typedef struct lstn_s {
sbool bCreatePath; /* auto-creation of socket directory? */
sbool bUseCreds; /* pull original creator credentials from socket */
sbool bAnnotate; /* annotate events with trusted properties */
+ sbool bParseTrusted; /* parse trusted properties */
sbool bWritePid; /* write original PID into tag */
sbool bUseSysTimeStamp; /* use timestamp from system (instead of from message) */
} lstn_t;
@@ -163,6 +164,8 @@ static int startIndexUxLocalSockets; /* process fd from that index on (used to
static int nfd = 1; /* number of Unix sockets open / read-only after startup */
static int sd_fds = 0; /* number of systemd activated sockets */
+static ee_ctx ctxee = NULL; /* library context */
+
/* config vars for legacy config system */
#define DFLT_bCreatePath 0
#define DFLT_ratelimitInterval 0
@@ -172,8 +175,10 @@ static struct configSettings_s {
int bOmitLocalLogging;
uchar *pLogSockName;
uchar *pLogHostName; /* host name to use with this socket */
- int bUseFlowCtl; /* use flow control or not (if yes, only LIGHT is used! */
+ int bUseFlowCtl; /* use flow control or not (if yes, only LIGHT is used!) */
+ int bUseFlowCtlSysSock;
int bIgnoreTimestamp; /* ignore timestamps present in the incoming message? */
+ int bIgnoreTimestampSysSock;
int bUseSysTimeStamp; /* use timestamp from system (rather than from message) */
int bUseSysTimeStampSysSock; /* same, for system log socket */
int bWritePid; /* use credentials from recvmsg() and fixup PID in TAG */
@@ -187,6 +192,7 @@ static struct configSettings_s {
int ratelimitSeveritySysSock;
int bAnnotate; /* annotate trusted properties */
int bAnnotateSysSock; /* same, for system log socket */
+ int bParseTrusted; /* parse trusted properties */
} cs;
struct instanceConf_s {
@@ -201,6 +207,7 @@ struct instanceConf_s {
int ratelimitBurst; /* max nbr of messages in interval */
int ratelimitSeverity;
int bAnnotate; /* annotate trusted properties */
+ int bParseTrusted; /* parse trusted properties */
struct instanceConf_s *next;
};
@@ -211,14 +218,37 @@ struct modConfData_s {
int ratelimitIntervalSysSock;
int ratelimitBurstSysSock;
int ratelimitSeveritySysSock;
+ int bAnnotateSysSock;
+ int bParseTrusted;
+ sbool bIgnoreTimestamp; /* ignore timestamps present in the incoming message? */
+ sbool bUseFlowCtl; /* use flow control or not (if yes, only LIGHT is used! */
sbool bOmitLocalLogging;
sbool bWritePidSysSock;
- int bAnnotateSysSock;
sbool bUseSysTimeStamp;
+ sbool configSetViaV2Method;
};
static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current load process */
+/* module-global parameters */
+static struct cnfparamdescr modpdescr[] = {
+ { "syssock.use", eCmdHdlrBinary, 0 },
+ { "syssock.name", eCmdHdlrGetWord, 0 },
+ { "syssock.ignoretimestamp", eCmdHdlrBinary, 0 },
+ { "syssock.flowcontrol", eCmdHdlrBinary, 0 },
+ { "syssock.usesystimestamp", eCmdHdlrBinary, 0 },
+ { "syssock.annotate", eCmdHdlrBinary, 0 },
+ { "syssock.usepidfromsystem", eCmdHdlrBinary, 0 },
+ { "syssock.ratelimit.interval", eCmdHdlrInt, 0 },
+ { "syssock.ratelimit.burst", eCmdHdlrInt, 0 },
+ { "syssock.ratelimit.severity", eCmdHdlrInt, 0 }
+};
+static struct cnfparamblk modpblk =
+ { CNFPARAMBLK_VERSION,
+ sizeof(modpdescr)/sizeof(struct cnfparamdescr),
+ modpdescr
+ };
+
/* we do not use this, because we do not bind to a ruleset so far
* enable when this is changed: #include "im-helper.h" */ /* must be included AFTER the type definitions! */
@@ -233,6 +263,8 @@ initRatelimitState(struct rs_ratelimit_state *rs, unsigned short interval, unsig
rs->begin = 0;
}
+static int bLegacyCnfModGlobalsPermitted;/* are legacy module-global config parameters permitted? */
+
/* ratelimiting support, modelled after the linux kernel
* returns 1 if message is within rate limit and shall be
@@ -289,27 +321,6 @@ finalize_it:
}
-/* set the timestamp ignore / not ignore option for the system
- * log socket. This must be done separtely, as it is not added via a command
- * but present by default. -- rgerhards, 2008-03-06
- */
-static rsRetVal setSystemLogTimestampIgnore(void __attribute__((unused)) *pVal, int iNewVal)
-{
- DEFiRet;
- listeners[0].flags = iNewVal ? IGNDATE : NOFLAG;
- RETiRet;
-}
-
-/* set flowcontrol for the system log socket
- */
-static rsRetVal setSystemLogFlowControl(void __attribute__((unused)) *pVal, int iNewVal)
-{
- DEFiRet;
- listeners[0].flowCtl = iNewVal ? eFLOWCTL_LIGHT_DELAY : eFLOWCTL_NO_DELAY;
- RETiRet;
-}
-
-
/* This function is called when a new listen socket instace shall be added to
* the current config object via the legacy config system. It just shuffles
* all parameters to the listener in-memory instance.
@@ -340,6 +351,7 @@ static rsRetVal addInstance(void __attribute__((unused)) *pVal, uchar *pNewVal)
inst->bUseSysTimeStamp = cs.bUseSysTimeStamp;
inst->bWritePid = cs.bWritePid;
inst->bAnnotate = cs.bAnnotate;
+ inst->bParseTrusted = cs.bParseTrusted;
inst->next = NULL;
/* node created, let's add to config */
@@ -388,7 +400,7 @@ addListner(instanceConf_t *inst)
if(inst->ratelimitInterval > 0) {
if((listeners[nfd].ht = create_hashtable(100, hash_from_key_fn, key_equals_fn, NULL)) == NULL) {
/* in this case, we simply turn off rate-limiting */
- dbgprintf("imuxsock: turning off rate limiting because we could not "
+ DBGPRINTF("imuxsock: turning off rate limiting because we could not "
"create hash table\n");
inst->ratelimitInterval = 0;
}
@@ -402,6 +414,7 @@ addListner(instanceConf_t *inst)
listeners[nfd].sockName = ustrdup(inst->sockName);
listeners[nfd].bUseCreds = (inst->bWritePid || inst->ratelimitInterval || inst->bAnnotate) ? 1 : 0;
listeners[nfd].bAnnotate = inst->bAnnotate;
+ listeners[nfd].bParseTrusted = inst->bParseTrusted;
listeners[nfd].bWritePid = inst->bWritePid;
listeners[nfd].bUseSysTimeStamp = inst->bUseSysTimeStamp;
nfd++;
@@ -458,7 +471,7 @@ createLogSocket(lstn_t *pLstn)
if(pLstn->fd < 0 || bind(pLstn->fd, (struct sockaddr *) &sunx, SUN_LEN(&sunx)) < 0 ||
chmod((char*)pLstn->sockName, 0666) < 0) {
errmsg.LogError(errno, NO_ERRCODE, "cannot create '%s'", pLstn->sockName);
- dbgprintf("cannot create %s (%d).\n", pLstn->sockName, errno);
+ DBGPRINTF("cannot create %s (%d).\n", pLstn->sockName, errno);
if(pLstn->fd != -1)
close(pLstn->fd);
pLstn->fd = -1;
@@ -493,7 +506,7 @@ openLogSocket(lstn_t *pLstn)
/* ok, it matches -- just use as is */
pLstn->fd = fd;
- dbgprintf("imuxsock: Acquired UNIX socket '%s' (fd %d) from systemd.\n",
+ DBGPRINTF("imuxsock: Acquired UNIX socket '%s' (fd %d) from systemd.\n",
pLstn->sockName, pLstn->fd);
break;
}
@@ -561,7 +574,7 @@ findRatelimiter(lstn_t *pLstn, struct ucred *cred, rs_ratelimit_state_t **prl)
rl = hashtable_search(pLstn->ht, &cred->pid);
if(rl == NULL) {
/* we need to add a new ratelimiter, process not seen before! */
- dbgprintf("imuxsock: no ratelimiter for pid %lu, creating one\n",
+ DBGPRINTF("imuxsock: no ratelimiter for pid %lu, creating one\n",
(unsigned long) cred->pid);
STATSCOUNTER_INC(ctrNumRatelimiters, mutCtrNumRatelimiters);
CHKmalloc(rl = malloc(sizeof(rs_ratelimit_state_t)));
@@ -705,6 +718,26 @@ copyescaped(uchar *dstbuf, uchar *inbuf, int inlen)
}
+/* Creates new field to be added to event
+ * used for SystemLogParseTrusted parsing
+ */
+struct ee_field *
+createNewField(char *fieldname, char *value, int lenValue) {
+ es_str_t *newStr;
+ struct ee_value *newVal;
+ struct ee_field *newField;
+
+ newStr = es_newStrFromBuf(value, (es_size_t) lenValue);
+
+ newVal = ee_newValue(ctxee);
+ ee_setStrValue(newVal, newStr);
+
+ newField = ee_newFieldFromNV(ctxee, fieldname, newVal);
+
+ return newField;
+}
+
+
/* submit received message to the queue engine
* We now parse the message according to expected format so that we
* can also mangle it if necessary.
@@ -730,6 +763,7 @@ SubmitMsg(uchar *pRcv, int lenRcv, lstn_t *pLstn, struct ucred *cred, struct tim
uchar *pmsgbuf;
int toffs; /* offset for trusted properties */
struct syslogTime dummyTS;
+ struct ee_event *event = NULL;
DEFiRet;
/* TODO: handle format errors?? */
@@ -774,37 +808,82 @@ SubmitMsg(uchar *pRcv, int lenRcv, lstn_t *pLstn, struct ucred *cred, struct tim
} else {
CHKmalloc(pmsgbuf = malloc(lenRcv+4096));
}
- memcpy(pmsgbuf, pRcv, lenRcv);
- memcpy(pmsgbuf+lenRcv, " @[", 3);
- toffs = lenRcv + 3; /* next free location */
- lenProp = snprintf((char*)propBuf, sizeof(propBuf), "_PID=%lu _UID=%lu _GID=%lu",
- (long unsigned) cred->pid, (long unsigned) cred->uid,
- (long unsigned) cred->gid);
- memcpy(pmsgbuf+toffs, propBuf, lenProp);
- toffs = toffs + lenProp;
- getTrustedProp(cred, "comm", propBuf, sizeof(propBuf), &lenProp);
- if(lenProp) {
- memcpy(pmsgbuf+toffs, " _COMM=", 7);
- memcpy(pmsgbuf+toffs+7, propBuf, lenProp);
- toffs = toffs + 7 + lenProp;
- }
- getTrustedExe(cred, propBuf, sizeof(propBuf), &lenProp);
- if(lenProp) {
- memcpy(pmsgbuf+toffs, " _EXE=", 6);
- memcpy(pmsgbuf+toffs+6, propBuf, lenProp);
- toffs = toffs + 6 + lenProp;
- }
- getTrustedProp(cred, "cmdline", propBuf, sizeof(propBuf), &lenProp);
- if(lenProp) {
- memcpy(pmsgbuf+toffs, " _CMDLINE=", 10);
- toffs = toffs + 10 +
- copyescaped(pmsgbuf+toffs+10, propBuf, lenProp);
+
+ if (pLstn->bParseTrusted) {
+ struct ee_field *newField;
+
+ if(ctxee == NULL) {
+ if((ctxee = ee_initCtx()) == NULL) {
+ errmsg.LogError(0, RS_RET_NO_RULESET, "error: could not initialize libee ctx, cannot "
+ "activate action");
+ ABORT_FINALIZE(RS_RET_ERR_LIBEE_INIT);
+ }
+ }
+
+ event = ee_newEvent(ctxee);
+
+ /* create value string, create field, and add it to event */
+ lenProp = snprintf((char *)propBuf, sizeof(propBuf), "%lu", (long unsigned) cred->pid);
+ newField = createNewField("pid", (char *)propBuf, lenProp);
+ ee_addFieldToEvent(event, newField);
+
+ lenProp = snprintf((char *)propBuf, sizeof(propBuf), "%lu", (long unsigned) cred->uid);
+ newField = createNewField("uid", (char *)propBuf, lenProp);
+ ee_addFieldToEvent(event, newField);
+
+ lenProp = snprintf((char *)propBuf, sizeof(propBuf), "%lu", (long unsigned) cred->gid);
+ newField = createNewField("gid", (char *)propBuf, lenProp);
+ ee_addFieldToEvent(event, newField);
+
+ getTrustedProp(cred, "comm", propBuf, sizeof(propBuf), &lenProp);
+ newField = createNewField("appname", (char *)propBuf, lenProp);
+ ee_addFieldToEvent(event, newField);
+
+ getTrustedExe(cred, propBuf, sizeof(propBuf), &lenProp);
+ newField = createNewField("exe", (char *)propBuf, lenProp);
+ ee_addFieldToEvent(event, newField);
+
+ getTrustedProp(cred, "cmdline", propBuf, sizeof(propBuf), &lenProp);
+ newField = createNewField("cmd", (char *)propBuf, lenProp);
+ ee_addFieldToEvent(event, newField);
+
+ } else {
+
+ memcpy(pmsgbuf, pRcv, lenRcv);
+ memcpy(pmsgbuf+lenRcv, " @[", 3);
+ toffs = lenRcv + 3; /* next free location */
+ lenProp = snprintf((char*)propBuf, sizeof(propBuf), "_PID=%lu _UID=%lu _GID=%lu",
+ (long unsigned) cred->pid, (long unsigned) cred->uid,
+ (long unsigned) cred->gid);
+ memcpy(pmsgbuf+toffs, propBuf, lenProp);
+ toffs = toffs + lenProp;
+
+ getTrustedProp(cred, "comm", propBuf, sizeof(propBuf), &lenProp);
+ if(lenProp) {
+ memcpy(pmsgbuf+toffs, " _COMM=", 7);
+ memcpy(pmsgbuf+toffs+7, propBuf, lenProp);
+ toffs = toffs + 7 + lenProp;
+ }
+ getTrustedExe(cred, propBuf, sizeof(propBuf), &lenProp);
+ if(lenProp) {
+ memcpy(pmsgbuf+toffs, " _EXE=", 6);
+ memcpy(pmsgbuf+toffs+6, propBuf, lenProp);
+ toffs = toffs + 6 + lenProp;
+ }
+ getTrustedProp(cred, "cmdline", propBuf, sizeof(propBuf), &lenProp);
+ if(lenProp) {
+ memcpy(pmsgbuf+toffs, " _CMDLINE=", 9);
+ toffs = toffs + 9 +
+ copyescaped(pmsgbuf+toffs+9, propBuf, lenProp);
+ }
+
+ /* finalize string */
+ pmsgbuf[toffs] = ']';
+ pmsgbuf[toffs+1] = '\0';
+
+ pRcv = pmsgbuf;
+ lenRcv = toffs + 1;
}
- /* finalize string */
- pmsgbuf[toffs] = ']';
- pmsgbuf[toffs+1] = '\0';
- pRcv = pmsgbuf;
- lenRcv = toffs + 1;
}
/* we now create our own message object and submit it to the queue */
@@ -821,6 +900,14 @@ SubmitMsg(uchar *pRcv, int lenRcv, lstn_t *pLstn, struct ucred *cred, struct tim
parse++; lenMsg--; /* '>' */
+ /* event is saved to pMsg */
+ if(pMsg->event != NULL) {
+ ee_deleteEvent(pMsg->event);
+ }
+ if (event != NULL) {
+ pMsg->event = event;
+ }
+
if(ts == NULL) {
if((pLstn->flags & IGNDATE)) {
/* in this case, we still need to find out if we have a valid
@@ -933,7 +1020,7 @@ static rsRetVal readSocket(lstn_t *pLstn)
msgh.msg_iovlen = 1;
iRcvd = recvmsg(pLstn->fd, &msgh, MSG_DONTWAIT);
- dbgprintf("Message from UNIX socket: #%d\n", pLstn->fd);
+ DBGPRINTF("Message from UNIX socket: #%d\n", pLstn->fd);
if(iRcvd > 0) {
cred = NULL;
ts = NULL;
@@ -943,16 +1030,12 @@ static rsRetVal readSocket(lstn_t *pLstn)
if( pLstn->bUseCreds
&& cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SCM_CREDENTIALS) {
cred = (struct ucred*) CMSG_DATA(cm);
- break;
}
# endif /* HAVE_SCM_CREDENTIALS */
# if HAVE_SO_TIMESTAMP
if( pLstn->bUseSysTimeStamp
&& cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP) {
ts = (struct timeval *)CMSG_DATA(cm);
- dbgprintf("XXX: got timestamp %ld.%ld\n",
- (long) ts->tv_sec, (long) ts->tv_usec);
- break;
}
# endif /* HAVE_SO_TIMESTAMP */
}
@@ -961,7 +1044,7 @@ static rsRetVal readSocket(lstn_t *pLstn)
} else if(iRcvd < 0 && errno != EINTR) {
char errStr[1024];
rs_strerror_r(errno, errStr, sizeof(errStr));
- dbgprintf("UNIX socket error: %d = %s.\n", errno, errStr);
+ DBGPRINTF("UNIX socket error: %d = %s.\n", errno, errStr);
errmsg.LogError(errno, NO_ERRCODE, "imuxsock: recvfrom UNIX");
}
@@ -1011,10 +1094,13 @@ activateListeners()
listeners[0].ratelimitInterval = runModConf->ratelimitIntervalSysSock;
listeners[0].ratelimitBurst = runModConf->ratelimitBurstSysSock;
listeners[0].ratelimitSev = runModConf->ratelimitSeveritySysSock;
- listeners[0].bUseCreds = (runModConf->bWritePidSysSock || runModConf->ratelimitIntervalSysSock) ? 1 : 0;
+ listeners[0].bUseCreds = (runModConf->bWritePidSysSock || runModConf->ratelimitIntervalSysSock || runModConf->bAnnotateSysSock) ? 1 : 0;
listeners[0].bWritePid = runModConf->bWritePidSysSock;
listeners[0].bAnnotate = runModConf->bAnnotateSysSock;
+ listeners[0].bParseTrusted = runModConf->bParseTrusted;
listeners[0].bUseSysTimeStamp = runModConf->bUseSysTimeStamp;
+ listeners[0].flags = runModConf->bIgnoreTimestamp ? IGNDATE : NOFLAG;
+ listeners[0].flowCtl = runModConf->bUseFlowCtl ? eFLOWCTL_LIGHT_DELAY : eFLOWCTL_NO_DELAY;
sd_fds = sd_listen_fds(0);
if(sd_fds < 0) {
@@ -1027,7 +1113,7 @@ activateListeners()
for (i = startIndexUxLocalSockets ; i < nfd ; i++) {
if(openLogSocket(&(listeners[i])) == RS_RET_OK) {
++actSocks;
- dbgprintf("imuxsock: Opened UNIX socket '%s' (fd %d).\n",
+ DBGPRINTF("imuxsock: Opened UNIX socket '%s' (fd %d).\n",
listeners[i].sockName, listeners[i].fd);
}
}
@@ -1047,16 +1133,90 @@ BEGINbeginCnfLoad
CODESTARTbeginCnfLoad
loadModConf = pModConf;
pModConf->pConf = pConf;
+ /* init our settings */
+ pModConf->pLogSockName = NULL;
+ pModConf->bOmitLocalLogging = 0;
+ pModConf->bIgnoreTimestamp = 1;
+ pModConf->bUseFlowCtl = 0;
+ pModConf->bUseSysTimeStamp = 1;
+ pModConf->bWritePidSysSock = 0;
+ pModConf->bAnnotateSysSock = 0;
+ pModConf->bParseTrusted = 0;
+ pModConf->ratelimitIntervalSysSock = DFLT_ratelimitInterval;
+ pModConf->ratelimitBurstSysSock = DFLT_ratelimitBurst;
+ pModConf->ratelimitSeveritySysSock = DFLT_ratelimitSeverity;
+ bLegacyCnfModGlobalsPermitted = 1;
/* reset legacy config vars */
resetConfigVariables(NULL, NULL);
ENDbeginCnfLoad
+BEGINsetModCnf
+ struct cnfparamvals *pvals = NULL;
+ int i;
+CODESTARTsetModCnf
+ pvals = nvlstGetParams(lst, &modpblk, NULL);
+ if(pvals == NULL) {
+ errmsg.LogError(0, RS_RET_MISSING_CNFPARAMS, "error processing module "
+ "config parameters [module(...)]");
+ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+ }
+
+ if(Debug) {
+ dbgprintf("module (global) param blk for imuxsock:\n");
+ cnfparamsPrint(&modpblk, pvals);
+ }
+
+ for(i = 0 ; i < modpblk.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(modpblk.descr[i].name, "syssock.use")) {
+ loadModConf->bOmitLocalLogging = ((int) pvals[i].val.d.n) ? 0 : 1;
+ } else if(!strcmp(modpblk.descr[i].name, "syssock.name")) {
+ loadModConf->pLogSockName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(modpblk.descr[i].name, "syssock.ignoretimestamp")) {
+ loadModConf->bIgnoreTimestamp = (int) pvals[i].val.d.n;
+ } else if(!strcmp(modpblk.descr[i].name, "syssock.flowcontrol")) {
+ loadModConf->bUseFlowCtl = (int) pvals[i].val.d.n;
+ } else if(!strcmp(modpblk.descr[i].name, "syssock.usesystimestamp")) {
+ loadModConf->bUseSysTimeStamp = (int) pvals[i].val.d.n;
+ } else if(!strcmp(modpblk.descr[i].name, "syssock.annotate")) {
+ loadModConf->bAnnotateSysSock = (int) pvals[i].val.d.n;
+ } else if(!strcmp(modpblk.descr[i].name, "syssock.usepidfromsystem")) {
+ loadModConf->bWritePidSysSock = (int) pvals[i].val.d.n;
+ } else if(!strcmp(modpblk.descr[i].name, "syssock.ratelimit.interval")) {
+ loadModConf->ratelimitIntervalSysSock = (int) pvals[i].val.d.n;
+ } else if(!strcmp(modpblk.descr[i].name, "syssock.ratelimit.burst")) {
+ loadModConf->ratelimitBurstSysSock = (int) pvals[i].val.d.n;
+ } else if(!strcmp(modpblk.descr[i].name, "syssock.ratelimit.severity")) {
+ loadModConf->ratelimitSeveritySysSock = (int) pvals[i].val.d.n;
+ } else {
+ dbgprintf("imuxsock: program error, non-handled "
+ "param '%s' in beginCnfLoad\n", modpblk.descr[i].name);
+ }
+ }
+
+ /* disable legacy module-global config directives */
+ bLegacyCnfModGlobalsPermitted = 0;
+ loadModConf->configSetViaV2Method = 1;
+
+finalize_it:
+ if(pvals != NULL)
+ cnfparamvalsDestruct(pvals, &modpblk);
+ENDsetModCnf
+
+
BEGINendCnfLoad
CODESTARTendCnfLoad
- /* persist module-specific settings from legacy config system */
- loadModConf->bOmitLocalLogging = cs.bOmitLocalLogging;
- loadModConf->pLogSockName = cs.pLogSockName;
+ if(!loadModConf->configSetViaV2Method) {
+ /* persist module-specific settings from legacy config system */
+ loadModConf->bOmitLocalLogging = cs.bOmitLocalLogging;
+ loadModConf->pLogSockName = cs.pLogSockName;
+ loadModConf->bIgnoreTimestamp = cs.bIgnoreTimestampSysSock;
+ loadModConf->bUseFlowCtl = cs.bUseFlowCtlSysSock;
+ loadModConf->bAnnotateSysSock = cs.bAnnotateSysSock;
+ loadModConf->bParseTrusted = cs.bParseTrusted;
+ }
loadModConf = NULL; /* done loading */
/* free legacy config vars */
@@ -1089,8 +1249,16 @@ ENDactivateCnf
BEGINfreeCnf
+ instanceConf_t *inst, *del;
CODESTARTfreeCnf
free(pModConf->pLogSockName);
+ for(inst = pModConf->root ; inst != NULL ; ) {
+ free(inst->sockName);
+ free(inst->pLogHostName);
+ del = inst;
+ inst = inst->next;
+ free(del);
+ }
ENDfreeCnf
@@ -1191,6 +1359,10 @@ CODESTARTafterRun
discardLogSockets();
nfd = 1;
+ if(ctxee != NULL) {
+ ee_exitCtx(ctxee);
+ ctxee = NULL;
+ }
ENDafterRun
@@ -1221,6 +1393,7 @@ BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
CODEqueryEtryPt_STD_CONF2_QUERIES
+CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES
CODEqueryEtryPt_STD_CONF2_PREPRIVDROP_QUERIES
CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
ENDqueryEtryPt
@@ -1233,13 +1406,16 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
cs.bOmitLocalLogging = 0;
cs.pLogHostName = NULL;
cs.bIgnoreTimestamp = 1;
+ cs.bIgnoreTimestampSysSock = 1;
cs.bUseFlowCtl = 0;
+ cs.bUseFlowCtlSysSock = 0;
cs.bUseSysTimeStamp = 1;
cs.bUseSysTimeStampSysSock = 1;
cs.bWritePid = 0;
cs.bWritePidSysSock = 0;
cs.bAnnotate = 0;
cs.bAnnotateSysSock = 0;
+ cs.bParseTrusted = 0;
cs.bCreatePath = DFLT_bCreatePath;
cs.ratelimitInterval = DFLT_ratelimitInterval;
cs.ratelimitIntervalSysSock = DFLT_ratelimitInterval;
@@ -1265,7 +1441,7 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(datetime, CORE_COMPONENT));
CHKiRet(objUse(parser, CORE_COMPONENT));
- dbgprintf("imuxsock version %s initializing\n", PACKAGE_VERSION);
+ DBGPRINTF("imuxsock version %s initializing\n", PACKAGE_VERSION);
/* init legacy config vars */
cs.pLogSockName = NULL;
@@ -1292,6 +1468,7 @@ CODEmodInit_QueryRegCFSLineHdlr
listeners[0].bParseHost = 0;
listeners[0].bUseCreds = 0;
listeners[0].bAnnotate = 0;
+ listeners[0].bParseTrusted = 0;
listeners[0].bCreatePath = 0;
listeners[0].bUseSysTimeStamp = 1;
@@ -1307,12 +1484,8 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(prop.ConstructFinalize(pLocalHostIP));
/* register config file handlers */
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"omitlocallogging", 0, eCmdHdlrBinary,
- NULL, &cs.bOmitLocalLogging, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketignoremsgtimestamp", 0, eCmdHdlrBinary,
NULL, &cs.bIgnoreTimestamp, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogsocketname", 0, eCmdHdlrGetWord,
- NULL, &cs.pLogSockName, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensockethostname", 0, eCmdHdlrGetWord,
NULL, &cs.pLogHostName, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketflowcontrol", 0, eCmdHdlrBinary,
@@ -1341,22 +1514,28 @@ CODEmodInit_QueryRegCFSLineHdlr
* for that. We should revisit all of that once we have the new config format...
* rgerhards, 2008-03-06
*/
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogsocketignoremsgtimestamp", 0, eCmdHdlrBinary,
- setSystemLogTimestampIgnore, NULL, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogsocketflowcontrol", 0, eCmdHdlrBinary,
- setSystemLogFlowControl, NULL, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogusesystimestamp", 0, eCmdHdlrBinary,
- NULL, &cs.bUseSysTimeStampSysSock, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogsocketannotate", 0, eCmdHdlrBinary,
- NULL, &cs.bAnnotateSysSock, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogusepidfromsystem", 0, eCmdHdlrBinary,
- NULL, &cs.bWritePidSysSock, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogratelimitinterval", 0, eCmdHdlrInt,
- NULL, &cs.ratelimitIntervalSysSock, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogratelimitburst", 0, eCmdHdlrInt,
- NULL, &cs.ratelimitBurstSysSock, STD_LOADABLE_MODULE_ID));
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogratelimitseverity", 0, eCmdHdlrInt,
- NULL, &cs.ratelimitSeveritySysSock, STD_LOADABLE_MODULE_ID));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"omitlocallogging", 0, eCmdHdlrBinary,
+ NULL, &cs.bOmitLocalLogging, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"systemlogsocketname", 0, eCmdHdlrGetWord,
+ NULL, &cs.pLogSockName, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"systemlogsocketignoremsgtimestamp", 0, eCmdHdlrBinary,
+ NULL, &cs.bIgnoreTimestampSysSock, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"systemlogsocketflowcontrol", 0, eCmdHdlrBinary,
+ NULL, &cs.bUseFlowCtlSysSock, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"systemlogusesystimestamp", 0, eCmdHdlrBinary,
+ NULL, &cs.bUseSysTimeStampSysSock, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"systemlogsocketannotate", 0, eCmdHdlrBinary,
+ NULL, &cs.bAnnotateSysSock, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"systemlogparsetrusted", 0, eCmdHdlrBinary,
+ NULL, &cs.bParseTrusted, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"systemlogusepidfromsystem", 0, eCmdHdlrBinary,
+ NULL, &cs.bWritePidSysSock, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"systemlogratelimitinterval", 0, eCmdHdlrInt,
+ NULL, &cs.ratelimitIntervalSysSock, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"systemlogratelimitburst", 0, eCmdHdlrInt,
+ NULL, &cs.ratelimitBurstSysSock, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
+ CHKiRet(regCfSysLineHdlr2((uchar *)"systemlogratelimitseverity", 0, eCmdHdlrInt,
+ NULL, &cs.ratelimitSeveritySysSock, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted));
/* support statistics gathering */
CHKiRet(statsobj.Construct(&modStats));
diff --git a/plugins/imzmq3/Makefile.am b/plugins/imzmq3/Makefile.am
new file mode 100644
index 00000000..f9c84e5d
--- /dev/null
+++ b/plugins/imzmq3/Makefile.am
@@ -0,0 +1,8 @@
+pkglib_LTLIBRARIES = imzmq3.la
+
+imzmq3_la_SOURCES = imzmq3.c
+imzmq3_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) $(CZMQ_CFLAGS)
+imzmq3_la_LDFLAGS = -module -avoid-version
+imzmq3_la_LIBADD = $(CZMQ_LIBS)
+
+EXTRA_DIST =
diff --git a/plugins/imzmq3/README b/plugins/imzmq3/README
new file mode 100644
index 00000000..88653b83
--- /dev/null
+++ b/plugins/imzmq3/README
@@ -0,0 +1,24 @@
+ZeroMQ 3.x Input Plugin
+
+Building this plugin:
+Requires libzmq and libczmq. First, install libzmq from the HEAD on github:
+http://github.com/zeromq/libzmq. You can clone the repository, build, then
+install it. The directions for doing so are there in the readme. Then, do
+the same for libczmq: http://github.com/zeromq/czmq. At some point, the 3.1
+version of libzmq will be released, and a supporting version of libczmq.
+At that time, you could simply download and install the tarballs instead of
+using git to clone the repositories. Those tarballs (when available) can
+be found at http://download.zeromq.org. As of this writing (5/31/2012), the
+most recent version of czmq (1.1.0) and libzmq (3.1.0-beta) will not compile
+properly.
+
+Imzmq3 allows you to push data into rsyslog from a zeromq socket. The example
+below binds a SUB socket to port 7172, and then any messages with the topic
+"foo" will be pushed into rsyslog.
+
+Example Rsyslog.conf snippet:
+-------------------------------------------------------------------------------
+
+$InputZmq3ServerRun action=BIND,type=SUB,description=tcp://*:7172,subscribe=foo
+
+-------------------------------------------------------------------------------
diff --git a/plugins/imzmq3/imzmq3.c b/plugins/imzmq3/imzmq3.c
new file mode 100644
index 00000000..dc1d64d3
--- /dev/null
+++ b/plugins/imzmq3/imzmq3.c
@@ -0,0 +1,657 @@
+/* imzmq3.c
+ *
+ * This input plugin enables rsyslog to read messages from a ZeroMQ
+ * queue.
+ *
+ * Copyright 2012 Talksum, Inc.
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: David Kelly
+ * <davidk@talksum.com>
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "rsyslog.h"
+
+#include "cfsysline.h"
+#include "config.h"
+#include "dirty.h"
+#include "errmsg.h"
+#include "glbl.h"
+#include "module-template.h"
+#include "msg.h"
+#include "net.h"
+#include "parser.h"
+#include "prop.h"
+#include "ruleset.h"
+#include "srUtils.h"
+#include "unicode-helper.h"
+
+#include <czmq.h>
+
+MODULE_TYPE_INPUT
+MODULE_TYPE_NOKEEP
+
+/* convienent symbols to denote a socket we want to bind
+ * vs one we want to just connect to
+ */
+#define ACTION_CONNECT 1
+#define ACTION_BIND 2
+
+/* Module static data */
+DEF_IMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(prop)
+DEFobjCurrIf(ruleset)
+
+
+/* ----------------------------------------------------------------------------
+ * structs to describe sockets
+ */
+typedef struct _socket_type {
+ char* name;
+ int type;
+} socket_type;
+
+/* more overkill, but seems nice to be consistent.*/
+typedef struct _socket_action {
+ char* name;
+ int action;
+} socket_action;
+
+typedef struct _poller_data {
+ ruleset_t* ruleset;
+ thrdInfo_t* thread;
+} poller_data;
+
+typedef struct _socket_info {
+ int type;
+ int action;
+ char* description;
+ int sndHWM; /* if you want more than 2^32 messages, */
+ int rcvHWM; /* then pass in 0 (the default). */
+ char* identity;
+ char** subscriptions;
+ ruleset_t* ruleset;
+ int sndBuf;
+ int rcvBuf;
+ int linger;
+ int backlog;
+ int sndTimeout;
+ int rcvTimeout;
+ int maxMsgSize;
+ int rate;
+ int recoveryIVL;
+ int multicastHops;
+ int reconnectIVL;
+ int reconnectIVLMax;
+ int ipv4Only;
+ int affinity;
+
+} socket_info;
+
+
+/* ----------------------------------------------------------------------------
+ * Static definitions/initializations.
+ */
+static socket_info* s_socketInfo = NULL;
+static size_t s_nitems = 0;
+static prop_t * s_namep = NULL;
+static zloop_t* s_zloop = NULL;
+static int s_io_threads = 1;
+static zctx_t* s_context = NULL;
+static ruleset_t* s_ruleset = NULL;
+static socket_type socketTypes[] = {
+ {"SUB", ZMQ_SUB },
+ {"PULL", ZMQ_PULL },
+ {"XSUB", ZMQ_XSUB }
+};
+
+static socket_action socketActions[] = {
+ {"BIND", ACTION_BIND},
+ {"CONNECT", ACTION_CONNECT},
+};
+
+
+/* ----------------------------------------------------------------------------
+ * Helper functions
+ */
+
+/* get the name of a socket type, return the ZMQ_XXX type
+ or -1 if not a supported type (see above)
+*/
+static int getSocketType(char* name) {
+ int type = -1;
+ uint i;
+
+ /* match name with known socket type */
+ for(i=0; i<sizeof(socketTypes)/sizeof(socket_type); ++i) {
+ if( !strcmp(socketTypes[i].name, name) ) {
+ type = socketTypes[i].type;
+ break;
+ }
+ }
+
+ /* whine if no match was found. */
+ if (type == -1)
+ errmsg.LogError(0, NO_ERRCODE, "unknown type %s",name);
+
+ return type;
+}
+
+
+static int getSocketAction(char* name) {
+ int action = -1;
+ uint i;
+
+ /* match name with known socket action */
+ for(i=0; i < sizeof(socketActions)/sizeof(socket_action); ++i) {
+ if(!strcmp(socketActions[i].name, name)) {
+ action = socketActions[i].action;
+ break;
+ }
+ }
+
+ /* whine if no matching action was found */
+ if (action == -1)
+ errmsg.LogError(0, NO_ERRCODE, "unknown action %s",name);
+
+ return action;
+}
+
+
+static void setDefaults(socket_info* info) {
+ info->type = ZMQ_SUB;
+ info->action = ACTION_BIND;
+ info->description = NULL;
+ info->sndHWM = 0;
+ info->rcvHWM = 0;
+ info->identity = NULL;
+ info->subscriptions = NULL;
+ info->ruleset = NULL;
+ info->sndBuf = -1;
+ info->rcvBuf = -1;
+ info->linger = -1;
+ info->backlog = -1;
+ info->sndTimeout = -1;
+ info->rcvTimeout = -1;
+ info->maxMsgSize = -1;
+ info->rate = -1;
+ info->recoveryIVL = -1;
+ info->multicastHops = -1;
+ info->reconnectIVL = -1;
+ info->reconnectIVLMax = -1;
+ info->ipv4Only = -1;
+ info->affinity = -1;
+
+};
+
+
+/* The config string should look like:
+ * "action=AAA,type=TTT,description=DDD,sndHWM=SSS,rcvHWM=RRR,subscribe='xxx',subscribe='yyy'"
+ *
+ */
+static rsRetVal parseConfig(char* config, socket_info* info) {
+ int nsubs = 0;
+
+ char* binding;
+ char* ptr1;
+ for (binding = strtok_r(config, ",", &ptr1);
+ binding != NULL;
+ binding = strtok_r(NULL, ",", &ptr1)) {
+
+ /* Each binding looks like foo=bar */
+ char * sep = strchr(binding, '=');
+ if (sep == NULL)
+ {
+ errmsg.LogError(0, NO_ERRCODE,
+ "Invalid argument format %s, ignoring ...",
+ binding);
+ continue;
+ }
+
+ /* Replace '=' with '\0'. */
+ *sep = '\0';
+
+ char * val = sep + 1;
+
+ if (strcmp(binding, "action") == 0) {
+ info->action = getSocketAction(val);
+ } else if (strcmp(binding, "type") == 0) {
+ info->type = getSocketType(val);
+ } else if (strcmp(binding, "description") == 0) {
+ info->description = strdup(val);
+ } else if (strcmp(binding, "sndHWM") == 0) {
+ info->sndHWM = atoi(val);
+ } else if (strcmp(binding, "rcvHWM") == 0) {
+ info->sndHWM = atoi(val);
+ } else if (strcmp(binding, "subscribe") == 0) {
+ /* Add the subscription value to the list.*/
+ char * substr = NULL;
+ substr = strdup(val);
+ info->subscriptions = realloc(info->subscriptions, sizeof(char *) * nsubs + 1);
+ info->subscriptions[nsubs] = substr;
+ ++nsubs;
+ } else if (strcmp(binding, "sndBuf") == 0) {
+ info->sndBuf = atoi(val);
+ } else if (strcmp(binding, "rcvBuf") == 0) {
+ info->rcvBuf = atoi(val);
+ } else if (strcmp(binding, "linger") == 0) {
+ info->linger = atoi(val);
+ } else if (strcmp(binding, "backlog") == 0) {
+ info->backlog = atoi(val);
+ } else if (strcmp(binding, "sndTimeout") == 0) {
+ info->sndTimeout = atoi(val);
+ } else if (strcmp(binding, "rcvTimeout") == 0) {
+ info->rcvTimeout = atoi(val);
+ } else if (strcmp(binding, "maxMsgSize") == 0) {
+ info->maxMsgSize = atoi(val);
+ } else if (strcmp(binding, "rate") == 0) {
+ info->rate = atoi(val);
+ } else if (strcmp(binding, "recoveryIVL") == 0) {
+ info->recoveryIVL = atoi(val);
+ } else if (strcmp(binding, "multicastHops") == 0) {
+ info->multicastHops = atoi(val);
+ } else if (strcmp(binding, "reconnectIVL") == 0) {
+ info->reconnectIVL = atoi(val);
+ } else if (strcmp(binding, "reconnectIVLMax") == 0) {
+ info->reconnectIVLMax = atoi(val);
+ } else if (strcmp(binding, "ipv4Only") == 0) {
+ info->ipv4Only = atoi(val);
+ } else if (strcmp(binding, "affinity") == 0) {
+ info->affinity = atoi(val);
+ } else {
+ errmsg.LogError(0, NO_ERRCODE, "Unknown argument %s", binding);
+ return RS_RET_INVALID_PARAMS;
+ }
+ }
+
+ return RS_RET_OK;
+}
+
+static rsRetVal validateConfig(socket_info* info) {
+
+ if (info->type == -1) {
+ errmsg.LogError(0, RS_RET_INVALID_PARAMS,
+ "you entered an invalid type");
+ return RS_RET_INVALID_PARAMS;
+ }
+ if (info->action == -1) {
+ errmsg.LogError(0, RS_RET_INVALID_PARAMS,
+ "you entered an invalid action");
+ return RS_RET_INVALID_PARAMS;
+ }
+ if (info->description == NULL) {
+ errmsg.LogError(0, RS_RET_INVALID_PARAMS,
+ "you didn't enter a description");
+ return RS_RET_INVALID_PARAMS;
+ }
+ if(info->type == ZMQ_SUB && info->subscriptions == NULL) {
+ errmsg.LogError(0, RS_RET_INVALID_PARAMS,
+ "SUB sockets need at least one subscription");
+ return RS_RET_INVALID_PARAMS;
+ }
+ if(info->type != ZMQ_SUB && info->subscriptions != NULL) {
+ errmsg.LogError(0, RS_RET_INVALID_PARAMS,
+ "only SUB sockets can have subscriptions");
+ return RS_RET_INVALID_PARAMS;
+ }
+ return RS_RET_OK;
+}
+
+static rsRetVal createContext() {
+ if (s_context == NULL) {
+ errmsg.LogError(0, NO_ERRCODE, "creating zctx.");
+ s_context = zctx_new();
+
+ if (s_context == NULL) {
+ errmsg.LogError(0, RS_RET_INVALID_PARAMS,
+ "zctx_new failed: %s",
+ strerror(errno));
+ /* DK: really should do better than invalid params...*/
+ return RS_RET_INVALID_PARAMS;
+ }
+
+ if (s_io_threads > 1) {
+ errmsg.LogError(0, NO_ERRCODE, "setting io worker threads to %d", s_io_threads);
+ zctx_set_iothreads(s_context, s_io_threads);
+ }
+ }
+ return RS_RET_OK;
+}
+
+static rsRetVal createSocket(socket_info* info, void** sock) {
+ size_t ii;
+ int rv;
+
+ *sock = zsocket_new(s_context, info->type);
+ if (!sock) {
+ errmsg.LogError(0,
+ RS_RET_INVALID_PARAMS,
+ "zsocket_new failed: %s, for type %d",
+ strerror(errno),info->type);
+ /* DK: invalid params seems right here */
+ return RS_RET_INVALID_PARAMS;
+ }
+
+ /* Set options *before* the connect/bind. */
+ if (info->identity) zsocket_set_identity(*sock, info->identity);
+ if (info->sndBuf > -1) zsocket_set_sndbuf(*sock, info->sndBuf);
+ if (info->rcvBuf > -1) zsocket_set_rcvbuf(*sock, info->rcvBuf);
+ if (info->linger > -1) zsocket_set_linger(*sock, info->linger);
+ if (info->backlog > -1) zsocket_set_backlog(*sock, info->backlog);
+ if (info->sndTimeout > -1) zsocket_set_sndtimeo(*sock, info->sndTimeout);
+ if (info->rcvTimeout > -1) zsocket_set_rcvtimeo(*sock, info->rcvTimeout);
+ if (info->maxMsgSize > -1) zsocket_set_maxmsgsize(*sock, info->maxMsgSize);
+ if (info->rate > -1) zsocket_set_rate(*sock, info->rate);
+ if (info->recoveryIVL > -1) zsocket_set_recovery_ivl(*sock, info->recoveryIVL);
+ if (info->multicastHops > -1) zsocket_set_multicast_hops(*sock, info->multicastHops);
+ if (info->reconnectIVL > -1) zsocket_set_reconnect_ivl(*sock, info->reconnectIVL);
+ if (info->reconnectIVLMax > -1) zsocket_set_reconnect_ivl_max(*sock, info->reconnectIVLMax);
+ if (info->ipv4Only > -1) zsocket_set_ipv4only(*sock, info->ipv4Only);
+ if (info->affinity > -1) zsocket_set_affinity(*sock, info->affinity);
+
+ /* since HWM have defaults, we always set them. No return codes to check, either.*/
+ zsocket_set_sndhwm(*sock, info->sndHWM);
+ zsocket_set_rcvhwm(*sock, info->rcvHWM);
+
+ /* Set subscriptions.*/
+ for (ii = 0; ii < sizeof(info->subscriptions)/sizeof(char*); ++ii)
+ zsocket_set_subscribe(*sock, info->subscriptions[ii]);
+
+
+
+ /* Do the bind/connect... */
+ if (info->action==ACTION_CONNECT) {
+ rv = zsocket_connect(*sock, info->description);
+ if (rv < 0) {
+ errmsg.LogError(0,
+ RS_RET_INVALID_PARAMS,
+ "zmq_connect using %s failed: %s",
+ info->description, strerror(errno));
+ return RS_RET_INVALID_PARAMS;
+ }
+ } else {
+ rv = zsocket_bind(*sock, info->description);
+ if (rv <= 0) {
+ errmsg.LogError(0,
+ RS_RET_INVALID_PARAMS,
+ "zmq_bind using %s failed: %s",
+ info->description, strerror(errno));
+ return RS_RET_INVALID_PARAMS;
+ }
+ }
+ return RS_RET_OK;
+}
+
+/* ----------------------------------------------------------------------------
+ * Module endpoints
+ */
+
+/* accept a new ruleset to bind. Checks if it exists and complains, if not. Note
+ * that this makes the assumption that after the bind ruleset is called in the config,
+ * another call will be made to add an endpoint.
+*/
+static rsRetVal
+set_ruleset(void __attribute__((unused)) *pVal, uchar *pszName) {
+ ruleset_t* ruleset_ptr;
+ rsRetVal localRet;
+ DEFiRet;
+
+ localRet = ruleset.GetRuleset(ourConf, &ruleset_ptr, pszName);
+ if(localRet == RS_RET_NOT_FOUND) {
+ errmsg.LogError(0, NO_ERRCODE, "error: "
+ "ruleset '%s' not found - ignored", pszName);
+ }
+ CHKiRet(localRet);
+ s_ruleset = ruleset_ptr;
+ DBGPRINTF("imzmq3 current bind ruleset '%s'\n", pszName);
+
+finalize_it:
+ free(pszName); /* no longer needed */
+ RETiRet;
+}
+
+/* add an actual endpoint
+ */
+static rsRetVal add_endpoint(void __attribute__((unused)) * oldp, uchar * valp) {
+ DEFiRet;
+
+ /* increment number of items and store old num items, as it will be handy.*/
+ size_t idx = s_nitems++;
+
+ /* allocate a new socket_info array to accomidate this new endpoint*/
+ socket_info* tmpSocketInfo;
+ CHKmalloc(tmpSocketInfo = (socket_info*)MALLOC(sizeof(socket_info) * s_nitems));
+
+ /* copy existing socket_info across into new array, if any, and free old storage*/
+ if(idx) {
+ memcpy(tmpSocketInfo, s_socketInfo, sizeof(socket_info) * idx);
+ free(s_socketInfo);
+ }
+
+ /* set the static to hold the new array */
+ s_socketInfo = tmpSocketInfo;
+
+ /* point to the new one */
+ socket_info* sockInfo = &s_socketInfo[idx];
+
+ /* set defaults for the new socket info */
+ setDefaults(sockInfo);
+
+ /* Make a writeable copy of the string so we can use strtok
+ in the parseConfig call */
+ char * copy = NULL;
+ CHKmalloc(copy = strdup((char *) valp));
+
+ /* parse the config string */
+ CHKiRet(parseConfig(copy, sockInfo));
+
+ /* validate it */
+ CHKiRet(validateConfig(sockInfo));
+
+ /* bind to the current ruleset (if any)*/
+ sockInfo->ruleset = s_ruleset;
+
+finalize_it:
+ free(valp); /* in any case, this is no longer needed */
+ RETiRet;
+}
+
+
+static int handlePoll(zloop_t __attribute__((unused)) * loop, zmq_pollitem_t *poller, void* pd) {
+ msg_t* logmsg;
+ poller_data* pollerData = (poller_data*)pd;
+
+ char* buf = zstr_recv(poller->socket);
+ if (msgConstruct(&logmsg) == RS_RET_OK) {
+ MsgSetRawMsg(logmsg, buf, strlen(buf));
+ MsgSetInputName(logmsg, s_namep);
+ MsgSetFlowControlType(logmsg, eFLOWCTL_NO_DELAY);
+ MsgSetRuleset(logmsg, pollerData->ruleset);
+ logmsg->msgFlags = NEEDS_PARSING;
+ submitMsg(logmsg);
+ }
+
+ /* gotta free the string returned from zstr_recv() */
+ free(buf);
+
+ if( pollerData->thread->bShallStop == TRUE) {
+ /* a handler that returns -1 will terminate the
+ czmq reactor loop
+ */
+ return -1;
+ }
+
+ return 0;
+}
+
+/* called when runInput is called by rsyslog
+ */
+static rsRetVal rcv_loop(thrdInfo_t* pThrd){
+ size_t i;
+ int rv;
+ zmq_pollitem_t* items;
+ poller_data* pollerData;
+
+ DEFiRet;
+
+ /* create the context*/
+ CHKiRet(createContext());
+
+ /* create the poll items*/
+ CHKmalloc(items = (zmq_pollitem_t*)MALLOC(sizeof(zmq_pollitem_t)*s_nitems));
+
+ /* create poller data (stuff to pass into the zmq closure called when we get a message)*/
+ CHKmalloc(pollerData = (poller_data*)MALLOC(sizeof(poller_data)*s_nitems));
+
+ /* loop through and initialize the poll items and poller_data arrays...*/
+ for(i=0; i<s_nitems;++i) {
+ /* create the socket, update items.*/
+ createSocket(&s_socketInfo[i], &items[i].socket);
+ items[i].events = ZMQ_POLLIN;
+
+ /* now update the poller_data for this item */
+ pollerData[i].thread = pThrd;
+ pollerData[i].ruleset = s_socketInfo[i].ruleset;
+ }
+
+ s_zloop = zloop_new();
+ for(i=0; i<s_nitems; ++i) {
+
+ rv = zloop_poller(s_zloop, &items[i], handlePoll, &pollerData[i]);
+ if (rv) {
+ errmsg.LogError(0, NO_ERRCODE, "imzmq3: zloop_poller failed for item %zu", i);
+ }
+ }
+ zloop_start(s_zloop);
+ zloop_destroy(&s_zloop);
+ finalize_it:
+ for(i=0; i< s_nitems; ++i) {
+ zsocket_destroy(s_context, items[i].socket);
+ }
+
+ zctx_destroy(&s_context);
+
+ free(items);
+ RETiRet;
+}
+
+/* ----------------------------------------------------------------------------
+ * input module functions
+ */
+
+BEGINrunInput
+CODESTARTrunInput
+ iRet = rcv_loop(pThrd);
+ RETiRet;
+ENDrunInput
+
+
+/* initialize and return if will run or not */
+BEGINwillRun
+CODESTARTwillRun
+ /* we need to create the inputName property (only once during our
+ lifetime) */
+ CHKiRet(prop.Construct(&s_namep));
+ CHKiRet(prop.SetString(s_namep,
+ UCHAR_CONSTANT("imzmq3"),
+ sizeof("imzmq3") - 1));
+ CHKiRet(prop.ConstructFinalize(s_namep));
+
+/* If there are no endpoints this is pointless ...*/
+ if (s_nitems == 0)
+ ABORT_FINALIZE(RS_RET_NO_RUN);
+
+finalize_it:
+ENDwillRun
+
+
+BEGINafterRun
+CODESTARTafterRun
+ /* do cleanup here */
+ if(s_namep != NULL)
+ prop.Destruct(&s_namep);
+ENDafterRun
+
+
+BEGINmodExit
+CODESTARTmodExit
+ /* release what we no longer need */
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(prop, CORE_COMPONENT);
+ objRelease(ruleset, CORE_COMPONENT);
+ENDmodExit
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURENonCancelInputTermination)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_IMOD_QUERIES
+CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
+ENDqueryEtryPt
+
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp,
+ void __attribute__((unused)) *pVal) {
+ return RS_RET_OK;
+}
+static rsRetVal setGlobalWorkerThreads(uchar __attribute__((unused)) *pp, int val) {
+ errmsg.LogError(0, NO_ERRCODE, "setGlobalWorkerThreads called with %d",val);
+ s_io_threads = val;
+ return RS_RET_OK;
+}
+
+BEGINmodInit()
+CODESTARTmodInit
+ /* we only support the current interface specification */
+ *ipIFVersProvided = CURR_MOD_IF_VERSION;
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(prop, CORE_COMPONENT));
+ CHKiRet(objUse(ruleset, CORE_COMPONENT));
+
+ /* register config file handlers */
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputzmq3serverbindruleset",
+ 0, eCmdHdlrGetWord,
+ set_ruleset, NULL,
+ STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputzmq3serverrun",
+ 0, eCmdHdlrGetWord,
+ add_endpoint, NULL,
+ STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables",
+ 1, eCmdHdlrCustomHandler,
+ resetConfigVariables, NULL,
+ STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputzmq3globalWorkerThreads",
+ 1, eCmdHdlrInt,
+ setGlobalWorkerThreads, NULL,
+ STD_LOADABLE_MODULE_ID));
+ENDmodInit
diff --git a/plugins/omhdfs/omhdfs.c b/plugins/omhdfs/omhdfs.c
index cd14d03c..f8a7e739 100644
--- a/plugins/omhdfs/omhdfs.c
+++ b/plugins/omhdfs/omhdfs.c
@@ -67,8 +67,8 @@ typedef struct configSettings_s {
uchar *dfltTplName; /* default template name to use */
int hdfsPort;
} configSettings_t;
+static configSettings_t cs;
-SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */
BEGINinitConfVars /* (re)set config variables to default values */
CODESTARTinitConfVars
diff --git a/plugins/ommongodb/README b/plugins/ommongodb/README
index 7581131a..ad4a8ea2 100644
--- a/plugins/ommongodb/README
+++ b/plugins/ommongodb/README
@@ -6,20 +6,13 @@ configuration:
in your /etc/rsyslog.conf, together with other modules:
$ModLoad ommongodb # provides mongodb support
+*.* action(type="ommongodb" db="..." collection="..." template="...")
-then in your /etc/rsyslog.d (check your distribution way to organize the configuration..) you create a file 10-mongodb.conf with the following content:
+Note: if no template is specified, a default schema will be used. That schema
+contains proper data types. However, if a template is specified, only strings
+are supported. This is a restriction of the rsyslog v6 core engine. This
+changed in v7.
-*.* action(type="ommongodb" db="..." collection="...")
-
-Note: currently templates are not supported. Ommongodb will pick a default
-schema and use the message object content for that (templateless).
-
-
-TODO
-we must ensure that the collection is a capped collection
-refactor my code :-)
-
-email Victor Pereira <victor.pereira@bigrails.com>
-twitter twitter.com/vpereira
-
-part of this doc by Rainer Gerhards <rgerhards@adiscon.com>
+If templates are used, it is suggested to use list-based templates. Constants
+can ONLY be inserted with list-based templates, as only these provide the
+capability to specify a field name (outname parameter).
diff --git a/plugins/ommongodb/ommongodb.c b/plugins/ommongodb/ommongodb.c
index aa20b33f..f2de33df 100644
--- a/plugins/ommongodb/ommongodb.c
+++ b/plugins/ommongodb/ommongodb.c
@@ -30,8 +30,12 @@
#include <errno.h>
#include <assert.h>
#include <signal.h>
+#include <stdint.h>
#include <time.h>
#include <mongo.h>
+#include <json/json.h>
+/* For struct json_object_iter, should not be necessary in future versions */
+#include <json/json_object_private.h>
#include "rsyslog.h"
#include "conf.h"
@@ -42,6 +46,7 @@
#include "datetime.h"
#include "errmsg.h"
#include "cfsysline.h"
+#include "unicode-helper.h"
MODULE_TYPE_OUTPUT
MODULE_TYPE_NOKEEP
@@ -54,6 +59,7 @@ DEFobjCurrIf(datetime)
typedef struct _instanceData {
mongo_sync_connection *conn;
+ struct json_tokener *json_tokener; /* only if (tplName != NULL) */
uchar *server;
int port;
uchar *db;
@@ -108,11 +114,14 @@ static void closeMongoDB(instanceData *pData)
BEGINfreeInstance
CODESTARTfreeInstance
closeMongoDB(pData);
+ if (pData->json_tokener != NULL)
+ json_tokener_free(pData->json_tokener);
free(pData->server);
free(pData->db);
free(pData->collection);
free(pData->uid);
free(pData->pwd);
+ free(pData->dbNcoll);
free(pData->tplName);
ENDfreeInstance
@@ -120,6 +129,7 @@ ENDfreeInstance
BEGINdbgPrintInstInfo
CODESTARTdbgPrintInstInfo
/* nothing special here */
+ (void)pData;
ENDdbgPrintInstInfo
@@ -139,6 +149,8 @@ reportMongoError(instanceData *pData)
errmsg.LogError(0, RS_RET_ERR, "ommongodb: we had an error, but can "
"not obtain specifics");
}
+#else
+ (void)pData;
#endif
}
@@ -202,13 +214,14 @@ i10pow(int exp)
}
return r;
}
-/* write to mongodb in MSG passing mode, that is without a template.
+/* Return a BSON document when an user hasn't specified a template.
* In this mode, we use the standard document format, which is somewhat
* aligned to cee (as described in project lumberjack). Note that this is
* a moving target, so we may run out of sync (and stay so to retain
* backward compatibility, which we consider pretty important).
*/
-rsRetVal writeMongoDB_msg(msg_t *pMsg, instanceData *pData)
+static bson *
+getDefaultBSON(msg_t *pMsg)
{
bson *doc = NULL;
uchar *procid; short unsigned procid_free; size_t procid_len;
@@ -219,12 +232,6 @@ rsRetVal writeMongoDB_msg(msg_t *pMsg, instanceData *pData)
int severity, facil;
gint64 ts_gen, ts_rcv; /* timestamps: generated, received */
int secfrac;
- DEFiRet;
-
- /* see if we are ready to proceed */
- if(pData->conn == NULL) {
- CHKiRet(initMongoDB(pData, 0));
- }
procid = MsgGetProp(pMsg, NULL, PROP_PROGRAMNAME, NULL, &procid_len, &procid_free);
tag = MsgGetProp(pMsg, NULL, PROP_SYSLOGTAG, NULL, &tag_len, &tag_free);
@@ -276,22 +283,127 @@ dbgprintf("ommongodb: secfrac is %d, precision %d\n", pMsg->tTIMESTAMP.secfrac,
if(sys_free) free(sys);
if(msg_free) free(msg);
- if(doc == NULL) {
- reportMongoError(pData);
- dbgprintf("ommongodb: error creating BSON doc\n");
- ABORT_FINALIZE(RS_RET_SUSPENDED);
- }
+ if(doc == NULL)
+ return doc;
bson_finish(doc);
- if(!mongo_sync_cmd_insert(pData->conn, (char*)pData->dbNcoll, doc, NULL)) {
- reportMongoError(pData);
- dbgprintf("ommongodb: insert error\n");
- ABORT_FINALIZE(RS_RET_SUSPENDED);
+ return doc;
+}
+
+static bson *BSONFromJSONArray(struct json_object *json);
+static bson *BSONFromJSONObject(struct json_object *json);
+
+/* Append a BSON variant of json to doc using name. Return TRUE on success */
+static gboolean
+BSONAppendJSONObject(bson *doc, const gchar *name, struct json_object *json)
+{
+ switch(json_object_get_type(json)) {
+ case json_type_boolean:
+ return bson_append_boolean(doc, name,
+ json_object_get_boolean(json));
+ case json_type_double:
+ return bson_append_double(doc, name,
+ json_object_get_double(json));
+ case json_type_int: {
+ int64_t i;
+
+ /* FIXME: the future version will have get_int64 */
+ i = json_object_get_int(json);
+ if (i >= INT32_MIN && i <= INT32_MAX)
+ return bson_append_int32(doc, name, i);
+ else
+ return bson_append_int64(doc, name, i);
+ }
+ case json_type_object: {
+ bson *sub;
+ gboolean ok;
+
+ sub = BSONFromJSONObject(json);
+ if (sub == NULL)
+ return FALSE;
+ ok = bson_append_document(doc, name, sub);
+ bson_free(sub);
+ return ok;
+ }
+ case json_type_array: {
+ bson *sub;
+ gboolean ok;
+
+ sub = BSONFromJSONArray(json);
+ if (sub == NULL)
+ return FALSE;
+ ok = bson_append_document(doc, name, sub);
+ bson_free(sub);
+ return ok;
}
+ case json_type_string:
+ return bson_append_string(doc, name,
+ json_object_get_string(json), -1);
-finalize_it:
+ default:
+ return FALSE;
+ }
+}
+
+/* Return a BSON variant of json, which must be a json_type_array */
+static bson *
+BSONFromJSONArray(struct json_object *json)
+{
+ /* Way more than necessary */
+ bson *doc = NULL;
+ size_t i, array_len;
+
+ doc = bson_new();
+ if(doc == NULL)
+ goto error;
+
+ array_len = json_object_array_length(json);
+ for (i = 0; i < array_len; i++) {
+ char buf[sizeof(size_t) * CHAR_BIT + 1];
+
+ if ((size_t)snprintf(buf, sizeof(buf), "%zu", i) >= sizeof(buf))
+ goto error;
+ if (BSONAppendJSONObject(doc, buf,
+ json_object_array_get_idx(json, i))
+ == FALSE)
+ goto error;
+ }
+
+ if(bson_finish(doc) == FALSE)
+ goto error;
+
+ return doc;
+
+error:
if(doc != NULL)
bson_free(doc);
- RETiRet;
+ return NULL;
+}
+
+/* Return a BSON variant of json, which must be a json_type_object */
+static bson *
+BSONFromJSONObject(struct json_object *json)
+{
+ bson *doc = NULL;
+ struct json_object_iter it;
+
+ doc = bson_new();
+ if(doc == NULL)
+ goto error;
+
+ json_object_object_foreachC(json, it) {
+ if (BSONAppendJSONObject(doc, it.key, it.val) == FALSE)
+ goto error;
+ }
+
+ if(bson_finish(doc) == FALSE)
+ goto error;
+
+ return doc;
+
+error:
+ if(doc != NULL)
+ bson_free(doc);
+ return NULL;
}
BEGINtryResume
@@ -302,10 +414,32 @@ CODESTARTtryResume
ENDtryResume
BEGINdoAction
+ bson *doc = NULL;
CODESTARTdoAction
+ /* see if we are ready to proceed */
+ if(pData->conn == NULL) {
+ CHKiRet(initMongoDB(pData, 0));
+ }
+
if(pData->tplName == NULL) {
- iRet = writeMongoDB_msg((msg_t*)ppString[0], pData);
+ doc = getDefaultBSON((msg_t*)ppString[0]);
+ } else {
+ doc = BSONFromJSONObject((struct json_object *)ppString[0]);
+ }
+ if(doc == NULL) {
+ dbgprintf("ommongodb: error creating BSON doc\n");
+ /* FIXME: is this a correct return code? */
+ ABORT_FINALIZE(RS_RET_ERR);
}
+ if(!mongo_sync_cmd_insert(pData->conn, (char*)pData->dbNcoll, doc, NULL)) {
+ reportMongoError(pData);
+ dbgprintf("ommongodb: insert error\n");
+ ABORT_FINALIZE(RS_RET_SUSPENDED);
+ }
+
+finalize_it:
+ if(doc != NULL)
+ bson_free(doc);
ENDdoAction
@@ -360,12 +494,9 @@ CODESTARTnewActInst
if(pData->tplName == NULL) {
CHKiRet(OMSRsetEntry(*ppOMSR, 0, NULL, OMSR_TPL_AS_MSG));
} else {
- errmsg.LogError(0, RS_RET_LEGA_ACT_NOT_SUPPORTED,
- "ommongodb: templates are not supported in this version");
- ABORT_FINALIZE(RS_RET_ERR);
- CHKiRet(OMSRsetEntry(*ppOMSR, 0,
- (uchar*) strdup((char*) pData->tplName),
- OMSR_TPL_AS_ARRAY));
+ CHKiRet(OMSRsetEntry(*ppOMSR, 0, ustrdup(pData->tplName),
+ OMSR_TPL_AS_JSON));
+ CHKmalloc(pData->json_tokener = json_tokener_new());
}
if(pData->db == NULL)
@@ -417,6 +548,10 @@ CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES
ENDqueryEtryPt
BEGINmodInit()
+ rsRetVal localRet;
+ rsRetVal (*pomsrGetSupportedTplOpts)(unsigned long *pOpts);
+ unsigned long opts;
+ int bJSONPassingSupported;
CODESTARTmodInit
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
@@ -424,5 +559,22 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(datetime, CORE_COMPONENT));
INITChkCoreFeature(bCoreSupportsBatching, CORE_FEATURE_BATCHING);
DBGPRINTF("ommongodb: module compiled with rsyslog version %s.\n", VERSION);
- //DBGPRINTF("ommongodb: %susing transactional output interface.\n", bCoreSupportsBatching ? "" : "not ");
+
+ /* check if the rsyslog core supports parameter passing code */
+ bJSONPassingSupported = 0;
+ localRet = pHostQueryEtryPt((uchar*)"OMSRgetSupportedTplOpts",
+ &pomsrGetSupportedTplOpts);
+ if(localRet == RS_RET_OK) {
+ /* found entry point, so let's see if core supports msg passing */
+ CHKiRet((*pomsrGetSupportedTplOpts)(&opts));
+ if(opts & OMSR_TPL_AS_JSON)
+ bJSONPassingSupported = 1;
+ } else if(localRet != RS_RET_ENTRY_POINT_NOT_FOUND) {
+ ABORT_FINALIZE(localRet); /* Something else went wrong, not acceptable */
+ }
+ if(!bJSONPassingSupported) {
+ DBGPRINTF("ommongodb: JSON-passing is not supported by rsyslog core, "
+ "can not continue.\n");
+ ABORT_FINALIZE(RS_RET_NO_JSON_PASSING);
+ }
ENDmodInit
diff --git a/plugins/omudpspoof/omudpspoof.c b/plugins/omudpspoof/omudpspoof.c
index 1db2f7f0..531a0dcf 100644
--- a/plugins/omudpspoof/omudpspoof.c
+++ b/plugins/omudpspoof/omudpspoof.c
@@ -107,23 +107,41 @@ typedef struct _instanceData {
#define DFLT_SOURCE_PORT_END 42000
typedef struct configSettings_s {
- uchar *pszTplName; /* name of the default template to use */
+ uchar *tplName; /* name of the default template to use */
uchar *pszSourceNameTemplate; /* name of the template containing the spoofing address */
uchar *pszTargetHost;
uchar *pszTargetPort;
- int iCompressionLevel; /* zlib compressionlevel, the usual values */
int iSourcePortStart;
int iSourcePortEnd;
} configSettings_t;
static configSettings_t cs;
+/* module-global parameters */
+static struct cnfparamdescr modpdescr[] = {
+ { "template", eCmdHdlrGetWord, 0 },
+};
+static struct cnfparamblk modpblk =
+ { CNFPARAMBLK_VERSION,
+ sizeof(modpdescr)/sizeof(struct cnfparamdescr),
+ modpdescr
+ };
+
+struct modConfData_s {
+ rsconf_t *pConf; /* our overall config object */
+ uchar *tplName; /* default template */
+};
+
+static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
+static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current exec process */
+
+
+
BEGINinitConfVars /* (re)set config variables to default values */
CODESTARTinitConfVars
- cs.pszTplName = NULL;
+ cs.tplName = NULL;
cs.pszSourceNameTemplate = NULL;
cs.pszTargetHost = NULL;
cs.pszTargetPort = NULL;
- cs.iCompressionLevel = 0;
cs.iSourcePortStart = DFLT_SOURCE_PORT_START;
cs.iSourcePortEnd = DFLT_SOURCE_PORT_END;
ENDinitConfVars
@@ -138,6 +156,44 @@ pthread_mutex_t mutLibnet;
static rsRetVal doTryResume(instanceData *pData);
+/* this function gets the default template. It coordinates action between
+ * old-style and new-style configuration parts.
+ */
+static inline uchar*
+getDfltTpl(void)
+{
+ if(loadModConf != NULL && loadModConf->tplName != NULL)
+ return loadModConf->tplName;
+ else if(cs.tplName == NULL)
+ return (uchar*)"RSYSLOG_FileFormat";
+ else
+ return cs.tplName;
+}
+
+
+/* set the default template to be used
+ * This is a module-global parameter, and as such needs special handling. It needs to
+ * be coordinated with values set via the v2 config system (rsyslog v6+). What we do
+ * is we do not permit this directive after the v2 config system has been used to set
+ * the parameter.
+ */
+rsRetVal
+setLegacyDfltTpl(void __attribute__((unused)) *pVal, uchar* newVal)
+{
+ DEFiRet;
+
+ if(loadModConf != NULL && loadModConf->tplName != NULL) {
+ free(newVal);
+ errmsg.LogError(0, RS_RET_ERR, "omudpspoof default template already set via module "
+ "global parameter - can no longer be changed");
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ free(cs.tplName);
+ cs.tplName = newVal;
+finalize_it:
+ RETiRet;
+}
+
/* Close the UDP sockets.
* rgerhards, 2009-05-29
*/
@@ -167,6 +223,72 @@ static inline uchar *getFwdPt(instanceData *pData)
}
+BEGINbeginCnfLoad
+CODESTARTbeginCnfLoad
+ loadModConf = pModConf;
+ pModConf->pConf = pConf;
+ pModConf->tplName = NULL;
+ENDbeginCnfLoad
+
+BEGINsetModCnf
+ struct cnfparamvals *pvals = NULL;
+ int i;
+CODESTARTsetModCnf
+ pvals = nvlstGetParams(lst, &modpblk, NULL);
+ if(pvals == NULL) {
+ errmsg.LogError(0, RS_RET_MISSING_CNFPARAMS, "error processing module "
+ "config parameters [module(...)]");
+ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+ }
+
+ if(Debug) {
+ dbgprintf("module (global) param blk for omudpspoof:\n");
+ cnfparamsPrint(&modpblk, pvals);
+ }
+
+ for(i = 0 ; i < modpblk.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(modpblk.descr[i].name, "template")) {
+ loadModConf->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ if(cs.tplName != NULL) {
+ errmsg.LogError(0, RS_RET_DUP_PARAM, "omudpspoof: warning: default template "
+ "was already set via legacy directive - may lead to inconsistent "
+ "results.");
+ }
+ } else {
+ dbgprintf("omudpspoof: program error, non-handled "
+ "param '%s' in beginCnfLoad\n", modpblk.descr[i].name);
+ }
+ }
+finalize_it:
+ if(pvals != NULL)
+ cnfparamvalsDestruct(pvals, &modpblk);
+ENDsetModCnf
+
+BEGINendCnfLoad
+CODESTARTendCnfLoad
+ loadModConf = NULL; /* done loading */
+ /* free legacy config vars */
+ free(cs.tplName);
+ cs.tplName = NULL;
+ENDendCnfLoad
+
+BEGINcheckCnf
+CODESTARTcheckCnf
+ENDcheckCnf
+
+BEGINactivateCnf
+CODESTARTactivateCnf
+ runModConf = pModConf;
+ENDactivateCnf
+
+BEGINfreeCnf
+CODESTARTfreeCnf
+ free(pModConf->tplName);
+ENDfreeCnf
+
+
BEGINcreateInstance
CODESTARTcreateInstance
ENDcreateInstance
@@ -421,13 +543,12 @@ CODE_STD_STRING_REQUESTparseSelectorAct(2)
else
CHKmalloc(pData->port = ustrdup(cs.pszTargetPort));
CHKiRet(OMSRsetEntry(*ppOMSR, 1, ustrdup(sourceTpl), OMSR_NO_RQD_TPL_OPTS));
- pData->compressionLevel = cs.iCompressionLevel;
pData->sourcePort = pData->sourcePortStart = cs.iSourcePortStart;
pData->sourcePortEnd = cs.iSourcePortEnd;
/* process template */
CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
- (cs.pszTplName == NULL) ? (uchar*)"RSYSLOG_TraditionalForwardFormat" : cs.pszTplName));
+ (cs.tplName == NULL) ? (uchar*)"RSYSLOG_TraditionalForwardFormat" : cs.tplName));
CODE_STD_FINALIZERparseSelectorAct
ENDparseSelectorAct
@@ -439,8 +560,8 @@ ENDparseSelectorAct
static void
freeConfigVars(void)
{
- free(cs.pszTplName);
- cs.pszTplName = NULL;
+ free(cs.tplName);
+ cs.tplName = NULL;
free(cs.pszTargetHost);
cs.pszTargetHost = NULL;
free(cs.pszTargetPort);
@@ -464,6 +585,8 @@ ENDmodExit
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_OMOD_QUERIES
+CODEqueryEtryPt_STD_CONF2_QUERIES
+CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES
ENDqueryEtryPt
@@ -474,7 +597,6 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
{
freeConfigVars();
/* we now must reset all non-string values */
- cs.iCompressionLevel = 0;
cs.iSourcePortStart = DFLT_SOURCE_PORT_START;
cs.iSourcePortEnd = DFLT_SOURCE_PORT_END;
return RS_RET_OK;
@@ -504,13 +626,12 @@ CODEmodInit_QueryRegCFSLineHdlr
}
pthread_mutex_init(&mutLibnet, NULL);
- CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspoofdefaulttemplate", 0, eCmdHdlrGetWord, NULL, &cs.pszTplName, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspoofdefaulttemplate", 0, eCmdHdlrGetWord, setLegacyDfltTpl, NULL, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspoofsourcenametemplate", 0, eCmdHdlrGetWord, NULL, &cs.pszSourceNameTemplate, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspooftargethost", 0, eCmdHdlrGetWord, NULL, &cs.pszTargetHost, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspooftargetport", 0, eCmdHdlrGetWord, NULL, &cs.pszTargetPort, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspoofsourceportstart", 0, eCmdHdlrInt, NULL, &cs.iSourcePortStart, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspoofsourceportend", 0, eCmdHdlrInt, NULL, &cs.iSourcePortEnd, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpcompressionlevel", 0, eCmdHdlrInt, NULL, &cs.iCompressionLevel, NULL));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
diff --git a/plugins/omuxsock/omuxsock.c b/plugins/omuxsock/omuxsock.c
index cf27c93c..583b9f94 100644
--- a/plugins/omuxsock/omuxsock.c
+++ b/plugins/omuxsock/omuxsock.c
@@ -71,6 +71,26 @@ typedef struct configSettings_s {
} configSettings_t;
static configSettings_t cs;
+/* module-global parameters */
+static struct cnfparamdescr modpdescr[] = {
+ { "template", eCmdHdlrGetWord, 0 },
+};
+static struct cnfparamblk modpblk =
+ { CNFPARAMBLK_VERSION,
+ sizeof(modpdescr)/sizeof(struct cnfparamdescr),
+ modpdescr
+ };
+
+struct modConfData_s {
+ rsconf_t *pConf; /* our overall config object */
+ uchar *tplName; /* default template */
+};
+
+static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
+static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current exec process */
+
+
+
BEGINinitConfVars /* (re)set config variables to default values */
CODESTARTinitConfVars
cs.tplName = NULL;
@@ -80,8 +100,45 @@ ENDinitConfVars
static rsRetVal doTryResume(instanceData *pData);
-/* Close socket.
+
+/* this function gets the default template. It coordinates action between
+ * old-style and new-style configuration parts.
+ */
+static inline uchar*
+getDfltTpl(void)
+{
+ if(loadModConf != NULL && loadModConf->tplName != NULL)
+ return loadModConf->tplName;
+ else if(cs.tplName == NULL)
+ return (uchar*)"RSYSLOG_TraditionalForwardFormat";
+ else
+ return cs.tplName;
+}
+
+/* set the default template to be used
+ * This is a module-global parameter, and as such needs special handling. It needs to
+ * be coordinated with values set via the v2 config system (rsyslog v6+). What we do
+ * is we do not permit this directive after the v2 config system has been used to set
+ * the parameter.
*/
+rsRetVal
+setLegacyDfltTpl(void __attribute__((unused)) *pVal, uchar* newVal)
+{
+ DEFiRet;
+
+ if(loadModConf != NULL && loadModConf->tplName != NULL) {
+ free(newVal);
+ errmsg.LogError(0, RS_RET_ERR, "omuxsock default template already set via module "
+ "global parameter - can no longer be changed");
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ free(cs.tplName);
+ cs.tplName = newVal;
+finalize_it:
+ RETiRet;
+}
+
+
static inline rsRetVal
closeSocket(instanceData *pData)
{
@@ -96,6 +153,72 @@ pData->bIsConnected = 0; // TODO: remove this variable altogether
+
+BEGINbeginCnfLoad
+CODESTARTbeginCnfLoad
+ loadModConf = pModConf;
+ pModConf->pConf = pConf;
+ pModConf->tplName = NULL;
+ENDbeginCnfLoad
+
+BEGINsetModCnf
+ struct cnfparamvals *pvals = NULL;
+ int i;
+CODESTARTsetModCnf
+ pvals = nvlstGetParams(lst, &modpblk, NULL);
+ if(pvals == NULL) {
+ errmsg.LogError(0, RS_RET_MISSING_CNFPARAMS, "error processing module "
+ "config parameters [module(...)]");
+ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+ }
+
+ if(Debug) {
+ dbgprintf("module (global) param blk for omuxsock:\n");
+ cnfparamsPrint(&modpblk, pvals);
+ }
+
+ for(i = 0 ; i < modpblk.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(modpblk.descr[i].name, "template")) {
+ loadModConf->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ if(cs.tplName != NULL) {
+ errmsg.LogError(0, RS_RET_DUP_PARAM, "omuxsock: warning: default template "
+ "was already set via legacy directive - may lead to inconsistent "
+ "results.");
+ }
+ } else {
+ dbgprintf("omuxsock: program error, non-handled "
+ "param '%s' in beginCnfLoad\n", modpblk.descr[i].name);
+ }
+ }
+finalize_it:
+ if(pvals != NULL)
+ cnfparamvalsDestruct(pvals, &modpblk);
+ENDsetModCnf
+
+BEGINendCnfLoad
+CODESTARTendCnfLoad
+ loadModConf = NULL; /* done loading */
+ /* free legacy config vars */
+ free(cs.tplName);
+ cs.tplName = NULL;
+ENDendCnfLoad
+
+BEGINcheckCnf
+CODESTARTcheckCnf
+ENDcheckCnf
+
+BEGINactivateCnf
+CODESTARTactivateCnf
+ runModConf = pModConf;
+ENDactivateCnf
+
+BEGINfreeCnf
+CODESTARTfreeCnf
+ free(pModConf->tplName);
+ENDfreeCnf
+
BEGINcreateInstance
CODESTARTcreateInstance
pData->sock = INVLD_SOCK;
@@ -250,8 +373,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
/* check if a non-standard template is to be applied */
if(*(p-1) == ';')
--p;
- CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, 0, cs.tplName == NULL ? UCHAR_CONSTANT("RSYSLOG_TraditionalForwardFormat")
- : cs.tplName ));
+ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, 0, getDfltTpl()));
if(cs.sockName == NULL) {
errmsg.LogError(0, RS_RET_NO_SOCK_CONFIGURED, "No output socket configured for omuxsock\n");
@@ -291,6 +413,8 @@ ENDmodExit
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_OMOD_QUERIES
+CODEqueryEtryPt_STD_CONF2_QUERIES
+CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES
ENDqueryEtryPt
@@ -312,7 +436,7 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(glbl, CORE_COMPONENT));
CHKiRet(objUse(errmsg, CORE_COMPONENT));
- CHKiRet(regCfSysLineHdlr((uchar *)"omuxsockdefaulttemplate", 0, eCmdHdlrGetWord, NULL, &cs.tplName, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"omuxsockdefaulttemplate", 0, eCmdHdlrGetWord, setLegacyDfltTpl, NULL, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"omuxsocksocket", 0, eCmdHdlrGetWord, NULL, &cs.sockName, NULL));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
diff --git a/plugins/omzmq3/Makefile.am b/plugins/omzmq3/Makefile.am
new file mode 100644
index 00000000..92cd7586
--- /dev/null
+++ b/plugins/omzmq3/Makefile.am
@@ -0,0 +1,8 @@
+pkglib_LTLIBRARIES = omzmq3.la
+
+omzmq3_la_SOURCES = omzmq3.c
+omzmq3_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) $(CZMQ_CFLAGS)
+omzmq3_la_LDFLAGS = -module -avoid-version
+omzmq3_la_LIBADD = $(CZMQ_LIBS)
+
+EXTRA_DIST =
diff --git a/plugins/omzmq3/README b/plugins/omzmq3/README
new file mode 100644
index 00000000..ccc96c74
--- /dev/null
+++ b/plugins/omzmq3/README
@@ -0,0 +1,25 @@
+ZeroMQ 3.x Output Plugin
+
+Building this plugin:
+Requires libzmq and libczmq. First, install libzmq from the HEAD on github:
+http://github.com/zeromq/libzmq. You can clone the repository, build, then
+install it. The directions for doing so are there in the readme. Then, do
+the same for libczmq: http://github.com/zeromq/czmq. At some point, the 3.1
+version of libzmq will be released, and a supporting version of libczmq.
+At that time, you could simply download and install the tarballs instead of
+using git to clone the repositories. Those tarballs (when available) can
+be found at http://download.zeromq.org. As of this writing (5/31/2012), the
+most recent version of czmq (1.1.0) and libzmq (3.1.0-beta) will not compile
+properly.
+
+Omzmq3 allows you to push data out of rsyslog from a zeromq socket. The example
+below binds a PUB socket to port 7171, and any message fitting the criteria will
+be output to the zmq socket.
+
+Example Rsyslog.conf snippet (NOTE: v6 format):
+-------------------------------------------------------------------------------
+if $msg then {
+ action(type="omzmq3", sockType="PUB", action="BIND",
+ description="tcp://*:7172)
+}
+-------------------------------------------------------------------------------
diff --git a/plugins/omzmq3/omzmq3.c b/plugins/omzmq3/omzmq3.c
new file mode 100644
index 00000000..e13011fb
--- /dev/null
+++ b/plugins/omzmq3/omzmq3.c
@@ -0,0 +1,462 @@
+/* omzmq3.c
+ * Copyright 2012 Talksum, Inc
+ * Using the czmq interface to zeromq, we output
+ * to a zmq socket.
+
+
+*
+* This program 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.
+*
+* This program 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 this program. If not, see
+* <http://www.gnu.org/licenses/>.
+*
+* Author: David Kelly
+* <davidk@talksum.com>
+*/
+
+
+#include "config.h"
+#include "rsyslog.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include "conf.h"
+#include "syslogd-types.h"
+#include "srUtils.h"
+#include "template.h"
+#include "module-template.h"
+#include "errmsg.h"
+#include "cfsysline.h"
+
+#include <czmq.h>
+
+MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("omzmq3")
+
+DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+
+/* convienent symbols to denote a socket we want to bind
+ vs one we want to just connect to
+*/
+#define ACTION_CONNECT 1
+#define ACTION_BIND 2
+
+
+/* ----------------------------------------------------------------------------
+ * structs to describe sockets
+ */
+struct socket_type {
+ char* name;
+ int type;
+};
+
+/* more overkill, but seems nice to be consistent. */
+struct socket_action {
+ char* name;
+ int action;
+};
+
+typedef struct _instanceData {
+ void* socket;
+ uchar* description;
+ int type;
+ int action;
+ int sndHWM;
+ int rcvHWM;
+ uchar* identity;
+ int sndBuf;
+ int rcvBuf;
+ int linger;
+ int backlog;
+ int sndTimeout;
+ int rcvTimeout;
+ int maxMsgSize;
+ int rate;
+ int recoveryIVL;
+ int multicastHops;
+ int reconnectIVL;
+ int reconnectIVLMax;
+ int ipv4Only;
+ int affinity;
+ uchar* tplName;
+} instanceData;
+
+
+/* ----------------------------------------------------------------------------
+ * Static definitions/initializations
+ */
+
+/* only 1 zctx for all the sockets, with an adjustable number of
+ worker threads which may be useful if we use affinity in particular
+ sockets
+*/
+static zctx_t* s_context = NULL;
+static int s_workerThreads = -1;
+
+static struct socket_type types[] = {
+ {"PUB", ZMQ_PUB },
+ {"PUSH", ZMQ_PUSH },
+ {"XPUB", ZMQ_XPUB }
+};
+
+static struct socket_action actions[] = {
+ {"BIND", ACTION_BIND},
+ {"CONNECT", ACTION_CONNECT},
+};
+
+static struct cnfparamdescr actpdescr[] = {
+ { "description", eCmdHdlrGetWord, 0 },
+ { "sockType", eCmdHdlrGetWord, 0 },
+ { "action", eCmdHdlrGetWord, 0 },
+ { "sndHWM", eCmdHdlrInt, 0 },
+ { "rcvHWM", eCmdHdlrInt, 0 },
+ { "identity", eCmdHdlrGetWord, 0 },
+ { "sndBuf", eCmdHdlrInt, 0 },
+ { "rcvBuf", eCmdHdlrInt, 0 },
+ { "linger", eCmdHdlrInt, 0 },
+ { "backlog", eCmdHdlrInt, 0 },
+ { "sndTimeout", eCmdHdlrInt, 0 },
+ { "rcvTimeout", eCmdHdlrInt, 0 },
+ { "maxMsgSize", eCmdHdlrInt, 0 },
+ { "rate", eCmdHdlrInt, 0 },
+ { "recoveryIVL", eCmdHdlrInt, 0 },
+ { "multicastHops", eCmdHdlrInt, 0 },
+ { "reconnectIVL", eCmdHdlrInt, 0 },
+ { "reconnectIVLMax", eCmdHdlrInt, 0 },
+ { "ipv4Only", eCmdHdlrInt, 0 },
+ { "affinity", eCmdHdlrInt, 0 },
+ { "globalWorkerThreads", eCmdHdlrInt, 0 },
+ { "template", eCmdHdlrGetWord, 1 }
+};
+
+static struct cnfparamblk actpblk = {
+ CNFPARAMBLK_VERSION,
+ sizeof(actpdescr)/sizeof(struct cnfparamdescr),
+ actpdescr
+};
+
+/* ----------------------------------------------------------------------------
+ * Helper Functions
+ */
+
+/* get the name of a socket type, return the ZMQ_XXX type
+ or -1 if not a supported type (see above)
+*/
+int getSocketType(char* name) {
+ int type = -1;
+ uint i;
+ for(i=0; i<sizeof(types)/sizeof(struct socket_type); ++i) {
+ if( !strcmp(types[i].name, name) ) {
+ type = types[i].type;
+ break;
+ }
+ }
+ return type;
+}
+
+
+static int getSocketAction(char* name) {
+ int action = -1;
+ uint i;
+ for(i=0; i < sizeof(actions)/sizeof(struct socket_action); ++i) {
+ if(!strcmp(actions[i].name, name)) {
+ action = actions[i].action;
+ break;
+ }
+ }
+ return action;
+}
+
+/* closeZMQ will destroy the context and
+ * associated socket
+ */
+static void closeZMQ(instanceData* pData) {
+ errmsg.LogError(0, NO_ERRCODE, "closeZMQ called");
+ if(s_context && pData->socket) {
+ if(pData->socket != NULL) {
+ zsocket_destroy(s_context, pData->socket);
+ }
+ }
+}
+
+
+static rsRetVal initZMQ(instanceData* pData) {
+ DEFiRet;
+
+ /* create the context if necessary. */
+ if (NULL == s_context) {
+ s_context = zctx_new();
+ if (s_workerThreads > 0) zctx_set_iothreads(s_context, s_workerThreads);
+ }
+
+ pData->socket = zsocket_new(s_context, pData->type);
+
+ /* ALWAYS set the HWM as the zmq3 default is 1000 and we default
+ to 0 (infinity) */
+ zsocket_set_rcvhwm(pData->socket, pData->rcvHWM);
+ zsocket_set_sndhwm(pData->socket, pData->sndHWM);
+
+ /* use czmq defaults for these, unless set to non-default values */
+ if(pData->identity) zsocket_set_identity(pData->socket, (char*)pData->identity);
+ if(pData->sndBuf > -1) zsocket_set_sndbuf(pData->socket, pData->sndBuf);
+ if(pData->rcvBuf > -1) zsocket_set_sndbuf(pData->socket, pData->rcvBuf);
+ if(pData->linger > -1) zsocket_set_linger(pData->socket, pData->linger);
+ if(pData->backlog > -1) zsocket_set_backlog(pData->socket, pData->backlog);
+ if(pData->sndTimeout > -1) zsocket_set_sndtimeo(pData->socket, pData->sndTimeout);
+ if(pData->rcvTimeout > -1) zsocket_set_rcvtimeo(pData->socket, pData->rcvTimeout);
+ if(pData->maxMsgSize > -1) zsocket_set_maxmsgsize(pData->socket, pData->maxMsgSize);
+ if(pData->rate > -1) zsocket_set_rate(pData->socket, pData->rate);
+ if(pData->recoveryIVL > -1) zsocket_set_recovery_ivl(pData->socket, pData->recoveryIVL);
+ if(pData->multicastHops > -1) zsocket_set_multicast_hops(pData->socket, pData->multicastHops);
+ if(pData->reconnectIVL > -1) zsocket_set_reconnect_ivl(pData->socket, pData->reconnectIVL);
+ if(pData->reconnectIVLMax > -1) zsocket_set_reconnect_ivl_max(pData->socket, pData->reconnectIVLMax);
+ if(pData->ipv4Only > -1) zsocket_set_ipv4only(pData->socket, pData->ipv4Only);
+ if(pData->affinity != 1) zsocket_set_affinity(pData->socket, pData->affinity);
+
+ /* bind or connect to it */
+ if (pData->action == ACTION_BIND) {
+ /* bind asserts, so no need to test return val here
+ which isn't the greatest api -- oh well */
+ zsocket_bind(pData->socket, (char*)pData->description);
+ } else {
+ if(zsocket_connect(pData->socket, (char*)pData->description) == -1) {
+ errmsg.LogError(0, RS_RET_SUSPENDED, "omzmq3: connect failed!");
+ ABORT_FINALIZE(RS_RET_SUSPENDED);
+ }
+ }
+ finalize_it:
+ RETiRet;
+}
+
+rsRetVal writeZMQ(uchar* msg, instanceData* pData) {
+ DEFiRet;
+
+ /* initialize if necessary */
+ if(NULL == pData->socket)
+ CHKiRet(initZMQ(pData));
+
+ /* send it */
+ int result = zstr_send(pData->socket, (char*)msg);
+
+ /* whine if things went wrong */
+ if (result == -1) {
+ errmsg.LogError(0, NO_ERRCODE, "omzmq3: send of %s failed with return %d", msg, result);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ finalize_it:
+ RETiRet;
+}
+
+static inline void
+setInstParamDefaults(instanceData* pData) {
+ pData->description = (uchar*)"tcp://*:7171";
+ pData->socket = NULL;
+ pData->tplName = NULL;
+ pData->type = ZMQ_PUB;
+ pData->action = ACTION_BIND;
+ pData->sndHWM = 0; /*unlimited*/
+ pData->rcvHWM = 0; /*unlimited*/
+ pData->identity = NULL;
+ pData->sndBuf = -1;
+ pData->rcvBuf = -1;
+ pData->linger = -1;
+ pData->backlog = -1;
+ pData->sndTimeout = -1;
+ pData->rcvTimeout = -1;
+ pData->maxMsgSize = -1;
+ pData->rate = -1;
+ pData->recoveryIVL = -1;
+ pData->multicastHops = -1;
+ pData->reconnectIVL = -1;
+ pData->reconnectIVLMax = -1;
+ pData->ipv4Only = -1;
+ pData->affinity = 1;
+}
+
+
+/* ----------------------------------------------------------------------------
+ * Output Module Functions
+ */
+
+BEGINcreateInstance
+CODESTARTcreateInstance
+ENDcreateInstance
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURERepeatedMsgReduction)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ENDdbgPrintInstInfo
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ closeZMQ(pData);
+ free(pData->description);
+ free(pData->tplName);
+ENDfreeInstance
+
+BEGINtryResume
+CODESTARTtryResume
+ if(NULL == pData->socket)
+ iRet = initZMQ(pData);
+ENDtryResume
+
+BEGINdoAction
+CODESTARTdoAction
+iRet = writeZMQ(ppString[0], pData);
+ENDdoAction
+
+
+BEGINnewActInst
+ struct cnfparamvals *pvals;
+ int i;
+CODESTARTnewActInst
+if((pvals = nvlstGetParams(lst, &actpblk, NULL)) == NULL) {
+ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+ }
+
+CHKiRet(createInstance(&pData));
+setInstParamDefaults(pData);
+
+CODE_STD_STRING_REQUESTparseSelectorAct(1)
+for(i = 0 ; i < actpblk.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(actpblk.descr[i].name, "description")) {
+ pData->description = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(actpblk.descr[i].name, "template")) {
+ pData->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(actpblk.descr[i].name, "sockType")){
+ pData->type = getSocketType(es_str2cstr(pvals[i].val.d.estr, NULL));
+ } else if(!strcmp(actpblk.descr[i].name, "action")){
+ pData->action = getSocketAction(es_str2cstr(pvals[i].val.d.estr, NULL));
+ } else if(!strcmp(actpblk.descr[i].name, "sndHWM")) {
+ pData->sndHWM = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "rcvHWM")) {
+ pData->rcvHWM = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "identity")){
+ pData->identity = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(actpblk.descr[i].name, "sndBuf")) {
+ pData->sndBuf = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "rcvBuf")) {
+ pData->rcvBuf = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "linger")) {
+ pData->linger = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "backlog")) {
+ pData->backlog = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "sndTimeout")) {
+ pData->sndTimeout = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "rcvTimeout")) {
+ pData->rcvTimeout = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "maxMsgSize")) {
+ pData->maxMsgSize = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "rate")) {
+ pData->rate = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "recoveryIVL")) {
+ pData->recoveryIVL = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "multicastHops")) {
+ pData->multicastHops = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "reconnectIVL")) {
+ pData->reconnectIVL = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "reconnectIVLMax")) {
+ pData->reconnectIVLMax = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "ipv4Only")) {
+ pData->ipv4Only = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "affinity")) {
+ pData->affinity = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "globalWorkerThreads")) {
+ s_workerThreads = (int) pvals[i].val.d.n;
+ } else {
+ errmsg.LogError(0, NO_ERRCODE, "omzmq3: program error, non-handled "
+ "param '%s'\n", actpblk.descr[i].name);
+ }
+ }
+
+if(pData->tplName == NULL) {
+ CHKiRet(OMSRsetEntry(*ppOMSR, 0, NULL, OMSR_TPL_AS_MSG));
+ } else {
+ CHKiRet(OMSRsetEntry(*ppOMSR, 0, (uchar*)pData->tplName, OMSR_NO_RQD_TPL_OPTS));
+ }
+
+if(pData->type == -1) {
+ errmsg.LogError(0, RS_RET_CONFIG_ERROR, "omzmq3: unknown socket type.");
+ ABORT_FINALIZE(RS_RET_CONFIG_ERROR);
+ }
+if(pData->action == -1) {
+ errmsg.LogError(0, RS_RET_CONFIG_ERROR, "omzmq3: unknown socket action");
+ ABORT_FINALIZE(RS_RET_CONFIG_ERROR);
+ }
+
+
+CODE_STD_FINALIZERnewActInst
+ cnfparamvalsDestruct(pvals, &actpblk);
+ENDnewActInst
+
+BEGINparseSelectorAct
+CODESTARTparseSelectorAct
+
+/* tell the engine we only want one template string */
+CODE_STD_STRING_REQUESTparseSelectorAct(1)
+ if(!strncmp((char*) p, ":omzmq3:", sizeof(":omzmq3:") - 1))
+ errmsg.LogError(0, RS_RET_LEGA_ACT_NOT_SUPPORTED,
+ "omzmq3 supports only v6 config format, use: "
+ "action(type=\"omzmq3\" serverport=...)");
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+BEGINinitConfVars /* (re)set config variables to defaults */
+CODESTARTinitConfVars
+s_workerThreads = -1;
+ENDinitConfVars
+
+BEGINmodExit
+CODESTARTmodExit
+if(NULL != s_context) {
+ zctx_destroy(&s_context);
+ s_context=NULL;
+ }
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_OMOD_QUERIES
+CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES
+ENDqueryEtryPt
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* only supports rsyslog 6 configs */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ INITChkCoreFeature(bCoreSupportsBatching, CORE_FEATURE_BATCHING);
+ DBGPRINTF("omzmq3: module compiled with rsyslog version %s.\n", VERSION);
+
+INITLegCnfVars
+CHKiRet(omsdRegCFSLineHdlr((uchar *)"omzmq3workerthreads", 0, eCmdHdlrInt, NULL, &s_workerThreads, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+
+
+
diff --git a/runtime/cfsysline.c b/runtime/cfsysline.c
index fdbb8f2a..6b06d427 100644
--- a/runtime/cfsysline.c
+++ b/runtime/cfsysline.c
@@ -686,7 +686,7 @@ static int cslchKeyCompare(void *pKey1, void *pKey2)
/* set data members for this object
*/
-rsRetVal cslchSetEntry(cslCmdHdlr_t *pThis, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData)
+rsRetVal cslchSetEntry(cslCmdHdlr_t *pThis, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData, int *permitted)
{
assert(pThis != NULL);
assert(eType != eCmdHdlrInvalid);
@@ -694,6 +694,7 @@ rsRetVal cslchSetEntry(cslCmdHdlr_t *pThis, ecslCmdHdrlType eType, rsRetVal (*pH
pThis->eType = eType;
pThis->cslCmdHdlr = pHdlr;
pThis->pData = pData;
+ pThis->permitted = permitted;
return RS_RET_OK;
}
@@ -810,7 +811,7 @@ finalize_it:
/* add a handler entry to a known command
*/
-static rsRetVal cslcAddHdlr(cslCmd_t *pThis, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData, void *pOwnerCookie)
+static rsRetVal cslcAddHdlr(cslCmd_t *pThis, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData, void *pOwnerCookie, int *permitted)
{
DEFiRet;
cslCmdHdlr_t *pCmdHdlr = NULL;
@@ -818,7 +819,7 @@ static rsRetVal cslcAddHdlr(cslCmd_t *pThis, ecslCmdHdrlType eType, rsRetVal (*p
assert(pThis != NULL);
CHKiRet(cslchConstruct(&pCmdHdlr));
- CHKiRet(cslchSetEntry(pCmdHdlr, eType, pHdlr, pData));
+ CHKiRet(cslchSetEntry(pCmdHdlr, eType, pHdlr, pData, permitted));
CHKiRet(llAppend(&pThis->llCmdHdlrs, pOwnerCookie, pCmdHdlr));
finalize_it:
@@ -836,8 +837,16 @@ finalize_it:
* buffer is automatically destroyed when the element is freed, the
* caller does not need to take care of that. The caller must, however,
* free pCmdName if he allocated it dynamically! -- rgerhards, 2007-08-09
+ * Parameter permitted has been added to support the v2 config system. With it,
+ * we can tell the legacy system (us here!) to check if a config directive is
+ * still permitted. For example, the v2 system will disable module global
+ * paramters if the are supplied via the native v2 callbacks. In order not
+ * to break exisiting modules, we have renamed the rgCfSysLinHdlr routine to
+ * version 2 and added a new one with the original name. It just calls the
+ * v2 function and supplies a "don't care (NULL)" pointer as this argument.
+ * rgerhards, 2012-06-26
*/
-rsRetVal regCfSysLineHdlr(uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData, void *pOwnerCookie)
+rsRetVal regCfSysLineHdlr2(uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData, void *pOwnerCookie, int *permitted)
{
DEFiRet;
cslCmd_t *pThis;
@@ -847,7 +856,7 @@ rsRetVal regCfSysLineHdlr(uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlTy
if(iRet == RS_RET_NOT_FOUND) {
/* new command */
CHKiRet(cslcConstruct(&pThis, bChainingPermitted));
- CHKiRet_Hdlr(cslcAddHdlr(pThis, eType, pHdlr, pData, pOwnerCookie)) {
+ CHKiRet_Hdlr(cslcAddHdlr(pThis, eType, pHdlr, pData, pOwnerCookie, permitted)) {
cslcDestruct(pThis);
FINALIZE;
}
@@ -867,7 +876,7 @@ rsRetVal regCfSysLineHdlr(uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlTy
if(pThis->bChainingPermitted == 0 || bChainingPermitted == 0) {
ABORT_FINALIZE(RS_RET_CHAIN_NOT_PERMITTED);
}
- CHKiRet_Hdlr(cslcAddHdlr(pThis, eType, pHdlr, pData, pOwnerCookie)) {
+ CHKiRet_Hdlr(cslcAddHdlr(pThis, eType, pHdlr, pData, pOwnerCookie, permitted)) {
cslcDestruct(pThis);
FINALIZE;
}
@@ -877,6 +886,13 @@ finalize_it:
RETiRet;
}
+rsRetVal regCfSysLineHdlr(uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData, void *pOwnerCookie)
+{
+ DEFiRet;
+ iRet = regCfSysLineHdlr2(pCmdName, bChainingPermitted, eType, pHdlr, pData, pOwnerCookie, NULL);
+ RETiRet;
+}
+
rsRetVal unregCfSysLineHdlrs(void)
{
@@ -965,7 +981,12 @@ rsRetVal processCfSysLineCommand(uchar *pCmdName, uchar **p)
* necessary). -- rgerhards, 2007-07-31
*/
pHdlrP = *p;
- if((iRet = cslchCallHdlr(pCmdHdlr, &pHdlrP)) == RS_RET_OK) {
+ if(pCmdHdlr->permitted != NULL && !*(pCmdHdlr->permitted)) {
+ errmsg.LogError(0, RS_RET_PARAM_NOT_PERMITTED, "command '%s' is currently not "
+ "permitted - did you already set it via a RainerScript command (v6+ config)?",
+ pCmdName);
+ ABORT_FINALIZE(RS_RET_PARAM_NOT_PERMITTED);
+ } else if((iRet = cslchCallHdlr(pCmdHdlr, &pHdlrP)) == RS_RET_OK) {
bWasOnceOK = 1;
pOKp = pHdlrP;
}
diff --git a/runtime/cfsysline.h b/runtime/cfsysline.h
index 2768243f..69389f84 100644
--- a/runtime/cfsysline.h
+++ b/runtime/cfsysline.h
@@ -33,6 +33,7 @@ struct cslCmdHdlr_s { /* config file sysline parse entry */
ecslCmdHdrlType eType; /* which type of handler is this? */
rsRetVal (*cslCmdHdlr)(); /* function pointer to use with handler (params depending on eType) */
void *pData; /* user-supplied data pointer */
+ int *permitted; /* is this parameter currently permitted? (NULL=don't check) */
};
typedef struct cslCmdHdlr_s cslCmdHdlr_t;
@@ -49,6 +50,7 @@ typedef struct cslCmd_s cslCmd_t;
/* prototypes */
rsRetVal regCfSysLineHdlr(uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData, void *pOwnerCookie);
+rsRetVal regCfSysLineHdlr2(uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlType eType, rsRetVal (*pHdlr)(), void *pData, void *pOwnerCookie, int *permitted);
rsRetVal unregCfSysLineHdlrs(void);
rsRetVal unregCfSysLineHdlrs4Owner(void *pOwnerCookie);
rsRetVal processCfSysLineCommand(uchar *pCmd, uchar **p);
diff --git a/runtime/conf.c b/runtime/conf.c
index eec04df8..488d1b86 100644
--- a/runtime/conf.c
+++ b/runtime/conf.c
@@ -116,18 +116,15 @@ doModLoad(uchar **pp, __attribute__((unused)) void* pVal)
skipWhiteSpace(pp); /* skip over any whitespace */
/* this below is a quick and dirty hack to provide compatibility with the
- * $ModLoad MySQL forward compatibility statement. TODO: clean this up
- * For the time being, it is clean enough, it just needs to be done
- * differently when we have a full design for loadable plug-ins. For the
- * time being, we just mangle the names a bit.
- * rgerhards, 2007-08-14
+ * $ModLoad MySQL forward compatibility statement. This needs to be supported
+ * for legacy format.
*/
if(!strcmp((char*) szName, "MySQL"))
pModName = (uchar*) "ommysql.so";
else
pModName = szName;
- CHKiRet(module.Load(pModName, 1));
+ CHKiRet(module.Load(pModName, 1, NULL));
finalize_it:
RETiRet;
diff --git a/runtime/debug.h b/runtime/debug.h
index 26672c3e..5bd26bd8 100644
--- a/runtime/debug.h
+++ b/runtime/debug.h
@@ -106,8 +106,13 @@ void dbgPrintAllDebugInfo(void);
void *dbgmalloc(size_t size);
/* macros */
-#define DBGPRINTF(...) if(Debug) { dbgprintf(__VA_ARGS__); }
-#define DBGOPRINT(...) if(Debug) { dbgoprint(__VA_ARGS__); }
+#ifdef DEBUGLESS
+# define DBGPRINTF(...) {}
+# define DBGOPRINT(...) {}
+#else
+# define DBGPRINTF(...) if(Debug) { dbgprintf(__VA_ARGS__); }
+# define DBGOPRINT(...) if(Debug) { dbgoprint(__VA_ARGS__); }
+#endif
#ifdef RTINST
# define BEGINfunc static dbgFuncDB_t *pdbgFuncDB; int dbgCALLStaCK_POP_POINT = dbgEntrFunc(&pdbgFuncDB, __FILE__, __func__, __LINE__);
# define ENDfunc dbgExitFunc(pdbgFuncDB, dbgCALLStaCK_POP_POINT, RS_RET_NO_IRET);
diff --git a/runtime/glbl.c b/runtime/glbl.c
index 537b7b4f..18993eef 100644
--- a/runtime/glbl.c
+++ b/runtime/glbl.c
@@ -553,10 +553,12 @@ glblProcessCnf(struct cnfobj *o)
cnfparamsPrint(&paramblk, cnfparamvals);
}
+#if 0 /* TODO: finally remove? rgerhards, 2012-06-20 */
rsRetVal
glblCheckCnf()
{
}
+#endif
void
glblDoneLoadCnf(void)
diff --git a/runtime/module-template.h b/runtime/module-template.h
index 75bf7312..5d32b909 100644
--- a/runtime/module-template.h
+++ b/runtime/module-template.h
@@ -4,7 +4,7 @@
*
* File begun on 2007-07-25 by RGerhards
*
- * Copyright 2007 Adiscon GmbH. This is Adiscon-exclusive code without any other
+ * Copyright 2007-2012 Adiscon GmbH. This is Adiscon-exclusive code without any other
* contributions. *** GPLv3 ***
*
* This file is part of the rsyslog runtime library.
@@ -503,6 +503,14 @@ static rsRetVal queryEtryPt(uchar *name, rsRetVal (**pEtryPoint)())\
} \
CODEqueryEtryPt_STD_CONF2_CNFNAME_QUERIES
+/* the following block is to be added for modules that support v2
+ * module global parameters [module(...)]
+ */
+#define CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES \
+ else if(!strcmp((char*) name, "setModCnf")) {\
+ *pEtryPoint = setModCnf;\
+ } \
+
/* the following block is to be added for output modules that support the v2
* config system. The config name is also provided.
*/
@@ -693,6 +701,28 @@ static rsRetVal beginCnfLoad(modConfData_t **ptr, __attribute__((unused)) rsconf
}
+/* setModCnf()
+ * This function permits to set module global parameters via the v2 config
+ * interface. It may be called multiple times, but parameters must not be
+ * set in a conflicting way. The module must use its current config load
+ * context when processing the directives.
+ * Note that lst may be NULL, especially if the module is loaded via the
+ * legacy config system. The module must check for this.
+ * NOTE: This entry point must only be implemented if module global
+ * parameters are actually required.
+ */
+#define BEGINsetModCnf \
+static rsRetVal setModCnf(struct nvlst *lst)\
+{\
+ DEFiRet;
+
+#define CODESTARTsetModCnf
+
+#define ENDsetModCnf \
+ RETiRet;\
+}
+
+
/* endCnfLoad()
* This is a function tells an input module that the current config load ended.
* It gets a last chance to make changes to its in-memory config object. After
diff --git a/runtime/modules.c b/runtime/modules.c
index 39f977dd..d3c51e90 100644
--- a/runtime/modules.c
+++ b/runtime/modules.c
@@ -11,7 +11,7 @@
*
* File begun on 2007-07-22 by RGerhards
*
- * Copyright 2007-2011 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007-2012 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of the rsyslog runtime library.
*
@@ -75,6 +75,18 @@ static struct dlhandle_s *pHandles = NULL;
static uchar *pModDir; /* directory where loadable modules are found */
+/* tables for interfacing with the v6 config system */
+/* action (instance) parameters */
+static struct cnfparamdescr actpdescr[] = {
+ { "load", eCmdHdlrGetWord, 1 }
+};
+static struct cnfparamblk pblk =
+ { CNFPARAMBLK_VERSION,
+ sizeof(actpdescr)/sizeof(struct cnfparamdescr),
+ actpdescr
+ };
+
+
/* we provide a set of dummy functions for modules that do not support the
* some interfaces.
* On the commit feature: As the modules do not support it, they commit each message they
@@ -337,7 +349,8 @@ addModToGlblList(modInfo_t *pThis)
}
-/* Add a module to the config module list for current loadConf
+/* Add a module to the config module list for current loadConf and
+ * provide its config params to it.
*/
rsRetVal
addModToCnfList(modInfo_t *pThis)
@@ -358,11 +371,16 @@ addModToCnfList(modInfo_t *pThis)
while(1) { /* loop broken inside */
if(pLast->pMod == pThis) {
DBGPRINTF("module '%s' already in this config\n", modGetName(pThis));
+ if(strncmp((char*)modGetName(pThis), "builtin:", sizeof("builtin:")-1)) {
+ errmsg.LogError(0, RS_RET_MODULE_ALREADY_IN_CONF,
+ "module '%s' already in this config, cannot be added\n", modGetName(pThis));
+ ABORT_FINALIZE(RS_RET_MODULE_ALREADY_IN_CONF);
+ }
FINALIZE;
}
if(pLast->next == NULL)
break;
- pLast = pLast -> next;
+ pLast = pLast->next;
}
}
@@ -535,7 +553,7 @@ doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)(), modInfo_
CHKiRet((*modGetType)(&pNew->eType));
CHKiRet((*pNew->modQueryEtryPt)((uchar*)"getKeepType", &modGetKeepType));
CHKiRet((*modGetKeepType)(&pNew->eKeepType));
- dbgprintf("module %s of type %d being loaded.\n", name, pNew->eType);
+ dbgprintf("module %s of type %d being loaded (keepType=%d).\n", name, pNew->eType, pNew->eKeepType);
/* OK, we know we can successfully work with the module. So we now fill the
* rest of the data elements. First we load the interfaces common to all
@@ -548,6 +566,11 @@ doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)(), modInfo_
pNew->isCompatibleWithFeature = dummyIsCompatibleWithFeature;
else if(localRet != RS_RET_OK)
ABORT_FINALIZE(localRet);
+ localRet = (*pNew->modQueryEtryPt)((uchar*)"setModCnf", &pNew->setModCnf);
+ if(localRet == RS_RET_MODULE_ENTRY_POINT_NOT_FOUND)
+ pNew->setModCnf = NULL;
+ else if(localRet != RS_RET_OK)
+ ABORT_FINALIZE(localRet);
/* optional calls for new config system */
localRet = (*pNew->modQueryEtryPt)((uchar*)"getModCnfName", &getModCnfName);
@@ -754,6 +777,7 @@ static void modPrintList(void)
dbgprintf("\tdbgPrintInstInfo: 0x%lx\n", (unsigned long) pMod->dbgPrintInstInfo);
dbgprintf("\tfreeInstance: 0x%lx\n", (unsigned long) pMod->freeInstance);
dbgprintf("\tbeginCnfLoad: 0x%lx\n", (unsigned long) pMod->beginCnfLoad);
+ dbgprintf("\tSetModCnf: 0x%lx\n", (unsigned long) pMod->setModCnf);
dbgprintf("\tcheckCnf: 0x%lx\n", (unsigned long) pMod->checkCnf);
dbgprintf("\tactivateCnfPrePrivDrop: 0x%lx\n", (unsigned long) pMod->activateCnfPrePrivDrop);
dbgprintf("\tactivateCnf: 0x%lx\n", (unsigned long) pMod->activateCnf);
@@ -931,14 +955,12 @@ findModule(uchar *pModName, int iModNameLen, modInfo_t **pMod)
* the system loads a module for internal reasons, this is not directly tied to a
* configuration. We could also think if it would be useful to add only certain types
* of modules, but the current implementation at least looks simpler.
+ * Note: pvals = NULL means legacy config system
*/
static rsRetVal
-Load(uchar *pModName, sbool bConfLoad)
+Load(uchar *pModName, sbool bConfLoad, struct nvlst *lst)
{
- DEFiRet;
-
size_t iPathLen, iModNameLen;
- uchar *pModNameCmp;
int bHasExtension;
void *pModHdlr, *pModInit;
modInfo_t *pModInfo;
@@ -952,9 +974,11 @@ Load(uchar *pModName, sbool bConfLoad)
# endif
uchar *pPathBuf = pathBuf;
size_t lenPathBuf = sizeof(pathBuf);
+ rsRetVal localRet;
+ DEFiRet;
assert(pModName != NULL);
- dbgprintf("Requested to load module '%s'\n", pModName);
+ DBGPRINTF("Requested to load module '%s'\n", pModName);
iModNameLen = strlen((char*)pModName);
/* overhead for a full path is potentially 1 byte for a slash,
@@ -972,9 +996,28 @@ Load(uchar *pModName, sbool bConfLoad)
CHKiRet(findModule(pModName, iModNameLen, &pModInfo));
if(pModInfo != NULL) {
- if(bConfLoad)
- addModToCnfList(pModInfo);
- dbgprintf("Module '%s' already loaded\n", pModName);
+ DBGPRINTF("Module '%s' already loaded\n", pModName);
+ if(bConfLoad) {
+ localRet = addModToCnfList(pModInfo);
+ if(pModInfo->setModCnf != NULL && localRet == RS_RET_OK) {
+ if(!strncmp((char*)pModName, "builtin:", sizeof("builtin:")-1)) {
+ if(pModInfo->bSetModCnfCalled) {
+ errmsg.LogError(0, RS_RET_DUP_PARAM,
+ "parameters for built-in module %s already set - ignored\n",
+ pModName);
+ ABORT_FINALIZE(RS_RET_DUP_PARAM);
+ } else {
+ /* for built-in moules, we need to call setModConf,
+ * because there is no way to set parameters at load
+ * time for obvious reasons...
+ */
+ if(lst != NULL)
+ pModInfo->setModCnf(lst);
+ pModInfo->bSetModCnfCalled = 1;
+ }
+ }
+ }
+ }
FINALIZE;
}
@@ -1082,8 +1125,15 @@ Load(uchar *pModName, sbool bConfLoad)
dlclose(pModHdlr);
ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_INIT_FAILED);
}
- if(bConfLoad)
+
+ if(bConfLoad) {
addModToCnfList(pModInfo);
+ if(pModInfo->setModCnf != NULL) {
+ if(lst != NULL)
+ pModInfo->setModCnf(lst);
+ pModInfo->bSetModCnfCalled = 1;
+ }
+ }
finalize_it:
if(pPathBuf != pathBuf) /* used malloc()ed memory? */
@@ -1093,6 +1143,39 @@ finalize_it:
}
+/* the v6+ way of loading modules: process a "module(...)" directive.
+ * rgerhards, 2012-06-20
+ */
+rsRetVal
+modulesProcessCnf(struct cnfobj *o)
+{
+ struct cnfparamvals *pvals;
+ uchar *cnfModName = NULL;
+ int typeIdx;
+ DEFiRet;
+
+ pvals = nvlstGetParams(o->nvlst, &pblk, NULL);
+ if(pvals == NULL) {
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ DBGPRINTF("modulesProcessCnf params:\n");
+ cnfparamsPrint(&pblk, pvals);
+ typeIdx = cnfparamGetIdx(&pblk, "load");
+ if(pvals[typeIdx].bUsed == 0) {
+ errmsg.LogError(0, RS_RET_CONF_RQRD_PARAM_MISSING, "module type missing");
+ ABORT_FINALIZE(RS_RET_CONF_RQRD_PARAM_MISSING);
+ }
+
+ cnfModName = (uchar*)es_str2cstr(pvals[typeIdx].val.d.estr, NULL);
+ iRet = Load(cnfModName, 1, o->nvlst);
+
+finalize_it:
+ free(cnfModName);
+ cnfparamvalsDestruct(pvals, &pblk);
+ RETiRet;
+}
+
+
/* set the default module load directory. A NULL value may be provided, in
* which case any previous value is deleted but no new one set. The caller-provided
* string is duplicated. If it needs to be freed, that's the caller's duty.
diff --git a/runtime/modules.h b/runtime/modules.h
index 6c5a2cba..6a143ae3 100644
--- a/runtime/modules.h
+++ b/runtime/modules.h
@@ -12,7 +12,7 @@
*
* File begun on 2007-07-22 by RGerhards
*
- * Copyright 2007-2011 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007-2012 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of the rsyslog runtime library.
*
@@ -101,6 +101,7 @@ struct modInfo_s {
uchar* pszName; /* printable module name, e.g. for dbgprintf */
uchar* cnfName; /* name to be used in config statements (e.g. 'name="omusrmsg"') */
unsigned uRefCnt; /* reference count for this module; 0 -> may be unloaded */
+ sbool bSetModCnfCalled;/* is setModCnf already called? Needed for built-in modules */
/* functions supported by all types of modules */
rsRetVal (*modInit)(int, int*, rsRetVal(**)()); /* initialize the module */
/* be sure to support version handshake! */
@@ -114,6 +115,7 @@ struct modInfo_s {
rsRetVal (*doHUP)(void *); /* non-restart type HUP handler */
/* v2 config system specific */
rsRetVal (*beginCnfLoad)(void*newCnf, rsconf_t *pConf);
+ rsRetVal (*setModCnf)(struct nvlst *lst);
rsRetVal (*endCnfLoad)(void*Cnf);
rsRetVal (*checkCnf)(void*Cnf);
rsRetVal (*activateCnfPrePrivDrop)(void*Cnf);
@@ -152,9 +154,7 @@ struct modInfo_s {
} mod;
void *pModHdlr; /* handler to the dynamic library holding the module */
# ifdef DEBUG
- /* we add some home-grown support to track our users (and detect who does not free us). In
- * the long term, this should probably be migrated into debug.c (TODO). -- rgerhards, 2008-03-11
- */
+ /* we add some home-grown support to track our users (and detect who does not free us). */
modUsr_t *pModUsrRoot;
# endif
};
@@ -171,20 +171,26 @@ BEGINinterface(module) /* name must also be changed in ENDinterface macro! */
void (*PrintList)(void);
rsRetVal (*UnloadAndDestructAll)(eModLinkType_t modLinkTypesToUnload);
rsRetVal (*doModInit)(rsRetVal (*modInit)(), uchar *name, void *pModHdlr, modInfo_t **pNew);
- rsRetVal (*Load)(uchar *name, sbool bConfLoad);
+ rsRetVal (*Load)(uchar *name, sbool bConfLoad, struct nvlst *lst);
rsRetVal (*SetModDir)(uchar *name);
modInfo_t *(*FindWithCnfName)(rsconf_t *cnf, uchar *name, eModType_t rqtdType); /* added v3, 2011-07-19 */
ENDinterface(module)
-#define moduleCURR_IF_VERSION 3 /* increment whenever you change the interface structure! */
+#define moduleCURR_IF_VERSION 4 /* increment whenever you change the interface structure! */
/* Changes:
* v2
* - added param bCondLoad to Load call - 2011-04-27
* - removed GetNxtType, added GetNxtCnfType - 2011-04-27
+ * v3 (see above)
+ * v4
+ * - added third parameter to Load() - 2012-06-20
*/
/* prototypes */
PROTOTYPEObj(module);
+/* in v6, we go back to in-core static link for core objects, at least those
+ * that are not called from plugins.
+ */
+rsRetVal modulesProcessCnf(struct cnfobj *o);
-/* TODO: remove "dirty" calls! */
rsRetVal addModToCnfList(modInfo_t *pThis);
#endif /* #ifndef MODULES_H_INCLUDED */
diff --git a/runtime/msg.c b/runtime/msg.c
index 44d36fef..187f0c22 100644
--- a/runtime/msg.c
+++ b/runtime/msg.c
@@ -43,6 +43,7 @@
#if HAVE_MALLOC_H
# include <malloc.h>
#endif
+#include <uuid/uuid.h>
#include "rsyslog.h"
#include "srUtils.h"
#include "stringbuf.h"
@@ -541,6 +542,8 @@ propNameStrToID(uchar *pName, propid_t *pPropID)
*pPropID = PROP_MSGID;
} else if(!strcmp((char*) pName, "parsesuccess")) {
*pPropID = PROP_PARSESUCCESS;
+ } else if(!strcmp((char*) pName, "uuid")) {
+ *pPropID = PROP_UUID;
/* here start system properties (those, that do not relate to the message itself */
} else if(!strcmp((char*) pName, "$now")) {
*pPropID = PROP_SYS_NOW;
@@ -666,6 +669,8 @@ uchar *propIDToName(propid_t propID)
return UCHAR_CONSTANT("$!all-json");
case PROP_SYS_BOM:
return UCHAR_CONSTANT("$BOM");
+ case PROP_UUID:
+ return UCHAR_CONSTANT("uuid");
default:
return UCHAR_CONSTANT("*invalid property id*");
}
@@ -745,6 +750,7 @@ static inline rsRetVal msgBaseConstruct(msg_t **ppThis)
pM->pszRcvdAt_SecFrac[0] = '\0';
pM->pszTIMESTAMP_Unix[0] = '\0';
pM->pszRcvdAt_Unix[0] = '\0';
+ pM->pszUUID = NULL;
/* DEV debugging only! dbgprintf("msgConstruct\t0x%x, ref 1\n", (int)pM);*/
@@ -875,6 +881,8 @@ CODESTARTobjDestruct(msg)
rsCStrDestruct(&pThis->pCSMSGID);
if(pThis->event != NULL)
ee_deleteEvent(pThis->event);
+ if(pThis->pszUUID != NULL)
+ free(pThis->pszUUID);
# ifndef HAVE_ATOMIC_BUILTINS
MsgUnlock(pThis);
# endif
@@ -1080,6 +1088,8 @@ static rsRetVal MsgSerialize(msg_t *pThis, strm_t *pStrm)
objSerializePTR(pStrm, pCSPROCID, CSTR);
objSerializePTR(pStrm, pCSMSGID, CSTR);
+ objSerializePTR(pStrm, pszUUID, PSZ);
+
if(pThis->pRuleset != NULL) {
rulesetGetName(pThis->pRuleset);
CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("pszRuleset"), PROPTYPE_PSZ,
@@ -1242,6 +1252,60 @@ char *getProtocolVersionString(msg_t *pM)
return(pM->iProtocolVersion ? "1" : "0");
}
+/* note: libuuid seems not to be thread-safe, so we need
+ * to get some safeguards in place.
+ */
+static void msgSetUUID(msg_t *pM)
+{
+ size_t lenRes = sizeof(uuid_t) * 2 + 1;
+ char hex_char [] = "0123456789ABCDEF";
+ unsigned int byte_nbr;
+ uuid_t uuid;
+ static pthread_mutex_t mutUUID = PTHREAD_MUTEX_INITIALIZER;
+
+ dbgprintf("[MsgSetUUID] START\n");
+ assert(pM != NULL);
+
+ if((pM->pszUUID = (uchar*) MALLOC(lenRes)) == NULL) {
+ pM->pszUUID = (uchar *)"";
+ } else {
+ pthread_mutex_lock(&mutUUID);
+ uuid_generate(uuid);
+ pthread_mutex_unlock(&mutUUID);
+ for (byte_nbr = 0; byte_nbr < sizeof (uuid_t); byte_nbr++) {
+ pM->pszUUID[byte_nbr * 2 + 0] = hex_char[uuid [byte_nbr] >> 4];
+ pM->pszUUID[byte_nbr * 2 + 1] = hex_char[uuid [byte_nbr] & 15];
+ }
+
+ dbgprintf("[MsgSetUUID] UUID : %s LEN: %d \n", pM->pszUUID, (int)lenRes);
+ pM->pszUUID[lenRes] = '\0';
+ }
+ dbgprintf("[MsgSetUUID] END\n");
+}
+
+void getUUID(msg_t *pM, uchar **pBuf, int *piLen)
+{
+ dbgprintf("[getUUID] START\n");
+ if(pM == NULL) {
+ dbgprintf("[getUUID] pM is NULL\n");
+ *pBuf= UCHAR_CONSTANT("");
+ *piLen = 0;
+ } else {
+ if(pM->pszUUID == NULL) {
+ dbgprintf("[getUUID] pM->pszUUID is NULL\n");
+ MsgLock(pM);
+ /* re-query, things may have changed in the mean time... */
+ if(pM->pszUUID == NULL)
+ msgSetUUID(pM);
+ MsgUnlock(pM);
+ } else { /* UUID already there we reuse it */
+ dbgprintf("[getUUID] pM->pszUUID already exists\n");
+ }
+ *pBuf = pM->pszUUID;
+ *piLen = sizeof(uuid_t) * 2;
+ }
+ dbgprintf("[getUUID] END\n");
+}
void
getRawMsg(msg_t *pM, uchar **pBuf, int *piLen)
@@ -1908,7 +1972,6 @@ static inline char *getStructuredData(msg_t *pM)
return (char*) pszRet;
}
-
/* check if we have a ProgramName, and, if not, try to aquire/emulate it.
* rgerhards, 2009-06-26
*/
@@ -2232,7 +2295,6 @@ finalize_it:
RETiRet;
}
-
/* set raw message in message object. Size of message is provided.
* The function makes sure that the stored rawmsg is properly
* terminated by '\0'.
@@ -2515,9 +2577,9 @@ jsonField(struct templateEntry *pTpe, uchar **ppRes, unsigned short *pbMustBeFre
pSrc = *ppRes;
buflen = (*pBufLen == -1) ? ustrlen(pSrc) : *pBufLen;
/* we hope we have only few escapes... */
- dst = es_newStr(buflen+es_strlen(pTpe->data.field.fieldName)+15);
+ dst = es_newStr(buflen+pTpe->lenFieldName+15);
es_addChar(&dst, '"');
- es_addStr(&dst, pTpe->data.field.fieldName);
+ es_addBuf(&dst, (char*)pTpe->fieldName, pTpe->lenFieldName);
es_addBufConstcstr(&dst, "\":\"");
CHKiRet(jsonAddVal(pSrc, buflen, &dst));
es_addChar(&dst, '"');
@@ -2676,6 +2738,9 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
case PROP_MSGID:
pRes = (uchar*)getMSGID(pMsg);
break;
+ case PROP_UUID:
+ getUUID(pMsg, &pRes, &bufLen);
+ break;
case PROP_PARSESUCCESS:
pRes = (uchar*)getParseSuccess(pMsg);
break;
diff --git a/runtime/msg.h b/runtime/msg.h
index ed2e9d04..f6b54a77 100644
--- a/runtime/msg.h
+++ b/runtime/msg.h
@@ -123,6 +123,7 @@ struct msg {
char pszRcvdAt_SecFrac[7]; /* same as above. Both are fractional seconds for their respective timestamp */
char pszTIMESTAMP_Unix[12]; /* almost as small as a pointer! */
char pszRcvdAt_Unix[12];
+ uchar *pszUUID; /* The message's UUID */
};
@@ -184,7 +185,6 @@ void getRawMsg(msg_t *pM, uchar **pBuf, int *piLen);
rsRetVal msgGetCEEVar(msg_t *pThis, cstr_t *propName, var_t **ppVar);
es_str_t* msgGetCEEVarNew(msg_t *pMsg, char *name);
-
/* TODO: remove these five (so far used in action.c) */
uchar *getMSG(msg_t *pM);
char *getHOSTNAME(msg_t *pM);
diff --git a/runtime/obj.c b/runtime/obj.c
index ffc8f608..eb151b67 100644
--- a/runtime/obj.c
+++ b/runtime/obj.c
@@ -1188,7 +1188,7 @@ UseObj(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf)
if(pObjFile == NULL) {
FINALIZE; /* no chance, we have lost... */
} else {
- CHKiRet(module.Load(pObjFile, 0));
+ CHKiRet(module.Load(pObjFile, 0, NULL));
/* NOW, we must find it or we have a problem... */
CHKiRet(FindObjInfo(pStr, &pObjInfo));
}
diff --git a/runtime/objomsr.c b/runtime/objomsr.c
index 7241fa27..9cf3781b 100644
--- a/runtime/objomsr.c
+++ b/runtime/objomsr.c
@@ -149,7 +149,8 @@ OMSRgetSupportedTplOpts(unsigned long *pOpts)
{
DEFiRet;
assert(pOpts != NULL);
- *pOpts = OMSR_RQD_TPL_OPT_SQL | OMSR_TPL_AS_ARRAY | OMSR_TPL_AS_MSG;
+ *pOpts = OMSR_RQD_TPL_OPT_SQL | OMSR_TPL_AS_ARRAY | OMSR_TPL_AS_MSG
+ | OMSR_TPL_AS_JSON;
RETiRet;
}
diff --git a/runtime/objomsr.h b/runtime/objomsr.h
index 643e02c5..3baccaa3 100644
--- a/runtime/objomsr.h
+++ b/runtime/objomsr.h
@@ -25,12 +25,13 @@
/* define flags for required template options */
#define OMSR_NO_RQD_TPL_OPTS 0
#define OMSR_RQD_TPL_OPT_SQL 1
-/* only one of OMSR_TPL_AS_ARRAY or _AS_MSG must be specified, if both are given
- * results are unpredictable.
+/* only one of OMSR_TPL_AS_ARRAY, _AS_MSG, or _AS_JSON must be specified,
+ * if all are given results are unpredictable.
*/
#define OMSR_TPL_AS_ARRAY 2 /* introduced in 4.1.6, 2009-04-03 */
#define OMSR_TPL_AS_MSG 4 /* introduced in 5.3.4, 2009-11-02 */
-/* next option is 8, 16, 32, ... */
+#define OMSR_TPL_AS_JSON 8 /* introduced in 6.5.1, 2012-09-02 */
+/* next option is 16, 32, 64, ... */
struct omodStringRequest_s { /* strings requested by output module for doAction() */
int iNumEntries; /* number of array entries for data elements below */
diff --git a/runtime/parser.c b/runtime/parser.c
index fd940e9d..645ea0f4 100644
--- a/runtime/parser.c
+++ b/runtime/parser.c
@@ -180,7 +180,7 @@ AddDfltParser(uchar *pName)
CHKiRet(FindParser(&pParser, pName));
CHKiRet(AddParserToList(&pDfltParsLst, pParser));
- dbgprintf("Parser '%s' added to default parser set.\n", pName);
+ DBGPRINTF("Parser '%s' added to default parser set.\n", pName);
finalize_it:
RETiRet;
@@ -209,7 +209,7 @@ finalize_it:
BEGINobjDestruct(parser) /* be sure to specify the object type also in END and CODESTART macros! */
CODESTARTobjDestruct(parser)
- dbgprintf("destructing parser '%s'\n", pThis->pName);
+ DBGPRINTF("destructing parser '%s'\n", pThis->pName);
free(pThis->pName);
ENDobjDestruct(parser)
@@ -521,7 +521,7 @@ ParseMsg(msg_t *pMsg)
bIsSanitized = RSTRUE;
}
localRet = pParser->pModule->mod.pm.parse(pMsg);
- dbgprintf("Parser '%s' returned %d\n", pParser->pName, localRet);
+ DBGPRINTF("Parser '%s' returned %d\n", pParser->pName, localRet);
if(localRet != RS_RET_COULD_NOT_PARSE)
break;
pParserList = pParserList->pNext;
diff --git a/runtime/queue.c b/runtime/queue.c
index 3c91fd02..bb9ea060 100644
--- a/runtime/queue.c
+++ b/runtime/queue.c
@@ -131,7 +131,7 @@ static void displayBatchState(batch_t *pBatch)
{
int i;
for(i = 0 ; i < pBatch->nElem ; ++i) {
- dbgprintf("XXXXX: displayBatchState %p[%d]: %d\n", pBatch, i, pBatch->pElem[i].state);
+ DBGPRINTF("displayBatchState %p[%d]: %d\n", pBatch, i, pBatch->pElem[i].state);
}
}
@@ -1467,7 +1467,8 @@ DoDeleteBatchFromQStore(qqueue_t *pThis, int nElem)
/* iQueueSize is not decremented by qDel(), so we need to do it ourselves */
ATOMIC_SUB(&pThis->iQueueSize, nElem, &pThis->mutQueueSize);
ATOMIC_SUB(&pThis->nLogDeq, nElem, &pThis->mutLogDeq);
-dbgprintf("delete batch from store, new sizes: log %d, phys %d\n", getLogicalQueueSize(pThis), getPhysicalQueueSize(pThis));
+ DBGPRINTF("delete batch from store, new sizes: log %d, phys %d\n",
+ getLogicalQueueSize(pThis), getPhysicalQueueSize(pThis));
++pThis->deqIDDel; /* one more batch dequeued */
RETiRet;
@@ -1503,7 +1504,7 @@ DeleteBatchFromQStore(qqueue_t *pThis, batch_t *pBatch)
DoDeleteBatchFromQStore(pThis, pBatch->nElem);
} else {
/* can not delete, insert into to-delete list */
- dbgprintf("not at head of to-delete list, enqueue %d\n", (int) pBatch->deqID);
+ DBGPRINTF("not at head of to-delete list, enqueue %d\n", (int) pBatch->deqID);
CHKiRet(tdlAdd(pThis, pBatch->deqID, pBatch->nElem));
}
@@ -1533,7 +1534,6 @@ DeleteProcessedBatch(qqueue_t *pThis, batch_t *pBatch)
pUsr = pBatch->pElem[i].pUsrp;
if( pBatch->pElem[i].state == BATCH_STATE_RDY
|| pBatch->pElem[i].state == BATCH_STATE_SUB) {
-dbgprintf("XXX: DeleteProcessedBatch re-enqueue %d of %d, state %d\n", i, pBatch->nElem, pBatch->pElem[i].state);
localRet = doEnqSingleObj(pThis, eFLOWCTL_NO_DELAY,
(obj_t*)MsgAddRef((msg_t*) pUsr));
++nEnqueued;
@@ -1544,7 +1544,7 @@ dbgprintf("XXX: DeleteProcessedBatch re-enqueue %d of %d, state %d\n", i, pBatch
objDestruct(pUsr);
}
- dbgprintf("we deleted %d objects and enqueued %d objects\n", i-nEnqueued, nEnqueued);
+ DBGPRINTF("we deleted %d objects and enqueued %d objects\n", i-nEnqueued, nEnqueued);
if(nEnqueued > 0)
qqueueChkPersist(pThis, nEnqueued);
@@ -2731,7 +2731,7 @@ qqueueApplyCnfParam(qqueue_t *pThis, struct cnfparamvals *pvals)
} else if(!strcmp(pblk.descr[i].name, "queuedequeuetimend.")) {
pThis->iDeqtWinToHr = pvals[i].val.d.n;
} else {
- dbgprintf("queue: program error, non-handled "
+ DBGPRINTF("queue: program error, non-handled "
"param '%s'\n", pblk.descr[i].name);
}
}
diff --git a/runtime/rsconf.c b/runtime/rsconf.c
index b83ba063..032d01a3 100644
--- a/runtime/rsconf.c
+++ b/runtime/rsconf.c
@@ -64,7 +64,9 @@
#include "threads.h"
#include "datetime.h"
#include "parserif.h"
+#include "modules.h"
#include "dirty.h"
+#include "template.h"
/* static data */
DEFobjStaticHelpers
@@ -151,10 +153,33 @@ rsRetVal rsconfConstructFinalize(rsconf_t __attribute__((unused)) *pThis)
}
+/* call freeCnf() module entry points AND free the module entries themselfes.
+ */
+static inline void
+freeCnf(rsconf_t *pThis)
+{
+ cfgmodules_etry_t *etry, *del;
+ etry = pThis->modules.root;
+ while(etry != NULL) {
+ if(etry->pMod->beginCnfLoad != NULL) {
+ dbgprintf("calling freeCnf(%p) for module '%s'\n",
+ etry->modCnf, (char*) module.GetName(etry->pMod));
+ etry->pMod->freeCnf(etry->modCnf);
+ }
+ del = etry;
+ etry = etry->next;
+ free(del);
+ }
+}
+
+
/* destructor for the rsconf object */
BEGINobjDestruct(rsconf) /* be sure to specify the object type also in END and CODESTART macros! */
CODESTARTobjDestruct(rsconf)
+ freeCnf(pThis);
+ tplDeleteAll(pThis);
free(pThis->globals.mainQ.pszMainMsgQFName);
+ free(pThis->globals.pszConfDAGFile);
llDestroy(&(pThis->rulesets.llRulesets));
ENDobjDestruct(rsconf)
@@ -376,17 +401,31 @@ yyerror(char *s)
}
void cnfDoObj(struct cnfobj *o)
{
+ int bChkUnuse = 1;
+
dbgprintf("cnf:global:obj: ");
cnfobjPrint(o);
switch(o->objType) {
case CNFOBJ_GLOBAL:
glblProcessCnf(o);
break;
+ case CNFOBJ_MODULE:
+ modulesProcessCnf(o);
+ break;
case CNFOBJ_ACTION:
actionProcessCnf(o);
break;
+ case CNFOBJ_TPL:
+ tplProcessCnf(o);
+ break;
+ case CNFOBJ_PROPERTY:
+ case CNFOBJ_CONSTANT:
+ /* these types are processed at a later stage */
+ bChkUnuse = 0;
+ break;
}
- nvlstChkUnused(o->nvlst);
+ if(bChkUnuse)
+ nvlstChkUnused(o->nvlst);
cnfobjDestruct(o);
}
@@ -700,9 +739,10 @@ runInputModules(void)
node = module.GetNxtCnfType(runConf, NULL, eMOD_IN);
while(node != NULL) {
if(node->canRun) {
- DBGPRINTF("running module %s with config %p\n", node->pMod->pszName, node);
bNeedsCancel = (node->pMod->isCompatibleWithFeature(sFEATURENonCancelInputTermination) == RS_RET_OK) ?
0 : 1;
+ DBGPRINTF("running module %s with config %p, term mode: %s\n", node->pMod->pszName, node,
+ bNeedsCancel ? "cancel" : "cooperative/SIGTTIN");
thrdCreate(node->pMod->mod.im.runInput, node->pMod->mod.im.afterRun, bNeedsCancel,
(node->pMod->cnfName == NULL) ? node->pMod->pszName : node->pMod->cnfName);
}
@@ -1006,12 +1046,12 @@ loadBuildInModules()
{
DEFiRet;
- CHKiRet(regBuildInModule(modInitFile, UCHAR_CONSTANT("builtin-file"), NULL));
- CHKiRet(regBuildInModule(modInitPipe, UCHAR_CONSTANT("builtin-pipe"), NULL));
+ CHKiRet(regBuildInModule(modInitFile, UCHAR_CONSTANT("builtin:omfile"), NULL));
+ CHKiRet(regBuildInModule(modInitPipe, UCHAR_CONSTANT("builtin:ompipe"), NULL));
CHKiRet(regBuildInModule(modInitShell, UCHAR_CONSTANT("builtin-shell"), NULL));
- CHKiRet(regBuildInModule(modInitDiscard, UCHAR_CONSTANT("builtin-discard"), NULL));
+ CHKiRet(regBuildInModule(modInitDiscard, UCHAR_CONSTANT("builtin:omdiscard"), NULL));
# ifdef SYSLOG_INET
- CHKiRet(regBuildInModule(modInitFwd, UCHAR_CONSTANT("builtin-fwd"), NULL));
+ CHKiRet(regBuildInModule(modInitFwd, UCHAR_CONSTANT("builtin:omfwd"), NULL));
# endif
/* dirty, but this must be for the time being: the usrmsg module must always be
@@ -1023,11 +1063,11 @@ loadBuildInModules()
* User names now must begin with:
* [a-zA-Z0-9_.]
*/
- CHKiRet(regBuildInModule(modInitUsrMsg, (uchar*) "builtin-usrmsg", NULL));
+ CHKiRet(regBuildInModule(modInitUsrMsg, (uchar*) "builtin:omusrmsg", NULL));
/* load build-in parser modules */
- CHKiRet(regBuildInModule(modInitpmrfc5424, UCHAR_CONSTANT("builtin-pmrfc5424"), NULL));
- CHKiRet(regBuildInModule(modInitpmrfc3164, UCHAR_CONSTANT("builtin-pmrfc3164"), NULL));
+ CHKiRet(regBuildInModule(modInitpmrfc5424, UCHAR_CONSTANT("builtin:pmrfc5424"), NULL));
+ CHKiRet(regBuildInModule(modInitpmrfc3164, UCHAR_CONSTANT("builtin:pmrfc3164"), NULL));
/* and set default parser modules. Order is *very* important, legacy
* (3164) parser needs to go last! */
@@ -1035,10 +1075,10 @@ loadBuildInModules()
CHKiRet(parser.AddDfltParser(UCHAR_CONSTANT("rsyslog.rfc3164")));
/* load build-in strgen modules */
- CHKiRet(regBuildInModule(modInitsmfile, UCHAR_CONSTANT("builtin-smfile"), NULL));
- CHKiRet(regBuildInModule(modInitsmtradfile, UCHAR_CONSTANT("builtin-smtradfile"), NULL));
- CHKiRet(regBuildInModule(modInitsmfwd, UCHAR_CONSTANT("builtin-smfwd"), NULL));
- CHKiRet(regBuildInModule(modInitsmtradfwd, UCHAR_CONSTANT("builtin-smtradfwd"), NULL));
+ CHKiRet(regBuildInModule(modInitsmfile, UCHAR_CONSTANT("builtin:smfile"), NULL));
+ CHKiRet(regBuildInModule(modInitsmtradfile, UCHAR_CONSTANT("builtin:smtradfile"), NULL));
+ CHKiRet(regBuildInModule(modInitsmfwd, UCHAR_CONSTANT("builtin:smfwd"), NULL));
+ CHKiRet(regBuildInModule(modInitsmtradfwd, UCHAR_CONSTANT("builtin:smtradfwd"), NULL));
finalize_it:
if(iRet != RS_RET_OK) {
diff --git a/runtime/rsconf.h b/runtime/rsconf.h
index 8715cf1b..484fec8c 100644
--- a/runtime/rsconf.h
+++ b/runtime/rsconf.h
@@ -97,8 +97,8 @@ struct defaults_s {
struct cfgmodules_etry_s {
cfgmodules_etry_t *next;
modInfo_t *pMod;
- /* the following data is input module specific */
void *modCnf; /* pointer to the input module conf */
+ /* the following data is input module specific */
sbool canActivate; /* OK to activate this config? */
sbool canRun; /* OK to run this config? */
};
diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h
index 1da56085..928c3ab9 100644
--- a/runtime/rsyslog.h
+++ b/runtime/rsyslog.h
@@ -142,6 +142,7 @@ typedef uintTiny propid_t;
#define PROP_CEE_ALL_JSON 201
#define PROP_SYS_BOM 159
#define PROP_SYS_UPTIME 160
+#define PROP_UUID 161
/* The error codes below are orginally "borrowed" from
@@ -375,6 +376,11 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth
RS_RET_LEGA_ACT_NOT_SUPPORTED = -2215, /**< the module (no longer) supports legacy action syntax */
RS_RET_MAX_OMSR_REACHED = -2216, /**< max nbr of string requests reached, not supported by core */
RS_RET_UID_MISSING = -2217, /**< a user id is missing (but e.g. a password provided) */
+ /* reserved for pre-v6.5 */
+ RS_RET_DUP_PARAM = -2220, /**< config parameter is given more than once */
+ RS_RET_MODULE_ALREADY_IN_CONF = -2221, /**< module already in current configuration */
+ RS_RET_PARAM_NOT_PERMITTED = -2222, /**< legacy parameter no longer permitted (usally already set by v2) */
+ RS_RET_NO_JSON_PASSING = -2223, /**< rsyslog core does not support JSON-passing plugin API */
/* RainerScript error messages (range 1000.. 1999) */
RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */
diff --git a/runtime/rule.c b/runtime/rule.c
index 18199230..6d14199b 100644
--- a/runtime/rule.c
+++ b/runtime/rule.c
@@ -132,14 +132,14 @@ shouldProcessThisMessage(rule_t *pRule, msg_t *pMsg, sbool *bProcessMsg)
} else if(pRule->eHostnameCmpMode == HN_COMP_MATCH) {
if(rsCStrSzStrCmp(pRule->pCSHostnameComp, (uchar*) getHOSTNAME(pMsg), getHOSTNAMELen(pMsg))) {
/* not equal, so we are already done... */
- dbgprintf("hostname filter '+%s' does not match '%s'\n",
+ DBGPRINTF("hostname filter '+%s' does not match '%s'\n",
rsCStrGetSzStrNoNULL(pRule->pCSHostnameComp), getHOSTNAME(pMsg));
FINALIZE;
}
} else { /* must be -hostname */
if(!rsCStrSzStrCmp(pRule->pCSHostnameComp, (uchar*) getHOSTNAME(pMsg), getHOSTNAMELen(pMsg))) {
- /* not equal, so we are already done... */
- dbgprintf("hostname filter '-%s' does not match '%s'\n",
+ /* not equal, SO WE ARe already done... */
+ DBGPRINTF("hostname filter '-%s' does not match '%s'\n",
rsCStrGetSzStrNoNULL(pRule->pCSHostnameComp), getHOSTNAME(pMsg));
FINALIZE;
}
@@ -171,7 +171,7 @@ shouldProcessThisMessage(rule_t *pRule, msg_t *pMsg, sbool *bProcessMsg)
if(pRule->f_filter_type == FILTER_PRI) {
/* skip messages that are incorrect priority */
- dbgprintf("testing filter, f_pmask %d\n", pRule->f_filterData.f_pmask[pMsg->iFacility]);
+ DBGPRINTF("testing filter, f_pmask %d\n", pRule->f_filterData.f_pmask[pMsg->iFacility]);
if ( (pRule->f_filterData.f_pmask[pMsg->iFacility] == TABLE_NOPRI) || \
((pRule->f_filterData.f_pmask[pMsg->iFacility] & (1<<pMsg->iSeverity)) == 0) )
bRet = 0;
@@ -179,7 +179,7 @@ shouldProcessThisMessage(rule_t *pRule, msg_t *pMsg, sbool *bProcessMsg)
bRet = 1;
} else if(pRule->f_filter_type == FILTER_EXPR) {
bRet = cnfexprEvalBool(pRule->f_filterData.expr, pMsg);
- dbgprintf("result of rainerscript filter evaluation: %d\n", bRet);
+ DBGPRINTF("result of rainerscript filter evaluation: %d\n", bRet);
} else {
assert(pRule->f_filter_type == FILTER_PROP); /* assert() just in case... */
pszPropVal = MsgGetProp(pMsg, NULL, pRule->f_filterData.prop.propID,
@@ -230,21 +230,21 @@ shouldProcessThisMessage(rule_t *pRule, msg_t *pMsg, sbool *bProcessMsg)
char *cstr;
if(pRule->f_filterData.prop.propID == PROP_CEE) {
cstr = es_str2cstr(pRule->f_filterData.prop.propName, NULL);
- dbgprintf("Filter: check for CEE property '%s' (value '%s') ",
+ DBGPRINTF("Filter: check for CEE property '%s' (value '%s') ",
cstr, pszPropVal);
free(cstr);
} else {
- dbgprintf("Filter: check for property '%s' (value '%s') ",
+ DBGPRINTF("Filter: check for property '%s' (value '%s') ",
propIDToName(pRule->f_filterData.prop.propID), pszPropVal);
}
if(pRule->f_filterData.prop.isNegated)
- dbgprintf("NOT ");
+ DBGPRINTF("NOT ");
if(pRule->f_filterData.prop.operation == FIOP_ISEMPTY) {
- dbgprintf("%s : %s\n",
+ DBGPRINTF("%s : %s\n",
getFIOPName(pRule->f_filterData.prop.operation),
bRet ? "TRUE" : "FALSE");
} else {
- dbgprintf("%s '%s': %s\n",
+ DBGPRINTF("%s '%s': %s\n",
getFIOPName(pRule->f_filterData.prop.operation),
rsCStrGetSzStrNoNULL(pRule->f_filterData.prop.pCSCompValue),
bRet ? "TRUE" : "FALSE");
@@ -335,6 +335,8 @@ CODESTARTobjDestruct(rule)
rsCStrRegexDestruct(&pThis->f_filterData.prop.regex_cache);
if(pThis->f_filterData.prop.propName != NULL)
es_deleteStr(pThis->f_filterData.prop.propName);
+ } else if(pThis->f_filter_type == FILTER_EXPR) {
+ cnfexprDestruct(pThis->f_filterData.expr);
}
llDestroy(&pThis->llActList);
diff --git a/runtime/ruleset.c b/runtime/ruleset.c
index a6eca307..5cb34148 100644
--- a/runtime/ruleset.c
+++ b/runtime/ruleset.c
@@ -143,9 +143,9 @@ DEFFUNC_llExecFunc(processBatchDoRules)
{
rsRetVal iRet;
ISOBJ_TYPE_assert(pData, rule);
- dbgprintf("Processing next rule\n");
+ DBGPRINTF("Processing next rule\n");
iRet = rule.ProcessBatch((rule_t*) pData, (batch_t*) pParam);
-dbgprintf("ruleset: get iRet %d from rule.ProcessMsg()\n", iRet);
+ DBGPRINTF("ruleset: get iRet %d from rule.ProcessMsg()\n", iRet);
return iRet;
}
@@ -266,7 +266,7 @@ addRule(ruleset_t *pThis, rule_t **ppRule)
rule.Destruct(ppRule);
} else {
CHKiRet(llAppend(&pThis->llRules, NULL, *ppRule));
- dbgprintf("selector line successfully processed, %d actions\n", iActionCnt);
+ DBGPRINTF("selector line successfully processed, %d actions\n", iActionCnt);
}
finalize_it:
@@ -337,7 +337,7 @@ SetDefaultRuleset(rsconf_t *conf, uchar *pszName)
CHKiRet(rulesetGetRuleset(conf, &pRuleset, pszName));
conf->rulesets.pDflt = pRuleset;
- dbgprintf("default rule set changed to %p: '%s'\n", pRuleset, pszName);
+ DBGPRINTF("default rule set changed to %p: '%s'\n", pRuleset, pszName);
finalize_it:
RETiRet;
@@ -355,7 +355,7 @@ SetCurrRuleset(rsconf_t *conf, uchar *pszName)
CHKiRet(rulesetGetRuleset(conf, &pRuleset, pszName));
conf->rulesets.pCurr = pRuleset;
- dbgprintf("current rule set changed to %p: '%s'\n", pRuleset, pszName);
+ DBGPRINTF("current rule set changed to %p: '%s'\n", pRuleset, pszName);
finalize_it:
RETiRet;
@@ -414,7 +414,7 @@ finalize_it:
/* destructor for the ruleset object */
BEGINobjDestruct(ruleset) /* be sure to specify the object type also in END and CODESTART macros! */
CODESTARTobjDestruct(ruleset)
- dbgprintf("destructing ruleset %p, name %p\n", pThis, pThis->pszName);
+ DBGPRINTF("destructing ruleset %p, name %p\n", pThis, pThis->pszName);
if(pThis->pQueue != NULL) {
qqueueDestruct(&pThis->pQueue);
}
@@ -559,8 +559,7 @@ doRulesetAddParser(rsconf_t *conf, uchar *pName)
CHKiRet(parser.AddParserToList(&conf->rulesets.pCurr->pParserLst, pParser));
- dbgprintf("added parser '%s' to ruleset '%s'\n", pName, conf->rulesets.pCurr->pszName);
-RUNLOG_VAR("%p", conf->rulesets.pCurr->pParserLst);
+ DBGPRINTF("added parser '%s' to ruleset '%s'\n", pName, conf->rulesets.pCurr->pszName);
finalize_it:
d_free(pName); /* no longer needed */
diff --git a/runtime/statsobj.c b/runtime/statsobj.c
index a21614f6..25275616 100644
--- a/runtime/statsobj.c
+++ b/runtime/statsobj.c
@@ -168,15 +168,18 @@ finalize_it:
/* get all the object's countes together as CEE. */
static rsRetVal
-getStatsLineCEE(statsobj_t *pThis, cstr_t **ppcstr)
+getStatsLineCEE(statsobj_t *pThis, cstr_t **ppcstr, int cee_cookie)
{
cstr_t *pcstr;
ctr_t *pCtr;
DEFiRet;
CHKiRet(cstrConstruct(&pcstr));
- rsCStrAppendStrWithLen(pcstr, UCHAR_CONSTANT("@cee: {"), 7);
+ if (cee_cookie == 1)
+ rsCStrAppendStrWithLen(pcstr, UCHAR_CONSTANT("@cee: "), 6);
+
+ rsCStrAppendStrWithLen(pcstr, UCHAR_CONSTANT("{"), 1);
rsCStrAppendStrWithLen(pcstr, UCHAR_CONSTANT("\""), 1);
rsCStrAppendStrWithLen(pcstr, UCHAR_CONSTANT("name"), 4);
rsCStrAppendStrWithLen(pcstr, UCHAR_CONSTANT("\""), 1);
@@ -273,8 +276,11 @@ getAllStatsLines(rsRetVal(*cb)(void*, cstr_t*), void *usrptr, statsFmtType_t fmt
case statsFmt_Legacy:
CHKiRet(getStatsLine(o, &cstr));
break;
+ case statsFmt_CEE:
+ CHKiRet(getStatsLineCEE(o, &cstr, 1));
+ break;
case statsFmt_JSON:
- CHKiRet(getStatsLineCEE(o, &cstr));
+ CHKiRet(getStatsLineCEE(o, &cstr, 0));
break;
}
CHKiRet(cb(usrptr, cstr));
diff --git a/runtime/statsobj.h b/runtime/statsobj.h
index f7de68ee..14b33215 100644
--- a/runtime/statsobj.h
+++ b/runtime/statsobj.h
@@ -46,7 +46,8 @@ typedef enum statsCtrType_e {
/* stats line format types */
typedef enum statsFmtType_e {
statsFmt_Legacy,
- statsFmt_JSON
+ statsFmt_JSON,
+ statsFmt_CEE
} statsFmtType_t;
diff --git a/runtime/wti.c b/runtime/wti.c
index 7b88c3eb..f91fb5a9 100644
--- a/runtime/wti.c
+++ b/runtime/wti.c
@@ -121,7 +121,7 @@ wtiWakeupThrd(wti_t *pThis)
if(wtiGetState(pThis)) {
/* we first try the cooperative "cancel" interface */
pthread_kill(pThis->thrdID, SIGTTIN);
- dbgprintf("sent SIGTTIN to worker thread 0x%x\n", (unsigned) pThis->thrdID);
+ DBGPRINTF("sent SIGTTIN to worker thread 0x%x\n", (unsigned) pThis->thrdID);
}
RETiRet;
@@ -148,13 +148,13 @@ wtiCancelThrd(wti_t *pThis)
if(wtiGetState(pThis)) {
/* we first try the cooperative "cancel" interface */
pthread_kill(pThis->thrdID, SIGTTIN);
- dbgprintf("sent SIGTTIN to worker thread 0x%x, giving it a chance to terminate\n", (unsigned) pThis->thrdID);
+ DBGPRINTF("sent SIGTTIN to worker thread 0x%x, giving it a chance to terminate\n", (unsigned) pThis->thrdID);
srSleep(0, 10000);
}
if(wtiGetState(pThis)) {
- dbgprintf("cooperative worker termination failed, using cancellation...\n");
- dbgoprint((obj_t*) pThis, "canceling worker thread\n");
+ DBGPRINTF("cooperative worker termination failed, using cancellation...\n");
+ DBGOPRINT((obj_t*) pThis, "canceling worker thread\n");
pthread_cancel(pThis->thrdID);
/* now wait until the thread terminates... */
while(wtiGetState(pThis)) {
@@ -195,7 +195,7 @@ wtiConstructFinalize(wti_t *pThis)
ISOBJ_TYPE_assert(pThis, wti);
- dbgprintf("%s: finalizing construction of worker instance data\n", wtiGetDbgHdr(pThis));
+ DBGPRINTF("%s: finalizing construction of worker instance data\n", wtiGetDbgHdr(pThis));
/* initialize our thread instance descriptor (no concurrency here) */
pThis->bIsRunning = RSFALSE;
@@ -257,7 +257,7 @@ doIdleProcessing(wti_t *pThis, wtp_t *pWtp, int *pbInactivityTOOccured)
*pbInactivityTOOccured = 1; /* indicate we had a timeout */
}
}
- dbgoprint((obj_t*) pThis, "worker awoke from idle processing\n");
+ DBGOPRINT((obj_t*) pThis, "worker awoke from idle processing\n");
ENDfunc
}
@@ -300,7 +300,7 @@ wtiWorker(wti_t *pThis)
if(terminateRet == RS_RET_TERMINATE_NOW) {
/* we now need to free the old batch */
localRet = pWtp->pfObjProcessed(pWtp->pUsr, pThis);
- dbgoprint((obj_t*) pThis, "terminating worker because of TERMINATE_NOW mode, del iRet %d\n",
+ DBGOPRINT((obj_t*) pThis, "terminating worker because of TERMINATE_NOW mode, del iRet %d\n",
localRet);
d_pthread_mutex_unlock(pWtp->pmutUsr);
break;
@@ -318,7 +318,7 @@ wtiWorker(wti_t *pThis)
} else if(localRet == RS_RET_IDLE) {
if(terminateRet == RS_RET_TERMINATE_WHEN_IDLE || bInactivityTOOccured) {
d_pthread_mutex_unlock(pWtp->pmutUsr);
- dbgoprint((obj_t*) pThis, "terminating worker terminateRet=%d, bInactivityTOOccured=%d\n",
+ DBGOPRINT((obj_t*) pThis, "terminating worker terminateRet=%d, bInactivityTOOccured=%d\n",
terminateRet, bInactivityTOOccured);
break; /* end of loop */
}
diff --git a/tcps_sess.c b/tcps_sess.c
index eb3740e4..e7149cb7 100644
--- a/tcps_sess.c
+++ b/tcps_sess.c
@@ -323,7 +323,7 @@ PrepareClose(tcps_sess_t *pThis)
* of message may occur. As such, we process the message in
* this case.
*/
- dbgprintf("Extra data at end of stream in legacy syslog/tcp message - processing\n");
+ DBGPRINTF("Extra data at end of stream in legacy syslog/tcp message - processing\n");
datetime.getCurrTime(&stTime, &ttGenTime);
defaultDoSubmitMessage(pThis, &stTime, ttGenTime, NULL);
}
@@ -382,21 +382,21 @@ processDataRcvd(tcps_sess_t *pThis, char c, struct syslogTime *stTime, time_t tt
if(isdigit(c)) {
pThis->iOctetsRemain = pThis->iOctetsRemain * 10 + c - '0';
} else { /* done with the octet count, so this must be the SP terminator */
- dbgprintf("TCP Message with octet-counter, size %d.\n", pThis->iOctetsRemain);
+ DBGPRINTF("TCP Message with octet-counter, size %d.\n", pThis->iOctetsRemain);
if(c != ' ') {
errmsg.LogError(0, NO_ERRCODE, "Framing Error in received TCP message: "
"delimiter is not SP but has ASCII value %d.\n", c);
}
if(pThis->iOctetsRemain < 1) {
/* TODO: handle the case where the octet count is 0! */
- dbgprintf("Framing Error: invalid octet count\n");
+ DBGPRINTF("Framing Error: invalid octet count\n");
errmsg.LogError(0, NO_ERRCODE, "Framing Error in received TCP message: "
"invalid octet count %d.\n", pThis->iOctetsRemain);
} else if(pThis->iOctetsRemain > iMaxLine) {
/* while we can not do anything against it, we can at least log an indication
* that something went wrong) -- rgerhards, 2008-03-14
*/
- dbgprintf("truncating message with %d octets - max msg size is %d\n",
+ DBGPRINTF("truncating message with %d octets - max msg size is %d\n",
pThis->iOctetsRemain, iMaxLine);
errmsg.LogError(0, NO_ERRCODE, "received oversize message: size is %d bytes, "
"max msg size is %d, truncating...\n", pThis->iOctetsRemain, iMaxLine);
@@ -407,7 +407,7 @@ processDataRcvd(tcps_sess_t *pThis, char c, struct syslogTime *stTime, time_t tt
assert(pThis->inputState == eInMsg);
if(pThis->iMsg >= iMaxLine) {
/* emergency, we now need to flush, no matter if we are at end of message or not... */
- dbgprintf("error: message received is larger than max msg size, we split it\n");
+ DBGPRINTF("error: message received is larger than max msg size, we split it\n");
defaultDoSubmitMessage(pThis, stTime, ttGenTime, pMultiSub);
/* we might think if it is better to ignore the rest of the
* message than to treat it as a new one. Maybe this is a good
diff --git a/tcpsrv.c b/tcpsrv.c
index 1f4dcb81..bf12f1fa 100644
--- a/tcpsrv.c
+++ b/tcpsrv.c
@@ -132,7 +132,7 @@ addNewLstnPort(tcpsrv_t *pThis, uchar *pszPort, int bSuppOctetFram)
/* create entry */
CHKmalloc(pEntry = MALLOC(sizeof(tcpLstnPortList_t)));
- pEntry->pszPort = pszPort;
+ CHKmalloc(pEntry->pszPort = ustrdup(pszPort));
pEntry->pSrv = pThis;
pEntry->pRuleset = pThis->pRuleset;
pEntry->bSuppOctetFram = bSuppOctetFram;
@@ -596,7 +596,6 @@ processWorksetItem(tcpsrv_t *pThis, nspoll_t *pPoll, int idx, void *pUsr)
iRet = SessAccept(pThis, pThis->ppLstnPort[idx], &pNewSess, pThis->ppLstn[idx]);
if(iRet == RS_RET_OK) {
if(pPoll != NULL) {
- dbgprintf("XXXXXX: processWorksetItem trying nspoll.ctl\n");
CHKiRet(nspoll.Ctl(pPoll, pNewSess->pStrm, 0, pNewSess, NSDPOLL_IN, NSDPOLL_ADD));
}
DBGPRINTF("New session created with NSD %p.\n", pNewSess);
@@ -661,7 +660,7 @@ processWorkset(tcpsrv_t *pThis, nspoll_t *pPoll, int numEntries, nsd_epworkset_t
int origEntries = numEntries;
DEFiRet;
- dbgprintf("tcpsrv: ready to process %d event entries\n", numEntries);
+ DBGPRINTF("tcpsrv: ready to process %d event entries\n", numEntries);
while(numEntries > 0) {
if(glbl.GetGlobalInputTermState() == 1)
@@ -862,21 +861,21 @@ Run(tcpsrv_t *pThis)
}
if(localRet != RS_RET_OK) {
/* fall back to select */
- dbgprintf("tcpsrv could not use epoll() interface, iRet=%d, using select()\n", localRet);
+ DBGPRINTF("tcpsrv could not use epoll() interface, iRet=%d, using select()\n", localRet);
iRet = RunSelect(pThis, workset, sizeof(workset)/sizeof(nsd_epworkset_t));
FINALIZE;
}
- dbgprintf("tcpsrv uses epoll() interface, nsdpoll driver found\n");
+ DBGPRINTF("tcpsrv uses epoll() interface, nsdpoll driver found\n");
/* flag that we are in epoll mode */
pThis->bUsingEPoll = RSTRUE;
/* Add the TCP listen sockets to the list of sockets to monitor */
for(i = 0 ; i < pThis->iLstnCurr ; ++i) {
- dbgprintf("Trying to add listener %d, pUsr=%p\n", i, pThis->ppLstn);
+ DBGPRINTF("Trying to add listener %d, pUsr=%p\n", i, pThis->ppLstn);
CHKiRet(nspoll.Ctl(pPoll, pThis->ppLstn[i], i, pThis->ppLstn, NSDPOLL_IN, NSDPOLL_ADD));
- dbgprintf("Added listener %d\n", i);
+ DBGPRINTF("Added listener %d\n", i);
}
while(1) {
@@ -1064,7 +1063,7 @@ static rsRetVal
SetKeepAlive(tcpsrv_t *pThis, int iVal)
{
DEFiRet;
- dbgprintf("tcpsrv: keep-alive set to %d\n", iVal);
+ DBGPRINTF("tcpsrv: keep-alive set to %d\n", iVal);
pThis->bUseKeepAlive = iVal;
RETiRet;
}
diff --git a/template.c b/template.c
index e80015da..27b508c6 100644
--- a/template.c
+++ b/template.c
@@ -34,6 +34,7 @@
#include <string.h>
#include <ctype.h>
#include <assert.h>
+#include <json/json.h>
#include "stringbuf.h"
#include "syslogd-types.h"
#include "template.h"
@@ -50,6 +51,60 @@ DEFobjCurrIf(obj)
DEFobjCurrIf(errmsg)
DEFobjCurrIf(strgen)
+/* tables for interfacing with the v6 config system */
+static struct cnfparamdescr cnfparamdescr[] = {
+ { "name", eCmdHdlrString, 1 },
+ { "type", eCmdHdlrString, 0 },
+ { "string", eCmdHdlrString, 0 },
+ { "plugin", eCmdHdlrString, 0 },
+ { "option.stdsql", eCmdHdlrBinary, 0 },
+ { "option.sql", eCmdHdlrBinary, 0 },
+ { "option.json", eCmdHdlrBinary, 0 }
+};
+static struct cnfparamblk pblk =
+ { CNFPARAMBLK_VERSION,
+ sizeof(cnfparamdescr)/sizeof(struct cnfparamdescr),
+ cnfparamdescr
+ };
+
+static struct cnfparamdescr cnfparamdescrProperty[] = {
+ { "name", eCmdHdlrString, 1 },
+ { "outname", eCmdHdlrString, 0 },
+ { "dateformat", eCmdHdlrString, 0 },
+ { "caseconversion", eCmdHdlrString, 0 },
+ { "controlcharacters", eCmdHdlrString, 0 },
+ { "securepath", eCmdHdlrString, 0 },
+ { "format", eCmdHdlrString, 0 },
+ { "position.from", eCmdHdlrInt, 0 },
+ { "position.to", eCmdHdlrInt, 0 },
+ { "field.number", eCmdHdlrInt, 0 },
+ { "field.delimiter", eCmdHdlrInt, 0 },
+ { "regex.expression", eCmdHdlrString, 0 },
+ { "regex.type", eCmdHdlrString, 0 },
+ { "regex.nomatchmode", eCmdHdlrString, 0 },
+ { "regex.match", eCmdHdlrInt, 0 },
+ { "regex.submatch", eCmdHdlrInt, 0 },
+ { "droplastlf", eCmdHdlrBinary, 0 },
+ { "mandatory", eCmdHdlrBinary, 0 },
+ { "spifno1stsp", eCmdHdlrBinary, 0 }
+};
+static struct cnfparamblk pblkProperty =
+ { CNFPARAMBLK_VERSION,
+ sizeof(cnfparamdescrProperty)/sizeof(struct cnfparamdescr),
+ cnfparamdescrProperty
+ };
+
+static struct cnfparamdescr cnfparamdescrConstant[] = {
+ { "value", eCmdHdlrString, 1 },
+ { "outname", eCmdHdlrString, 0 },
+};
+static struct cnfparamblk pblkConstant =
+ { CNFPARAMBLK_VERSION,
+ sizeof(cnfparamdescrConstant)/sizeof(struct cnfparamdescr),
+ cnfparamdescrConstant
+ };
+
+
#ifdef FEATURE_REGEXP
DEFobjCurrIf(regexp)
static int bFirstRegexpErrmsg = 1; /**< did we already do a "can't load regexp" error message? */
@@ -217,6 +272,49 @@ finalize_it:
}
+/* This functions converts a template into a json object.
+ * For further general details, see the very similar funtion
+ * tpltoString().
+ * rgerhards, 2012-08-29
+ */
+rsRetVal tplToJSON(struct template *pTpl, msg_t *pMsg, struct json_object **pjson)
+{
+ struct templateEntry *pTpe;
+ size_t propLen;
+ unsigned short bMustBeFreed;
+ uchar *pVal;
+ struct json_object *json, *jsonf;
+ DEFiRet;
+
+ assert(pTpl != NULL);
+ assert(pMsg != NULL);
+ assert(json != NULL);
+
+ json = json_object_new_object();
+ for(pTpe = pTpl->pEntryRoot ; pTpe != NULL ; pTpe = pTpe->pNext) {
+ if(pTpe->eEntryType == CONSTANT) {
+ if(pTpe->fieldName == NULL)
+ continue;
+ jsonf = json_object_new_string((char*) pTpe->data.constant.pConstant);
+ json_object_object_add(json, (char*)pTpe->fieldName, jsonf);
+ } else if(pTpe->eEntryType == FIELD) {
+ pVal = (uchar*) MsgGetProp(pMsg, pTpe, pTpe->data.field.propid,
+ pTpe->data.field.propName, &propLen, &bMustBeFreed);
+ if(pTpe->data.field.options.bMandatory || propLen > 0) {
+ jsonf = json_object_new_string_len((char*)pVal, propLen);
+ json_object_object_add(json, (char*)pTpe->fieldName, jsonf);
+ }
+ if(bMustBeFreed) { /* json-c makes its own private copy! */
+ free(pVal);
+ }
+ }
+ }
+
+ *pjson = (iRet == RS_RET_OK) ? json : NULL;
+ RETiRet;
+}
+
+
/* Helper to doEscape. This is called if doEscape
* runs out of memory allocating the escaped string.
* Then we are in trouble. We can
@@ -569,6 +667,8 @@ static void doOptions(unsigned char **pp, struct templateEntry *pTpe)
} else {
pTpe->data.field.options.bJSONf = 1;
}
+ } else if(!strcmp((char*)Buf, "mandatory-field")) {
+ pTpe->data.field.options.bMandatory = 1;
} else {
dbgprintf("Invalid field option '%s' specified - ignored.\n", Buf);
}
@@ -924,17 +1024,21 @@ static int do_Parameter(unsigned char **pp, struct template *pTpl)
/* save field name - if none was given, use the property name instead */
if(pStrField == NULL) {
- if((pTpe->data.field.fieldName =
- es_newStrFromCStr((char*)cstrGetSzStrNoNULL(pStrProp), cstrLen(pStrProp))) == NULL) {
- return 1;
+ if(pTpe->data.field.propid == PROP_CEE) {
+ /* in CEE case, we remove "$!" from the fieldname - it's just our indicator */
+ pTpe->fieldName = ustrdup(cstrGetSzStrNoNULL(pStrProp)+2);
+ pTpe->lenFieldName = cstrLen(pStrProp)-2;
+ } else {
+ pTpe->fieldName = ustrdup(cstrGetSzStrNoNULL(pStrProp));
+ pTpe->lenFieldName = cstrLen(pStrProp);
}
} else {
- if((pTpe->data.field.fieldName =
- es_newStrFromCStr((char*)cstrGetSzStrNoNULL(pStrField), cstrLen(pStrField))) == NULL) {
- return 1;
- }
+ pTpe->fieldName = ustrdup(cstrGetSzStrNoNULL(pStrField));
+ pTpe->lenFieldName = cstrLen(pStrProp);
cstrDestruct(&pStrField);
}
+ if(pTpe->fieldName == NULL)
+ return 1;
cstrDestruct(&pStrProp);
@@ -1130,6 +1234,532 @@ struct template *tplAddLine(rsconf_t *conf, char* pName, uchar** ppRestOfConfLin
return(pTpl);
}
+static rsRetVal
+createConstantTpe(struct template *pTpl, struct cnfobj *o)
+{
+ struct templateEntry *pTpe;
+ es_str_t *value;
+ int i;
+ struct cnfparamvals *pvals;
+ uchar *outname = NULL;
+ DEFiRet;
+
+ /* pull params */
+ pvals = nvlstGetParams(o->nvlst, &pblkConstant, NULL);
+ cnfparamsPrint(&pblkConstant, pvals);
+
+ for(i = 0 ; i < pblkConstant.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(pblkConstant.descr[i].name, "value")) {
+ value = pvals[i].val.d.estr;
+ } else if(!strcmp(pblkConstant.descr[i].name, "outname")) {
+ outname = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else {
+ dbgprintf("template:constantTpe: program error, non-handled "
+ "param '%s'\n", pblkConstant.descr[i].name);
+ }
+ }
+
+ /* sanity check */
+
+ /* apply */
+ CHKmalloc(pTpe = tpeConstruct(pTpl));
+ es_unescapeStr(value);
+ pTpe->eEntryType = CONSTANT;
+ pTpe->fieldName = outname;
+ if(outname != NULL)
+ pTpe->lenFieldName = ustrlen(outname);
+ pTpe->data.constant.iLenConstant = es_strlen(value);
+ pTpe->data.constant.pConstant = (uchar*)es_str2cstr(value, NULL);
+
+finalize_it:
+ RETiRet;
+}
+
+static rsRetVal
+createPropertyTpe(struct template *pTpl, struct cnfobj *o)
+{
+ struct templateEntry *pTpe;
+ cstr_t *name;
+ uchar *outname = NULL;
+ int i;
+ int droplastlf = 0;
+ int spifno1stsp = 0;
+ int mandatory = 0;
+ int frompos = -1;
+ int topos = -1;
+ int fieldnum = -1;
+ int fielddelim = 9; /* default is HT (USACSII 9) */
+ int re_matchToUse = 0;
+ int re_submatchToUse = 0;
+ char *re_expr = NULL;
+ struct cnfparamvals *pvals;
+ enum {F_NONE, F_CSV, F_JSON, F_JSONF} formatType = F_NONE;
+ enum {CC_NONE, CC_ESCAPE, CC_SPACE, CC_DROP} controlchr = CC_NONE;
+ enum {SP_NONE, SP_DROP, SP_REPLACE} secpath = SP_NONE;
+ enum tplFormatCaseConvTypes caseconv = tplCaseConvNo;
+ enum tplFormatTypes datefmt = tplFmtDefault;
+ enum tplRegexType re_type = TPL_REGEX_BRE;
+ enum tlpRegexNoMatchType re_nomatchType = TPL_REGEX_NOMATCH_USE_DFLTSTR;
+ DEFiRet;
+
+ /* pull params */
+ pvals = nvlstGetParams(o->nvlst, &pblkProperty, NULL);
+ cnfparamsPrint(&pblkProperty, pvals);
+
+ for(i = 0 ; i < pblkProperty.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(pblkProperty.descr[i].name, "name")) {
+ rsCStrConstructFromszStr(&name,
+ (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL));
+ cstrFinalize(name);
+ } else if(!strcmp(pblkProperty.descr[i].name, "droplastlf")) {
+ droplastlf = pvals[i].val.d.n;
+ } else if(!strcmp(pblkProperty.descr[i].name, "mandatory")) {
+ mandatory = pvals[i].val.d.n;
+ } else if(!strcmp(pblkProperty.descr[i].name, "spifno1stsp")) {
+ spifno1stsp = pvals[i].val.d.n;
+ } else if(!strcmp(pblkProperty.descr[i].name, "outname")) {
+ outname = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(pblkProperty.descr[i].name, "position.from")) {
+ frompos = pvals[i].val.d.n;
+ } else if(!strcmp(pblkProperty.descr[i].name, "position.to")) {
+ topos = pvals[i].val.d.n;
+ } else if(!strcmp(pblkProperty.descr[i].name, "field.number")) {
+ fieldnum = pvals[i].val.d.n;
+ } else if(!strcmp(pblkProperty.descr[i].name, "field.delimiter")) {
+ fielddelim = pvals[i].val.d.n;
+ } else if(!strcmp(pblkProperty.descr[i].name, "regex.expression")) {
+ re_expr = es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(pblkProperty.descr[i].name, "regex.type")) {
+ if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"BRE", sizeof("BRE")-1)) {
+ re_type = TPL_REGEX_BRE;
+ } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"ERE", sizeof("ERE")-1)) {
+ re_type = TPL_REGEX_ERE;
+ } else {
+ uchar *typeStr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL);
+ errmsg.LogError(0, RS_RET_ERR, "invalid regex.type '%s' for property",
+ typeStr);
+ free(typeStr);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ } else if(!strcmp(pblkProperty.descr[i].name, "regex.nomatchmode")) {
+ if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"DFLT", sizeof("DFLT")-1)) {
+ re_nomatchType = TPL_REGEX_NOMATCH_USE_DFLTSTR;
+ } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"BLANK", sizeof("BLANK")-1)) {
+ re_nomatchType = TPL_REGEX_NOMATCH_USE_BLANK;
+ } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"FIELD", sizeof("FIELD")-1)) {
+ re_nomatchType = TPL_REGEX_NOMATCH_USE_WHOLE_FIELD;
+ } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"ZERO", sizeof("ZERO")-1)) {
+ re_nomatchType = TPL_REGEX_NOMATCH_USE_ZERO;
+ } else {
+ uchar *typeStr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL);
+ errmsg.LogError(0, RS_RET_ERR, "invalid format type '%s' for property",
+ typeStr);
+ free(typeStr);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ } else if(!strcmp(pblkProperty.descr[i].name, "regex.match")) {
+ re_matchToUse = pvals[i].val.d.n;
+ } else if(!strcmp(pblkProperty.descr[i].name, "regex.submatch")) {
+ re_submatchToUse = pvals[i].val.d.n;
+ } else if(!strcmp(pblkProperty.descr[i].name, "format")) {
+ if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"csv", sizeof("csv")-1)) {
+ formatType = F_CSV;
+ } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"json", sizeof("json")-1)) {
+ formatType = F_JSON;
+ } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"jsonf", sizeof("jsonf")-1)) {
+ formatType = F_JSONF;
+ } else {
+ uchar *typeStr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL);
+ errmsg.LogError(0, RS_RET_ERR, "invalid format type '%s' for property",
+ typeStr);
+ free(typeStr);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ } else if(!strcmp(pblkProperty.descr[i].name, "controlcharacters")) {
+ if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"escape", sizeof("escape")-1)) {
+ controlchr = CC_ESCAPE;
+ } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"space", sizeof("space")-1)) {
+ controlchr = CC_SPACE;
+ } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"drop", sizeof("drop")-1)) {
+ controlchr = CC_DROP;
+ } else {
+ uchar *typeStr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL);
+ errmsg.LogError(0, RS_RET_ERR, "invalid controlcharacter mode '%s' for property",
+ typeStr);
+ free(typeStr);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ } else if(!strcmp(pblkProperty.descr[i].name, "securepath")) {
+ if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"drop", sizeof("drop")-1)) {
+ secpath = SP_DROP;
+ } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"replace", sizeof("replace")-1)) {
+ secpath = SP_REPLACE;
+ } else {
+ uchar *typeStr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL);
+ errmsg.LogError(0, RS_RET_ERR, "invalid securepath mode '%s' for property",
+ typeStr);
+ free(typeStr);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ } else if(!strcmp(pblkProperty.descr[i].name, "caseconversion")) {
+ if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"lower", sizeof("lower")-1)) {
+ caseconv = tplCaseConvLower;
+ } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"upper", sizeof("upper")-1)) {
+ caseconv = tplCaseConvUpper;
+ } else {
+ uchar *typeStr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL);
+ errmsg.LogError(0, RS_RET_ERR, "invalid caseconversion type '%s' for property",
+ typeStr);
+ free(typeStr);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ } else if(!strcmp(pblkProperty.descr[i].name, "dateformat")) {
+ if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"mysql", sizeof("mysql")-1)) {
+ datefmt = tplFmtMySQLDate;
+ } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"pgsql", sizeof("pgsql")-1)) {
+ datefmt = tplFmtPgSQLDate;
+ } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"rfc3164", sizeof("rfc3164")-1)) {
+ datefmt = tplFmtRFC3164Date;
+ } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"rfc3164-buggyday", sizeof("rfc3164-buggyday")-1)) {
+ datefmt = tplFmtRFC3164BuggyDate;
+ } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"rfc3339", sizeof("rfc3339")-1)) {
+ datefmt = tplFmtRFC3339Date;
+ } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"unixtimestamp", sizeof("unixtimestamp")-1)) {
+ datefmt = tplFmtUnixDate;
+ } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"subseconds", sizeof("subseconds")-1)) {
+ datefmt = tplFmtSecFrac;
+ } else {
+ uchar *typeStr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL);
+ errmsg.LogError(0, RS_RET_ERR, "invalid date format '%s' for property",
+ typeStr);
+ free(typeStr);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ } else {
+ dbgprintf("template:propertyTpe: program error, non-handled "
+ "param '%s'\n", pblkProperty.descr[i].name);
+ }
+ }
+ if(outname == NULL) {
+ uchar *psz = cstrGetSzStrNoNULL(name);
+ /* we need to drop "$!" prefix, if present */
+ if(!strncmp((char*)psz, "$!", 2))
+ outname = ustrdup(psz + 2);
+ else
+ outname = ustrdup(psz);
+ }
+
+ /* sanity check */
+ if(topos == -1 && frompos != -1)
+ topos = 2000000000; /* large enough ;) */
+ if(frompos == -1 && topos != -1)
+ frompos = 0;
+ if(topos < frompos) {
+ errmsg.LogError(0, RS_RET_ERR, "position.to=%d is lower than postion.from=%d\n",
+ topos, frompos);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ if(fieldnum != -1 && re_expr != NULL) {
+ errmsg.LogError(0, RS_RET_ERR, "both field extraction and regex extraction "
+ "specified - this is not possible, remove one");
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+ /* apply */
+ CHKmalloc(pTpe = tpeConstruct(pTpl));
+ pTpe->eEntryType = FIELD;
+ CHKiRet(propNameToID(name, &pTpe->data.field.propid));
+ if(pTpe->data.field.propid == PROP_CEE) {
+ /* in CEE case, we need to preserve the actual property name */
+ pTpe->data.field.propName = es_newStrFromCStr((char*)cstrGetSzStrNoNULL(name)+2,
+ cstrLen(name)-2);
+ }
+ pTpe->data.field.options.bDropLastLF = droplastlf;
+ pTpe->data.field.options.bSPIffNo1stSP = spifno1stsp;
+ pTpe->data.field.options.bMandatory = mandatory;
+ pTpe->data.field.eCaseConv = caseconv;
+ switch(formatType) {
+ case F_NONE:
+ /* all set ;) */
+ break;
+ case F_CSV:
+ pTpe->data.field.options.bCSV = 1;
+ break;
+ case F_JSON:
+ pTpe->data.field.options.bJSON = 1;
+ break;
+ case F_JSONF:
+ pTpe->data.field.options.bJSONf = 1;
+ break;
+ }
+ switch(controlchr) {
+ case CC_NONE:
+ /* all set ;) */
+ break;
+ case CC_ESCAPE:
+ pTpe->data.field.options.bEscapeCC = 1;
+ break;
+ case CC_SPACE:
+ pTpe->data.field.options.bSpaceCC = 1;
+ break;
+ case CC_DROP:
+ pTpe->data.field.options.bDropCC = 1;
+ break;
+ }
+ switch(secpath) {
+ case SP_NONE:
+ /* all set ;) */
+ break;
+ case SP_DROP:
+ pTpe->data.field.options.bSecPathDrop = 1;
+ break;
+ case SP_REPLACE:
+ pTpe->data.field.options.bSecPathReplace = 1;
+ break;
+ }
+ pTpe->fieldName = ustrdup(outname);
+ if(outname != NULL)
+ pTpe->lenFieldName = ustrlen(outname);
+ pTpe->data.field.eDateFormat = datefmt;
+ if(fieldnum != -1) {
+ pTpe->data.field.has_fields = 1;
+ pTpe->data.field.iFieldNr = fieldnum;
+ pTpe->data.field.field_delim = fielddelim;
+ }
+ if(frompos != -1) {
+ pTpe->data.field.iFromPos = frompos;
+ pTpe->data.field.iToPos = topos;
+ }
+ if(re_expr != NULL) {
+ rsRetVal iRetLocal;
+ pTpe->data.field.typeRegex = re_type;
+ pTpe->data.field.nomatchAction = re_nomatchType;
+ pTpe->data.field.iMatchToUse = re_matchToUse;
+ pTpe->data.field.iSubMatchToUse = re_submatchToUse;
+ pTpe->data.field.has_regex = 1;
+ if((iRetLocal = objUse(regexp, LM_REGEXP_FILENAME)) == RS_RET_OK) {
+ int iOptions;
+ iOptions = (pTpe->data.field.typeRegex == TPL_REGEX_ERE) ? REG_EXTENDED : 0;
+ if(regexp.regcomp(&(pTpe->data.field.re), (char*) re_expr, iOptions) != 0) {
+ dbgprintf("error: can not compile regex: '%s'\n", re_expr);
+ errmsg.LogError(0, NO_ERRCODE, "error compiling regex '%s'", re_expr);
+ pTpe->data.field.has_regex = 2;
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ } else {
+ /* regexp object could not be loaded */
+ if(bFirstRegexpErrmsg) { /* prevent flood of messages, maybe even an endless loop! */
+ bFirstRegexpErrmsg = 0;
+ errmsg.LogError(0, NO_ERRCODE, "regexp library could not be loaded (error %d), "
+ "regexp ignored", iRetLocal);
+ }
+ pTpe->data.field.has_regex = 2;
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+/* create a template in list mode, is build from sub-objects */
+static rsRetVal
+createListTpl(struct template *pTpl, struct cnfobj *o)
+{
+ struct objlst *lst;
+ DEFiRet;
+
+ dbgprintf("create template from subobjs\n");
+ objlstPrint(o->subobjs);
+
+ for(lst = o->subobjs ; lst != NULL ; lst = lst->next) {
+ switch(lst->obj->objType) {
+ case CNFOBJ_PROPERTY:
+ CHKiRet(createPropertyTpe(pTpl, lst->obj));
+ break;
+ case CNFOBJ_CONSTANT:
+ CHKiRet(createConstantTpe(pTpl, lst->obj));
+ break;
+ default:dbgprintf("program error: invalid object type %d "
+ "in createLstTpl\n", lst->obj->objType);
+ break;
+ }
+ nvlstChkUnused(lst->obj->nvlst);
+ }
+finalize_it:
+ RETiRet;
+}
+
+/* Add a new template via the v6 config system. */
+rsRetVal
+tplProcessCnf(struct cnfobj *o)
+{
+ struct template *pTpl = NULL;
+ struct cnfparamvals *pvals;
+ int lenName;
+ char *name = NULL;
+ uchar *tplStr = NULL;
+ uchar *plugin = NULL;
+ uchar *p;
+ enum { T_STRING, T_PLUGIN, T_LIST } tplType;
+ int i;
+ int o_sql=0, o_stdsql=0, o_json=0; /* options */
+ int numopts;
+ rsRetVal localRet;
+ DEFiRet;
+
+ pvals = nvlstGetParams(o->nvlst, &pblk, NULL);
+ cnfparamsPrint(&pblk, pvals);
+
+ for(i = 0 ; i < pblk.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(pblk.descr[i].name, "name")) {
+ lenName = es_strlen(pvals[i].val.d.estr);
+ name = es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(pblk.descr[i].name, "type")) {
+ if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"string", sizeof("string")-1)) {
+ tplType = T_STRING;
+ } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"plugin", sizeof("plugin")-1)) {
+ tplType = T_PLUGIN;
+ } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"list", sizeof("list")-1)) {
+ tplType = T_LIST;
+ } else {
+ uchar *typeStr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL);
+ errmsg.LogError(0, RS_RET_ERR, "invalid template type '%s'",
+ typeStr);
+ free(typeStr);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ } else if(!strcmp(pblk.descr[i].name, "string")) {
+ tplStr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(pblk.descr[i].name, "plugin")) {
+ plugin = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(pblk.descr[i].name, "option.stdsql")) {
+ o_stdsql = pvals[i].val.d.n;
+ } else if(!strcmp(pblk.descr[i].name, "option.sql")) {
+ o_sql = pvals[i].val.d.n;
+ } else if(!strcmp(pblk.descr[i].name, "option.json")) {
+ o_json = pvals[i].val.d.n;
+ } else {
+ dbgprintf("template: program error, non-handled "
+ "param '%s'\n", pblk.descr[i].name);
+ }
+ }
+
+ /* do config sanity checks */
+ if(tplStr == NULL) {
+ if(tplType == T_STRING) {
+ errmsg.LogError(0, RS_RET_ERR, "template '%s' of type string needs "
+ "string parameter", name);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ } else {
+ if(tplType != T_STRING) {
+ errmsg.LogError(0, RS_RET_ERR, "template '%s' is not a string "
+ "template but has a string specified - ignored", name);
+ }
+ }
+
+ if(plugin == NULL) {
+ if(tplType == T_PLUGIN) {
+ errmsg.LogError(0, RS_RET_ERR, "template '%s' of type plugin needs "
+ "plugin parameter", name);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ } else {
+ if(tplType != T_PLUGIN) {
+ errmsg.LogError(0, RS_RET_ERR, "template '%s' is not a plugin "
+ "template but has a plugin specified - ignored", name);
+ }
+ }
+
+ if(o->subobjs == NULL) {
+ if(tplType == T_LIST) {
+ errmsg.LogError(0, RS_RET_ERR, "template '%s' of type list has "
+ "has no parameters specified", name);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ } else {
+ if(tplType != T_LIST) {
+ errmsg.LogError(0, RS_RET_ERR, "template '%s' is not a list "
+ "template but has parameters specified - ignored", name);
+ }
+ }
+
+ numopts = 0;
+ if(o_sql) ++numopts;
+ if(o_stdsql) ++numopts;
+ if(o_json) ++numopts;
+ if(numopts > 1) {
+ errmsg.LogError(0, RS_RET_ERR, "template '%s' has multiple incompatible "
+ "options of sql, stdsql or json specified", name);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+ /* config ok */
+ if((pTpl = tplConstruct(loadConf)) == NULL) {
+ DBGPRINTF("template.c: tplConstruct failed!\n");
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ pTpl->pszName = name;
+ pTpl->iLenName = lenName;
+
+ switch(tplType) {
+ case T_STRING: p = tplStr;
+ while(*p) {
+ switch(*p) {
+ case '%': /* parameter */
+ ++p; /* eat '%' */
+ do_Parameter(&p, pTpl);
+ break;
+ default: /* constant */
+ do_Constant(&p, pTpl);
+ break;
+ }
+ }
+ break;
+ case T_PLUGIN: p = plugin;
+ /* TODO: the use of tplAddTplMod() can be improved! */
+ localRet = tplAddTplMod(pTpl, &p);
+ if(localRet != RS_RET_OK) {
+ errmsg.LogError(0, localRet, "template '%s': error %d "
+ "defining template via plugin (strgen) module",
+ pTpl->pszName, localRet);
+ ABORT_FINALIZE(localRet);
+ }
+ break;
+ case T_LIST: createListTpl(pTpl, o);
+ break;
+ }
+
+ pTpl->optFormatEscape = NO_ESCAPE;
+ if(o_stdsql)
+ pTpl->optFormatEscape = STDSQL_ESCAPE;
+ else if(o_sql)
+ pTpl->optFormatEscape = SQL_ESCAPE;
+ else if(o_json)
+ pTpl->optFormatEscape = JSON_ESCAPE;
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pTpl != NULL) {
+ /* we simply make the template defunct in this case by setting
+ * its name to a zero-string. We do not free it, as this would
+ * require additional code and causes only a very small memory
+ * consumption. TODO: maybe in next iteration...
+ */
+ *pTpl->pszName = '\0';
+ }
+ }
+
+ RETiRet;
+}
+
/* Find a template object based on name. Search
* currently is case-senstive (should we change?).
@@ -1194,11 +1824,10 @@ void tplDeleteAll(rsconf_t *conf)
}
if(pTpeDel->data.field.propName != NULL)
es_deleteStr(pTpeDel->data.field.propName);
- if(pTpeDel->data.field.fieldName != NULL)
- es_deleteStr(pTpeDel->data.field.fieldName);
#endif
break;
}
+ free(pTpeDel->fieldName);
/*dbgprintf("\n");*/
free(pTpeDel);
}
@@ -1369,6 +1998,9 @@ void tplPrintList(rsconf_t *conf)
if(pTpe->data.field.options.bJSONf) {
dbgprintf("[format as JSON field] ");
}
+ if(pTpe->data.field.options.bMandatory) {
+ dbgprintf("[mandatory field] ");
+ }
if(pTpe->data.field.options.bDropLastLF) {
dbgprintf("[drop last LF in msg] ");
}
diff --git a/template.h b/template.h
index 76a42ea0..e30f96b4 100644
--- a/template.h
+++ b/template.h
@@ -30,6 +30,7 @@
#ifndef TEMPLATE_H_INCLUDED
#define TEMPLATE_H_INCLUDED 1
+#include <json/json.h>
#include <libestr.h>
#include "regexp.h"
#include "stringbuf.h"
@@ -58,6 +59,9 @@ enum tplFormatTypes { tplFmtDefault = 0, tplFmtMySQLDate = 1,
tplFmtRFC3164Date = 2, tplFmtRFC3339Date = 3, tplFmtPgSQLDate = 4,
tplFmtSecFrac = 5, tplFmtRFC3164BuggyDate = 6, tplFmtUnixDate};
enum tplFormatCaseConvTypes { tplCaseConvNo = 0, tplCaseConvUpper = 1, tplCaseConvLower = 2 };
+enum tplRegexType { TPL_REGEX_BRE = 0, /* posix BRE */
+ TPL_REGEX_ERE = 1 /* posix ERE */
+ };
#include "msg.h"
@@ -65,6 +69,8 @@ enum tplFormatCaseConvTypes { tplCaseConvNo = 0, tplCaseConvUpper = 1, tplCaseCo
struct templateEntry {
struct templateEntry *pNext;
enum EntryTypes eEntryType;
+ uchar *fieldName; /**< field name to be used for structured output */
+ int lenFieldName;
union {
struct {
uchar *pConstant; /* pointer to constant value */
@@ -80,11 +86,8 @@ struct templateEntry {
short has_regex;
short iMatchToUse;/* which match should be obtained (10 max) */
short iSubMatchToUse;/* which submatch should be obtained (10 max) */
- enum {
- TPL_REGEX_BRE = 0, /* posix BRE */
- TPL_REGEX_ERE = 1 /* posix ERE */
- } typeRegex;
- enum {
+ enum tplRegexType typeRegex;
+ enum tlpRegexNoMatchType {
TPL_REGEX_NOMATCH_USE_DFLTSTR = 0, /* use the (old style) default "**NO MATCH**" string */
TPL_REGEX_NOMATCH_USE_BLANK = 1, /* use a blank string */
TPL_REGEX_NOMATCH_USE_WHOLE_FIELD = 2, /* use the full field contents that we were searching in*/
@@ -99,7 +102,6 @@ struct templateEntry {
#endif
es_str_t *propName; /**< property name (currently being used for CEE only) */
- es_str_t *fieldName; /**< field name to be used for structured output */
enum tplFormatTypes eDateFormat;
enum tplFormatCaseConvTypes eCaseConv;
@@ -114,6 +116,7 @@ struct templateEntry {
unsigned bCSV: 1; /* format field in CSV (RFC 4180) format */
unsigned bJSON: 1; /* format field JSON escaped */
unsigned bJSONf: 1; /* format field JSON *field* (n/v pair) */
+ unsigned bMandatory: 1; /* mandatory field - emit even if empty */
} options; /* options as bit fields */
} field;
} data;
@@ -145,9 +148,11 @@ rsRetVal ExtendBuf(uchar **pBuf, size_t *pLenBuf, size_t iMinSize);
*/
rsRetVal tplToArray(struct template *pTpl, msg_t *pMsg, uchar*** ppArr);
rsRetVal tplToString(struct template *pTpl, msg_t *pMsg, uchar** ppSz, size_t *);
+rsRetVal tplToJSON(struct template *pTpl, msg_t *pMsg, struct json_object **);
rsRetVal doEscape(uchar **pp, size_t *pLen, unsigned short *pbMustBeFreed, int escapeMode);
rsRetVal templateInit();
+rsRetVal tplProcessCnf(struct cnfobj *o);
#endif /* #ifndef TEMPLATE_H_INCLUDED */
/* vim:set ai:
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 4e457426..60a2dd61 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -40,7 +40,7 @@ rsyslogd_CPPFLAGS = $(PTHREADS_CFLAGS) $(RSRT_CFLAGS) $(CNF_LIBS)
# note: it looks like librsyslog.la must be explicitely given on LDDADD,
# otherwise dependencies are not properly calculated (resulting in a
# potentially incomplete build, a problem we had several times...)
-rsyslogd_LDADD = $(ZLIB_LIBS) $(PTHREADS_LIBS) $(RSRT_LIBS) $(SOL_LIBS) $(LIBEE_LIBS) $(LIBLOGNORM_LIBS) $(CNF_LIBS) ../grammar/libgrammar.la ../runtime/librsyslog.la
+rsyslogd_LDADD = $(ZLIB_LIBS) $(PTHREADS_LIBS) $(RSRT_LIBS) $(SOL_LIBS) $(LIBEE_LIBS) $(LIBLOGNORM_LIBS) $(CNF_LIBS) $(LIBUUID_LIBS) ../grammar/libgrammar.la ../runtime/librsyslog.la
rsyslogd_LDFLAGS = -export-dynamic
if ENABLE_DIAGTOOLS
diff --git a/tools/omfile.c b/tools/omfile.c
index 90b452bf..1a36343f 100644
--- a/tools/omfile.c
+++ b/tools/omfile.c
@@ -121,7 +121,7 @@ struct s_dynaFileCacheEntry {
typedef struct s_dynaFileCacheEntry dynaFileCacheEntry;
-#define IOBUF_DFLT_SIZE 1024 /* default size for io buffers */
+#define IOBUF_DFLT_SIZE 4096 /* default size for io buffers */
#define FLUSH_INTRVL_DFLT 1 /* default buffer flush interval (in seconds) */
#define USE_ASYNCWRITER_DFLT 0 /* default buffer use async writer */
#define FLUSHONTX_DFLT 1 /* default for flush on TX end */
@@ -180,7 +180,25 @@ typedef struct configSettings_s {
static configSettings_t cs;
uchar *pszFileDfltTplName; /* name of the default template to use */
+struct modConfData_s {
+ rsconf_t *pConf; /* our overall config object */
+ uchar *tplName; /* default template */
+};
+
+static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
+static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current exec process */
+
/* tables for interfacing with the v6 config system */
+/* module-global parameters */
+static struct cnfparamdescr modpdescr[] = {
+ { "template", eCmdHdlrGetWord, 0 },
+};
+static struct cnfparamblk modpblk =
+ { CNFPARAMBLK_VERSION,
+ sizeof(modpdescr)/sizeof(struct cnfparamdescr),
+ modpdescr
+ };
+
/* action (instance) parameters */
static struct cnfparamdescr actpdescr[] = {
{ "dynafilecachesize", eCmdHdlrInt, 0 }, /* legacy: dynafilecachesize */
@@ -201,7 +219,6 @@ static struct cnfparamdescr actpdescr[] = {
{ "file", eCmdHdlrString, 0 }, /* either "file" or ... */
{ "dynafile", eCmdHdlrString, 0 }, /* "dynafile" MUST be present */
{ "template", eCmdHdlrGetWord, 0 },
- //{ "", eCmdHdlrGetWord, 0 }, /* legacy: */
};
static struct cnfparamblk actpblk =
{ CNFPARAMBLK_VERSION,
@@ -210,6 +227,21 @@ static struct cnfparamblk actpblk =
};
+/* this function gets the default template. It coordinates action between
+ * old-style and new-style configuration parts.
+ */
+static inline uchar*
+getDfltTpl(void)
+{
+ if(loadModConf != NULL && loadModConf->tplName != NULL)
+ return loadModConf->tplName;
+ else if(pszFileDfltTplName == NULL)
+ return (uchar*)"RSYSLOG_FileFormat";
+ else
+ return pszFileDfltTplName;
+}
+
+
BEGINinitConfVars /* (re)set config variables to default values */
CODESTARTinitConfVars
pszFileDfltTplName = NULL; /* make sure this can be free'ed! */
@@ -246,6 +278,31 @@ CODESTARTdbgPrintInstInfo
ENDdbgPrintInstInfo
+
+/* set the default template to be used
+ * This is a module-global parameter, and as such needs special handling. It needs to
+ * be coordinated with values set via the v2 config system (rsyslog v6+). What we do
+ * is we do not permit this directive after the v2 config system has been used to set
+ * the parameter.
+ */
+rsRetVal
+setLegacyDfltTpl(void __attribute__((unused)) *pVal, uchar* newVal)
+{
+ DEFiRet;
+
+ if(loadModConf != NULL && loadModConf->tplName != NULL) {
+ free(newVal);
+ errmsg.LogError(0, RS_RET_ERR, "omfile default template already set via module "
+ "global parameter - can no longer be changed");
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ free(pszFileDfltTplName);
+ pszFileDfltTplName = newVal;
+finalize_it:
+ RETiRet;
+}
+
+
/* set the dynaFile cache size. Does some limit checking.
* rgerhards, 2007-07-31
*/
@@ -333,8 +390,7 @@ static rsRetVal cflineParseOutchannel(instanceData *pData, uchar* p, omodStringR
*/
pData->pszSizeLimitCmd = pOch->cmdOnSizeLimit;
- iRet = cflineParseTemplateName(&p, pOMSR, iEntry, iTplOpts,
- (pszFileDfltTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszFileDfltTplName);
+ iRet = cflineParseTemplateName(&p, pOMSR, iEntry, iTplOpts, getDfltTpl());
finalize_it:
RETiRet;
@@ -647,7 +703,7 @@ doWrite(instanceData *pData, uchar *pszBuf, int lenBuf)
ASSERT(pData != NULL);
ASSERT(pszBuf != NULL);
-dbgprintf("write to stream, pData->pStrm %p, lenBuf %d\n", pData->pStrm, lenBuf);
+ DBGPRINTF("write to stream, pData->pStrm %p, lenBuf %d\n", pData->pStrm, lenBuf);
if(pData->pStrm != NULL){
CHKiRet(strm.Write(pData->pStrm, pszBuf, lenBuf));
FINALIZE;
@@ -690,6 +746,72 @@ finalize_it:
}
+BEGINbeginCnfLoad
+CODESTARTbeginCnfLoad
+ loadModConf = pModConf;
+ pModConf->pConf = pConf;
+ pModConf->tplName = NULL;
+ENDbeginCnfLoad
+
+BEGINsetModCnf
+ struct cnfparamvals *pvals = NULL;
+ int i;
+CODESTARTsetModCnf
+ pvals = nvlstGetParams(lst, &modpblk, NULL);
+ if(pvals == NULL) {
+ errmsg.LogError(0, RS_RET_MISSING_CNFPARAMS, "error processing module "
+ "config parameters [module(...)]");
+ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+ }
+
+ if(Debug) {
+ dbgprintf("module (global) param blk for omfile:\n");
+ cnfparamsPrint(&modpblk, pvals);
+ }
+
+ for(i = 0 ; i < modpblk.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(modpblk.descr[i].name, "template")) {
+ loadModConf->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ if(pszFileDfltTplName != NULL) {
+ errmsg.LogError(0, RS_RET_DUP_PARAM, "omfile: warning: default template "
+ "was already set via legacy directive - may lead to inconsistent "
+ "results.");
+ }
+ } else {
+ dbgprintf("omfile: program error, non-handled "
+ "param '%s' in beginCnfLoad\n", modpblk.descr[i].name);
+ }
+ }
+finalize_it:
+ if(pvals != NULL)
+ cnfparamvalsDestruct(pvals, &modpblk);
+ENDsetModCnf
+
+BEGINendCnfLoad
+CODESTARTendCnfLoad
+ loadModConf = NULL; /* done loading */
+ /* free legacy config vars */
+ free(pszFileDfltTplName);
+ pszFileDfltTplName = NULL;
+ENDendCnfLoad
+
+BEGINcheckCnf
+CODESTARTcheckCnf
+ENDcheckCnf
+
+BEGINactivateCnf
+CODESTARTactivateCnf
+ runModConf = pModConf;
+ENDactivateCnf
+
+BEGINfreeCnf
+CODESTARTfreeCnf
+ free(pModConf->tplName);
+ENDfreeCnf
+
+
BEGINcreateInstance
CODESTARTcreateInstance
pData->pStrm = NULL;
@@ -837,9 +959,7 @@ CODESTARTnewActInst
ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
}
- CHKiRet(OMSRsetEntry(*ppOMSR, 0, ustrdup((pData->tplName == NULL) ?
- (uchar*)"RSYSLOG_FileFormat" : pData->tplName),
- OMSR_NO_RQD_TPL_OPTS));
+ CHKiRet(OMSRsetEntry(*ppOMSR, 0, ustrdup(getDfltTpl()), OMSR_NO_RQD_TPL_OPTS));
if(pData->bDynamicName) {
/* "filename" is actually a template name, we need this as string 1. So let's add it
@@ -902,8 +1022,7 @@ CODESTARTparseSelectorAct
*/
CODE_STD_STRING_REQUESTparseSelectorAct(2)
++p; /* eat '?' */
- CHKiRet(cflineParseFileName(p, fname, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
- (pszFileDfltTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszFileDfltTplName));
+ CHKiRet(cflineParseFileName(p, fname, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, getDfltTpl()));
pData->f_fname = ustrdup(fname);
pData->bDynamicName = 1;
pData->iCurrElt = -1; /* no current element */
@@ -919,8 +1038,7 @@ CODESTARTparseSelectorAct
case '/':
case '.':
CODE_STD_STRING_REQUESTparseSelectorAct(1)
- CHKiRet(cflineParseFileName(p, fname, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
- (pszFileDfltTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszFileDfltTplName));
+ CHKiRet(cflineParseFileName(p, fname, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, getDfltTpl()));
pData->f_fname = ustrdup(fname);
pData->bDynamicName = 0;
break;
@@ -991,7 +1109,6 @@ BEGINmodExit
CODESTARTmodExit
objRelease(errmsg, CORE_COMPONENT);
objRelease(strm, CORE_COMPONENT);
- free(pszFileDfltTplName);
DESTROY_ATOMIC_HELPER_MUT(mutClock);
ENDmodExit
@@ -999,6 +1116,8 @@ ENDmodExit
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_OMOD_QUERIES
+CODEqueryEtryPt_STD_CONF2_QUERIES
+CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES
CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES
CODEqueryEtryPt_TXIF_OMOD_QUERIES /* we support the transactional interface! */
CODEqueryEtryPt_doHUP
@@ -1033,7 +1152,7 @@ INITLegCnfVars
CHKiRet(omsdRegCFSLineHdlr((uchar *)"failonchownfailure", 0, eCmdHdlrBinary, NULL, &cs.bFailOnChown, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileforcechown", 0, eCmdHdlrGoneAway, NULL, NULL, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionfileenablesync", 0, eCmdHdlrBinary, NULL, &cs.bEnableSync, STD_LOADABLE_MODULE_ID));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionfiledefaulttemplate", 0, eCmdHdlrGetWord, NULL, &pszFileDfltTplName, NULL));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionfiledefaulttemplate", 0, eCmdHdlrGetWord, setLegacyDfltTpl, NULL, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
/* vi:set ai:
diff --git a/tools/omfwd.c b/tools/omfwd.c
index b4a28c34..2fd24bdf 100644
--- a/tools/omfwd.c
+++ b/tools/omfwd.c
@@ -115,6 +115,16 @@ typedef struct configSettings_s {
static configSettings_t cs;
/* tables for interfacing with the v6 config system */
+/* module-global parameters */
+static struct cnfparamdescr modpdescr[] = {
+ { "template", eCmdHdlrGetWord, 0 },
+};
+static struct cnfparamblk modpblk =
+ { CNFPARAMBLK_VERSION,
+ sizeof(modpdescr)/sizeof(struct cnfparamdescr),
+ modpdescr
+ };
+
/* action (instance) parameters */
static struct cnfparamdescr actpdescr[] = {
{ "target", eCmdHdlrGetWord, 0 },
@@ -135,6 +145,14 @@ static struct cnfparamblk actpblk =
actpdescr
};
+struct modConfData_s {
+ rsconf_t *pConf; /* our overall config object */
+ uchar *tplName; /* default template */
+};
+
+static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
+static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current exec process */
+
BEGINinitConfVars /* (re)set config variables to default values */
CODESTARTinitConfVars
@@ -151,6 +169,44 @@ ENDinitConfVars
static rsRetVal doTryResume(instanceData *pData);
+/* this function gets the default template. It coordinates action between
+ * old-style and new-style configuration parts.
+ */
+static inline uchar*
+getDfltTpl(void)
+{
+ if(loadModConf != NULL && loadModConf->tplName != NULL)
+ return loadModConf->tplName;
+ else if(cs.pszTplName == NULL)
+ return (uchar*)"RSYSLOG_TraditionalForwardFormat";
+ else
+ return cs.pszTplName;
+}
+
+
+/* set the default template to be used
+ * This is a module-global parameter, and as such needs special handling. It needs to
+ * be coordinated with values set via the v2 config system (rsyslog v6+). What we do
+ * is we do not permit this directive after the v2 config system has been used to set
+ * the parameter.
+ */
+static rsRetVal
+setLegacyDfltTpl(void __attribute__((unused)) *pVal, uchar* newVal)
+{
+ DEFiRet;
+
+ if(loadModConf != NULL && loadModConf->tplName != NULL) {
+ free(newVal);
+ errmsg.LogError(0, RS_RET_ERR, "omfwd default template already set via module "
+ "global parameter - can no longer be changed");
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ free(cs.pszTplName);
+ cs.pszTplName = newVal;
+finalize_it:
+ RETiRet;
+}
+
/* Close the UDP sockets.
* rgerhards, 2009-05-29
*/
@@ -189,6 +245,72 @@ DestructTCPInstanceData(instanceData *pData)
netstrms.Destruct(&pData->pNS);
}
+
+BEGINbeginCnfLoad
+CODESTARTbeginCnfLoad
+ loadModConf = pModConf;
+ pModConf->pConf = pConf;
+ pModConf->tplName = NULL;
+ENDbeginCnfLoad
+
+BEGINsetModCnf
+ struct cnfparamvals *pvals = NULL;
+ int i;
+CODESTARTsetModCnf
+ pvals = nvlstGetParams(lst, &modpblk, NULL);
+ if(pvals == NULL) {
+ errmsg.LogError(0, RS_RET_MISSING_CNFPARAMS, "error processing module "
+ "config parameters [module(...)]");
+ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+ }
+
+ if(Debug) {
+ dbgprintf("module (global) param blk for omfwd:\n");
+ cnfparamsPrint(&modpblk, pvals);
+ }
+
+ for(i = 0 ; i < modpblk.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(modpblk.descr[i].name, "template")) {
+ loadModConf->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ if(cs.pszTplName != NULL) {
+ errmsg.LogError(0, RS_RET_DUP_PARAM, "omfwd: warning: default template "
+ "was already set via legacy directive - may lead to inconsistent "
+ "results.");
+ }
+ } else {
+ dbgprintf("omfwd: program error, non-handled "
+ "param '%s' in beginCnfLoad\n", modpblk.descr[i].name);
+ }
+ }
+finalize_it:
+ if(pvals != NULL)
+ cnfparamvalsDestruct(pvals, &modpblk);
+ENDsetModCnf
+
+BEGINendCnfLoad
+CODESTARTendCnfLoad
+ loadModConf = NULL; /* done loading */
+ /* free legacy config vars */
+ free(cs.pszTplName);
+ cs.pszTplName = NULL;
+ENDendCnfLoad
+
+BEGINcheckCnf
+CODESTARTcheckCnf
+ENDcheckCnf
+
+BEGINactivateCnf
+CODESTARTactivateCnf
+ runModConf = pModConf;
+ENDactivateCnf
+
+BEGINfreeCnf
+CODESTARTfreeCnf
+ free(pModConf->tplName);
+ENDfreeCnf
+
BEGINcreateInstance
CODESTARTcreateInstance
pData->offsSndBuf = 0;
@@ -759,9 +881,7 @@ CODESTARTnewActInst
}
CODE_STD_STRING_REQUESTnewActInst(1)
- CHKiRet(OMSRsetEntry(*ppOMSR, 0, ustrdup((pData->tplName == NULL) ?
- (uchar*)"RSYSLOG_TraditionalForwardFormat" : (uchar*)pData->tplName),
- OMSR_NO_RQD_TPL_OPTS));
+ CHKiRet(OMSRsetEntry(*ppOMSR, 0, ustrdup(getDfltTpl()), OMSR_NO_RQD_TPL_OPTS));
CHKiRet(initTCP(pData));
CODE_STD_FINALIZERnewActInst
@@ -917,8 +1037,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
cs.iTCPRebindInterval : cs.iUDPRebindInterval;
/* process template */
- CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
- (cs.pszTplName == NULL) ? (uchar*)"RSYSLOG_TraditionalForwardFormat" : cs.pszTplName));
+ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, getDfltTpl()));
if(pData->protocol == FORW_TCP) {
pData->bResendLastOnRecon = cs.bResendLastOnRecon;
@@ -944,8 +1063,6 @@ ENDparseSelectorAct
static void
freeConfigVars(void)
{
- free(cs.pszTplName);
- cs.pszTplName = NULL;
free(cs.pszStrmDrvr);
cs.pszStrmDrvr = NULL;
free(cs.pszStrmDrvrAuthMode);
@@ -971,6 +1088,8 @@ ENDmodExit
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_OMOD_QUERIES
+CODEqueryEtryPt_STD_CONF2_QUERIES
+CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES
CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES
CODEqueryEtryPt_TXIF_OMOD_QUERIES /* we support the transactional interface! */
ENDqueryEtryPt
@@ -1002,7 +1121,7 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(errmsg, CORE_COMPONENT));
CHKiRet(objUse(net,LM_NET_FILENAME));
- CHKiRet(regCfSysLineHdlr((uchar *)"actionforwarddefaulttemplate", 0, eCmdHdlrGetWord, NULL, &cs.pszTplName, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionforwarddefaulttemplate", 0, eCmdHdlrGetWord, setLegacyDfltTpl, NULL, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionsendtcprebindinterval", 0, eCmdHdlrInt, NULL, &cs.iTCPRebindInterval, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionsendudprebindinterval", 0, eCmdHdlrInt, NULL, &cs.iUDPRebindInterval, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionsendstreamdriver", 0, eCmdHdlrGetWord, NULL, &cs.pszStrmDrvr, NULL));
diff --git a/tools/ompipe.c b/tools/ompipe.c
index 30cb9bfc..d293114f 100644
--- a/tools/ompipe.c
+++ b/tools/ompipe.c
@@ -65,10 +65,6 @@ DEF_OMOD_STATIC_DATA
DEFobjCurrIf(errmsg)
-/* globals for default values */
-/* end globals for default values */
-
-
typedef struct _instanceData {
uchar *pipe; /* pipe or template name (display only) */
uchar *tplName; /* format template to use */
@@ -82,6 +78,16 @@ typedef struct configSettings_s {
static configSettings_t __attribute__((unused)) cs;
/* tables for interfacing with the v6 config system */
+/* module-global parameters */
+static struct cnfparamdescr modpdescr[] = {
+ { "template", eCmdHdlrGetWord, 0 },
+};
+static struct cnfparamblk modpblk =
+ { CNFPARAMBLK_VERSION,
+ sizeof(modpdescr)/sizeof(struct cnfparamdescr),
+ modpdescr
+ };
+
/* action (instance) parameters */
static struct cnfparamdescr actpdescr[] = {
{ "pipe", eCmdHdlrString, CNFPARAM_REQUIRED },
@@ -93,6 +99,25 @@ static struct cnfparamblk actpblk =
actpdescr
};
+struct modConfData_s {
+ rsconf_t *pConf; /* our overall config object */
+ uchar *tplName; /* default template */
+};
+
+static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
+static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current exec process */
+
+/* this function gets the default template */
+static inline uchar*
+getDfltTpl(void)
+{
+ if(loadModConf != NULL && loadModConf->tplName != NULL)
+ return loadModConf->tplName;
+ else
+ return (uchar*)"RSYSLOG_FileFormat";
+}
+
+
BEGINinitConfVars /* (re)set config variables to default values */
CODESTARTinitConfVars
ENDinitConfVars
@@ -181,6 +206,71 @@ finalize_it:
}
+BEGINbeginCnfLoad
+CODESTARTbeginCnfLoad
+ loadModConf = pModConf;
+ pModConf->pConf = pConf;
+ pModConf->tplName = NULL;
+ENDbeginCnfLoad
+
+BEGINsetModCnf
+ struct cnfparamvals *pvals = NULL;
+ int i;
+CODESTARTsetModCnf
+ pvals = nvlstGetParams(lst, &modpblk, NULL);
+ if(pvals == NULL) {
+ errmsg.LogError(0, RS_RET_MISSING_CNFPARAMS, "error processing module "
+ "config parameters [module(...)]");
+ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+ }
+
+ if(Debug) {
+ dbgprintf("module (global) param blk for ompipe:\n");
+ cnfparamsPrint(&modpblk, pvals);
+ }
+
+ for(i = 0 ; i < modpblk.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(modpblk.descr[i].name, "template")) {
+ loadModConf->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ if(pszFileDfltTplName != NULL) {
+ errmsg.LogError(0, RS_RET_DUP_PARAM, "ompipe: warning: default template "
+ "was already set via legacy directive - may lead to inconsistent "
+ "results.");
+ }
+ } else {
+ dbgprintf("ompipe: program error, non-handled "
+ "param '%s' in beginCnfLoad\n", modpblk.descr[i].name);
+ }
+ }
+finalize_it:
+ if(pvals != NULL)
+ cnfparamvalsDestruct(pvals, &modpblk);
+ENDsetModCnf
+
+BEGINendCnfLoad
+CODESTARTendCnfLoad
+ loadModConf = NULL; /* done loading */
+ /* free legacy config vars */
+ free(pszFileDfltTplName);
+ pszFileDfltTplName = NULL;
+ENDendCnfLoad
+
+BEGINcheckCnf
+CODESTARTcheckCnf
+ENDcheckCnf
+
+BEGINactivateCnf
+CODESTARTactivateCnf
+ runModConf = pModConf;
+ENDactivateCnf
+
+BEGINfreeCnf
+CODESTARTfreeCnf
+ free(pModConf->tplName);
+ENDfreeCnf
+
BEGINcreateInstance
CODESTARTcreateInstance
pData->pipe = NULL;
@@ -273,7 +363,7 @@ CODESTARTparseSelectorAct
CHKmalloc(pData->pipe = malloc(512));
++p;
CHKiRet(cflineParseFileName(p, (uchar*) pData->pipe, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
- (pszFileDfltTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszFileDfltTplName));
+ getDfltTpl()));
CODE_STD_FINALIZERparseSelectorAct
ENDparseSelectorAct
@@ -297,7 +387,9 @@ BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_OMOD_QUERIES
CODEqueryEtryPt_doHUP
+CODEqueryEtryPt_STD_CONF2_QUERIES
CODEqueryEtryPt_STD_CONF2_CNFNAME_QUERIES
+CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES
CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES
ENDqueryEtryPt
diff --git a/tools/pidfile.c b/tools/pidfile.c
index e7744513..e9601232 100644
--- a/tools/pidfile.c
+++ b/tools/pidfile.c
@@ -125,7 +125,7 @@ int write_pid (char *pidfile)
char errStr[1024];
rs_strerror_r(errno, errStr, sizeof(errStr));
printf("Can't write pid , %s.\n", errStr);
- close(fd);
+ fclose(f);
return 0;
}
fflush(f);
@@ -135,11 +135,11 @@ int write_pid (char *pidfile)
char errStr[1024];
rs_strerror_r(errno, errStr, sizeof(errStr));
printf("Can't unlock pidfile %s, %s.\n", pidfile, errStr);
- close(fd);
+ fclose(f);
return 0;
}
#endif
- close(fd);
+ fclose(f);
return pid;
}
diff --git a/tools/pmrfc3164.c b/tools/pmrfc3164.c
index 2657780d..bcded428 100644
--- a/tools/pmrfc3164.c
+++ b/tools/pmrfc3164.c
@@ -79,7 +79,7 @@ BEGINparse
uchar bufParseTAG[CONF_TAG_MAXSIZE];
uchar bufParseHOSTNAME[CONF_HOSTNAME_MAXSIZE];
CODESTARTparse
- dbgprintf("Message will now be parsed by the legacy syslog parser (one size fits all... ;)).\n");
+ DBGPRINTF("Message will now be parsed by the legacy syslog parser (one size fits all... ;)).\n");
assert(pMsg != NULL);
assert(pMsg->pszRawMsg != NULL);
lenMsg = pMsg->iLenRawMsg - pMsg->offAfterPRI; /* note: offAfterPRI is already the number of PRI chars (do not add one!) */
@@ -229,7 +229,7 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(parser, CORE_COMPONENT));
CHKiRet(objUse(datetime, CORE_COMPONENT));
- dbgprintf("rfc3164 parser init called\n");
+ DBGPRINTF("rfc3164 parser init called\n");
bParseHOSTNAMEandTAG = glbl.GetParseHOSTNAMEandTAG(); /* cache value, is set only during rsyslogd option processing */
diff --git a/tools/syslogd.c b/tools/syslogd.c
index 81150677..219b41ab 100644
--- a/tools/syslogd.c
+++ b/tools/syslogd.c
@@ -125,6 +125,7 @@
#include "rsconf.h"
#include "dnscache.h"
#include "sd-daemon.h"
+#include "rainerscript.h"
/* definitions for objects we access */
DEFobjCurrIf(obj)
@@ -453,7 +454,6 @@ logmsgInternal(int iErr, int pri, uchar *msg, int flags)
MsgSetRawMsgWOSize(pMsg, (char*)msg);
MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName(), ustrlen(glbl.GetLocalHostName()));
MsgSetRcvFrom(pMsg, glbl.GetLocalHostNameProp());
-dbgprintf("ZZZZ: pLocalHostIPIF used!\n");
MsgSetRcvFromIP(pMsg, glbl.GetLocalHostIP());
MsgSetMSGoffs(pMsg, 0);
/* check if we have an error code associated and, if so,
@@ -807,26 +807,6 @@ static void doDie(int sig)
}
-/* This function frees all dynamically allocated memory for program termination.
- * It must be called only immediately before exit(). It is primarily an aid
- * for memory debuggers, which prevents cluttered outupt.
- * rgerhards, 2008-03-20
- */
-static void
-freeAllDynMemForTermination(void)
-{
- free(ourConf->globals.pszConfDAGFile);
- struct queuefilenames_s *qfn, *qfnDel;
-
- for(qfn = queuefilenames ; qfn != NULL ; ) {
- qfnDel = qfn;
- qfn = qfn->next;
- free(qfnDel->name);
- free(qfnDel);
- }
-}
-
-
/* Finalize and destruct all actions.
*/
static inline void
@@ -896,14 +876,16 @@ die(int sig)
destructAllActions();
DBGPRINTF("all primary multi-thread sources have been terminated - now doing aux cleanup...\n");
+
+ DBGPRINTF("destructing current config...\n");
+ rsconf.Destruct(&runConf);
+
/* rger 2005-02-22
* now clean up the in-memory structures. OK, the OS
* would also take care of that, but if we do it
* ourselfs, this makes finding memory leaks a lot
* easier.
*/
- tplDeleteAll(runConf);
-
/* de-init some modules */
modExitIminternal();
@@ -927,15 +909,8 @@ die(int sig)
/* dbgClassExit MUST be the last one, because it de-inits the debug system */
dbgClassExit();
- /* free all remaining memory blocks - this is not absolutely necessary, but helps
- * us keep memory debugger logs clean and this is in aid in developing. It doesn't
- * cost much time, so we do it always. -- rgerhards, 2008-03-20
- */
- freeAllDynMemForTermination();
- /* NO CODE HERE - feeelAllDynMemForTermination() must be the last thing before exit()! */
-
+ /* NO CODE HERE - dbgClassExit() must be the last thing before exit()! */
remove_pid(PidFile);
-
exit(0); /* "good" exit, this is the terminator function for rsyslog [die()] */
}
@@ -1501,6 +1476,7 @@ InitGlobalClasses(void)
pErrObj = "net";
CHKiRet(objUse(net, LM_NET_FILENAME));
dnscacheInit();
+ initRainerscript();
finalize_it:
if(iRet != RS_RET_OK) {