summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile.am92
-rw-r--r--tools/gethostn.c47
-rwxr-xr-xtools/gnutls/cert-gen-selfsigned6
-rwxr-xr-xtools/gnutls/cert-show-fingerprint6
-rw-r--r--tools/iminternal.c183
-rw-r--r--tools/iminternal.h47
-rw-r--r--tools/logctl.c456
-rw-r--r--tools/logsigner.c159
-rw-r--r--tools/msggen.c39
-rw-r--r--tools/omdiscard.c128
-rw-r--r--tools/omdiscard.h32
-rw-r--r--tools/omfile.c1392
-rw-r--r--tools/omfile.h37
-rw-r--r--tools/omfwd.c1267
-rw-r--r--tools/omfwd.h33
-rw-r--r--tools/ompipe.c405
-rw-r--r--tools/ompipe.h30
-rw-r--r--tools/omshell.c154
-rw-r--r--tools/omshell.h33
-rw-r--r--tools/omusrmsg.c451
-rw-r--r--tools/omusrmsg.h32
-rw-r--r--tools/pidfile.c158
-rw-r--r--tools/pidfile.h51
-rw-r--r--tools/pmrfc3164.c239
-rw-r--r--tools/pmrfc3164.h33
-rw-r--r--tools/pmrfc5424.c329
-rw-r--r--tools/pmrfc5424.h33
-rwxr-xr-xtools/recover_qi.pl207
-rw-r--r--tools/regexp.c73
-rw-r--r--tools/rscryutil.c512
-rw-r--r--tools/rscryutil.rst199
-rw-r--r--tools/rsgtutil.c431
-rw-r--r--tools/rsgtutil.rst177
-rw-r--r--tools/rsyslog.conf.5771
-rw-r--r--tools/rsyslogd.8401
-rw-r--r--tools/smfile.c136
-rw-r--r--tools/smfile.h31
-rw-r--r--tools/smfwd.c143
-rw-r--r--tools/smfwd.h30
-rw-r--r--tools/smtradfile.c129
-rw-r--r--tools/smtradfile.h31
-rw-r--r--tools/smtradfwd.c140
-rw-r--r--tools/smtradfwd.h30
-rw-r--r--tools/syncdemo.c440
-rw-r--r--tools/syslogd.c2079
-rw-r--r--tools/syslogd.h38
-rw-r--r--tools/zpipe.c262
47 files changed, 12132 insertions, 0 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am
new file mode 100644
index 00000000..6832494e
--- /dev/null
+++ b/tools/Makefile.am
@@ -0,0 +1,92 @@
+sbin_PROGRAMS =
+bin_PROGRAMS =
+CLEANFILES =
+man1_MANS =
+man_MANS = rsyslogd.8 rsyslog.conf.5
+
+sbin_PROGRAMS += rsyslogd
+rsyslogd_SOURCES = \
+ syslogd.c \
+ syslogd.h \
+ omshell.c \
+ omshell.h \
+ omusrmsg.c \
+ omusrmsg.h \
+ omfwd.c \
+ omfwd.h \
+ omfile.c \
+ omfile.h \
+ ompipe.c \
+ ompipe.h \
+ omdiscard.c \
+ omdiscard.h \
+ pmrfc5424.c \
+ pmrfc5424.h \
+ pmrfc3164.c \
+ pmrfc3164.h \
+ smtradfile.c \
+ smtradfile.h \
+ smfile.c \
+ smfile.h \
+ smfwd.c \
+ smfwd.h \
+ smtradfwd.c \
+ smtradfwd.h \
+ iminternal.c \
+ iminternal.h \
+ pidfile.c \
+ pidfile.h \
+ \
+ ../dirty.h
+rsyslogd_CPPFLAGS = $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
+# 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 = ../grammar/libgrammar.la ../runtime/librsyslog.la $(ZLIB_LIBS) $(PTHREADS_LIBS) $(RSRT_LIBS) $(SOL_LIBS) $(LIBUUID_LIBS)
+rsyslogd_LDFLAGS = -export-dynamic
+
+EXTRA_DIST = $(man_MANS) \
+ rsgtutil.rst \
+ rsgtutil.1 \
+ rscryutil.rst \
+ rscryutil.1 \
+ recover_qi.pl
+
+if ENABLE_DIAGTOOLS
+sbin_PROGRAMS += rsyslog_diag_hostname msggen zpipe
+rsyslog_diag_hostname_SOURCES = gethostn.c
+zpipe_SOURCES = zpipe.c
+zpipe_LDADD = -lz
+msggen_SOURCES = msggen.c
+endif
+
+if ENABLE_USERTOOLS
+if ENABLE_OMMONGODB
+bin_PROGRAMS += logctl
+logctl_SOURCES = logctl.c
+logctl_CPPFLAGS = $(LIBMONGO_CLIENT_CFLAGS)
+logctl_LDADD = $(LIBMONGO_CLIENT_LIBS)
+endif
+if ENABLE_GUARDTIME
+bin_PROGRAMS += rsgtutil
+rsgtutil = rsgtutil.c
+rsgtutil_CPPFLAGS = $(RSRT_CFLAGS) $(GUARDTIME_CFLAGS)
+rsgtutil_LDADD = ../runtime/librsgt.la $(GUARDTIME_LIBS)
+rsgtutil.1: rsgtutil.rst
+ $(AM_V_GEN) $(RST2MAN) $< $@
+man1_MANS += rsgtutil.1
+CLEANFILES += rsgtutil.1
+EXTRA_DIST+= rsgtutil.1
+endif
+if ENABLE_LIBGCRYPT
+bin_PROGRAMS += rscryutil
+rscryutil = rscryutil.c
+rscryutil_CPPFLAGS = -I../runtime $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS)
+rscryutil_LDADD = ../runtime/libgcry.la $(LIBGCRYPT_LIBS)
+rscryutil.1: rscryutil.rst
+ $(AM_V_GEN) $(RST2MAN) $< $@
+man1_MANS += rscryutil.1
+CLEANFILES += rscryutil.1
+EXTRA_DIST+= rscryutil.1
+endif
+endif
diff --git a/tools/gethostn.c b/tools/gethostn.c
new file mode 100644
index 00000000..df7ce38b
--- /dev/null
+++ b/tools/gethostn.c
@@ -0,0 +1,47 @@
+/* gethostn - a small diagnostic utility to show what the
+ * gethostname() API returns. Of course, this tool duplicates
+ * functionality already found in other tools. But the point is
+ * that the API shall be called by a program that is compiled like
+ * rsyslogd and does exactly what rsyslog does.
+ *
+ * Copyright 2008 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int main(int __attribute__((unused)) argc, char __attribute__((unused)) *argv[])
+{
+ char hostname[4096]; /* this should always be sufficient ;) */
+ int err;
+
+ err = gethostname(hostname, sizeof(hostname));
+
+ if(err) {
+ perror("gethostname failed");
+ exit(1);
+ }
+
+ printf("hostname of this system is '%s'.\n", hostname);
+
+ return 0;
+}
diff --git a/tools/gnutls/cert-gen-selfsigned b/tools/gnutls/cert-gen-selfsigned
new file mode 100755
index 00000000..e1c25386
--- /dev/null
+++ b/tools/gnutls/cert-gen-selfsigned
@@ -0,0 +1,6 @@
+#/bin/sh
+# generates a self-signed certificate and key suitable for use with rsyslog
+# 2008-05-08, rgerhards
+# TODO: make this a robust shell script
+certtool --generate-privkey --outfile $1-key.pem
+certtool --generate-self-signed --load-privkey $1-key.pem --outfile $1-cert.pem
diff --git a/tools/gnutls/cert-show-fingerprint b/tools/gnutls/cert-show-fingerprint
new file mode 100755
index 00000000..f61c6840
--- /dev/null
+++ b/tools/gnutls/cert-show-fingerprint
@@ -0,0 +1,6 @@
+#/bin/sh
+# must be called with the certificate file as first parameter. Displays all
+# fingerprints for the first certificate.
+# 2008-05-08, rgerhards
+# TODO: make this a robust shell script
+certtool -i < $1|grep Fingerprint
diff --git a/tools/iminternal.c b/tools/iminternal.c
new file mode 100644
index 00000000..167e2b29
--- /dev/null
+++ b/tools/iminternal.c
@@ -0,0 +1,183 @@
+/* iminternal.c
+ * This file set implements the internal messages input module for rsyslog.
+ * Note: we currently do not have an input module spec, but
+ * we will have one in the future. This module needs then to be
+ * adapted.
+ *
+ * File begun on 2007-08-03 by RGerhards
+ *
+ * Copyright 2007 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include "rsyslog.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "syslogd.h"
+#include "linkedlist.h"
+#include "iminternal.h"
+
+static linkedList_t llMsgs;
+
+
+/* destructs an iminternal object
+ */
+static rsRetVal iminternalDestruct(iminternal_t *pThis)
+{
+ DEFiRet;
+
+ assert(pThis != NULL);
+
+ if(pThis->pMsg != NULL)
+ msgDestruct(&pThis->pMsg);
+
+ free(pThis);
+
+ RETiRet;
+}
+
+
+/* Construct an iminternal object
+ */
+static rsRetVal iminternalConstruct(iminternal_t **ppThis)
+{
+ DEFiRet;
+ iminternal_t *pThis;
+
+ assert(ppThis != NULL);
+
+ if((pThis = (iminternal_t*) calloc(1, sizeof(iminternal_t))) == NULL) {
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ }
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pThis != NULL)
+ iminternalDestruct(pThis);
+ }
+
+ *ppThis = pThis;
+
+ RETiRet;
+}
+
+
+/* add a message to the linked list
+ * Note: the pMsg reference counter is not incremented. Consequently,
+ * the caller must NOT decrement it. The caller actually hands over
+ * full ownership of the pMsg object.
+ * The interface of this function is modelled after syslogd/logmsg(),
+ * for which it is an "replacement".
+ */
+rsRetVal iminternalAddMsg(msg_t *pMsg)
+{
+ DEFiRet;
+ iminternal_t *pThis;
+
+ assert(pMsg != NULL);
+
+ CHKiRet(iminternalConstruct(&pThis));
+
+ pThis->pMsg = pMsg;
+
+ CHKiRet(llAppend(&llMsgs, NULL, (void*) pThis));
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ dbgprintf("iminternalAddMsg() error %d - can not otherwise report this error, message lost\n", iRet);
+ if(pThis != NULL)
+ iminternalDestruct(pThis);
+ }
+
+ RETiRet;
+}
+
+
+/* pull the first error message from the linked list, remove it
+ * from the list and return it to the caller. The caller is
+ * responsible for freeing the message!
+ */
+rsRetVal iminternalRemoveMsg(msg_t **ppMsg)
+{
+ DEFiRet;
+ iminternal_t *pThis;
+ linkedListCookie_t llCookie = NULL;
+
+ assert(ppMsg != NULL);
+
+ CHKiRet(llGetNextElt(&llMsgs, &llCookie, (void*)&pThis));
+ *ppMsg = pThis->pMsg;
+ pThis->pMsg = NULL; /* we do no longer own it - important for destructor */
+
+ if(llDestroyRootElt(&llMsgs) != RS_RET_OK) {
+ dbgprintf("Root element of iminternal linked list could not be destroyed - there is "
+ "nothing we can do against it, we ignore it for now. Things may go wild "
+ "from here on. This is most probably a program logic error.\n");
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+/* tell the caller if we have any messages ready for processing.
+ * 0 means we have none, everything else means there is at least
+ * one message ready.
+ */
+rsRetVal iminternalHaveMsgReady(int* pbHaveOne)
+{
+ assert(pbHaveOne != NULL);
+
+ return llGetNumElts(&llMsgs, pbHaveOne);
+}
+
+
+/* initialize the iminternal subsystem
+ * must be called once at the start of the program
+ */
+rsRetVal modInitIminternal(void)
+{
+ DEFiRet;
+
+ iRet = llInit(&llMsgs, iminternalDestruct, NULL, NULL);
+
+ RETiRet;
+}
+
+
+/* de-initialize the iminternal subsystem
+ * must be called once at the end of the program
+ * Note: the error list must have been pulled first. We do
+ * NOT care if there are any errors left - we simply destroy
+ * them.
+ */
+rsRetVal modExitIminternal(void)
+{
+ DEFiRet;
+
+ iRet = llDestroy(&llMsgs);
+
+ RETiRet;
+}
+
+/* vim:set ai:
+ */
diff --git a/tools/iminternal.h b/tools/iminternal.h
new file mode 100644
index 00000000..8a9e2506
--- /dev/null
+++ b/tools/iminternal.h
@@ -0,0 +1,47 @@
+/* Definition of the internal messages input module.
+ *
+ * Note: we currently do not have an input module spec, but
+ * we will have one in the future. This module needs then to be
+ * adapted.
+ *
+ * Copyright 2007 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+
+#ifndef IMINTERNAL_H_INCLUDED
+#define IMINTERNAL_H_INCLUDED
+#include "template.h"
+
+/* this is a single entry for a parse routine. It describes exactly
+ * one entry point/handler.
+ * The short name is cslch (Configfile SysLine CommandHandler)
+ */
+struct iminternal_s { /* config file sysline parse entry */
+ msg_t *pMsg; /* the message (in all its glory) */
+};
+typedef struct iminternal_s iminternal_t;
+
+/* prototypes */
+rsRetVal modInitIminternal(void);
+rsRetVal modExitIminternal(void);
+rsRetVal iminternalAddMsg(msg_t *pMsg);
+rsRetVal iminternalHaveMsgReady(int* pbHaveOne);
+rsRetVal iminternalRemoveMsg(msg_t **ppMsg);
+
+#endif /* #ifndef IMINTERNAL_H_INCLUDED */
diff --git a/tools/logctl.c b/tools/logctl.c
new file mode 100644
index 00000000..1ab8ead0
--- /dev/null
+++ b/tools/logctl.c
@@ -0,0 +1,456 @@
+/**
+ * logctl - a tool to access lumberjack logs in MongoDB
+ * ... and potentially other sources in the future.
+ *
+ * Copyright 2012 Ulrike Gerhards and Adiscon GmbH.
+ *
+ * long short
+
+ * level l read records with level x
+ * severity s read records with severity x
+ * ret r number of records to return
+ * skip k number of records to skip
+ * sys y read records of system x
+ * msg m read records with message containing x
+ * datef f read records starting on time received x
+ * dateu u read records until time received x
+ *
+ * examples:
+ *
+ * logctl -f 15/05/2012-12:00:00 -u 15/05/2012-12:37:00
+ * logctl -s 50 --ret 10
+ * logctl -m "closed"
+ * logctl -l "INFO"
+ * logctl -s 3
+ * logctl -y "ubuntu"
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include <stdio.h>
+#include <mongo.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <getopt.h>
+#include <unistd.h>
+
+#define N 80
+
+static struct option long_options[] =
+{
+ {"level", required_argument, NULL, 'l'},
+ {"severity", required_argument, NULL, 's'},
+ {"ret", required_argument, NULL, 'r'},
+ {"skip", required_argument, NULL, 'k'},
+ {"sys", required_argument, NULL, 'y'},
+ {"msg", required_argument, NULL, 'm'},
+ {"datef", required_argument, NULL, 'f'},
+ {"dateu", required_argument, NULL, 'u'},
+ {NULL, 0, NULL, 0}
+};
+
+struct queryopt
+{
+ gint32 e_sever;
+ gint32 e_ret;
+ gint32 e_skip;
+ char *e_date;
+ char *e_level;
+ char *e_msg;
+ char *e_sys;
+ char *e_dateu;
+ int bsever;
+ int blevel;
+ int bskip;
+ int bret;
+ int bsys;
+ int bmsg;
+ int bdate;
+ int bdatef;
+ int bdateu;
+};
+
+struct ofields
+{
+ const char *msg;
+ const char *syslog_tag;
+ const char *prog;
+ char *date;
+ gint64 date_r;
+};
+
+struct query_doc
+{
+ bson *query;
+};
+
+struct select_doc
+{
+ bson *select;
+};
+
+struct db_connect
+{
+ mongo_sync_connection *conn;
+};
+
+struct output
+{
+ mongo_packet *p;
+};
+
+struct db_cursor
+{
+ mongo_sync_cursor *cursor;
+};
+
+struct results
+{
+ bson *result;
+};
+
+
+
+void formater(struct ofields *fields)
+{
+ time_t rtime;
+ rtime = (time_t) (fields->date_r / 1000);
+ char str[N];
+ strftime(str, N, "%b %d %H:%M:%S", gmtime(&rtime));
+ printf("%s %s %s %s\n", str, fields->prog, fields->syslog_tag, fields->msg);
+
+}
+
+struct ofields* get_data(struct results *res)
+{
+ struct ofields *fields;
+ const char *msg;
+ const char *prog;
+ const char *syslog_tag;
+ gint64 date_r;
+ bson_cursor *c;
+
+ fields = malloc(sizeof(struct ofields));
+
+ c = bson_find (res->result, "msg");
+ if (!bson_cursor_get_string (c, &msg))
+ {
+ perror ("bson_cursor_get_string()");
+ exit (1);
+ }
+ bson_cursor_free (c);
+
+ c = bson_find (res->result, "sys");
+ if (!bson_cursor_get_string (c, &prog))
+ {
+ perror ("bson_cursor_get_string()");
+ exit (1);
+ }
+ bson_cursor_free (c);
+
+ c = bson_find (res->result, "syslog_tag");
+ if (!bson_cursor_get_string (c, &syslog_tag))
+ {
+ perror ("bson_cursor_get_string()");
+ exit (1);
+ }
+ bson_cursor_free (c);
+
+ c = bson_find (res->result, "time_rcvd");
+ if (!bson_cursor_get_utc_datetime (c, &date_r))
+ {
+ perror ("bson_cursor_get_utc_datetime()");
+ exit (1);
+ }
+
+ bson_cursor_free (c);
+ fields->msg = msg;
+ fields->prog = prog;
+ fields->syslog_tag = syslog_tag;
+ fields->date_r = date_r;
+
+ return fields;
+
+}
+
+void getoptions(int argc, char *argv[], struct queryopt *opt)
+{
+ int iarg;
+ while ((iarg = getopt_long(argc, argv, "l:s:r:k:y:f:u:m:", long_options, NULL)) != -1)
+ {
+ // check to see if a single character or long option came through
+ switch (iarg)
+ {
+ // short option 's'
+ case 's':
+ opt->bsever = 1;
+ opt->e_sever = atoi(optarg);
+ break;
+ // short option 'r'
+ case 'r':
+ opt->bret = 1;
+ opt->e_ret = atoi(optarg);
+ break;
+ // short option 'f' : date from
+ case 'f':
+ opt->bdate = 1;
+ opt->bdatef = 1;
+ opt->e_date = optarg;
+ break;
+ // short option 'u': date until
+ case 'u':
+ opt->bdate = 1;
+ opt->bdateu = 1;
+ opt->e_dateu = optarg;
+ break;
+ // short option 'k'
+ case 'k':
+ opt->bskip = 1;
+ opt->e_skip = atoi(optarg);
+ break;
+ // short option 'l'
+ case 'l':
+ opt->blevel = 1;
+ opt->e_level = optarg;
+ break;
+ // short option 'm'
+ case 'm':
+ opt->bmsg = 1;
+ opt->e_msg = optarg;
+ break;
+ // short option 'y'
+ case 'y':
+ opt->bsys = 1;
+ opt->e_sys = optarg;
+ break;
+ } // end switch iarg
+ } // end while
+
+} // end void getoptions
+
+struct select_doc* create_select()
+// BSON object indicating the fields to return
+{
+ struct select_doc *s_doc;
+ s_doc = malloc(sizeof(struct select_doc));
+ s_doc->select = bson_new ();
+ bson_append_string (s_doc->select, "syslog_tag", "s", -1);
+ bson_append_string (s_doc->select, "msg", "ERROR", -1);
+ bson_append_string (s_doc->select, "sys", "sys", -1);
+ bson_append_utc_datetime (s_doc->select, "time_rcvd", 1ll);
+ bson_finish (s_doc->select);
+ return s_doc;
+}
+
+struct query_doc* create_query(struct queryopt *opt)
+{
+ struct query_doc *qu_doc;
+ bson *query_what, *order_what, *msg_what, *date_what;
+ struct tm tm;
+ time_t t;
+ gint64 ts;
+ qu_doc = malloc(sizeof(struct query_doc));
+ qu_doc->query = bson_new ();
+
+ query_what = bson_new ();
+ if (opt->bsever == 1)
+ {
+ bson_append_int32 (query_what, "syslog_sever", opt->e_sever);
+ }
+ if (opt->blevel == 1)
+ {
+ bson_append_string (query_what, "level", opt->e_level, -1);
+ }
+
+ if (opt->bmsg == 1)
+ {
+ msg_what = bson_new ();
+ bson_append_string (msg_what, "$regex", opt->e_msg, -1);
+ bson_append_string (msg_what, "$options", "i", -1);
+ bson_finish (msg_what);
+ bson_append_document (query_what, "msg", msg_what);
+ }
+
+ if (opt->bdate == 1)
+ {
+ date_what = bson_new ();
+ if (opt->bdatef == 1)
+ {
+ tm.tm_isdst = -1;
+ strptime(opt->e_date, "%d/%m/%Y-%H:%M:%S", &tm);
+ tm.tm_hour = tm.tm_hour + 1;
+ t = mktime(&tm);
+ ts = 1000 * (gint64) t;
+
+ bson_append_utc_datetime (date_what,"$gt", ts) ;
+ }
+
+ if (opt->bdateu == 1)
+ {
+ tm.tm_isdst = -1;
+ strptime(opt->e_dateu, "%d/%m/%Y-%H:%M:%S", &tm);
+ tm.tm_hour = tm.tm_hour +1;
+ t = mktime(&tm);
+ ts = 1000 * (gint64) t;
+ bson_append_utc_datetime (date_what,"$lt", ts);
+
+ }
+ bson_finish (date_what);
+ bson_append_document (query_what, "time_rcvd", date_what);
+ }
+
+ if (opt->bsys == 1)
+ {
+ bson_append_string (query_what, "sys", opt->e_sys, -1);
+ }
+
+ bson_finish (query_what);
+
+ order_what = bson_new ();
+ bson_append_utc_datetime (order_what, "time_rcvd", 1ll);
+ bson_finish (order_what);
+
+ bson_append_document (qu_doc->query, "$query", query_what);
+ bson_append_document (qu_doc->query, "$orderby", order_what);
+ bson_finish (qu_doc->query);
+ bson_free (order_what);
+ return qu_doc;
+}
+
+struct db_connect* create_conn()
+{
+ struct db_connect *db_conn;
+ db_conn = malloc(sizeof(struct db_connect));
+ db_conn->conn = mongo_sync_connect ("localhost", 27017, TRUE);
+
+ if (!db_conn->conn)
+ {
+ perror ("mongo_sync_connect()");
+ exit (1);
+ }
+ return db_conn;
+}
+
+void close_conn(struct db_connect *db_conn)
+{
+ mongo_sync_disconnect (db_conn->conn);
+}
+
+void free_cursor(struct db_cursor *db_c)
+{
+ mongo_sync_cursor_free (db_c->cursor);
+}
+
+struct output* launch_query(struct queryopt *opt, struct select_doc *s_doc,
+ struct query_doc *qu_doc,
+ struct db_connect *db_conn)
+{
+ struct output *out;
+ out = malloc(sizeof(struct output));
+ out->p = mongo_sync_cmd_query (db_conn->conn, "syslog.log", 0,
+ opt->e_skip, opt->e_ret, qu_doc->query, s_doc->select);
+ if (!out->p)
+ {
+ perror ("mongo_sync_cmd_query()");
+ printf("no records found\n");
+ exit (1);
+ }
+ return out;
+}
+
+struct db_cursor* open_cursor(struct db_connect *db_conn, struct output *out)
+{
+ struct db_cursor *db_c;
+ db_c = malloc(sizeof(struct db_cursor));
+
+ db_c->cursor = mongo_sync_cursor_new (db_conn->conn, "syslog.log", out->p);
+ if (!db_c->cursor)
+ {
+ perror ("mongo_sync_cursor_new()");
+ exit (1);
+ }
+ return db_c;
+}
+
+struct results* read_data(struct db_cursor *db_c)
+{
+ struct results *res;
+ res = malloc(sizeof(struct results));
+ res->result = mongo_sync_cursor_get_data (db_c->cursor);
+ if (!res->result)
+ {
+ perror ("mongo_sync_cursor_get_data()");
+ exit (1);
+ }
+ return res;
+}
+
+gboolean cursor_next (struct db_cursor *db_c)
+{
+ if (!mongo_sync_cursor_next (db_c->cursor))
+ return FALSE;
+ else
+ return TRUE;
+
+}
+
+int main (int argc, char *argv[])
+{
+
+ struct queryopt opt;
+ struct ofields *fields;
+ struct select_doc *s_doc;
+ struct query_doc *qu_doc;
+ struct db_connect *db_conn;
+ struct output *out;
+ struct db_cursor *db_c;
+ struct results *res;
+
+ opt.e_skip = 0; // standard
+ opt.e_ret = 0; // standard
+ opt.bsever = 0;
+ opt.blevel = 0;
+ opt.bdate = 0;
+ opt.bdateu = 0;
+ opt.bdatef = 0;
+ opt.bmsg = 0;
+ opt.bskip = 0;
+ opt.bsys = 0;
+
+ getoptions(argc, argv, &opt);
+ qu_doc = create_query(&opt); // crate query
+ s_doc = create_select();
+ db_conn = create_conn(); // create connection
+ out = launch_query(&opt, s_doc, qu_doc, db_conn); // launch the query
+ db_c = open_cursor(db_conn, out); // open cursor
+
+ while (cursor_next(db_c))
+ {
+ res = read_data(db_c);
+ fields = get_data(res);
+ formater(fields); // formate output
+ free(fields);
+ }
+
+ free_cursor(db_c);
+ close_conn(db_conn);
+
+ return (0);
+}
diff --git a/tools/logsigner.c b/tools/logsigner.c
new file mode 100644
index 00000000..f6887696
--- /dev/null
+++ b/tools/logsigner.c
@@ -0,0 +1,159 @@
+/* This is a tool for offline signing logfiles via the guardtime API.
+ *
+ * NOTE: this currently is only a PoC and WiP! NOT suitable for
+ * production use!
+ *
+ * Current hardcoded timestamper (use this if you do not have an
+ * idea of which one else to use):
+ * http://stamper.guardtime.net/gt-signingservice
+ * Check the GuardTime website for the URLs of nearest public services.
+ *
+ * Copyright 2013 Adiscon GmbH
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either exprs or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <gt_base.h>
+#include <gt_http.h>
+
+#include "librsgt.h"
+
+
+#if 0
+void
+outputhash(GTDataHash *hash)
+{
+ int i;
+ for(i = 0 ; i < hash->digest_length ; ++i)
+ printf("%2.2x", hash->digest[i]);
+ printf("\n");
+}
+
+void
+timestampIt(GTDataHash *hash)
+{
+ int r = GT_OK;
+ GTTimestamp *timestamp = NULL;
+ unsigned char *der = NULL;
+ char *sigFile = "logsigner.TIMESTAMP";
+ size_t der_len;
+
+ /* Get the timestamp. */
+ r = GTHTTP_createTimestampHash(hash,
+ "http://stamper.guardtime.net/gt-signingservice", &timestamp);
+
+ if(r != GT_OK) {
+ fprintf(stderr, "GTHTTP_createTimestampHash() failed: %d (%s)\n",
+ r, GTHTTP_getErrorString(r));
+ goto done;
+ }
+
+ /* Encode timestamp. */
+ r = GTTimestamp_getDEREncoded(timestamp, &der, &der_len);
+ if(r != GT_OK) {
+ fprintf(stderr, "GTTimestamp_getDEREncoded() failed: %d (%s)\n",
+ r, GT_getErrorString(r));
+ goto done;
+ }
+
+ /* Save DER-encoded timestamp to file. */
+ r = GT_saveFile(sigFile, der, der_len);
+ if(r != GT_OK) {
+ fprintf(stderr, "Cannot save timestamp to file %s: %d (%s)\n",
+ sigFile, r, GT_getErrorString(r));
+ if(r == GT_IO_ERROR) {
+ fprintf(stderr, "\t%d (%s)\n", errno, strerror(errno));
+ }
+ goto done;
+ }
+ printf("Timestamping succeeded!\n");
+done:
+ GT_free(der);
+ GTTimestamp_free(timestamp);
+}
+
+
+void
+sign(const char *buf, const size_t len)
+{
+ int r;
+ GTDataHash *hash = NULL;
+
+ printf("hash for '%s' is ", buf);
+ r = GTDataHash_create(GT_HASHALG_SHA256, (const unsigned char*)buf, len, &hash);
+ if(r != GT_OK) {
+ fprintf(stderr, "GTTDataHash_create() failed: %d (%s)\n",
+ r, GT_getErrorString(r));
+ goto done;
+ }
+ outputhash(hash);
+ timestampIt(hash); /* of course, this needs to be moved to once at end ;) */
+done: GTDataHash_free(hash);
+}
+#endif
+
+void
+processFile(char *name)
+{
+ FILE *fp;
+ size_t len;
+ char line[64*1024+1];
+ gtctx ctx = NULL;
+
+ ctx = rsgtCtxNew((unsigned char*)"SIGFILE", GT_HASHALG_SHA256);
+ sigblkInit(ctx);
+ if(!strcmp(name, "-"))
+ fp = stdin;
+ else
+ fp = fopen(name, "r");
+
+ while(1) {
+ if(fgets(line, sizeof(line), fp) == NULL) {
+ if(!feof(fp))
+ perror(name);
+ break;
+ }
+ len = strlen(line);
+ if(line[len-1] == '\n') {
+ --len;
+ line[len] = '\0';
+ }
+ //sign(line, len);
+ sigblkAddRecord(ctx, (unsigned char*)line, len);
+ }
+
+ if(fp != stdin)
+ fclose(fp);
+ sigblkFinish(ctx);
+ rsgtCtxDel(ctx);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ rsgtInit("rsyslog logsigner " VERSION);
+ processFile("-");
+ rsgtExit();
+ return 0;
+}
diff --git a/tools/msggen.c b/tools/msggen.c
new file mode 100644
index 00000000..29ade3a7
--- /dev/null
+++ b/tools/msggen.c
@@ -0,0 +1,39 @@
+/* msggen - a small diagnostic utility that does very quick
+ * syslog() calls.
+ *
+ * Copyright 2008 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <syslog.h>
+
+int main(int __attribute__((unused)) argc, char __attribute__((unused)) *argv[])
+{
+ int i;
+
+ openlog("msggen", 0 , LOG_LOCAL0);
+
+ for(i = 0 ; i < 10 ; ++i)
+ syslog(LOG_NOTICE, "This is message number %d", i);
+
+ closelog();
+ return 0;
+}
diff --git a/tools/omdiscard.c b/tools/omdiscard.c
new file mode 100644
index 00000000..15c6ea82
--- /dev/null
+++ b/tools/omdiscard.c
@@ -0,0 +1,128 @@
+/* omdiscard.c
+ * This is the implementation of the built-in discard output module.
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * File begun on 2007-07-24 by RGerhards
+ *
+ * Copyright 2007-2012 Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "syslogd.h"
+#include "syslogd-types.h"
+#include "omdiscard.h"
+#include "module-template.h"
+#include "errmsg.h"
+
+MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
+
+/* internal structures
+ */
+DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg);
+
+typedef struct _instanceData {
+ EMPTY_STRUCT
+} instanceData;
+
+/* we do not need a createInstance()!
+BEGINcreateInstance
+CODESTARTcreateInstance
+ENDcreateInstance
+*/
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ /* do nothing */
+ENDdbgPrintInstInfo
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ /* we are not compatible with repeated msg reduction feature, so do not allow it */
+ENDisCompatibleWithFeature
+
+
+BEGINtryResume
+CODESTARTtryResume
+ENDtryResume
+
+BEGINdoAction
+CODESTARTdoAction
+ dbgprintf("\n");
+ iRet = RS_RET_DISCARDMSG;
+ENDdoAction
+
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ /* we do not have instance data, so we do not need to
+ * do anything here. -- rgerhards, 2007-07-25
+ */
+ENDfreeInstance
+
+
+BEGINparseSelectorAct
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(0)
+ pData = NULL; /* this action does not have any instance data */
+ p = *pp;
+
+ if(*p == '~') {
+ dbgprintf("discard\n");
+ errmsg.LogError(0, RS_RET_DEPRECATED, "warning: ~ action "
+ "is deprecated, consider using the 'stop' "
+ "statement instead");
+ } else {
+ iRet = RS_RET_CONFLINE_UNPROCESSED;
+ }
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+
+BEGINmodExit
+CODESTARTmodExit
+ objRelease(errmsg, CORE_COMPONENT);
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_OMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit(Discard)
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ENDmodInit
+/*
+ * vi:set ai:
+ */
diff --git a/tools/omdiscard.h b/tools/omdiscard.h
new file mode 100644
index 00000000..505ff85f
--- /dev/null
+++ b/tools/omdiscard.h
@@ -0,0 +1,32 @@
+/* omdiscard.h
+ * These are the definitions for the built-in discard output module.
+ *
+ * File begun on 2007-07-24 by RGerhards
+ *
+ * Copyright 2007-2012 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef OMDISCARD_H_INCLUDED
+#define OMDISCARD_H_INCLUDED 1
+
+/* prototypes */
+rsRetVal modInitDiscard(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*);
+
+#endif /* #ifndef OMDISCARD_H_INCLUDED */
+/* vi:set ai:
+ */
diff --git a/tools/omfile.c b/tools/omfile.c
new file mode 100644
index 00000000..2ebb7df9
--- /dev/null
+++ b/tools/omfile.c
@@ -0,0 +1,1392 @@
+/* omfile.c
+ * This is the implementation of the build-in file output module.
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * File begun on 2007-07-21 by RGerhards (extracted from syslogd.c, which
+ * at the time of the fork from sysklogd was under BSD license)
+ *
+ * A large re-write of this file was done in June, 2009. The focus was
+ * to introduce many more features (like zipped writing), clean up the code
+ * and make it more reliable. In short, that rewrite tries to provide a new
+ * solid basis for the next three to five years to come. During it, bugs
+ * may have been introduced ;) -- rgerhards, 2009-06-04
+ *
+ * Note that as of 2010-02-28 this module does no longer handle
+ * pipes. These have been moved to ompipe, to reduced the entanglement
+ * between the two different functionalities. -- rgerhards
+ *
+ * Copyright 2007-2013 Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <assert.h>
+#include <errno.h>
+#include <ctype.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <sys/file.h>
+#ifdef OS_SOLARIS
+# include <fcntl.h>
+#endif
+#ifdef HAVE_ATOMIC_BUILTINS
+# include <pthread.h>
+#endif
+
+
+#include "conf.h"
+#include "syslogd-types.h"
+#include "srUtils.h"
+#include "template.h"
+#include "outchannel.h"
+#include "omfile.h"
+#include "cfsysline.h"
+#include "module-template.h"
+#include "errmsg.h"
+#include "stream.h"
+#include "unicode-helper.h"
+#include "atomic.h"
+#include "statsobj.h"
+#include "sigprov.h"
+#include "cryprov.h"
+
+MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("omfile")
+
+/* forward definitions */
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal);
+
+/* internal structures
+ */
+DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(strm)
+DEFobjCurrIf(statsobj)
+
+/* for our current LRU mechanism, we need a monotonically increasing counters. We use
+ * it much like a "Lamport logical clock": we do not need the actual time, we just need
+ * to know the sequence in which files were accessed. So we use a simple counter to
+ * create that sequence. We use an unsigned 64 bit value which is extremely unlike to
+ * wrap within the lifetime of a process. If we process 1,000,000 file writes per
+ * second, the process could still exist over 500,000 years before a wrap to 0 happens.
+ * That should be sufficient (and even than, there would no really bad effect ;)).
+ * The variable below is the global counter/clock.
+ */
+#if HAVE_ATOMIC_BUILTINS_64BIT
+static uint64 clockFileAccess = 0;
+#else
+static unsigned clockFileAccess = 0;
+#endif
+/* and the "tick" function */
+#ifndef HAVE_ATOMIC_BUILTINS
+static pthread_mutex_t mutClock;
+#endif
+static inline uint64
+getClockFileAccess(void)
+{
+#if HAVE_ATOMIC_BUILTINS_64BIT
+ return ATOMIC_INC_AND_FETCH_uint64(&clockFileAccess, &mutClock);
+#else
+ return ATOMIC_INC_AND_FETCH_unsigned(&clockFileAccess, &mutClock);
+#endif
+}
+
+
+/* The following structure is a dynafile name cache entry.
+ */
+struct s_dynaFileCacheEntry {
+ uchar *pName; /* name currently open, if dynamic name */
+ strm_t *pStrm; /* our output stream */
+ void *sigprovFileData; /* opaque data ptr for provider use */
+ uint64 clkTickAccessed;/* for LRU - based on clockFileAccess */
+};
+typedef struct s_dynaFileCacheEntry dynaFileCacheEntry;
+
+
+#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 */
+
+
+typedef struct _instanceData {
+ uchar *f_fname; /* file or template name (display only) */
+ uchar *tplName; /* name of assigned template */
+ strm_t *pStrm; /* our output stream */
+ char bDynamicName; /* 0 - static name, 1 - dynamic name (with properties) */
+ int fCreateMode; /* file creation mode for open() */
+ int fDirCreateMode; /* creation mode for mkdir() */
+ int bCreateDirs; /* auto-create directories? */
+ int bSyncFile; /* should the file by sync()'ed? 1- yes, 0- no */
+ uid_t fileUID; /* IDs for creation */
+ uid_t dirUID;
+ gid_t fileGID;
+ gid_t dirGID;
+ int bFailOnChown; /* fail creation if chown fails? */
+ uchar *sigprovName; /* signature provider */
+ uchar *sigprovNameFull;/* full internal signature provider name */
+ sigprov_if_t sigprov; /* ptr to signature provider interface */
+ void *sigprovData; /* opaque data ptr for provider use */
+ void *sigprovFileData;/* opaque data ptr for file instance */
+ sbool useSigprov; /* quicker than checkig ptr (1 vs 8 bytes!) */
+ uchar *cryprovName; /* crypto provider */
+ uchar *cryprovNameFull;/* full internal crypto provider name */
+ void *cryprovData; /* opaque data ptr for provider use */
+ cryprov_if_t cryprov; /* ptr to crypto provider interface */
+ sbool useCryprov; /* quicker than checkig ptr (1 vs 8 bytes!) */
+ int iCurrElt; /* currently active cache element (-1 = none) */
+ int iCurrCacheSize; /* currently cache size (1-based) */
+ int iDynaFileCacheSize; /* size of file handle cache */
+ /* The cache is implemented as an array. An empty element is indicated
+ * by a NULL pointer. Memory is allocated as needed. The following
+ * pointer points to the overall structure.
+ */
+ dynaFileCacheEntry **dynCache;
+ off_t iSizeLimit; /* file size limit, 0 = no limit */
+ uchar *pszSizeLimitCmd; /* command to carry out when size limit is reached */
+ int iZipLevel; /* zip mode to use for this selector */
+ int iIOBufSize; /* size of associated io buffer */
+ int iFlushInterval; /* how fast flush buffer on inactivity? */
+ sbool bFlushOnTXEnd; /* flush write buffers when transaction has ended? */
+ sbool bUseAsyncWriter; /* use async stream writer? */
+ sbool bVeryRobustZip;
+ statsobj_t *stats; /* dynafile, primarily cache stats */
+ STATSCOUNTER_DEF(ctrRequests, mutCtrRequests);
+ STATSCOUNTER_DEF(ctrLevel0, mutCtrLevel0);
+ STATSCOUNTER_DEF(ctrEvict, mutCtrEvict);
+ STATSCOUNTER_DEF(ctrMiss, mutCtrMiss);
+ STATSCOUNTER_DEF(ctrMax, mutCtrMax);
+} instanceData;
+
+
+typedef struct configSettings_s {
+ int iDynaFileCacheSize; /* max cache for dynamic files */
+ int fCreateMode; /* mode to use when creating files */
+ int fDirCreateMode; /* mode to use when creating files */
+ int bFailOnChown; /* fail if chown fails? */
+ uid_t fileUID; /* UID to be used for newly created files */
+ uid_t fileGID; /* GID to be used for newly created files */
+ uid_t dirUID; /* UID to be used for newly created directories */
+ uid_t dirGID; /* GID to be used for newly created directories */
+ int bCreateDirs;/* auto-create directories for dynaFiles: 0 - no, 1 - yes */
+ int bEnableSync;/* enable syncing of files (no dash in front of pathname in conf): 0 - no, 1 - yes */
+ int iZipLevel; /* zip compression mode (0..9 as usual) */
+ sbool bFlushOnTXEnd;/* flush write buffers when transaction has ended? */
+ int64 iIOBufSize; /* size of an io buffer */
+ int iFlushInterval; /* how often flush the output buffer on inactivity? */
+ int bUseAsyncWriter; /* should we enable asynchronous writing? */
+ EMPTY_STRUCT
+} configSettings_t;
+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 */
+ { "ziplevel", eCmdHdlrInt, 0 }, /* legacy: omfileziplevel */
+ { "flushinterval", eCmdHdlrInt, 0 }, /* legacy: omfileflushinterval */
+ { "asyncwriting", eCmdHdlrBinary, 0 }, /* legacy: omfileasyncwriting */
+ { "veryrobustzip", eCmdHdlrBinary, 0 },
+ { "flushontxend", eCmdHdlrBinary, 0 }, /* legacy: omfileflushontxend */
+ { "iobuffersize", eCmdHdlrSize, 0 }, /* legacy: omfileiobuffersize */
+ { "dirowner", eCmdHdlrUID, 0 }, /* legacy: dirowner */
+ { "dirgroup", eCmdHdlrGID, 0 }, /* legacy: dirgroup */
+ { "fileowner", eCmdHdlrUID, 0 }, /* legacy: fileowner */
+ { "filegroup", eCmdHdlrGID, 0 }, /* legacy: filegroup */
+ { "dircreatemode", eCmdHdlrFileCreateMode, 0 }, /* legacy: dircreatemode */
+ { "filecreatemode", eCmdHdlrFileCreateMode, 0 }, /* legacy: filecreatemode */
+ { "failonchownfailure", eCmdHdlrBinary, 0 }, /* legacy: failonchownfailure */
+ { "createdirs", eCmdHdlrBinary, 0 }, /* legacy: createdirs */
+ { "sync", eCmdHdlrBinary, 0 }, /* legacy: actionfileenablesync */
+ { "file", eCmdHdlrString, 0 }, /* either "file" or ... */
+ { "dynafile", eCmdHdlrString, 0 }, /* "dynafile" MUST be present */
+ { "sig.provider", eCmdHdlrGetWord, 0 },
+ { "cry.provider", eCmdHdlrGetWord, 0 },
+ { "template", eCmdHdlrGetWord, 0 }
+};
+static struct cnfparamblk actpblk =
+ { CNFPARAMBLK_VERSION,
+ sizeof(actpdescr)/sizeof(struct cnfparamdescr),
+ actpdescr
+ };
+
+
+/* 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! */
+ iRet = resetConfigVariables(NULL, NULL); /* params are dummies */
+ENDinitConfVars
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURERepeatedMsgReduction)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ if(pData->bDynamicName) {
+ dbgprintf("[dynamic]\n");
+ } else { /* regular file */
+ dbgprintf("%s%s\n", pData->f_fname,
+ (pData->pStrm == NULL) ? " (closed)" : "");
+ }
+
+ dbgprintf("\ttemplate='%s'\n", pData->f_fname);
+ dbgprintf("\tuse async writer=%d\n", pData->bUseAsyncWriter);
+ dbgprintf("\tflush on TX end=%d\n", pData->bFlushOnTXEnd);
+ dbgprintf("\tflush interval=%d\n", pData->iFlushInterval);
+ dbgprintf("\tfile cache size=%d\n", pData->iDynaFileCacheSize);
+ dbgprintf("\tcreate directories: %s\n", pData->bCreateDirs ? "on" : "off");
+ dbgprintf("\tvery robust zip: %s\n", pData->bCreateDirs ? "on" : "off");
+ dbgprintf("\tfile owner %d, group %d\n", (int) pData->fileUID, (int) pData->fileGID);
+ dbgprintf("\tdirectory owner %d, group %d\n", (int) pData->dirUID, (int) pData->dirGID);
+ dbgprintf("\tdir create mode 0%3.3o, file create mode 0%3.3o\n",
+ pData->fDirCreateMode, pData->fCreateMode);
+ dbgprintf("\tfail if owner/group can not be set: %s\n", pData->bFailOnChown ? "yes" : "no");
+ENDdbgPrintInstInfo
+
+
+
+/* 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
+ */
+rsRetVal setDynaFileCacheSize(void __attribute__((unused)) *pVal, int iNewVal)
+{
+ DEFiRet;
+ uchar errMsg[128]; /* for dynamic error messages */
+
+ if(iNewVal < 1) {
+ snprintf((char*) errMsg, sizeof(errMsg)/sizeof(uchar),
+ "DynaFileCacheSize must be greater 0 (%d given), changed to 1.", iNewVal);
+ errno = 0;
+ errmsg.LogError(0, RS_RET_VAL_OUT_OF_RANGE, "%s", errMsg);
+ iRet = RS_RET_VAL_OUT_OF_RANGE;
+ iNewVal = 1;
+ } else if(iNewVal > 1000) {
+ snprintf((char*) errMsg, sizeof(errMsg)/sizeof(uchar),
+ "DynaFileCacheSize maximum is 1,000 (%d given), changed to 1,000.", iNewVal);
+ errno = 0;
+ errmsg.LogError(0, RS_RET_VAL_OUT_OF_RANGE, "%s", errMsg);
+ iRet = RS_RET_VAL_OUT_OF_RANGE;
+ iNewVal = 1000;
+ }
+
+ cs.iDynaFileCacheSize = iNewVal;
+ DBGPRINTF("DynaFileCacheSize changed to %d.\n", iNewVal);
+
+ RETiRet;
+}
+
+
+/* Helper to cfline(). Parses a output channel name up until the first
+ * comma and then looks for the template specifier. Tries
+ * to find that template. Maps the output channel to the
+ * proper filed structure settings. Everything is stored in the
+ * filed struct. Over time, the dependency on filed might be
+ * removed.
+ * rgerhards 2005-06-21
+ */
+static rsRetVal cflineParseOutchannel(instanceData *pData, uchar* p, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts)
+{
+ DEFiRet;
+ size_t i;
+ struct outchannel *pOch;
+ char szBuf[128]; /* should be more than sufficient */
+
+ ++p; /* skip '$' */
+ i = 0;
+ /* get outchannel name */
+ while(*p && *p != ';' && *p != ' ' &&
+ i < sizeof(szBuf) / sizeof(char)) {
+ szBuf[i++] = *p++;
+ }
+ szBuf[i] = '\0';
+
+ /* got the name, now look up the channel... */
+ pOch = ochFind(szBuf, i);
+
+ if(pOch == NULL) {
+ char errMsg[128];
+ errno = 0;
+ snprintf(errMsg, sizeof(errMsg)/sizeof(char),
+ "outchannel '%s' not found - ignoring action line",
+ szBuf);
+ errmsg.LogError(0, RS_RET_NOT_FOUND, "%s", errMsg);
+ ABORT_FINALIZE(RS_RET_NOT_FOUND);
+ }
+
+ /* check if there is a file name in the outchannel... */
+ if(pOch->pszFileTemplate == NULL) {
+ char errMsg[128];
+ errno = 0;
+ snprintf(errMsg, sizeof(errMsg)/sizeof(char),
+ "outchannel '%s' has no file name template - ignoring action line",
+ szBuf);
+ errmsg.LogError(0, RS_RET_ERR, "%s", errMsg);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+ /* OK, we finally got a correct template. So let's use it... */
+ pData->f_fname = ustrdup(pOch->pszFileTemplate);
+ pData->iSizeLimit = pOch->uSizeLimit;
+ /* WARNING: It is dangerous "just" to pass the pointer. As we
+ * never rebuild the output channel description, this is acceptable here.
+ */
+ pData->pszSizeLimitCmd = pOch->cmdOnSizeLimit;
+
+ iRet = cflineParseTemplateName(&p, pOMSR, iEntry, iTplOpts, getDfltTpl());
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* This function deletes an entry from the dynamic file name
+ * cache. A pointer to the cache must be passed in as well
+ * as the index of the to-be-deleted entry. This index may
+ * point to an unallocated entry, in whcih case the
+ * function immediately returns. Parameter bFreeEntry is 1
+ * if the entry should be d_free()ed and 0 if not.
+ */
+static rsRetVal
+dynaFileDelCacheEntry(instanceData *pData, int iEntry, int bFreeEntry)
+{
+ dynaFileCacheEntry **pCache = pData->dynCache;
+ DEFiRet;
+ ASSERT(pCache != NULL);
+
+ if(pCache[iEntry] == NULL)
+ FINALIZE;
+
+ DBGPRINTF("Removing entry %d for file '%s' from dynaCache.\n", iEntry,
+ pCache[iEntry]->pName == NULL ? UCHAR_CONSTANT("[OPEN FAILED]") : pCache[iEntry]->pName);
+
+ if(pCache[iEntry]->pName != NULL) {
+ d_free(pCache[iEntry]->pName);
+ pCache[iEntry]->pName = NULL;
+ }
+
+ if(pCache[iEntry]->pStrm != NULL) {
+ strm.Destruct(&pCache[iEntry]->pStrm);
+ if(pData->useSigprov) {
+ pData->sigprov.OnFileClose(pCache[iEntry]->sigprovFileData);
+ pCache[iEntry]->sigprovFileData = NULL;
+ }
+ }
+
+ if(bFreeEntry) {
+ d_free(pCache[iEntry]);
+ pCache[iEntry] = NULL;
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* This function frees all dynamic file name cache entries and closes the
+ * relevant files. Part of Shutdown and HUP processing.
+ * rgerhards, 2008-10-23
+ */
+static inline void
+dynaFileFreeCacheEntries(instanceData *pData)
+{
+ register int i;
+ ASSERT(pData != NULL);
+
+ BEGINfunc;
+ for(i = 0 ; i < pData->iCurrCacheSize ; ++i) {
+ dynaFileDelCacheEntry(pData, i, 1);
+ }
+ pData->iCurrElt = -1; /* invalidate current element */
+ ENDfunc;
+}
+
+
+/* This function frees the dynamic file name cache.
+ */
+static void dynaFileFreeCache(instanceData *pData)
+{
+ ASSERT(pData != NULL);
+
+ BEGINfunc;
+ dynaFileFreeCacheEntries(pData);
+ if(pData->dynCache != NULL)
+ d_free(pData->dynCache);
+ ENDfunc;
+}
+
+
+/* close current file */
+static rsRetVal
+closeFile(instanceData *pData)
+{
+ DEFiRet;
+ if(pData->useSigprov) {
+ pData->sigprov.OnFileClose(pData->sigprovFileData);
+ pData->sigprovFileData = NULL;
+ }
+ strm.Destruct(&pData->pStrm);
+ RETiRet;
+}
+
+
+/* This prepares the signature provider to process a file */
+static rsRetVal
+sigprovPrepare(instanceData *pData, uchar *fn)
+{
+ DEFiRet;
+ pData->sigprov.OnFileOpen(pData->sigprovData, fn, &pData->sigprovFileData);
+ RETiRet;
+}
+
+/* This is now shared code for all types of files. It simply prepares
+ * file access, which, among others, means the the file wil be opened
+ * and any directories in between will be created (based on config, of
+ * course). -- rgerhards, 2008-10-22
+ * changed to iRet interface - 2009-03-19
+ */
+static rsRetVal
+prepareFile(instanceData *pData, uchar *newFileName)
+{
+ int fd;
+ DEFiRet;
+
+ pData->pStrm = NULL;
+ if(access((char*)newFileName, F_OK) != 0) {
+ /* file does not exist, create it (and eventually parent directories */
+ if(pData->bCreateDirs) {
+ /* We first need to create parent dirs if they are missing.
+ * We do not report any errors here ourselfs but let the code
+ * fall through to error handler below.
+ */
+ if(makeFileParentDirs(newFileName, ustrlen(newFileName),
+ pData->fDirCreateMode, pData->dirUID,
+ pData->dirGID, pData->bFailOnChown) != 0) {
+ ABORT_FINALIZE(RS_RET_ERR); /* we give up */
+ }
+ }
+ /* no matter if we needed to create directories or not, we now try to create
+ * the file. -- rgerhards, 2008-12-18 (based on patch from William Tisater)
+ */
+ fd = open((char*) newFileName, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY|O_CLOEXEC,
+ pData->fCreateMode);
+ if(fd != -1) {
+ /* check and set uid/gid */
+ if(pData->fileUID != (uid_t)-1 || pData->fileGID != (gid_t) -1) {
+ /* we need to set owner/group */
+ if(fchown(fd, pData->fileUID, pData->fileGID) != 0) {
+ if(pData->bFailOnChown) {
+ int eSave = errno;
+ close(fd);
+ fd = -1;
+ errno = eSave;
+ }
+ /* we will silently ignore the chown() failure
+ * if configured to do so.
+ */
+ }
+ }
+ close(fd); /* close again, as we need a stream further on */
+ }
+ }
+
+ /* the copies below are clumpsy, but there is no way around given the
+ * anomalies in dirname() and basename() [they MODIFY the provided buffer...]
+ */
+ uchar szNameBuf[MAXFNAME];
+ uchar szDirName[MAXFNAME];
+ uchar szBaseName[MAXFNAME];
+ ustrncpy(szNameBuf, newFileName, MAXFNAME);
+ ustrncpy(szDirName, (uchar*)dirname((char*)szNameBuf), MAXFNAME);
+ ustrncpy(szNameBuf, newFileName, MAXFNAME);
+ ustrncpy(szBaseName, (uchar*)basename((char*)szNameBuf), MAXFNAME);
+
+ CHKiRet(strm.Construct(&pData->pStrm));
+ CHKiRet(strm.SetFName(pData->pStrm, szBaseName, ustrlen(szBaseName)));
+ CHKiRet(strm.SetDir(pData->pStrm, szDirName, ustrlen(szDirName)));
+ CHKiRet(strm.SetiZipLevel(pData->pStrm, pData->iZipLevel));
+ CHKiRet(strm.SetbVeryReliableZip(pData->pStrm, pData->bVeryRobustZip));
+ CHKiRet(strm.SetsIOBufSize(pData->pStrm, (size_t) pData->iIOBufSize));
+ CHKiRet(strm.SettOperationsMode(pData->pStrm, STREAMMODE_WRITE_APPEND));
+ CHKiRet(strm.SettOpenMode(pData->pStrm, cs.fCreateMode));
+ CHKiRet(strm.SetbSync(pData->pStrm, pData->bSyncFile));
+ CHKiRet(strm.SetsType(pData->pStrm, STREAMTYPE_FILE_SINGLE));
+ CHKiRet(strm.SetiSizeLimit(pData->pStrm, pData->iSizeLimit));
+ if(pData->useCryprov) {
+ CHKiRet(strm.Setcryprov(pData->pStrm, &pData->cryprov));
+ CHKiRet(strm.SetcryprovData(pData->pStrm, pData->cryprovData));
+ }
+ /* set the flush interval only if we actually use it - otherwise it will activate
+ * async processing, which is a real performance waste if we do not do buffered
+ * writes! -- rgerhards, 2009-07-06
+ */
+ if(pData->bUseAsyncWriter)
+ CHKiRet(strm.SetiFlushInterval(pData->pStrm, pData->iFlushInterval));
+ if(pData->pszSizeLimitCmd != NULL)
+ CHKiRet(strm.SetpszSizeLimitCmd(pData->pStrm, ustrdup(pData->pszSizeLimitCmd)));
+ CHKiRet(strm.ConstructFinalize(pData->pStrm));
+
+ if(pData->useSigprov)
+ sigprovPrepare(pData, szNameBuf);
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pData->pStrm != NULL) {
+ closeFile(pData);
+ }
+ }
+ RETiRet;
+}
+
+
+/* This function handles dynamic file names. It checks if the
+ * requested file name is already open and, if not, does everything
+ * needed to switch to the it.
+ * Function returns 0 if all went well and non-zero otherwise.
+ * On successful return pData->fd must point to the correct file to
+ * be written.
+ * This is a helper to writeFile(). rgerhards, 2007-07-03
+ */
+static inline rsRetVal
+prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts)
+{
+ uint64 ctOldest; /* "timestamp" of oldest element */
+ int iOldest;
+ int i;
+ int iFirstFree;
+ rsRetVal localRet;
+ dynaFileCacheEntry **pCache;
+ DEFiRet;
+
+ ASSERT(pData != NULL);
+ ASSERT(newFileName != NULL);
+
+ pCache = pData->dynCache;
+
+ /* first check, if we still have the current file */
+ if( (pData->iCurrElt != -1)
+ && !ustrcmp(newFileName, pCache[pData->iCurrElt]->pName)) {
+ /* great, we are all set */
+ pCache[pData->iCurrElt]->clkTickAccessed = getClockFileAccess();
+ STATSCOUNTER_INC(pData->ctrLevel0, pData->mutCtrLevel0);
+ /* LRU needs only a strictly monotonically increasing counter, so such a one could do */
+ FINALIZE;
+ }
+
+ /* ok, no luck. Now let's search the table if we find a matching spot.
+ * While doing so, we also prepare for creation of a new one.
+ */
+ pData->iCurrElt = -1; /* invalid current element pointer */
+ iFirstFree = -1; /* not yet found */
+ iOldest = 0; /* we assume the first element to be the oldest - that will change as we loop */
+ ctOldest = getClockFileAccess(); /* there must always be an older one */
+ for(i = 0 ; i < pData->iCurrCacheSize ; ++i) {
+ if(pCache[i] == NULL || pCache[i]->pName == NULL) {
+ if(iFirstFree == -1)
+ iFirstFree = i;
+ } else { /* got an element, let's see if it matches */
+ if(!ustrcmp(newFileName, pCache[i]->pName)) {
+ /* we found our element! */
+ pData->pStrm = pCache[i]->pStrm;
+ if(pData->useSigprov)
+ pData->sigprovFileData = pCache[i]->sigprovFileData;
+ pData->iCurrElt = i;
+ pCache[i]->clkTickAccessed = getClockFileAccess(); /* update "timestamp" for LRU */
+ FINALIZE;
+ }
+ /* did not find it - so lets keep track of the counters for LRU */
+ if(pCache[i]->clkTickAccessed < ctOldest) {
+ ctOldest = pCache[i]->clkTickAccessed;
+ iOldest = i;
+ }
+ }
+ }
+
+ /* we have not found an entry */
+ STATSCOUNTER_INC(pData->ctrMiss, pData->mutCtrMiss);
+
+ /* invalidate iCurrElt as we may error-exit out of this function when the currrent
+ * iCurrElt has been freed or otherwise become unusable. This is a precaution, and
+ * performance-wise it may be better to do that in each of the exits. However, that
+ * is error-prone, so I prefer to do it here. -- rgerhards, 2010-03-02
+ */
+ pData->iCurrElt = -1;
+ /* similarly, we need to set the current pStrm to NULL, because otherwise, if prepareFile() fails,
+ * we may end up using an old stream. This bug depends on how exactly prepareFile fails,
+ * but it could be triggered in the common case of a failed open() system call.
+ * rgerhards, 2010-03-22
+ */
+ pData->pStrm = NULL, pData->sigprovFileData = NULL;
+
+ if(iFirstFree == -1 && (pData->iCurrCacheSize < pData->iDynaFileCacheSize)) {
+ /* there is space left, so set it to that index */
+ iFirstFree = pData->iCurrCacheSize++;
+ STATSCOUNTER_SETMAX_NOMUT(pData->ctrMax, (unsigned) pData->iCurrCacheSize);
+ }
+
+ /* Note that the following code sequence does not work with the cache entry itself,
+ * but rather with pData->pStrm, the (sole) stream pointer in the non-dynafile case.
+ * The cache array is only updated after the open was successful. -- rgerhards, 2010-03-21
+ */
+ if(iFirstFree == -1) {
+ dynaFileDelCacheEntry(pData, iOldest, 0);
+ STATSCOUNTER_INC(pData->ctrEvict, pData->mutCtrEvict);
+ iFirstFree = iOldest; /* this one *is* now free ;) */
+ } else {
+ /* we need to allocate memory for the cache structure */
+ CHKmalloc(pCache[iFirstFree] = (dynaFileCacheEntry*) calloc(1, sizeof(dynaFileCacheEntry)));
+ }
+
+ /* Ok, we finally can open the file */
+ localRet = prepareFile(pData, newFileName); /* ignore exact error, we check fd below */
+
+ /* check if we had an error */
+ if(localRet != RS_RET_OK) {
+ /* do not report anything if the message is an internally-generated
+ * message. Otherwise, we could run into a never-ending loop. The bad
+ * news is that we also lose errors on startup messages, but so it is.
+ */
+ if(iMsgOpts & INTERNAL_MSG) {
+ DBGPRINTF("Could not open dynaFile '%s', state %d, discarding message\n",
+ newFileName, localRet);
+ } else {
+ errmsg.LogError(0, localRet, "Could not open dynamic file '%s' [state %d] - discarding message", newFileName, localRet);
+ }
+ ABORT_FINALIZE(localRet);
+ }
+
+ if((pCache[iFirstFree]->pName = ustrdup(newFileName)) == NULL) {
+ closeFile(pData); /* need to free failed entry! */
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ }
+ pCache[iFirstFree]->pStrm = pData->pStrm;
+ if(pData->useSigprov)
+ pCache[iFirstFree]->sigprovFileData = pData->sigprovFileData;
+ pCache[iFirstFree]->clkTickAccessed = getClockFileAccess();
+ pData->iCurrElt = iFirstFree;
+ DBGPRINTF("Added new entry %d for file cache, file '%s'.\n", iFirstFree, newFileName);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* do the actual write process. This function is to be called once we are ready for writing.
+ * It will do buffered writes and persist data only when the buffer is full. Note that we must
+ * be careful to detect when the file handle changed.
+ * rgerhards, 2009-06-03
+ */
+static rsRetVal
+doWrite(instanceData *pData, uchar *pszBuf, int lenBuf)
+{
+ DEFiRet;
+ ASSERT(pData != NULL);
+ ASSERT(pszBuf != NULL);
+
+ DBGPRINTF("write to stream, pData->pStrm %p, lenBuf %d\n", pData->pStrm, lenBuf);
+ if(pData->pStrm != NULL){
+ CHKiRet(strm.Write(pData->pStrm, pszBuf, lenBuf));
+ if(pData->useSigprov) {
+ CHKiRet(pData->sigprov.OnRecordWrite(pData->sigprovFileData, pszBuf, lenBuf));
+ }
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* rgerhards 2004-11-11: write to a file output. */
+static rsRetVal
+writeFile(uchar **ppString, unsigned iMsgOpts, instanceData *pData)
+{
+ DEFiRet;
+
+ ASSERT(pData != NULL);
+
+ /* first check if we have a dynamic file name and, if so,
+ * check if it still is ok or a new file needs to be created
+ */
+ if(pData->bDynamicName) {
+ CHKiRet(prepareDynFile(pData, ppString[1], iMsgOpts));
+ } else { /* "regular", non-dynafile */
+ if(pData->pStrm == NULL) {
+ CHKiRet(prepareFile(pData, pData->f_fname));
+ if(pData->pStrm == NULL) {
+ errmsg.LogError(0, RS_RET_NO_FILE_ACCESS, "Could no open output file '%s'", pData->f_fname);
+ }
+ }
+ }
+
+ CHKiRet(doWrite(pData, ppString[0], strlen(CHAR_CONVERT(ppString[0]))));
+
+finalize_it:
+ RETiRet;
+}
+
+
+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;
+ENDcreateInstance
+
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ free(pData->tplName);
+ free(pData->f_fname);
+ if(pData->bDynamicName) {
+ dynaFileFreeCache(pData);
+ } else if(pData->pStrm != NULL)
+ closeFile(pData);
+ if(pData->useSigprov) {
+ pData->sigprov.Destruct(&pData->sigprovData);
+ obj.ReleaseObj(__FILE__, pData->sigprovNameFull+2, pData->sigprovNameFull,
+ (void*) &pData->sigprov);
+ free(pData->sigprovName);
+ free(pData->sigprovNameFull);
+ }
+ if(pData->useCryprov) {
+ pData->cryprov.Destruct(&pData->cryprovData);
+ obj.ReleaseObj(__FILE__, pData->cryprovNameFull+2, pData->cryprovNameFull,
+ (void*) &pData->cryprov);
+ free(pData->cryprovName);
+ free(pData->cryprovNameFull);
+ }
+ENDfreeInstance
+
+
+BEGINtryResume
+CODESTARTtryResume
+ENDtryResume
+
+BEGINbeginTransaction
+CODESTARTbeginTransaction
+ /* we have nothing to do to begin a transaction */
+ENDbeginTransaction
+
+
+BEGINendTransaction
+CODESTARTendTransaction
+ /* Note: pStrm may be NULL if there was an error opening the stream */
+ if(pData->bFlushOnTXEnd && pData->pStrm != NULL) {
+ /* if we have an async writer, it controls the flush via
+ * a timeout. However, without it, we actually need to flush,
+ * else incomplete records are written.
+ */
+ if(!pData->bUseAsyncWriter)
+ CHKiRet(strm.Flush(pData->pStrm));
+ }
+finalize_it:
+ENDendTransaction
+
+
+BEGINdoAction
+CODESTARTdoAction
+ DBGPRINTF("file to log to: %s\n",
+ (pData->bDynamicName) ? ppString[1] : pData->f_fname);
+ DBGPRINTF("omfile: start of data: '%.128s'\n", ppString[0]);
+ STATSCOUNTER_INC(pData->ctrRequests, pData->mutCtrRequests);
+ CHKiRet(writeFile(ppString, iMsgOpts, pData));
+ if(!bCoreSupportsBatching && pData->bFlushOnTXEnd) {
+ CHKiRet(strm.Flush(pData->pStrm));
+ }
+finalize_it:
+ if(iRet == RS_RET_OK)
+ iRet = RS_RET_DEFER_COMMIT;
+ENDdoAction
+
+
+static inline void
+setInstParamDefaults(instanceData *pData)
+{
+ pData->f_fname = NULL;
+ pData->tplName = NULL;
+ pData->fileUID = -1;
+ pData->fileGID = -1;
+ pData->dirUID = -1;
+ pData->dirGID = -1;
+ pData->bFailOnChown = 1;
+ pData->iDynaFileCacheSize = 10;
+ pData->fCreateMode = 0644;
+ pData->fDirCreateMode = 0700;
+ pData->bCreateDirs = 1;
+ pData->bSyncFile = 0;
+ pData->iZipLevel = 0;
+ pData->bVeryRobustZip = 0;
+ pData->bFlushOnTXEnd = FLUSHONTX_DFLT;
+ pData->iIOBufSize = IOBUF_DFLT_SIZE;
+ pData->iFlushInterval = FLUSH_INTRVL_DFLT;
+ pData->bUseAsyncWriter = USE_ASYNCWRITER_DFLT;
+ pData->sigprovName = NULL;
+ pData->cryprovName = NULL;
+ pData->useSigprov = 0;
+ pData->useCryprov = 0;
+}
+
+
+static rsRetVal
+setupInstStatsCtrs(instanceData *pData)
+{
+ uchar ctrName[512];
+ DEFiRet;
+
+ if(!pData->bDynamicName) {
+ FINALIZE;
+ }
+
+ /* support statistics gathering */
+ snprintf((char*)ctrName, sizeof(ctrName), "dynafile cache %s", pData->f_fname);
+ ctrName[sizeof(ctrName)-1] = '\0'; /* be on the save side */
+ CHKiRet(statsobj.Construct(&(pData->stats)));
+ CHKiRet(statsobj.SetName(pData->stats, ctrName));
+ STATSCOUNTER_INIT(pData->ctrRequests, pData->mutCtrRequests);
+ CHKiRet(statsobj.AddCounter(pData->stats, UCHAR_CONSTANT("requests"),
+ ctrType_IntCtr, &(pData->ctrRequests)));
+ STATSCOUNTER_INIT(pData->ctrLevel0, pData->mutCtrLevel0);
+ CHKiRet(statsobj.AddCounter(pData->stats, UCHAR_CONSTANT("level0"),
+ ctrType_IntCtr, &(pData->ctrLevel0)));
+ STATSCOUNTER_INIT(pData->ctrMiss, pData->mutCtrMiss);
+ CHKiRet(statsobj.AddCounter(pData->stats, UCHAR_CONSTANT("missed"),
+ ctrType_IntCtr, &(pData->ctrMiss)));
+ STATSCOUNTER_INIT(pData->ctrEvict, pData->mutCtrEvict);
+ CHKiRet(statsobj.AddCounter(pData->stats, UCHAR_CONSTANT("evicted"),
+ ctrType_IntCtr, &(pData->ctrEvict)));
+ STATSCOUNTER_INIT(pData->ctrMax, pData->mutCtrMax);
+ CHKiRet(statsobj.AddCounter(pData->stats, UCHAR_CONSTANT("maxused"),
+ ctrType_IntCtr, &(pData->ctrMax)));
+ CHKiRet(statsobj.ConstructFinalize(pData->stats));
+
+finalize_it:
+ RETiRet;
+}
+
+static inline void
+initSigprov(instanceData *pData, struct nvlst *lst)
+{
+ uchar szDrvrName[1024];
+
+ if(snprintf((char*)szDrvrName, sizeof(szDrvrName), "lmsig_%s", pData->sigprovName)
+ == sizeof(szDrvrName)) {
+ errmsg.LogError(0, RS_RET_ERR, "omfile: signature provider "
+ "name is too long: '%s' - signatures disabled",
+ pData->sigprovName);
+ goto done;
+ }
+ pData->sigprovNameFull = ustrdup(szDrvrName);
+
+ pData->sigprov.ifVersion = sigprovCURR_IF_VERSION;
+ /* The pDrvrName+2 below is a hack to obtain the object name. It
+ * safes us to have yet another variable with the name without "lm" in
+ * front of it. If we change the module load interface, we may re-think
+ * about this hack, but for the time being it is efficient and clean enough.
+ */
+ if(obj.UseObj(__FILE__, szDrvrName, szDrvrName, (void*) &pData->sigprov)
+ != RS_RET_OK) {
+ errmsg.LogError(0, RS_RET_LOAD_ERROR, "omfile: could not load "
+ "signature provider '%s' - signatures disabled",
+ szDrvrName);
+ goto done;
+ }
+
+ if(pData->sigprov.Construct(&pData->sigprovData) != RS_RET_OK) {
+ errmsg.LogError(0, RS_RET_SIGPROV_ERR, "omfile: error constructing "
+ "signature provider %s dataset - signatures disabled",
+ szDrvrName);
+ goto done;
+ }
+ pData->sigprov.SetCnfParam(pData->sigprovData, lst);
+
+ dbgprintf("loaded signature provider %s, data instance at %p\n",
+ szDrvrName, pData->sigprovData);
+ pData->useSigprov = 1;
+done: return;
+}
+
+static inline rsRetVal
+initCryprov(instanceData *pData, struct nvlst *lst)
+{
+ uchar szDrvrName[1024];
+ DEFiRet;
+
+ if(snprintf((char*)szDrvrName, sizeof(szDrvrName), "lmcry_%s", pData->cryprovName)
+ == sizeof(szDrvrName)) {
+ errmsg.LogError(0, RS_RET_ERR, "omfile: crypto provider "
+ "name is too long: '%s' - encryption disabled",
+ pData->cryprovName);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ pData->cryprovNameFull = ustrdup(szDrvrName);
+
+ pData->cryprov.ifVersion = cryprovCURR_IF_VERSION;
+ /* The pDrvrName+2 below is a hack to obtain the object name. It
+ * safes us to have yet another variable with the name without "lm" in
+ * front of it. If we change the module load interface, we may re-think
+ * about this hack, but for the time being it is efficient and clean enough.
+ */
+ if(obj.UseObj(__FILE__, szDrvrName, szDrvrName, (void*) &pData->cryprov)
+ != RS_RET_OK) {
+ errmsg.LogError(0, RS_RET_LOAD_ERROR, "omfile: could not load "
+ "crypto provider '%s' - encryption disabled",
+ szDrvrName);
+ ABORT_FINALIZE(RS_RET_CRYPROV_ERR);
+ }
+
+ if(pData->cryprov.Construct(&pData->cryprovData) != RS_RET_OK) {
+ errmsg.LogError(0, RS_RET_CRYPROV_ERR, "omfile: error constructing "
+ "crypto provider %s dataset - encryption disabled",
+ szDrvrName);
+ ABORT_FINALIZE(RS_RET_CRYPROV_ERR);
+ }
+ CHKiRet(pData->cryprov.SetCnfParam(pData->cryprovData, lst, CRYPROV_PARAMTYPE_REGULAR));
+
+ dbgprintf("loaded crypto provider %s, data instance at %p\n",
+ szDrvrName, pData->cryprovData);
+ pData->useCryprov = 1;
+finalize_it:
+ RETiRet;
+}
+
+BEGINnewActInst
+ struct cnfparamvals *pvals;
+ uchar *tplToUse;
+ int i;
+CODESTARTnewActInst
+ DBGPRINTF("newActInst (omfile)\n");
+
+ pvals = nvlstGetParams(lst, &actpblk, NULL);
+ if(pvals == NULL) {
+ errmsg.LogError(0, RS_RET_MISSING_CNFPARAMS, "omfile: either the \"file\" or "
+ "\"dynfile\" parameter must be given");
+ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+ }
+
+ if(Debug) {
+ dbgprintf("action param blk in omfile:\n");
+ cnfparamsPrint(&actpblk, pvals);
+ }
+
+ CHKiRet(createInstance(&pData));
+ setInstParamDefaults(pData);
+
+ for(i = 0 ; i < actpblk.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(actpblk.descr[i].name, "dynafilecachesize")) {
+ pData->iDynaFileCacheSize = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "ziplevel")) {
+ pData->iZipLevel = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "flushinterval")) {
+ pData->iFlushInterval = pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "veryrobustzip")) {
+ pData->bVeryRobustZip = pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "asyncwriting")) {
+ pData->bUseAsyncWriter = pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "flushontxend")) {
+ pData->bFlushOnTXEnd = pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "iobuffersize")) {
+ pData->iIOBufSize = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "dirowner")) {
+ pData->dirUID = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "dirgroup")) {
+ pData->dirGID = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "fileowner")) {
+ pData->fileUID = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "filegroup")) {
+ pData->fileGID = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "dircreatemode")) {
+ pData->fDirCreateMode = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "filecreatemode")) {
+ pData->fCreateMode = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "failonchownfailure")) {
+ pData->bFailOnChown = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "sync")) {
+ pData->bSyncFile = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "createdirs")) {
+ pData->bCreateDirs = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "file")) {
+ pData->f_fname = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ CODE_STD_STRING_REQUESTnewActInst(1)
+ pData->bDynamicName = 0;
+ } else if(!strcmp(actpblk.descr[i].name, "dynafile")) {
+ pData->f_fname = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ CODE_STD_STRING_REQUESTnewActInst(2)
+ pData->bDynamicName = 1;
+ } 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, "sig.provider")) {
+ pData->sigprovName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(actpblk.descr[i].name, "cry.provider")) {
+ pData->cryprovName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else {
+ dbgprintf("omfile: program error, non-handled "
+ "param '%s'\n", actpblk.descr[i].name);
+ }
+ }
+
+ if(pData->f_fname == NULL) {
+ errmsg.LogError(0, RS_RET_MISSING_CNFPARAMS, "omfile: either the \"file\" or "
+ "\"dynfile\" parameter must be given");
+ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+ }
+
+ if(pData->sigprovName != NULL) {
+ initSigprov(pData, lst);
+ }
+
+ if(pData->cryprovName != NULL) {
+ CHKiRet(initCryprov(pData, lst));
+ }
+
+ tplToUse = ustrdup((pData->tplName == NULL) ? getDfltTpl() : pData->tplName);
+ CHKiRet(OMSRsetEntry(*ppOMSR, 0, tplToUse, 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
+ * to the pOMSR. -- rgerhards, 2007-07-27
+ */
+ CHKiRet(OMSRsetEntry(*ppOMSR, 1, ustrdup(pData->f_fname), OMSR_NO_RQD_TPL_OPTS));
+ // TODO: create unified code for this (legacy+v6 system)
+ /* we now allocate the cache table */
+ CHKmalloc(pData->dynCache = (dynaFileCacheEntry**)
+ calloc(pData->iDynaFileCacheSize, sizeof(dynaFileCacheEntry*)));
+ pData->iCurrElt = -1; /* no current element */
+ }
+// TODO: add pData->iSizeLimit = 0; /* default value, use outchannels to configure! */
+ setupInstStatsCtrs(pData);
+
+CODE_STD_FINALIZERnewActInst
+ cnfparamvalsDestruct(pvals, &actpblk);
+ENDnewActInst
+
+
+BEGINparseSelectorAct
+ uchar fname[MAXFNAME];
+CODESTARTparseSelectorAct
+ /* Note: the indicator sequence permits us to use '$' to signify
+ * outchannel, what otherwise is not possible due to truely
+ * unresolvable grammar conflicts (*this time no way around*).
+ * rgerhards, 2011-07-09
+ */
+ if(!strncmp((char*) p, ":omfile:", sizeof(":omfile:") - 1)) {
+ p += sizeof(":omfile:") - 1;
+ }
+ if(!(*p == '$' || *p == '?' || *p == '/' || *p == '.' || *p == '-'))
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+
+ CHKiRet(createInstance(&pData));
+
+ if(*p == '-') {
+ pData->bSyncFile = 0;
+ p++;
+ } else {
+ pData->bSyncFile = cs.bEnableSync;
+ }
+ pData->iSizeLimit = 0; /* default value, use outchannels to configure! */
+
+ switch(*p) {
+ case '$':
+ CODE_STD_STRING_REQUESTparseSelectorAct(1)
+ /* rgerhards 2005-06-21: this is a special setting for output-channel
+ * definitions. In the long term, this setting will probably replace
+ * anything else, but for the time being we must co-exist with the
+ * traditional mode lines.
+ * rgerhards, 2007-07-24: output-channels will go away. We keep them
+ * for compatibility reasons, but seems to have been a bad idea.
+ */
+ CHKiRet(cflineParseOutchannel(pData, p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS));
+ pData->bDynamicName = 0;
+ break;
+
+ case '?': /* This is much like a regular file handle, but we need to obtain
+ * a template name. rgerhards, 2007-07-03
+ */
+ CODE_STD_STRING_REQUESTparseSelectorAct(2)
+ ++p; /* eat '?' */
+ 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 */
+ /* "filename" is actually a template name, we need this as string 1. So let's add it
+ * to the pOMSR. -- rgerhards, 2007-07-27
+ */
+ CHKiRet(OMSRsetEntry(*ppOMSR, 1, ustrdup(pData->f_fname), OMSR_NO_RQD_TPL_OPTS));
+ /* we now allocate the cache table */
+ CHKmalloc(pData->dynCache = (dynaFileCacheEntry**)
+ calloc(cs.iDynaFileCacheSize, sizeof(dynaFileCacheEntry*)));
+ break;
+
+ case '/':
+ case '.':
+ CODE_STD_STRING_REQUESTparseSelectorAct(1)
+ CHKiRet(cflineParseFileName(p, fname, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, getDfltTpl()));
+ pData->f_fname = ustrdup(fname);
+ pData->bDynamicName = 0;
+ break;
+ default:
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+ }
+
+ /* freeze current paremeters for this action */
+ pData->iDynaFileCacheSize = cs.iDynaFileCacheSize;
+ pData->fCreateMode = cs.fCreateMode;
+ pData->fDirCreateMode = cs.fDirCreateMode;
+ pData->bCreateDirs = cs.bCreateDirs;
+ pData->bFailOnChown = cs.bFailOnChown;
+ pData->fileUID = cs.fileUID;
+ pData->fileGID = cs.fileGID;
+ pData->dirUID = cs.dirUID;
+ pData->dirGID = cs.dirGID;
+ pData->iZipLevel = cs.iZipLevel;
+ pData->bFlushOnTXEnd = cs.bFlushOnTXEnd;
+ pData->iIOBufSize = (int) cs.iIOBufSize;
+ pData->iFlushInterval = cs.iFlushInterval;
+ pData->bUseAsyncWriter = cs.bUseAsyncWriter;
+ pData->bVeryRobustZip = 0; /* cannot be specified via legacy conf */
+ setupInstStatsCtrs(pData);
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+
+/* Reset config variables for this module to default values.
+ * rgerhards, 2007-07-17
+ */
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
+{
+ cs.fileUID = -1;
+ cs.fileGID = -1;
+ cs.dirUID = -1;
+ cs.dirGID = -1;
+ cs.bFailOnChown = 1;
+ cs.iDynaFileCacheSize = 10;
+ cs.fCreateMode = 0644;
+ cs.fDirCreateMode = 0700;
+ cs.bCreateDirs = 1;
+ cs.bEnableSync = 0;
+ cs.iZipLevel = 0;
+ cs.bFlushOnTXEnd = FLUSHONTX_DFLT;
+ cs.iIOBufSize = IOBUF_DFLT_SIZE;
+ cs.iFlushInterval = FLUSH_INTRVL_DFLT;
+ cs.bUseAsyncWriter = USE_ASYNCWRITER_DFLT;
+ free(pszFileDfltTplName);
+ pszFileDfltTplName = NULL;
+ return RS_RET_OK;
+}
+
+
+BEGINdoHUP
+CODESTARTdoHUP
+ if(pData->bDynamicName) {
+ dynaFileFreeCacheEntries(pData);
+ } else {
+ if(pData->pStrm != NULL) {
+ closeFile(pData);
+ }
+ }
+ENDdoHUP
+
+
+BEGINmodExit
+CODESTARTmodExit
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(strm, CORE_COMPONENT);
+ objRelease(statsobj, CORE_COMPONENT);
+ DESTROY_ATOMIC_HELPER_MUT(mutClock);
+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
+ENDqueryEtryPt
+
+
+BEGINmodInit(File)
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+INITLegCnfVars
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(strm, CORE_COMPONENT));
+ CHKiRet(objUse(statsobj, CORE_COMPONENT));
+
+ INIT_ATOMIC_HELPER_MUT(mutClock);
+
+ INITChkCoreFeature(bCoreSupportsBatching, CORE_FEATURE_BATCHING);
+ DBGPRINTF("omfile: %susing transactional output interface.\n", bCoreSupportsBatching ? "" : "not ");
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"dynafilecachesize", 0, eCmdHdlrInt, (void*) setDynaFileCacheSize, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileziplevel", 0, eCmdHdlrInt, NULL, &cs.iZipLevel, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileflushinterval", 0, eCmdHdlrInt, NULL, &cs.iFlushInterval, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileasyncwriting", 0, eCmdHdlrBinary, NULL, &cs.bUseAsyncWriter, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileflushontxend", 0, eCmdHdlrBinary, NULL, &cs.bFlushOnTXEnd, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileiobuffersize", 0, eCmdHdlrSize, NULL, &cs.iIOBufSize, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"dirowner", 0, eCmdHdlrUID, NULL, &cs.dirUID, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"dirgroup", 0, eCmdHdlrGID, NULL, &cs.dirGID, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"fileowner", 0, eCmdHdlrUID, NULL, &cs.fileUID, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"filegroup", 0, eCmdHdlrGID, NULL, &cs.fileGID, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"dircreatemode", 0, eCmdHdlrFileCreateMode, NULL, &cs.fDirCreateMode, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"filecreatemode", 0, eCmdHdlrFileCreateMode, NULL, &cs.fCreateMode, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"createdirs", 0, eCmdHdlrBinary, NULL, &cs.bCreateDirs, STD_LOADABLE_MODULE_ID));
+ 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(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/omfile.h b/tools/omfile.h
new file mode 100644
index 00000000..eca88590
--- /dev/null
+++ b/tools/omfile.h
@@ -0,0 +1,37 @@
+/* omfile.h
+ * These are the definitions for the build-in file output module.
+ *
+ * File begun on 2007-07-21 by RGerhards
+ *
+ * Copyright 2007-2012 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef OMFILE_H_INCLUDED
+#define OMFILE_H_INCLUDED 1
+
+/* prototypes */
+rsRetVal modInitFile(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*);
+
+/* the define below is dirty, but we need it for ompipe integration. There is no
+ * other way to have the functionality (well, one way would be to go through the
+ * globals, but that seems not yet justified. -- rgerhards, 2010-03-01
+ */
+uchar *pszFileDfltTplName;
+#endif /* #ifndef OMFILE_H_INCLUDED */
+/* vi:set ai:
+ */
diff --git a/tools/omfwd.c b/tools/omfwd.c
new file mode 100644
index 00000000..179e9614
--- /dev/null
+++ b/tools/omfwd.c
@@ -0,0 +1,1267 @@
+/* omfwd.c
+ * This is the implementation of the build-in forwarding output module.
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * Copyright 2007-2013 Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * TODO v6 config:
+ * - permitted peer *list*
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <fnmatch.h>
+#include <assert.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdint.h>
+#ifdef USE_NETZIP
+#include <zlib.h>
+#endif
+#include <pthread.h>
+#include "syslogd.h"
+#include "conf.h"
+#include "syslogd-types.h"
+#include "srUtils.h"
+#include "net.h"
+#include "netstrms.h"
+#include "netstrm.h"
+#include "omfwd.h"
+#include "template.h"
+#include "msg.h"
+#include "tcpclt.h"
+#include "cfsysline.h"
+#include "module-template.h"
+#include "glbl.h"
+#include "errmsg.h"
+#include "unicode-helper.h"
+
+MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("omfwd")
+
+/* internal structures
+ */
+DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(net)
+DEFobjCurrIf(netstrms)
+DEFobjCurrIf(netstrm)
+DEFobjCurrIf(tcpclt)
+
+typedef struct _instanceData {
+ uchar *tplName; /* name of assigned template */
+ netstrms_t *pNS; /* netstream subsystem */
+ netstrm_t *pNetstrm; /* our output netstream */
+ uchar *pszStrmDrvr;
+ uchar *pszStrmDrvrAuthMode;
+ permittedPeers_t *pPermPeers;
+ int iStrmDrvrMode;
+ char *target;
+ int *pSockArray; /* sockets to use for UDP */
+ int bIsConnected; /* are we connected to remote host? 0 - no, 1 - yes, UDP means addr resolved */
+ struct addrinfo *f_addr;
+ int compressionLevel; /* 0 - no compression, else level for zlib */
+ char *port;
+ int protocol;
+ int iRebindInterval; /* rebind interval */
+ int nXmit; /* number of transmissions since last (re-)bind */
+# define FORW_UDP 0
+# define FORW_TCP 1
+ /* following fields for TCP-based delivery */
+ TCPFRAMINGMODE tcp_framing;
+ int bResendLastOnRecon; /* should the last message be re-sent on a successful reconnect? */
+ tcpclt_t *pTCPClt; /* our tcpclt object */
+# define COMPRESS_NEVER 0
+# define COMPRESS_SINGLE_MSG 1 /* old, single-message compression */
+ /* all other settings are for stream-compression */
+# define COMPRESS_STREAM_ALWAYS 2
+ uint8_t compressionMode;
+ sbool bzInitDone; /* did we do an init of zstrm already? */
+ z_stream zstrm; /* zip stream to use for tcp compression */
+ uchar sndBuf[16*1024]; /* this is intensionally fixed -- see no good reason to make configurable */
+ unsigned offsSndBuf; /* next free spot in send buffer */
+} instanceData;
+
+/* config data */
+typedef struct configSettings_s {
+ uchar *pszTplName; /* name of the default template to use */
+ uchar *pszStrmDrvr; /* name of the stream driver to use */
+ int iStrmDrvrMode; /* mode for stream driver, driver-dependent (0 mostly means plain tcp) */
+ int bResendLastOnRecon; /* should the last message be re-sent on a successful reconnect? */
+ uchar *pszStrmDrvrAuthMode; /* authentication mode to use */
+ int iTCPRebindInterval; /* support for automatic re-binding (load balancers!). 0 - no rebind */
+ int iUDPRebindInterval; /* support for automatic re-binding (load balancers!). 0 - no rebind */
+ permittedPeers_t *pPermPeers;
+} configSettings_t;
+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 },
+ { "port", eCmdHdlrGetWord, 0 },
+ { "protocol", eCmdHdlrGetWord, 0 },
+ { "tcp_framing", eCmdHdlrGetWord, 0 },
+ { "ziplevel", eCmdHdlrInt, 0 },
+ { "compression.mode", eCmdHdlrGetWord, 0 },
+ { "rebindinterval", eCmdHdlrInt, 0 },
+ { "streamdriver", eCmdHdlrGetWord, 0 },
+ { "streamdrivermode", eCmdHdlrInt, 0 },
+ { "streamdriverauthmode", eCmdHdlrGetWord, 0 },
+ { "streamdriverpermittedpeers", eCmdHdlrGetWord, 0 },
+ { "resendlastmsgonreconnect", eCmdHdlrBinary, 0 },
+ { "template", eCmdHdlrGetWord, 0 },
+};
+static struct cnfparamblk actpblk =
+ { CNFPARAMBLK_VERSION,
+ sizeof(actpdescr)/sizeof(struct cnfparamdescr),
+ 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
+ cs.pszTplName = NULL; /* name of the default template to use */
+ cs.pszStrmDrvr = NULL; /* name of the stream driver to use */
+ cs.iStrmDrvrMode = 0; /* mode for stream driver, driver-dependent (0 mostly means plain tcp) */
+ cs.bResendLastOnRecon = 0; /* should the last message be re-sent on a successful reconnect? */
+ cs.pszStrmDrvrAuthMode = NULL; /* authentication mode to use */
+ cs.iUDPRebindInterval = 0; /* support for automatic re-binding (load balancers!). 0 - no rebind */
+ cs.iTCPRebindInterval = 0; /* support for automatic re-binding (load balancers!). 0 - no rebind */
+ cs.pPermPeers = NULL;
+ENDinitConfVars
+
+
+static rsRetVal doTryResume(instanceData *pData);
+static rsRetVal doZipFinish(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
+ */
+static rsRetVal
+closeUDPSockets(instanceData *pData)
+{
+ DEFiRet;
+ assert(pData != NULL);
+ if(pData->pSockArray != NULL) {
+ net.closeUDPListenSockets(pData->pSockArray);
+ pData->pSockArray = NULL;
+ freeaddrinfo(pData->f_addr);
+ pData->f_addr = NULL;
+ }
+pData->bIsConnected = 0; // TODO: remove this variable altogether
+ RETiRet;
+}
+
+
+/* destruct the TCP helper objects
+ * This, for example, is needed after something went wrong.
+ * This function is void because it "can not" fail.
+ * rgerhards, 2008-06-04
+ * Note that we DO NOT discard the current buffer contents
+ * (if any). This permits us to save data between sessions. In
+ * the wort case, some duplication occurs, but we do not
+ * loose data.
+ */
+static inline void
+DestructTCPInstanceData(instanceData *pData)
+{
+ assert(pData != NULL);
+ doZipFinish(pData);
+ if(pData->pNetstrm != NULL)
+ netstrm.Destruct(&pData->pNetstrm);
+ if(pData->pNS != NULL)
+ 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;
+ENDcreateInstance
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURERepeatedMsgReduction)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ /* final cleanup */
+ DestructTCPInstanceData(pData);
+ closeUDPSockets(pData);
+
+ if(pData->protocol == FORW_TCP) {
+ tcpclt.Destruct(&pData->pTCPClt);
+ }
+
+ free(pData->port);
+ free(pData->target);
+ free(pData->pszStrmDrvr);
+ free(pData->pszStrmDrvrAuthMode);
+ net.DestructPermittedPeers(&pData->pPermPeers);
+ENDfreeInstance
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ dbgprintf("%s", pData->target);
+ENDdbgPrintInstInfo
+
+
+/* Send a message via UDP
+ * rgehards, 2007-12-20
+ */
+static rsRetVal UDPSend(instanceData *pData, char *msg, size_t len)
+{
+ DEFiRet;
+ struct addrinfo *r;
+ int i;
+ unsigned lsent = 0;
+ int bSendSuccess;
+
+ if(pData->iRebindInterval && (pData->nXmit++ % pData->iRebindInterval == 0)) {
+ dbgprintf("omfwd dropping UDP 'connection' (as configured)\n");
+ pData->nXmit = 1; /* else we have an addtl wrap at 2^31-1 */
+ CHKiRet(closeUDPSockets(pData));
+ }
+
+ if(pData->pSockArray == NULL) {
+ CHKiRet(doTryResume(pData));
+ }
+
+ if(pData->pSockArray != NULL) {
+ /* we need to track if we have success sending to the remote
+ * peer. Success is indicated by at least one sendto() call
+ * succeeding. We track this be bSendSuccess. We can not simply
+ * rely on lsent, as a call might initially work, but a later
+ * call fails. Then, lsent has the error status, even though
+ * the sendto() succeeded. -- rgerhards, 2007-06-22
+ */
+ bSendSuccess = RSFALSE;
+ for (r = pData->f_addr; r; r = r->ai_next) {
+ for (i = 0; i < *pData->pSockArray; i++) {
+ lsent = sendto(pData->pSockArray[i+1], msg, len, 0, r->ai_addr, r->ai_addrlen);
+ if (lsent == len) {
+ bSendSuccess = RSTRUE;
+ break;
+ } else {
+ int eno = errno;
+ char errStr[1024];
+ dbgprintf("sendto() error: %d = %s.\n",
+ eno, rs_strerror_r(eno, errStr, sizeof(errStr)));
+ }
+ }
+ if (lsent == len && !send_to_all)
+ break;
+ }
+ /* finished looping */
+ if (bSendSuccess == RSFALSE) {
+ dbgprintf("error forwarding via udp, suspending\n");
+ iRet = RS_RET_SUSPENDED;
+ }
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* set the permitted peers -- rgerhards, 2008-05-19
+ */
+static rsRetVal
+setPermittedPeer(void __attribute__((unused)) *pVal, uchar *pszID)
+{
+ DEFiRet;
+ CHKiRet(net.AddPermittedPeer(&cs.pPermPeers, pszID));
+ free(pszID); /* no longer needed, but we must free it as of interface def */
+finalize_it:
+ RETiRet;
+}
+
+
+
+/* CODE FOR SENDING TCP MESSAGES */
+
+static rsRetVal
+TCPSendBufUncompressed(instanceData *pData, uchar *buf, unsigned len)
+{
+ DEFiRet;
+ unsigned alreadySent;
+ ssize_t lenSend;
+
+ alreadySent = 0;
+ CHKiRet(netstrm.CheckConnection(pData->pNetstrm)); /* hack for plain tcp syslog - see ptcp driver for details */
+
+ while(alreadySent != len) {
+ lenSend = len - alreadySent;
+ CHKiRet(netstrm.Send(pData->pNetstrm, buf+alreadySent, &lenSend));
+ DBGPRINTF("omfwd: TCP sent %ld bytes, requested %u\n", (long) lenSend, len - alreadySent);
+ alreadySent += lenSend;
+ }
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ /* error! */
+ dbgprintf("TCPSendBuf error %d, destruct TCP Connection!\n", iRet);
+ DestructTCPInstanceData(pData);
+ iRet = RS_RET_SUSPENDED;
+ }
+ RETiRet;
+}
+
+static rsRetVal
+TCPSendBufCompressed(instanceData *pData, uchar *buf, unsigned len)
+{
+ int zRet; /* zlib return state */
+ unsigned outavail;
+ uchar zipBuf[32*1024];
+ DEFiRet;
+
+ if(!pData->bzInitDone) {
+ /* allocate deflate state */
+ pData->zstrm.zalloc = Z_NULL;
+ pData->zstrm.zfree = Z_NULL;
+ pData->zstrm.opaque = Z_NULL;
+ /* see note in file header for the params we use with deflateInit2() */
+ zRet = deflateInit(&pData->zstrm, 9);
+ if(zRet != Z_OK) {
+ DBGPRINTF("error %d returned from zlib/deflateInit()\n", zRet);
+ ABORT_FINALIZE(RS_RET_ZLIB_ERR);
+ }
+ pData->bzInitDone = RSTRUE;
+ }
+
+ /* now doing the compression */
+ pData->zstrm.next_in = (Bytef*) buf;
+ pData->zstrm.avail_in = len;
+ /* run deflate() on buffer until everything has been compressed */
+ do {
+ DBGPRINTF("omfwd: in deflate() loop, avail_in %d, total_in %ld\n", pData->zstrm.avail_in, pData->zstrm.total_in);
+ pData->zstrm.avail_out = sizeof(zipBuf);
+ pData->zstrm.next_out = zipBuf;
+ zRet = deflate(&pData->zstrm, Z_NO_FLUSH); /* no bad return value */
+ DBGPRINTF("after deflate, ret %d, avail_out %d\n", zRet, pData->zstrm.avail_out);
+ outavail = sizeof(zipBuf) - pData->zstrm.avail_out;
+ if(outavail != 0) {
+ CHKiRet(TCPSendBufUncompressed(pData, zipBuf, outavail));
+ }
+ } while (pData->zstrm.avail_out == 0);
+
+finalize_it:
+ RETiRet;
+}
+
+static rsRetVal
+TCPSendBuf(instanceData *pData, uchar *buf, unsigned len)
+{
+ DEFiRet;
+ if(pData->compressionMode >= COMPRESS_STREAM_ALWAYS)
+ iRet = TCPSendBufCompressed(pData, buf, len);
+ else
+ iRet = TCPSendBufUncompressed(pData, buf, len);
+ RETiRet;
+}
+
+/* finish zlib buffer, to be called before closing the ZIP file (if
+ * running in stream mode).
+ */
+static rsRetVal
+doZipFinish(instanceData *pData)
+{
+ int zRet; /* zlib return state */
+ DEFiRet;
+ unsigned outavail;
+ uchar zipBuf[32*1024];
+
+ if(!pData->bzInitDone)
+ goto done;
+
+// TODO: can we get this into a single common function?
+dbgprintf("DDDD: in doZipFinish()\n");
+ pData->zstrm.avail_in = 0;
+ /* run deflate() on buffer until everything has been compressed */
+ do {
+ DBGPRINTF("in deflate() loop, avail_in %d, total_in %ld\n", pData->zstrm.avail_in, pData->zstrm.total_in);
+ pData->zstrm.avail_out = sizeof(zipBuf);
+ pData->zstrm.next_out = zipBuf;
+ zRet = deflate(&pData->zstrm, Z_FINISH); /* no bad return value */
+ DBGPRINTF("after deflate, ret %d, avail_out %d\n", zRet, pData->zstrm.avail_out);
+ outavail = sizeof(zipBuf) - pData->zstrm.avail_out;
+ if(outavail != 0) {
+ CHKiRet(TCPSendBufUncompressed(pData, zipBuf, outavail));
+ }
+ } while (pData->zstrm.avail_out == 0);
+
+finalize_it:
+ zRet = deflateEnd(&pData->zstrm);
+ if(zRet != Z_OK) {
+ DBGPRINTF("error %d returned from zlib/deflateEnd()\n", zRet);
+ }
+
+ pData->bzInitDone = 0;
+done: RETiRet;
+}
+
+
+/* Add frame to send buffer (or send, if requried)
+ */
+static rsRetVal TCPSendFrame(void *pvData, char *msg, size_t len)
+{
+ DEFiRet;
+ instanceData *pData = (instanceData *) pvData;
+
+ DBGPRINTF("omfwd: add %u bytes to send buffer (curr offs %u)\n",
+ (unsigned) len, pData->offsSndBuf);
+ if(pData->offsSndBuf != 0 && pData->offsSndBuf + len >= sizeof(pData->sndBuf)) {
+ /* no buffer space left, need to commit previous records */
+ CHKiRet(TCPSendBuf(pData, pData->sndBuf, pData->offsSndBuf));
+ pData->offsSndBuf = 0;
+ iRet = RS_RET_PREVIOUS_COMMITTED;
+ }
+
+ /* check if the message is too large to fit into buffer */
+ if(len > sizeof(pData->sndBuf)) {
+ CHKiRet(TCPSendBuf(pData, (uchar*)msg, len));
+ ABORT_FINALIZE(RS_RET_OK); /* committed everything so far */
+ }
+
+ /* we now know the buffer has enough free space */
+ memcpy(pData->sndBuf + pData->offsSndBuf, msg, len);
+ pData->offsSndBuf += len;
+ iRet = RS_RET_DEFER_COMMIT;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* This function is called immediately before a send retry is attempted.
+ * It shall clean up whatever makes sense.
+ * rgerhards, 2007-12-28
+ */
+static rsRetVal TCPSendPrepRetry(void *pvData)
+{
+ DEFiRet;
+ instanceData *pData = (instanceData *) pvData;
+dbgprintf("TCPSendPrepRetry performs a DestructTCPInstanceData\n");
+
+ assert(pData != NULL);
+ DestructTCPInstanceData(pData);
+ RETiRet;
+}
+
+
+/* initializes everything so that TCPSend can work.
+ * rgerhards, 2007-12-28
+ */
+static rsRetVal TCPSendInit(void *pvData)
+{
+ DEFiRet;
+ instanceData *pData = (instanceData *) pvData;
+
+ assert(pData != NULL);
+ if(pData->pNetstrm == NULL) {
+ dbgprintf("TCPSendInit CREATE\n");
+ CHKiRet(netstrms.Construct(&pData->pNS));
+ /* the stream driver must be set before the object is finalized! */
+ CHKiRet(netstrms.SetDrvrName(pData->pNS, pData->pszStrmDrvr));
+ CHKiRet(netstrms.ConstructFinalize(pData->pNS));
+
+ /* now create the actual stream and connect to the server */
+ CHKiRet(netstrms.CreateStrm(pData->pNS, &pData->pNetstrm));
+ CHKiRet(netstrm.ConstructFinalize(pData->pNetstrm));
+ CHKiRet(netstrm.SetDrvrMode(pData->pNetstrm, pData->iStrmDrvrMode));
+ /* now set optional params, but only if they were actually configured */
+ if(pData->pszStrmDrvrAuthMode != NULL) {
+ CHKiRet(netstrm.SetDrvrAuthMode(pData->pNetstrm, pData->pszStrmDrvrAuthMode));
+ }
+ if(pData->pPermPeers != NULL) {
+ CHKiRet(netstrm.SetDrvrPermPeers(pData->pNetstrm, pData->pPermPeers));
+ }
+ /* params set, now connect */
+ CHKiRet(netstrm.Connect(pData->pNetstrm, glbl.GetDefPFFamily(),
+ (uchar*)pData->port, (uchar*)pData->target));
+ }
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ dbgprintf("TCPSendInit FAILED with %d.\n", iRet);
+ DestructTCPInstanceData(pData);
+ }
+
+ RETiRet;
+}
+
+
+/* try to resume connection if it is not ready
+ * rgerhards, 2007-08-02
+ */
+static rsRetVal doTryResume(instanceData *pData)
+{
+ int iErr;
+ struct addrinfo *res;
+ struct addrinfo hints;
+ DEFiRet;
+
+ if(pData->bIsConnected)
+ FINALIZE;
+
+ /* The remote address is not yet known and needs to be obtained */
+ dbgprintf(" %s\n", pData->target);
+ if(pData->protocol == FORW_UDP) {
+ memset(&hints, 0, sizeof(hints));
+ /* port must be numeric, because config file syntax requires this */
+ hints.ai_flags = AI_NUMERICSERV;
+ hints.ai_family = glbl.GetDefPFFamily();
+ hints.ai_socktype = SOCK_DGRAM;
+ if((iErr = (getaddrinfo(pData->target, pData->port, &hints, &res))) != 0) {
+ dbgprintf("could not get addrinfo for hostname '%s':'%s': %d%s\n",
+ pData->target, pData->port, iErr, gai_strerror(iErr));
+ ABORT_FINALIZE(RS_RET_SUSPENDED);
+ }
+ dbgprintf("%s found, resuming.\n", pData->target);
+ pData->f_addr = res;
+ pData->bIsConnected = 1;
+ if(pData->pSockArray == NULL) {
+ pData->pSockArray = net.create_udp_socket((uchar*)pData->target, NULL, 0);
+ }
+ } else {
+ CHKiRet(TCPSendInit((void*)pData));
+ }
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pData->f_addr != NULL) {
+ freeaddrinfo(pData->f_addr);
+ pData->f_addr = NULL;
+ }
+ iRet = RS_RET_SUSPENDED;
+ }
+
+ RETiRet;
+}
+
+
+BEGINtryResume
+CODESTARTtryResume
+ iRet = doTryResume(pData);
+ENDtryResume
+
+
+BEGINbeginTransaction
+CODESTARTbeginTransaction
+dbgprintf("omfwd: beginTransaction\n");
+ENDbeginTransaction
+
+
+BEGINdoAction
+ char *psz; /* temporary buffering */
+ register unsigned l;
+ int iMaxLine;
+# ifdef USE_NETZIP
+ Bytef *out = NULL; /* for compression */
+# endif
+CODESTARTdoAction
+ CHKiRet(doTryResume(pData));
+
+ iMaxLine = glbl.GetMaxLine();
+
+ dbgprintf(" %s:%s/%s\n", pData->target, pData->port,
+ pData->protocol == FORW_UDP ? "udp" : "tcp");
+
+ psz = (char*) ppString[0];
+ l = strlen((char*) psz);
+ if((int) l > iMaxLine)
+ l = iMaxLine;
+
+# ifdef USE_NETZIP
+ /* Check if we should compress and, if so, do it. We also
+ * check if the message is large enough to justify compression.
+ * The smaller the message, the less likely is a gain in compression.
+ * To save CPU cycles, we do not try to compress very small messages.
+ * What "very small" means needs to be configured. Currently, it is
+ * hard-coded but this may be changed to a config parameter.
+ * rgerhards, 2006-11-30
+ */
+ if(pData->compressionMode == COMPRESS_SINGLE_MSG && (l > CONF_MIN_SIZE_FOR_COMPRESS)) {
+ uLongf destLen = iMaxLine + iMaxLine/100 +12; /* recommended value from zlib doc */
+ uLong srcLen = l;
+ int ret;
+ CHKmalloc(out = (Bytef*) MALLOC(destLen));
+ out[0] = 'z';
+ out[1] = '\0';
+ ret = compress2((Bytef*) out+1, &destLen, (Bytef*) psz,
+ srcLen, pData->compressionLevel);
+ dbgprintf("Compressing message, length was %d now %d, return state %d.\n",
+ l, (int) destLen, ret);
+ if(ret != Z_OK) {
+ /* if we fail, we complain, but only in debug mode
+ * Otherwise, we are silent. In any case, we ignore the
+ * failed compression and just sent the uncompressed
+ * data, which is still valid. So this is probably the
+ * best course of action.
+ * rgerhards, 2006-11-30
+ */
+ dbgprintf("Compression failed, sending uncompressed message\n");
+ } else if(destLen+1 < l) {
+ /* only use compression if there is a gain in using it! */
+ dbgprintf("there is gain in compression, so we do it\n");
+ psz = (char*) out;
+ l = destLen + 1; /* take care for the "z" at message start! */
+ }
+ ++destLen;
+ }
+# endif
+
+ if(pData->protocol == FORW_UDP) {
+ /* forward via UDP */
+ CHKiRet(UDPSend(pData, psz, l));
+ } else {
+ /* forward via TCP */
+ iRet = tcpclt.Send(pData->pTCPClt, pData, psz, l);
+ if(iRet != RS_RET_OK && iRet != RS_RET_DEFER_COMMIT && iRet != RS_RET_PREVIOUS_COMMITTED) {
+ /* error! */
+ dbgprintf("error forwarding via tcp, suspending\n");
+ DestructTCPInstanceData(pData);
+ iRet = RS_RET_SUSPENDED;
+ }
+ }
+finalize_it:
+# ifdef USE_NETZIP
+ free(out); /* is NULL if it was never used... */
+# endif
+ENDdoAction
+
+
+BEGINendTransaction
+CODESTARTendTransaction
+dbgprintf("omfwd: endTransaction, offsSndBuf %u\n", pData->offsSndBuf);
+ if(pData->offsSndBuf != 0) {
+ iRet = TCPSendBuf(pData, pData->sndBuf, pData->offsSndBuf);
+ pData->offsSndBuf = 0;
+ }
+ENDendTransaction
+
+
+
+/* This function loads TCP support, if not already loaded. It will be called
+ * during config processing. To server ressources, TCP support will only
+ * be loaded if it actually is used. -- rgerhard, 2008-04-17
+ */
+static rsRetVal
+loadTCPSupport(void)
+{
+ DEFiRet;
+ CHKiRet(objUse(netstrms, LM_NETSTRMS_FILENAME));
+ CHKiRet(objUse(netstrm, LM_NETSTRMS_FILENAME));
+ CHKiRet(objUse(tcpclt, LM_TCPCLT_FILENAME));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* initialize TCP structures (if necessary) after the instance has been
+ * created.
+ */
+static rsRetVal
+initTCP(instanceData *pData)
+{
+ DEFiRet;
+ if(pData->protocol == FORW_TCP) {
+ /* create our tcpclt */
+ CHKiRet(tcpclt.Construct(&pData->pTCPClt));
+ CHKiRet(tcpclt.SetResendLastOnRecon(pData->pTCPClt, pData->bResendLastOnRecon));
+ /* and set callbacks */
+ CHKiRet(tcpclt.SetSendInit(pData->pTCPClt, TCPSendInit));
+ CHKiRet(tcpclt.SetSendFrame(pData->pTCPClt, TCPSendFrame));
+ CHKiRet(tcpclt.SetSendPrepRetry(pData->pTCPClt, TCPSendPrepRetry));
+ CHKiRet(tcpclt.SetFraming(pData->pTCPClt, pData->tcp_framing));
+ CHKiRet(tcpclt.SetRebindInterval(pData->pTCPClt, pData->iRebindInterval));
+ pData->iStrmDrvrMode = cs.iStrmDrvrMode;
+ if(cs.pszStrmDrvr != NULL)
+ CHKmalloc(pData->pszStrmDrvr = (uchar*)strdup((char*)cs.pszStrmDrvr));
+ if(cs.pszStrmDrvrAuthMode != NULL)
+ CHKmalloc(pData->pszStrmDrvrAuthMode =
+ (uchar*)strdup((char*)cs.pszStrmDrvrAuthMode));
+ }
+finalize_it:
+ RETiRet;
+}
+
+
+static inline void
+setInstParamDefaults(instanceData *pData)
+{
+ pData->tplName = NULL;
+ pData->protocol = FORW_UDP;
+ pData->tcp_framing = TCP_FRAMING_OCTET_STUFFING;
+ pData->pszStrmDrvr = NULL;
+ pData->pszStrmDrvrAuthMode = NULL;
+ pData->iStrmDrvrMode = 0;
+ pData->iRebindInterval = 0;
+ pData->bResendLastOnRecon = 0;
+ pData->pPermPeers = NULL;
+ pData->compressionLevel = 9;
+ pData->compressionMode = COMPRESS_NEVER;
+}
+
+BEGINnewActInst
+ struct cnfparamvals *pvals;
+ uchar *tplToUse;
+ char *cstr;
+ int i;
+ rsRetVal localRet;
+ int complevel = -1;
+CODESTARTnewActInst
+ DBGPRINTF("newActInst (omfwd)\n");
+
+ pvals = nvlstGetParams(lst, &actpblk, NULL);
+ if(pvals == NULL) {
+ errmsg.LogError(0, RS_RET_MISSING_CNFPARAMS, "omfwd: either the \"file\" or "
+ "\"dynfile\" parameter must be given");
+ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+ }
+
+ if(Debug) {
+ dbgprintf("action param blk in omfwd:\n");
+ cnfparamsPrint(&actpblk, pvals);
+ }
+
+ CHKiRet(createInstance(&pData));
+ setInstParamDefaults(pData);
+
+ for(i = 0 ; i < actpblk.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(actpblk.descr[i].name, "target")) {
+ pData->target = es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(actpblk.descr[i].name, "port")) {
+ pData->port = es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(actpblk.descr[i].name, "protocol")) {
+ if(!es_strcasebufcmp(pvals[i].val.d.estr, (uchar*)"udp", 3)) {
+ pData->protocol = FORW_UDP;
+ } else if(!es_strcasebufcmp(pvals[i].val.d.estr, (uchar*)"tcp", 3)) {
+ localRet = loadTCPSupport();
+ if(localRet != RS_RET_OK) {
+ errmsg.LogError(0, localRet, "could not activate network stream modules for TCP "
+ "(internal error %d) - are modules missing?", localRet);
+ ABORT_FINALIZE(localRet);
+ }
+ pData->protocol = FORW_TCP;
+ } else {
+ uchar *str;
+ str = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ errmsg.LogError(0, RS_RET_INVLD_PROTOCOL,
+ "omfwd: invalid protocol \"%s\"", str);
+ free(str);
+ ABORT_FINALIZE(RS_RET_INVLD_PROTOCOL);
+ }
+ } else if(!strcmp(actpblk.descr[i].name, "tcp_framing")) {
+ if(!es_strcasebufcmp(pvals[i].val.d.estr, (uchar*)"traditional", 11)) {
+ pData->tcp_framing = TCP_FRAMING_OCTET_STUFFING;
+ } else if(!es_strcasebufcmp(pvals[i].val.d.estr, (uchar*)"octet-counted", 13)) {
+ pData->tcp_framing = TCP_FRAMING_OCTET_COUNTING;
+ } else {
+ uchar *str;
+ str = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ errmsg.LogError(0, RS_RET_CNF_INVLD_FRAMING,
+ "omfwd: invalid framing \"%s\"", str);
+ free(str);
+ ABORT_FINALIZE(RS_RET_CNF_INVLD_FRAMING );
+ }
+ } else if(!strcmp(actpblk.descr[i].name, "rebindinterval")) {
+ pData->iRebindInterval = (int) pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "streamdriver")) {
+ pData->pszStrmDrvr = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(actpblk.descr[i].name, "streamdrivermode")) {
+ pData->iStrmDrvrMode = pvals[i].val.d.n;
+ } else if(!strcmp(actpblk.descr[i].name, "streamdriverauthmode")) {
+ pData->pszStrmDrvrAuthMode = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if(!strcmp(actpblk.descr[i].name, "streamdriverpermittedpeers")) {
+ uchar *start, *str;
+ uchar save;
+ uchar *p;
+ int lenStr;
+ str = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ start = str;
+ lenStr = ustrlen(start); /* we need length after '\0' has been dropped... */
+ while(lenStr > 0) {
+ p = start;
+ while(*p && *p != ',' && lenStr--)
+ p++;
+ if(*p == ',') {
+ *p = '\0';
+ }
+ save = *(p+1); /* we always have this, at least the \0 byte at EOS */
+ *(p+1) = '\0';
+ if(*start == '\0') {
+ DBGPRINTF("omfwd: ignoring empty permitted peer\n");
+ } else {
+ dbgprintf("omfwd: adding permitted peer: '%s'\n", start);
+ CHKiRet(net.AddPermittedPeer(&(pData->pPermPeers), start));
+ }
+ start = p+1;
+ if(lenStr)
+ --lenStr;
+ *(p+1) = save;
+ }
+ free(str);
+ } else if(!strcmp(actpblk.descr[i].name, "ziplevel")) {
+# ifdef USE_NETZIP
+ complevel = pvals[i].val.d.n;
+ if(complevel >= 0 && complevel <= 10) {
+ pData->compressionLevel = complevel;
+ pData->compressionMode = COMPRESS_SINGLE_MSG;
+ } else {
+ errmsg.LogError(0, NO_ERRCODE, "Invalid ziplevel %d specified in "
+ "forwardig action - NOT turning on compression.",
+ complevel);
+ }
+# else
+ errmsg.LogError(0, NO_ERRCODE, "Compression requested, but rsyslogd is not compiled "
+ "with compression support - request ignored.");
+# endif /* #ifdef USE_NETZIP */
+ } else if(!strcmp(actpblk.descr[i].name, "resendlastmsgonreconnect")) {
+ pData->bResendLastOnRecon = (int) pvals[i].val.d.n;
+ } 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, "compression.mode")) {
+ cstr = es_str2cstr(pvals[i].val.d.estr, NULL);
+ if(!strcasecmp(cstr, "stream:always")) {
+ pData->compressionMode = COMPRESS_STREAM_ALWAYS;
+ } else if(!strcasecmp(cstr, "none")) {
+ pData->compressionMode = COMPRESS_NEVER;
+ } else if(!strcasecmp(cstr, "single")) {
+ pData->compressionMode = COMPRESS_SINGLE_MSG;
+ } else {
+ errmsg.LogError(0, RS_RET_PARAM_ERROR, "omfwd: invalid value for 'compression.mode' "
+ "parameter (given is '%s')", cstr);
+ free(cstr);
+ ABORT_FINALIZE(RS_RET_PARAM_ERROR);
+ }
+ free(cstr);
+ } else {
+ DBGPRINTF("omfwd: program error, non-handled "
+ "param '%s'\n", actpblk.descr[i].name);
+ }
+ }
+
+ if(complevel != -1) {
+ pData->compressionLevel = complevel;
+ if(pData->compressionMode == COMPRESS_NEVER) {
+ /* to keep compatible with pre-7.3.11, only setting the
+ * compresion level means old-style single-message mode.
+ */
+ pData->compressionMode = COMPRESS_SINGLE_MSG;
+ }
+ }
+
+ CODE_STD_STRING_REQUESTnewActInst(1)
+
+ tplToUse = ustrdup((pData->tplName == NULL) ? getDfltTpl() : pData->tplName);
+ CHKiRet(OMSRsetEntry(*ppOMSR, 0, tplToUse, OMSR_NO_RQD_TPL_OPTS));
+
+ CHKiRet(initTCP(pData));
+CODE_STD_FINALIZERnewActInst
+ cnfparamvalsDestruct(pvals, &actpblk);
+ENDnewActInst
+
+
+BEGINparseSelectorAct
+ uchar *q;
+ int i;
+ rsRetVal localRet;
+ struct addrinfo;
+ TCPFRAMINGMODE tcp_framing = TCP_FRAMING_OCTET_STUFFING;
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(1)
+ if(*p != '@')
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+
+ CHKiRet(createInstance(&pData));
+
+ ++p; /* eat '@' */
+ if(*p == '@') { /* indicator for TCP! */
+ localRet = loadTCPSupport();
+ if(localRet != RS_RET_OK) {
+ errmsg.LogError(0, localRet, "could not activate network stream modules for TCP "
+ "(internal error %d) - are modules missing?", localRet);
+ ABORT_FINALIZE(localRet);
+ }
+ pData->protocol = FORW_TCP;
+ ++p; /* eat this '@', too */
+ } else {
+ pData->protocol = FORW_UDP;
+ }
+ /* we are now after the protocol indicator. Now check if we should
+ * use compression. We begin to use a new option format for this:
+ * @(option,option)host:port
+ * The first option defined is "z[0..9]" where the digit indicates
+ * the compression level. If it is not given, 9 (best compression) is
+ * assumed. An example action statement might be:
+ * @@(z5,o)127.0.0.1:1400
+ * Which means send via TCP with medium (5) compresion (z) to the local
+ * host on port 1400. The '0' option means that octet-couting (as in
+ * IETF I-D syslog-transport-tls) is to be used for framing (this option
+ * applies to TCP-based syslog only and is ignored when specified with UDP).
+ * That is not yet implemented.
+ * rgerhards, 2006-12-07
+ * In order to support IPv6 addresses, we must introduce an extension to
+ * the hostname. If it is in square brackets, whatever is in them is treated as
+ * the hostname - without any exceptions ;) -- rgerhards, 2008-08-05
+ */
+ if(*p == '(') {
+ /* at this position, it *must* be an option indicator */
+ do {
+ ++p; /* eat '(' or ',' (depending on when called) */
+ /* check options */
+ if(*p == 'z') { /* compression */
+# ifdef USE_NETZIP
+ ++p; /* eat */
+ if(isdigit((int) *p)) {
+ int iLevel;
+ iLevel = *p - '0';
+ ++p; /* eat */
+ pData->compressionLevel = iLevel;
+ pData->compressionMode = COMPRESS_SINGLE_MSG;
+ } else {
+ errmsg.LogError(0, NO_ERRCODE, "Invalid compression level '%c' specified in "
+ "forwardig action - NOT turning on compression.",
+ *p);
+ }
+# else
+ errmsg.LogError(0, NO_ERRCODE, "Compression requested, but rsyslogd is not compiled "
+ "with compression support - request ignored.");
+# endif /* #ifdef USE_NETZIP */
+ } else if(*p == 'o') { /* octet-couting based TCP framing? */
+ ++p; /* eat */
+ /* no further options settable */
+ tcp_framing = TCP_FRAMING_OCTET_COUNTING;
+ } else { /* invalid option! Just skip it... */
+ errmsg.LogError(0, NO_ERRCODE, "Invalid option %c in forwarding action - ignoring.", *p);
+ ++p; /* eat invalid option */
+ }
+ /* the option processing is done. We now do a generic skip
+ * to either the next option or the end of the option
+ * block.
+ */
+ while(*p && *p != ')' && *p != ',')
+ ++p; /* just skip it */
+ } while(*p && *p == ','); /* Attention: do.. while() */
+ if(*p == ')')
+ ++p; /* eat terminator, on to next */
+ else
+ /* we probably have end of string - leave it for the rest
+ * of the code to handle it (but warn the user)
+ */
+ errmsg.LogError(0, NO_ERRCODE, "Option block not terminated in forwarding action.");
+ }
+
+ /* extract the host first (we do a trick - we replace the ';' or ':' with a '\0')
+ * now skip to port and then template name. rgerhards 2005-07-06
+ */
+ if(*p == '[') { /* everything is hostname upto ']' */
+ ++p; /* skip '[' */
+ for(q = p ; *p && *p != ']' ; ++p)
+ /* JUST SKIP */;
+ if(*p == ']') {
+ *p = '\0'; /* trick to obtain hostname (later)! */
+ ++p; /* eat it */
+ }
+ } else { /* traditional view of hostname */
+ for(q = p ; *p && *p != ';' && *p != ':' && *p != '#' ; ++p)
+ /* JUST SKIP */;
+ }
+
+ pData->tcp_framing = tcp_framing;
+ pData->port = NULL;
+ if(*p == ':') { /* process port */
+ uchar * tmp;
+
+ *p = '\0'; /* trick to obtain hostname (later)! */
+ tmp = ++p;
+ for(i=0 ; *p && isdigit((int) *p) ; ++p, ++i)
+ /* SKIP AND COUNT */;
+ pData->port = MALLOC(i + 1);
+ if(pData->port == NULL) {
+ errmsg.LogError(0, NO_ERRCODE, "Could not get memory to store syslog forwarding port, "
+ "using default port, results may not be what you intend\n");
+ /* we leave f_forw.port set to NULL, this is then handled below */
+ } else {
+ memcpy(pData->port, tmp, i);
+ *(pData->port + i) = '\0';
+ }
+ }
+ /* check if no port is set. If so, we use the IANA-assigned port of 514 */
+ if(pData->port == NULL) {
+ CHKmalloc(pData->port = strdup("514"));
+ }
+
+ /* now skip to template */
+ while(*p && *p != ';' && *p != '#' && !isspace((int) *p))
+ ++p; /*JUST SKIP*/
+
+ if(*p == ';' || *p == '#' || isspace(*p)) {
+ uchar cTmp = *p;
+ *p = '\0'; /* trick to obtain hostname (later)! */
+ CHKmalloc(pData->target = strdup((char*) q));
+ *p = cTmp;
+ } else {
+ CHKmalloc(pData->target = strdup((char*) q));
+ }
+
+ /* copy over config data as needed */
+ pData->iRebindInterval = (pData->protocol == FORW_TCP) ?
+ cs.iTCPRebindInterval : cs.iUDPRebindInterval;
+
+ /* process template */
+ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, getDfltTpl()));
+
+ if(pData->protocol == FORW_TCP) {
+ pData->bResendLastOnRecon = cs.bResendLastOnRecon;
+ pData->iStrmDrvrMode = cs.iStrmDrvrMode;
+ if(cs.pszStrmDrvr != NULL)
+ CHKmalloc(pData->pszStrmDrvr = (uchar*)strdup((char*)cs.pszStrmDrvr));
+ if(cs.pszStrmDrvrAuthMode != NULL)
+ CHKmalloc(pData->pszStrmDrvrAuthMode =
+ (uchar*)strdup((char*)cs.pszStrmDrvrAuthMode));
+ if(cs.pPermPeers != NULL) {
+ pData->pPermPeers = cs.pPermPeers;
+ cs.pPermPeers = NULL;
+ }
+ }
+ CHKiRet(initTCP(pData));
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+
+/* a common function to free our configuration variables - used both on exit
+ * and on $ResetConfig processing. -- rgerhards, 2008-05-16
+ */
+static void
+freeConfigVars(void)
+{
+ free(cs.pszStrmDrvr);
+ cs.pszStrmDrvr = NULL;
+ free(cs.pszStrmDrvrAuthMode);
+ cs.pszStrmDrvrAuthMode = NULL;
+ free(cs.pPermPeers);
+ cs.pPermPeers = NULL; /* TODO: fix in older builds! */
+}
+
+
+BEGINmodExit
+CODESTARTmodExit
+ /* release what we no longer need */
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(net, LM_NET_FILENAME);
+ objRelease(netstrm, LM_NETSTRMS_FILENAME);
+ objRelease(netstrms, LM_NETSTRMS_FILENAME);
+ objRelease(tcpclt, LM_TCPCLT_FILENAME);
+ freeConfigVars();
+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
+
+
+/* Reset config variables for this module to default values.
+ * rgerhards, 2008-03-28
+ */
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
+{
+ freeConfigVars();
+
+ /* we now must reset all non-string values */
+ cs.iStrmDrvrMode = 0;
+ cs.bResendLastOnRecon = 0;
+ cs.iUDPRebindInterval = 0;
+ cs.iTCPRebindInterval = 0;
+
+ return RS_RET_OK;
+}
+
+
+BEGINmodInit(Fwd)
+CODESTARTmodInit
+INITLegCnfVars
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(net,LM_NET_FILENAME));
+
+ 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));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionsendstreamdrivermode", 0, eCmdHdlrInt, NULL, &cs.iStrmDrvrMode, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionsendstreamdriverauthmode", 0, eCmdHdlrGetWord, NULL, &cs.pszStrmDrvrAuthMode, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionsendstreamdriverpermittedpeer", 0, eCmdHdlrGetWord, setPermittedPeer, NULL, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionsendresendlastmsgonreconnect", 0, eCmdHdlrBinary, NULL, &cs.bResendLastOnRecon, NULL));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+
+/* vim:set ai:
+ */
diff --git a/tools/omfwd.h b/tools/omfwd.h
new file mode 100644
index 00000000..379da4fd
--- /dev/null
+++ b/tools/omfwd.h
@@ -0,0 +1,33 @@
+/* omfwd.h
+ * These are the definitions for the build-in forwarding output module.
+ *
+ * File begun on 2007-07-13 by RGerhards
+ *
+ * Copyright 2007-2012 Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef OMFWD_H_INCLUDED
+#define OMFWD_H_INCLUDED 1
+
+/* prototypes */
+rsRetVal modInitFwd(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*);
+
+#endif /* #ifndef OMFWD_H_INCLUDED */
+/*
+ * vi:set ai:
+ */
diff --git a/tools/ompipe.c b/tools/ompipe.c
new file mode 100644
index 00000000..420e2b11
--- /dev/null
+++ b/tools/ompipe.c
@@ -0,0 +1,405 @@
+/* ompipe.c
+ * This is the implementation of the build-in pipe output module.
+ * Note that this module stems back to the "old" (4.4.2 and below)
+ * omfile. There were some issues with the new omfile code and pipes
+ * (namely in regard to xconsole), so we took out the pipe code and moved
+ * that to a separate module. That a) immediately solves the issue for a
+ * less common use case and probably makes it much easier to enhance
+ * file and pipe support (now independently) in the future (we always
+ * needed to think about pipes in omfile so far, what we now no longer
+ * need to, hopefully resulting in reduction of complexity).
+ *
+ * NOTE: read comments in module-template.h to understand how this pipe
+ * works!
+ *
+ * Copyright 2007-2012 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/file.h>
+
+#include "syslogd.h"
+#include "syslogd-types.h"
+#include "srUtils.h"
+#include "template.h"
+#include "ompipe.h"
+#include "omfile.h" /* for dirty trick: access to $ActionFileDefaultTemplate value */
+#include "cfsysline.h"
+#include "module-template.h"
+#include "conf.h"
+#include "errmsg.h"
+
+MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("ompipe")
+
+/* internal structures
+ */
+DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+
+
+typedef struct _instanceData {
+ uchar *pipe; /* pipe or template name (display only) */
+ uchar *tplName; /* format template to use */
+ short fd; /* pipe descriptor for (current) pipe */
+ sbool bHadError; /* did we already have/report an error on this pipe? */
+} instanceData;
+
+typedef struct configSettings_s {
+ EMPTY_STRUCT
+} configSettings_t;
+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 },
+ { "template", eCmdHdlrGetWord, 0 }
+};
+static struct cnfparamblk actpblk =
+ { CNFPARAMBLK_VERSION,
+ sizeof(actpdescr)/sizeof(struct cnfparamdescr),
+ 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
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURERepeatedMsgReduction)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ dbgprintf("pipe %s", pData->pipe);
+ if (pData->fd == -1)
+ dbgprintf(" (unused)");
+ENDdbgPrintInstInfo
+
+
+/* This is now shared code for all types of files. It simply prepares
+ * pipe access, which, among others, means the the pipe wil be opened
+ * and any directories in between will be created (based on config, of
+ * course). -- rgerhards, 2008-10-22
+ * changed to iRet interface - 2009-03-19
+ */
+static inline rsRetVal
+preparePipe(instanceData *pData)
+{
+ DEFiRet;
+ pData->fd = open((char*) pData->pipe, O_RDWR|O_NONBLOCK|O_CLOEXEC);
+ if(pData->fd < 0 ) {
+ pData->fd = -1;
+ if(!pData->bHadError) {
+ char errStr[1024];
+ rs_strerror_r(errno, errStr, sizeof(errStr));
+ errmsg.LogError(0, RS_RET_NO_FILE_ACCESS, "Could no open output pipe '%s': %s",
+ pData->pipe, errStr);
+ pData->bHadError = 1;
+ }
+ DBGPRINTF("Error opening log pipe: %s\n", pData->pipe);
+ }
+ RETiRet;
+}
+
+
+/* rgerhards 2004-11-11: write to a pipe output. This
+ * will be called for all outputs using pipe semantics,
+ * for example also for pipes.
+ */
+static rsRetVal writePipe(uchar **ppString, instanceData *pData)
+{
+ int iLenWritten;
+ DEFiRet;
+
+ ASSERT(pData != NULL);
+
+ if(pData->fd == -1) {
+ rsRetVal iRetLocal;
+ iRetLocal = preparePipe(pData);
+ if((iRetLocal != RS_RET_OK) || (pData->fd == -1))
+ ABORT_FINALIZE(RS_RET_SUSPENDED); /* whatever the failure was, we need to retry */
+ }
+
+ /* create the message based on format specified */
+ iLenWritten = write(pData->fd, ppString[0], strlen((char*)ppString[0]));
+ if(iLenWritten < 0) {
+ int e = errno;
+ char errStr[1024];
+ rs_strerror_r(errno, errStr, sizeof(errStr));
+ DBGPRINTF("pipe (%d) write error %d: %s\n", pData->fd, e, errStr);
+
+ /* If a named pipe is full, we suspend this action for a while */
+ if(e == EAGAIN)
+ ABORT_FINALIZE(RS_RET_SUSPENDED);
+
+ close(pData->fd);
+ pData->fd = -1; /* tell that fd is no longer open! */
+ iRet = RS_RET_SUSPENDED;
+ errno = e;
+ errmsg.LogError(0, NO_ERRCODE, "%s", pData->pipe);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+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;
+ pData->fd = -1;
+ pData->bHadError = 0;
+ENDcreateInstance
+
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ free(pData->pipe);
+ if(pData->fd != -1)
+ close(pData->fd);
+ENDfreeInstance
+
+
+BEGINtryResume
+CODESTARTtryResume
+ENDtryResume
+
+BEGINdoAction
+CODESTARTdoAction
+ DBGPRINTF(" (%s)\n", pData->pipe);
+ iRet = writePipe(ppString, pData);
+ENDdoAction
+
+
+static inline void
+setInstParamDefaults(instanceData *pData)
+{
+ pData->tplName = NULL;
+}
+
+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_REQUESTnewActInst(1)
+ for(i = 0 ; i < actpblk.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(actpblk.descr[i].name, "pipe")) {
+ pData->pipe = (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 {
+ dbgprintf("ompipe: program error, non-handled "
+ "param '%s'\n", actpblk.descr[i].name);
+ }
+ }
+
+ if(pData->tplName == NULL) {
+ CHKiRet(OMSRsetEntry(*ppOMSR, 0, (uchar*) "RSYSLOG_FileFormat",
+ OMSR_NO_RQD_TPL_OPTS));
+ } else {
+ CHKiRet(OMSRsetEntry(*ppOMSR, 0,
+ (uchar*) strdup((char*) pData->tplName),
+ OMSR_NO_RQD_TPL_OPTS));
+ }
+CODE_STD_FINALIZERnewActInst
+ cnfparamvalsDestruct(pvals, &actpblk);
+ENDnewActInst
+
+BEGINparseSelectorAct
+CODESTARTparseSelectorAct
+ /* yes, the if below is redundant, but I need it now. Will go away as
+ * the code further changes. -- rgerhards, 2007-07-25
+ */
+ if(*p == '|') {
+ if((iRet = createInstance(&pData)) != RS_RET_OK) {
+ ENDfunc
+ return iRet; /* this can not use RET_iRet! */
+ }
+ } else {
+ /* this is not clean, but we need it for the time being
+ * TODO: remove when cleaning up modularization
+ */
+ ENDfunc
+ return RS_RET_CONFLINE_UNPROCESSED;
+ }
+
+ CODE_STD_STRING_REQUESTparseSelectorAct(1)
+ CHKmalloc(pData->pipe = malloc(512));
+ ++p;
+ CHKiRet(cflineParseFileName(p, (uchar*) pData->pipe, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
+ getDfltTpl()));
+
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+
+BEGINdoHUP
+CODESTARTdoHUP
+ if(pData->fd != -1) {
+ close(pData->fd);
+ pData->fd = -1;
+ }
+ENDdoHUP
+
+
+BEGINmodExit
+CODESTARTmodExit
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_OMOD_QUERIES
+CODEqueryEtryPt_doHUP
+CODEqueryEtryPt_STD_CONF2_QUERIES
+CODEqueryEtryPt_STD_CONF2_CNFNAME_QUERIES
+CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES
+CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit(Pipe)
+CODESTARTmodInit
+INITLegCnfVars
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ENDmodInit
+/* vi:set ai:
+ */
diff --git a/tools/ompipe.h b/tools/ompipe.h
new file mode 100644
index 00000000..d855def2
--- /dev/null
+++ b/tools/ompipe.h
@@ -0,0 +1,30 @@
+/* ompipe.h
+ * These are the definitions for the build-in pipe output module.
+ *
+ * Copyright 2007-2012 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This pipe is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef OMPIPE_H_INCLUDED
+#define OMPIPE_H_INCLUDED 1
+
+/* prototypes */
+rsRetVal modInitPipe(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*);
+
+#endif /* #ifndef OMPIPE_H_INCLUDED */
+/* vi:set ai:
+ */
diff --git a/tools/omshell.c b/tools/omshell.c
new file mode 100644
index 00000000..ac62fa62
--- /dev/null
+++ b/tools/omshell.c
@@ -0,0 +1,154 @@
+/* omshell.c
+ * This is the implementation of the build-in shell output module.
+ *
+ * ************* DO NOT EXTEND THIS MODULE **************
+ * This is pure legacy, omprog has much better and more
+ * secure functionality than this module. It is NOT
+ * recommended to base new work on it!
+ * 2012-01-19 rgerhards
+ * ******************************************************
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * shell support was initially written by bkalkbrenner 2005-09-20
+ *
+ * File begun on 2007-07-20 by RGerhards (extracted from syslogd.c)
+ * This file is under development and has not yet arrived at being fully
+ * self-contained and a real object. So far, it is mostly an excerpt
+ * of the "old" message code without any modifications. However, it
+ * helps to have things at the right place one we go to the meat of it.
+ *
+ * Copyright 2007-2012 Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "conf.h"
+#include "syslogd-types.h"
+#include "srUtils.h"
+#include "omshell.h"
+#include "module-template.h"
+#include "errmsg.h"
+
+MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
+
+/* internal structures
+ */
+DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+
+typedef struct _instanceData {
+ uchar progName[MAXFNAME]; /* program to execute */
+} instanceData;
+
+BEGINcreateInstance
+CODESTARTcreateInstance
+ENDcreateInstance
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURERepeatedMsgReduction)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ENDfreeInstance
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ printf("%s", pData->progName);
+ENDdbgPrintInstInfo
+
+
+BEGINtryResume
+CODESTARTtryResume
+ENDtryResume
+
+BEGINdoAction
+CODESTARTdoAction
+ /* TODO: using pData->progName is not clean from the point of
+ * modularization. We'll change that as we go ahead with modularization.
+ * rgerhards, 2007-07-20
+ */
+ dbgprintf("\n");
+ if(execProg((uchar*) pData->progName, 1, ppString[0]) == 0)
+ errmsg.LogError(0, NO_ERRCODE, "Executing program '%s' failed", (char*)pData->progName);
+ENDdoAction
+
+
+BEGINparseSelectorAct
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(1)
+ /* yes, the if below is redundant, but I need it now. Will go away as
+ * the code further changes. -- rgerhards, 2007-07-25
+ */
+ if(*p == '^') {
+ if((iRet = createInstance(&pData)) != RS_RET_OK)
+ goto finalize_it;
+ }
+
+
+ switch (*p)
+ {
+ case '^': /* bkalkbrenner 2005-09-20: execute shell command */
+ dbgprintf("exec\n");
+ ++p;
+ iRet = cflineParseFileName(p, (uchar*) pData->progName, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
+ (uchar*)"RSYSLOG_TraditionalFileFormat");
+ break;
+ default:
+ iRet = RS_RET_CONFLINE_UNPROCESSED;
+ break;
+ }
+
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+
+BEGINmodExit
+CODESTARTmodExit
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_OMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit(Shell)
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ENDmodInit
+
+/*
+ * vi:set ai:
+ */
diff --git a/tools/omshell.h b/tools/omshell.h
new file mode 100644
index 00000000..3b393ac5
--- /dev/null
+++ b/tools/omshell.h
@@ -0,0 +1,33 @@
+/* omshell.c
+ * These are the definitions for the build-in shell output module.
+ *
+ * File begun on 2007-07-13 by RGerhards (extracted from syslogd.c)
+ *
+ * Copyright 2007-2012 Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ACTSHELL_H_INCLUDED
+#define ACTSHELL_H_INCLUDED 1
+
+/* prototypes */
+rsRetVal modInitShell(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*);
+
+#endif /* #ifndef ACTSHELL_H_INCLUDED */
+/*
+ * vi:set ai:
+ */
diff --git a/tools/omusrmsg.c b/tools/omusrmsg.c
new file mode 100644
index 00000000..f4cc4094
--- /dev/null
+++ b/tools/omusrmsg.c
@@ -0,0 +1,451 @@
+/* omusrmsg.c
+ * This is the implementation of the build-in output module for sending
+ * user messages.
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * File begun on 2007-07-20 by RGerhards (extracted from syslogd.c, which at the
+ * time of the fork from sysklogd was under BSD license)
+ *
+ * Copyright 2007-2012 Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#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 <ctype.h>
+#include <sys/param.h>
+#ifdef HAVE_UTMP_H
+# include <utmp.h>
+# define STRUCTUTMP struct utmp
+# define UTNAME ut_name
+#else
+# include <utmpx.h>
+# define STRUCTUTMP struct utmpx
+# define UTNAME ut_user
+#endif
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <errno.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#else
+#include <sys/msgbuf.h>
+#endif
+#if HAVE_PATHS_H
+#include <paths.h>
+#endif
+#include "srUtils.h"
+#include "stringbuf.h"
+#include "syslogd-types.h"
+#include "conf.h"
+#include "omusrmsg.h"
+#include "module-template.h"
+#include "errmsg.h"
+
+
+/* portability: */
+#ifndef _PATH_DEV
+# define _PATH_DEV "/dev/"
+#endif
+
+
+MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("omusrmsg")
+
+/* internal structures
+ */
+DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+
+typedef struct _instanceData {
+ int bIsWall; /* 1- is wall, 0 - individual users */
+ char uname[MAXUNAMES][UNAMESZ+1];
+ uchar *tplName;
+} instanceData;
+
+typedef struct configSettings_s {
+ EMPTY_STRUCT
+} configSettings_t;
+static configSettings_t __attribute__((unused)) cs;
+
+
+/* tables for interfacing with the v6 config system */
+/* action (instance) parameters */
+static struct cnfparamdescr actpdescr[] = {
+ { "users", eCmdHdlrString, CNFPARAM_REQUIRED },
+ { "template", eCmdHdlrGetWord, 0 }
+};
+static struct cnfparamblk actpblk =
+ { CNFPARAMBLK_VERSION,
+ sizeof(actpdescr)/sizeof(struct cnfparamdescr),
+ actpdescr
+ };
+
+BEGINinitConfVars /* (re)set config variables to default values */
+CODESTARTinitConfVars
+ENDinitConfVars
+
+
+BEGINcreateInstance
+CODESTARTcreateInstance
+ENDcreateInstance
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURERepeatedMsgReduction)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ free(pData->tplName);
+ENDfreeInstance
+
+
+BEGINdbgPrintInstInfo
+ register int i;
+CODESTARTdbgPrintInstInfo
+ for (i = 0; i < MAXUNAMES && *pData->uname[i]; i++)
+ dbgprintf("%s, ", pData->uname[i]);
+ENDdbgPrintInstInfo
+
+
+/**
+ * BSD setutent/getutent() replacement routines
+ * The following routines emulate setutent() and getutent() under
+ * BSD because they are not available there. We only emulate what we actually
+ * need! rgerhards 2005-03-18
+ */
+#ifdef OS_BSD
+/* Since version 900007, FreeBSD has a POSIX compliant <utmpx.h> */
+#if defined(__FreeBSD__) && (__FreeBSD_version >= 900007)
+# define setutent(void) setutxent(void)
+# define getutent(void) getutxent(void)
+# define endutent(void) endutxent(void)
+#else
+static FILE *BSD_uf = NULL;
+void setutent(void)
+{
+ assert(BSD_uf == NULL);
+ if ((BSD_uf = fopen(_PATH_UTMP, "r")) == NULL) {
+ errmsg.LogError(0, NO_ERRCODE, "%s", _PATH_UTMP);
+ return;
+ }
+}
+
+STRUCTUTMP* getutent(void)
+{
+ static STRUCTUTMP st_utmp;
+
+ if(fread((char *)&st_utmp, sizeof(st_utmp), 1, BSD_uf) != 1)
+ return NULL;
+
+ return(&st_utmp);
+}
+
+void endutent(void)
+{
+ fclose(BSD_uf);
+ BSD_uf = NULL;
+}
+#endif /* if defined(__FreeBSD__) */
+#endif /* #ifdef OS_BSD */
+
+
+/* WALLMSG -- Write a message to the world at large
+ *
+ * Write the specified message to either the entire
+ * world, or a list of approved users.
+ *
+ * rgerhards, 2005-10-19: applying the following sysklogd patch:
+ * Tue May 4 16:52:01 CEST 2004: Solar Designer <solar@openwall.com>
+ * Adjust the size of a variable to prevent a buffer overflow
+ * should _PATH_DEV ever contain something different than "/dev/".
+ * rgerhards, 2008-07-04: changing the function to no longer use fork() but
+ * continue run on its thread instead.
+ */
+static rsRetVal wallmsg(uchar* pMsg, instanceData *pData)
+{
+
+ uchar szErr[512];
+ char p[sizeof(_PATH_DEV) + UNAMESZ];
+ register int i;
+ int errnoSave;
+ int ttyf;
+ int wrRet;
+ STRUCTUTMP ut;
+ STRUCTUTMP *uptr;
+ struct stat statb;
+ DEFiRet;
+
+ assert(pMsg != NULL);
+
+ /* open the user login file */
+ setutent();
+
+ /* scan the user login file */
+ while((uptr = getutent())) {
+ memcpy(&ut, uptr, sizeof(ut));
+ /* is this slot used? */
+ if(ut.UTNAME[0] == '\0')
+ continue;
+#ifndef OS_BSD
+ if(ut.ut_type != USER_PROCESS)
+ continue;
+#endif
+ if(!(strncmp (ut.UTNAME,"LOGIN", 6))) /* paranoia */
+ continue;
+
+ /* should we send the message to this user? */
+ if(pData->bIsWall == 0) {
+ for(i = 0; i < MAXUNAMES; i++) {
+ if(!pData->uname[i][0]) {
+ i = MAXUNAMES;
+ break;
+ }
+ if(strncmp(pData->uname[i], ut.UTNAME, UNAMESZ) == 0)
+ break;
+ }
+ if(i == MAXUNAMES) /* user not found? */
+ continue; /* on to next user! */
+ }
+
+ /* compute the device name */
+ strcpy(p, _PATH_DEV);
+ strncat(p, ut.ut_line, UNAMESZ);
+
+ /* we must be careful when writing to the terminal. A terminal may block
+ * (for example, a user has pressed <ctl>-s). In that case, we can not
+ * wait indefinitely. So we need to use non-blocking I/O. In case we would
+ * block, we simply do not send the message, because that's the best we can
+ * do. -- rgerhards, 2008-07-04
+ */
+
+ /* open the terminal */
+ if((ttyf = open(p, O_WRONLY|O_NOCTTY|O_NONBLOCK)) >= 0) {
+ if(fstat(ttyf, &statb) == 0 && (statb.st_mode & S_IWRITE)) {
+ wrRet = write(ttyf, pMsg, strlen((char*)pMsg));
+ if(Debug && wrRet == -1) {
+ /* we record the state to the debug log */
+ errnoSave = errno;
+ rs_strerror_r(errno, (char*)szErr, sizeof(szErr));
+ dbgprintf("write to terminal '%s' failed with [%d]:%s\n",
+ p, errnoSave, szErr);
+ }
+ }
+ close(ttyf);
+ }
+ }
+
+ /* close the user login file */
+ endutent();
+ RETiRet;
+}
+
+
+BEGINtryResume
+CODESTARTtryResume
+ENDtryResume
+
+BEGINdoAction
+CODESTARTdoAction
+ dbgprintf("\n");
+ iRet = wallmsg(ppString[0], pData);
+ENDdoAction
+
+
+static inline void
+populateUsers(instanceData *pData, es_str_t *usrs)
+{
+ int i;
+ int iDst;
+ es_size_t iUsr;
+ es_size_t len;
+ uchar *c;
+
+ len = es_strlen(usrs);
+ c = es_getBufAddr(usrs);
+ pData->bIsWall = 0; /* write to individual users */
+ iUsr = 0;
+ for(i = 0 ; i < MAXUNAMES && iUsr < len ; ++i) {
+ for( iDst = 0
+ ; iDst < UNAMESZ && iUsr < len && c[iUsr] != ','
+ ; ++iDst, ++iUsr) {
+ pData->uname[i][iDst] = c[iUsr];
+ }
+ pData->uname[i][iDst] = '\0';
+ DBGPRINTF("omusrmsg: send to user '%s'\n", pData->uname[i]);
+ if(iUsr < len && c[iUsr] != ',') {
+ errmsg.LogError(0, RS_RET_ERR, "user name '%s...' too long - "
+ "ignored", pData->uname[i]);
+ --i;
+ ++iUsr;
+ while(iUsr < len && c[iUsr] != ',')
+ ++iUsr; /* skip to next name */
+ } else if(iDst == 0) {
+ errmsg.LogError(0, RS_RET_ERR, "no user name given - "
+ "ignored");
+ --i;
+ ++iUsr;
+ while(iUsr < len && c[iUsr] != ',')
+ ++iUsr; /* skip to next name */
+ }
+ if(iUsr < len) {
+ ++iUsr; /* skip "," */
+ while(iUsr < len && isspace(c[iUsr]))
+ ++iUsr; /* skip whitespace */
+ }
+ }
+ if(i == MAXUNAMES && iUsr != len) {
+ errmsg.LogError(0, RS_RET_ERR, "omusrmsg supports only up to %d "
+ "user names in a single action - all others have been ignored",
+ MAXUNAMES);
+ }
+}
+
+
+static inline void
+setInstParamDefaults(instanceData *pData)
+{
+ pData->bIsWall = 0;
+ pData->tplName = NULL;
+}
+
+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_REQUESTnewActInst(1)
+ for(i = 0 ; i < actpblk.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(actpblk.descr[i].name, "users")) {
+ if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"*", 1)) {
+ pData->bIsWall = 1;
+ } else {
+ populateUsers(pData, pvals[i].val.d.estr);
+ }
+ } else if(!strcmp(actpblk.descr[i].name, "template")) {
+ pData->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else {
+ dbgprintf("omusrmsg: program error, non-handled "
+ "param '%s'\n", actpblk.descr[i].name);
+ }
+ }
+
+ if(pData->tplName == NULL) {
+ CHKiRet(OMSRsetEntry(*ppOMSR, 0,
+ (uchar*) strdup(pData->bIsWall ? " WallFmt" : " StdUsrMsgFmt"),
+ OMSR_NO_RQD_TPL_OPTS));
+ } else {
+ CHKiRet(OMSRsetEntry(*ppOMSR, 0,
+ (uchar*) strdup((char*) pData->tplName),
+ OMSR_NO_RQD_TPL_OPTS));
+ }
+CODE_STD_FINALIZERnewActInst
+ cnfparamvalsDestruct(pvals, &actpblk);
+ENDnewActInst
+
+
+BEGINparseSelectorAct
+ es_str_t *usrs;
+ int bHadWarning;
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(1)
+ bHadWarning = 0;
+ if(!strncmp((char*) p, ":omusrmsg:", sizeof(":omusrmsg:") - 1)) {
+ p += sizeof(":omusrmsg:") - 1; /* eat indicator sequence (-1 because of '\0'!) */
+ } else {
+ if(!*p || !((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z')
+ || (*p >= '0' && *p <= '9') || *p == '_' || *p == '.' || *p == '*')) {
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+ } else {
+ errmsg.LogError(0, RS_RET_OUTDATED_STMT,
+ "action '%s' treated as ':omusrmsg:%s' - please "
+ "change syntax, '%s' will not be supported in the future",
+ p, p, p);
+ bHadWarning = 1;
+ }
+ }
+
+ CHKiRet(createInstance(&pData));
+
+ if(*p == '*') { /* wall */
+ dbgprintf("write-all");
+ ++p; /* eat '*' */
+ pData->bIsWall = 1; /* write to all users */
+ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar*) " WallFmt"));
+ } else {
+ /* everything else is currently treated as a user name */
+ usrs = es_newStr(128);
+ while(*p && *p != ';') {
+ es_addChar(&usrs, *p);
+ ++p;
+ }
+ populateUsers(pData, usrs);
+ es_deleteStr(usrs);
+ if((iRet = cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar*)" StdUsrMsgFmt"))
+ != RS_RET_OK)
+ goto finalize_it;
+ }
+ if(iRet == RS_RET_OK && bHadWarning)
+ iRet = RS_RET_OK_WARN;
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+
+BEGINmodExit
+CODESTARTmodExit
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_OMOD_QUERIES
+CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit(UsrMsg)
+CODESTARTmodInit
+INITLegCnfVars
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ENDmodInit
+
+/* vim:set ai:
+ */
diff --git a/tools/omusrmsg.h b/tools/omusrmsg.h
new file mode 100644
index 00000000..dc9c4565
--- /dev/null
+++ b/tools/omusrmsg.h
@@ -0,0 +1,32 @@
+/* omusrmsg.c
+ * These are the definitions for the build-in user message output module.
+ *
+ * File begun on 2007-07-13 by RGerhards
+ *
+ * Copyright 20072-2012 Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef OMUSRMSG_H_INCLUDED
+#define OMUSRMSG_H_INCLUDED 1
+
+/* prototypes */
+rsRetVal modInitUsrMsg(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*);
+
+#endif /* #ifndef OMUSRMSG_H_INCLUDED */
+/* vi:set ai:
+ */
diff --git a/tools/pidfile.c b/tools/pidfile.c
new file mode 100644
index 00000000..8298b94e
--- /dev/null
+++ b/tools/pidfile.c
@@ -0,0 +1,158 @@
+/*
+ pidfile.c - interact with pidfiles
+ Copyright (c) 1995 Martin Schulze <Martin.Schulze@Linux.DE>
+
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+*/
+#include "config.h"
+
+
+#include "rsyslog.h"
+
+/*
+ * Sat Aug 19 13:24:33 MET DST 1995: Martin Schulze
+ * First version (v0.2) released
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#ifdef __sun
+#include <fcntl.h>
+#endif
+
+#include "srUtils.h"
+
+/* read_pid
+ *
+ * Reads the specified pidfile and returns the read pid.
+ * 0 is returned if either there's no pidfile, it's empty
+ * or no pid can be read.
+ */
+int read_pid (char *pidfile)
+{
+ FILE *f;
+ int pid;
+
+ if (!(f=fopen(pidfile,"r")))
+ return 0;
+ if(fscanf(f,"%d", &pid) != 1)
+ pid = 0;
+ fclose(f);
+ return pid;
+}
+
+/* check_pid
+ *
+ * Reads the pid using read_pid and looks up the pid in the process
+ * table (using /proc) to determine if the process already exists. If
+ * so 1 is returned, otherwise 0.
+ */
+int check_pid (char *pidfile)
+{
+ int pid = read_pid(pidfile);
+
+ /* Amazing ! _I_ am already holding the pid file... */
+ if ((!pid) || (pid == getpid ()))
+ return 0;
+
+ /*
+ * The 'standard' method of doing this is to try and do a 'fake' kill
+ * of the process. If an ESRCH error is returned the process cannot
+ * be found -- GW
+ */
+ /* But... errno is usually changed only on error.. */
+ if (kill(pid, 0) && errno == ESRCH)
+ return(0);
+
+ return pid;
+}
+
+/* write_pid
+ *
+ * Writes the pid to the specified file. If that fails 0 is
+ * returned, otherwise the pid.
+ */
+int write_pid (char *pidfile)
+{
+ FILE *f;
+ int fd;
+ int pid;
+
+ if ( ((fd = open(pidfile, O_RDWR|O_CREAT|O_CLOEXEC, 0644)) == -1)
+ || ((f = fdopen(fd, "r+")) == NULL) ) {
+ fprintf(stderr, "Can't open or create %s.\n", pidfile);
+ return 0;
+ }
+
+ /* It seems to be acceptable that we do not lock the pid file
+ * if we run under Solaris. In any case, it is highly unlikely
+ * that two instances try to access this file. And flock is really
+ * causing me grief on my initial steps on Solaris. Some time later,
+ * we might re-enable it (or use some alternate method).
+ * 2006-02-16 rgerhards
+ */
+
+#if HAVE_FLOCK
+ if (flock(fd, LOCK_EX|LOCK_NB) == -1) {
+ if(fscanf(f, "%d", &pid) != 1)
+ pid = 0;
+ fclose(f);
+ printf("Can't lock, lock is held by pid %d.\n", pid);
+ return 0;
+ }
+#endif
+
+ pid = getpid();
+ if (!fprintf(f,"%d\n", pid)) {
+ char errStr[1024];
+ rs_strerror_r(errno, errStr, sizeof(errStr));
+ printf("Can't write pid , %s.\n", errStr);
+ fclose(f);
+ return 0;
+ }
+ fflush(f);
+
+#if HAVE_FLOCK
+ if (flock(fd, LOCK_UN) == -1) {
+ char errStr[1024];
+ rs_strerror_r(errno, errStr, sizeof(errStr));
+ printf("Can't unlock pidfile %s, %s.\n", pidfile, errStr);
+ fclose(f);
+ return 0;
+ }
+#endif
+ fclose(f);
+
+ return pid;
+}
+
+/* remove_pid
+ *
+ * Remove the the specified file. The result from unlink(2)
+ * is returned
+ */
+int remove_pid (char *pidfile)
+{
+ return unlink (pidfile);
+}
+
diff --git a/tools/pidfile.h b/tools/pidfile.h
new file mode 100644
index 00000000..40be9069
--- /dev/null
+++ b/tools/pidfile.h
@@ -0,0 +1,51 @@
+/*
+ pidfile.h - interact with pidfiles
+ Copyright (c) 1995 Martin Schulze <Martin.Schulze@Linux.DE>
+
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+*/
+
+/* read_pid
+ *
+ * Reads the specified pidfile and returns the read pid.
+ * 0 is returned if either there's no pidfile, it's empty
+ * or no pid can be read.
+ */
+int read_pid (char *pidfile);
+
+/* check_pid
+ *
+ * Reads the pid using read_pid and looks up the pid in the process
+ * table (using /proc) to determine if the process already exists. If
+ * so 1 is returned, otherwise 0.
+ */
+int check_pid (char *pidfile);
+
+/* write_pid
+ *
+ * Writes the pid to the specified file. If that fails 0 is
+ * returned, otherwise the pid.
+ */
+int write_pid (char *pidfile);
+
+/* remove_pid
+ *
+ * Remove the the specified file. The result from unlink(2)
+ * is returned
+ */
+int remove_pid (char *pidfile);
diff --git a/tools/pmrfc3164.c b/tools/pmrfc3164.c
new file mode 100644
index 00000000..5dfa74f0
--- /dev/null
+++ b/tools/pmrfc3164.c
@@ -0,0 +1,239 @@
+/* pmrfc3164.c
+ * This is a parser module for RFC3164(legacy syslog)-formatted messages.
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * File begun on 2009-11-04 by RGerhards
+ *
+ * Copyright 2007, 2009 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <ctype.h>
+#include "syslogd.h"
+#include "conf.h"
+#include "syslogd-types.h"
+#include "template.h"
+#include "msg.h"
+#include "module-template.h"
+#include "glbl.h"
+#include "errmsg.h"
+#include "parser.h"
+#include "datetime.h"
+#include "unicode-helper.h"
+
+MODULE_TYPE_PARSER
+MODULE_TYPE_NOKEEP
+PARSER_NAME("rsyslog.rfc3164")
+
+/* internal structures
+ */
+DEF_PMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(parser)
+DEFobjCurrIf(datetime)
+
+
+/* static data */
+static int bParseHOSTNAMEandTAG; /* cache for the equally-named global param - performance enhancement */
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATUREAutomaticSanitazion)
+ iRet = RS_RET_OK;
+ if(eFeat == sFEATUREAutomaticPRIParsing)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
+/* parse a legay-formatted syslog message.
+ */
+BEGINparse
+ uchar *p2parse;
+ int lenMsg;
+ int i; /* general index for parsing */
+ 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");
+ 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!) */
+ p2parse = pMsg->pszRawMsg + pMsg->offAfterPRI; /* point to start of text, after PRI */
+ setProtocolVersion(pMsg, 0);
+
+ /* Check to see if msg contains a timestamp. We start by assuming
+ * that the message timestamp is the time of reception (which we
+ * generated ourselfs and then try to actually find one inside the
+ * message. There we go from high-to low precison and are done
+ * when we find a matching one. -- rgerhards, 2008-09-16
+ */
+ if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse, &lenMsg) == RS_RET_OK) {
+ /* we are done - parse pointer is moved by ParseTIMESTAMP3339 */;
+ } else if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &p2parse, &lenMsg) == RS_RET_OK) {
+ /* we are done - parse pointer is moved by ParseTIMESTAMP3164 */;
+ } else if(*p2parse == ' ' && lenMsg > 1) { /* try to see if it is slighly malformed - HP procurve seems to do that sometimes */
+ ++p2parse; /* move over space */
+ --lenMsg;
+ if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &p2parse, &lenMsg) == RS_RET_OK) {
+ /* indeed, we got it! */
+ /* we are done - parse pointer is moved by ParseTIMESTAMP3164 */;
+ } else {/* parse pointer needs to be restored, as we moved it off-by-one
+ * for this try.
+ */
+ --p2parse;
+ ++lenMsg;
+ }
+ }
+
+ if(pMsg->msgFlags & IGNDATE) {
+ /* we need to ignore the msg data, so simply copy over reception date */
+ memcpy(&pMsg->tTIMESTAMP, &pMsg->tRcvdAt, sizeof(struct syslogTime));
+ }
+
+ /* rgerhards, 2006-03-13: next, we parse the hostname and tag. But we
+ * do this only when the user has not forbidden this. I now introduce some
+ * code that allows a user to configure rsyslogd to treat the rest of the
+ * message as MSG part completely. In this case, the hostname will be the
+ * machine that we received the message from and the tag will be empty. This
+ * is meant to be an interim solution, but for now it is in the code.
+ */
+ if(bParseHOSTNAMEandTAG && !(pMsg->msgFlags & INTERNAL_MSG)) {
+ /* parse HOSTNAME - but only if this is network-received!
+ * rger, 2005-11-14: we still have a problem with BSD messages. These messages
+ * do NOT include a host name. In most cases, this leads to the TAG to be treated
+ * as hostname and the first word of the message as the TAG. Clearly, this is not
+ * of advantage ;) I think I have now found a way to handle this situation: there
+ * are certain characters which are frequently used in TAG (e.g. ':'), which are
+ * *invalid* in host names. So while parsing the hostname, I check for these characters.
+ * If I find them, I set a simple flag but continue. After parsing, I check the flag.
+ * If it was set, then we most probably do not have a hostname but a TAG. Thus, I change
+ * the fields. I think this logic shall work with any type of syslog message.
+ * rgerhards, 2009-06-23: and I now have extended this logic to every character
+ * that is not a valid hostname.
+ */
+ if(lenMsg > 0 && pMsg->msgFlags & PARSE_HOSTNAME) {
+ i = 0;
+ while(i < lenMsg && (isalnum(p2parse[i]) || p2parse[i] == '.'
+ || p2parse[i] == '_' || p2parse[i] == '-') && i < (CONF_HOSTNAME_MAXSIZE - 1)) {
+ bufParseHOSTNAME[i] = p2parse[i];
+ ++i;
+ }
+
+ if(i == lenMsg) {
+ /* we have a message that is empty immediately after the hostname,
+ * but the hostname thus is valid! -- rgerhards, 2010-02-22
+ */
+ p2parse += i;
+ lenMsg -= i;
+ bufParseHOSTNAME[i] = '\0';
+ MsgSetHOSTNAME(pMsg, bufParseHOSTNAME, i);
+ } else if(i > 0 && p2parse[i] == ' ' && isalnum(p2parse[i-1])) {
+ /* we got a hostname! */
+ p2parse += i + 1; /* "eat" it (including SP delimiter) */
+ lenMsg -= i + 1;
+ bufParseHOSTNAME[i] = '\0';
+ MsgSetHOSTNAME(pMsg, bufParseHOSTNAME, i);
+ }
+ }
+
+ /* now parse TAG - that should be present in message from all sources.
+ * This code is somewhat not compliant with RFC 3164. As of 3164,
+ * the TAG field is ended by any non-alphanumeric character. In
+ * practice, however, the TAG often contains dashes and other things,
+ * which would end the TAG. So it is not desirable. As such, we only
+ * accept colon and SP to be terminators. Even there is a slight difference:
+ * a colon is PART of the TAG, while a SP is NOT part of the tag
+ * (it is CONTENT). Starting 2008-04-04, we have removed the 32 character
+ * size limit (from RFC3164) on the tag. This had bad effects on existing
+ * envrionments, as sysklogd didn't obey it either (probably another bug
+ * in RFC3164...). We now receive the full size, but will modify the
+ * outputs so that only 32 characters max are used by default.
+ */
+ i = 0;
+ while(lenMsg > 0 && *p2parse != ':' && *p2parse != ' ' && i < CONF_TAG_MAXSIZE - 2) {
+ bufParseTAG[i++] = *p2parse++;
+ --lenMsg;
+ }
+ if(lenMsg > 0 && *p2parse == ':') {
+ ++p2parse;
+ --lenMsg;
+ bufParseTAG[i++] = ':';
+ }
+
+ /* no TAG can only be detected if the message immediatly ends, in which case an empty TAG
+ * is considered OK. So we do not need to check for empty TAG. -- rgerhards, 2009-06-23
+ */
+ bufParseTAG[i] = '\0'; /* terminate string */
+ MsgSetTAG(pMsg, bufParseTAG, i);
+ } else {/* we enter this code area when the user has instructed rsyslog NOT
+ * to parse HOSTNAME and TAG - rgerhards, 2006-03-13
+ */
+ if(!(pMsg->msgFlags & INTERNAL_MSG)) {
+ DBGPRINTF("HOSTNAME and TAG not parsed by user configuraton.\n");
+ }
+ }
+
+ /* The rest is the actual MSG */
+ MsgSetMSGoffs(pMsg, p2parse - pMsg->pszRawMsg);
+ENDparse
+
+
+BEGINmodExit
+CODESTARTmodExit
+ /* release what we no longer need */
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(parser, CORE_COMPONENT);
+ objRelease(datetime, CORE_COMPONENT);
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_PMOD_QUERIES
+CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit(pmrfc3164)
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(parser, CORE_COMPONENT));
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
+
+ DBGPRINTF("rfc3164 parser init called\n");
+ bParseHOSTNAMEandTAG = glbl.GetParseHOSTNAMEandTAG(); /* cache value, is set only during rsyslogd option processing */
+
+
+ENDmodInit
+
+/* vim:set ai:
+ */
diff --git a/tools/pmrfc3164.h b/tools/pmrfc3164.h
new file mode 100644
index 00000000..24304094
--- /dev/null
+++ b/tools/pmrfc3164.h
@@ -0,0 +1,33 @@
+/* pmrfc3164.h
+ * These are the definitions for the RFC3164 parser module.
+ *
+ * File begun on 2009-11-04 by RGerhards
+ *
+ * Copyright 2009 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#ifndef PMRFC3164_H_INCLUDED
+#define PMRFC3164_H_INCLUDED 1
+
+/* prototypes */
+rsRetVal modInitpmrfc3164(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*);
+
+#endif /* #ifndef PMRFC3164_H_INCLUDED */
+/* vi:set ai:
+ */
diff --git a/tools/pmrfc5424.c b/tools/pmrfc5424.c
new file mode 100644
index 00000000..9b5c6165
--- /dev/null
+++ b/tools/pmrfc5424.c
@@ -0,0 +1,329 @@
+/* pmrfc5424.c
+ * This is a parser module for RFC5424-formatted messages.
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * File begun on 2009-11-03 by RGerhards
+ *
+ * Copyright 2007, 2009 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include "syslogd.h"
+#include "conf.h"
+#include "syslogd-types.h"
+#include "template.h"
+#include "msg.h"
+#include "module-template.h"
+#include "glbl.h"
+#include "errmsg.h"
+#include "parser.h"
+#include "datetime.h"
+#include "unicode-helper.h"
+
+MODULE_TYPE_PARSER
+MODULE_TYPE_NOKEEP
+PARSER_NAME("rsyslog.rfc5424")
+
+/* internal structures
+ */
+DEF_PMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(parser)
+DEFobjCurrIf(datetime)
+
+
+/* config data */
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATUREAutomaticSanitazion)
+ iRet = RS_RET_OK;
+ if(eFeat == sFEATUREAutomaticPRIParsing)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
+/* Helper to parseRFCSyslogMsg. This function parses a field up to
+ * (and including) the SP character after it. The field contents is
+ * returned in a caller-provided buffer. The parsepointer is advanced
+ * to after the terminating SP. The caller must ensure that the
+ * provided buffer is large enough to hold the to be extracted value.
+ * Returns 0 if everything is fine or 1 if either the field is not
+ * SP-terminated or any other error occurs. -- rger, 2005-11-24
+ * The function now receives the size of the string and makes sure
+ * that it does not process more than that. The *pLenStr counter is
+ * updated on exit. -- rgerhards, 2009-09-23
+ */
+static int parseRFCField(uchar **pp2parse, uchar *pResult, int *pLenStr)
+{
+ uchar *p2parse;
+ int iRet = 0;
+
+ assert(pp2parse != NULL);
+ assert(*pp2parse != NULL);
+ assert(pResult != NULL);
+
+ p2parse = *pp2parse;
+
+ /* this is the actual parsing loop */
+ while(*pLenStr > 0 && *p2parse != ' ') {
+ *pResult++ = *p2parse++;
+ --(*pLenStr);
+ }
+
+ if(*pLenStr > 0 && *p2parse == ' ') {
+ ++p2parse; /* eat SP, but only if not at end of string */
+ --(*pLenStr);
+ } else {
+ iRet = 1; /* there MUST be an SP! */
+ }
+ *pResult = '\0';
+
+ /* set the new parse pointer */
+ *pp2parse = p2parse;
+ return iRet;
+}
+
+
+/* Helper to parseRFCSyslogMsg. This function parses the structured
+ * data field of a message. It does NOT parse inside structured data,
+ * just gets the field as whole. Parsing the single entities is left
+ * to other functions. The parsepointer is advanced
+ * to after the terminating SP. The caller must ensure that the
+ * provided buffer is large enough to hold the to be extracted value.
+ * Returns 0 if everything is fine or 1 if either the field is not
+ * SP-terminated or any other error occurs. -- rger, 2005-11-24
+ * The function now receives the size of the string and makes sure
+ * that it does not process more than that. The *pLenStr counter is
+ * updated on exit. -- rgerhards, 2009-09-23
+ */
+static int parseRFCStructuredData(uchar **pp2parse, uchar *pResult, int *pLenStr)
+{
+ uchar *p2parse;
+ int bCont = 1;
+ int iRet = 0;
+ int lenStr;
+
+ assert(pp2parse != NULL);
+ assert(*pp2parse != NULL);
+ assert(pResult != NULL);
+
+ p2parse = *pp2parse;
+ lenStr = *pLenStr;
+
+ /* this is the actual parsing loop
+ * Remeber: structured data starts with [ and includes any characters
+ * until the first ] followed by a SP. There may be spaces inside
+ * structured data. There may also be \] inside the structured data, which
+ * do NOT terminate an element.
+ */
+ if(lenStr == 0 || (*p2parse != '[' && *p2parse != '-'))
+ return 1; /* this is NOT structured data! */
+
+ if(*p2parse == '-') { /* empty structured data? */
+ *pResult++ = '-';
+ ++p2parse;
+ --lenStr;
+ } else {
+ while(bCont) {
+ if(lenStr < 2) {
+ /* we now need to check if we have only structured data */
+ if(lenStr > 0 && *p2parse == ']') {
+ *pResult++ = *p2parse;
+ p2parse++;
+ lenStr--;
+ bCont = 0;
+ } else {
+ iRet = 1; /* this is not valid! */
+ bCont = 0;
+ }
+ } else if(*p2parse == '\\' && *(p2parse+1) == ']') {
+ /* this is escaped, need to copy both */
+ *pResult++ = *p2parse++;
+ *pResult++ = *p2parse++;
+ lenStr -= 2;
+ } else if(*p2parse == ']' && *(p2parse+1) == ' ') {
+ /* found end, just need to copy the ] and eat the SP */
+ *pResult++ = *p2parse;
+ p2parse += 2;
+ lenStr -= 2;
+ bCont = 0;
+ } else {
+ *pResult++ = *p2parse++;
+ --lenStr;
+ }
+ }
+ }
+
+ if(lenStr > 0 && *p2parse == ' ') {
+ ++p2parse; /* eat SP, but only if not at end of string */
+ --lenStr;
+ } else {
+ iRet = 1; /* there MUST be an SP! */
+ }
+ *pResult = '\0';
+
+ /* set the new parse pointer */
+ *pp2parse = p2parse;
+ *pLenStr = lenStr;
+ return iRet;
+}
+
+/* parse a RFC5424-formatted syslog message. This function returns
+ * 0 if processing of the message shall continue and 1 if something
+ * went wrong and this messe should be ignored. This function has been
+ * implemented in the effort to support syslog-protocol. Please note that
+ * the name (parse *RFC*) stems from the hope that syslog-protocol will
+ * some time become an RFC. Do not confuse this with informational
+ * RFC 3164 (which is legacy syslog).
+ *
+ * currently supported format:
+ *
+ * <PRI>VERSION SP TIMESTAMP SP HOSTNAME SP APP-NAME SP PROCID SP MSGID SP [SD-ID]s SP MSG
+ *
+ * <PRI> is already stripped when this function is entered. VERSION already
+ * has been confirmed to be "1", but has NOT been stripped from the message.
+ *
+ * rger, 2005-11-24
+ */
+BEGINparse
+ uchar *p2parse;
+ uchar *pBuf = NULL;
+ int lenMsg;
+ int bContParse = 1;
+CODESTARTparse
+ assert(pMsg != NULL);
+ assert(pMsg->pszRawMsg != NULL);
+ p2parse = pMsg->pszRawMsg + pMsg->offAfterPRI; /* point to start of text, after PRI */
+ lenMsg = pMsg->iLenRawMsg - pMsg->offAfterPRI;
+
+ /* check if we are the right parser */
+ if(lenMsg < 2 || p2parse[0] != '1' || p2parse[1] != ' ') {
+ ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE);
+ }
+ DBGPRINTF("Message has RFC5424/syslog-protocol format.\n");
+ setProtocolVersion(pMsg, 1);
+ p2parse += 2;
+ lenMsg -= 2;
+
+ /* Now get us some memory we can use as a work buffer while parsing.
+ * We simply allocated a buffer sufficiently large to hold all of the
+ * message, so we can not run into any troubles. I think this is
+ * wiser than to use individual buffers.
+ */
+ CHKmalloc(pBuf = MALLOC(sizeof(uchar) * (lenMsg + 1)));
+
+ /* IMPORTANT NOTE:
+ * Validation is not actually done below nor are any errors handled. I have
+ * NOT included this for the current proof of concept. However, it is strongly
+ * advisable to add it when this code actually goes into production.
+ * rgerhards, 2005-11-24
+ */
+
+ /* TIMESTAMP */
+ if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse, &lenMsg) == RS_RET_OK) {
+ if(pMsg->msgFlags & IGNDATE) {
+ /* we need to ignore the msg data, so simply copy over reception date */
+ memcpy(&pMsg->tTIMESTAMP, &pMsg->tRcvdAt, sizeof(struct syslogTime));
+ }
+ } else {
+ DBGPRINTF("no TIMESTAMP detected!\n");
+ bContParse = 0;
+ }
+
+ /* HOSTNAME */
+ if(bContParse) {
+ parseRFCField(&p2parse, pBuf, &lenMsg);
+ MsgSetHOSTNAME(pMsg, pBuf, ustrlen(pBuf));
+ }
+
+ /* APP-NAME */
+ if(bContParse) {
+ parseRFCField(&p2parse, pBuf, &lenMsg);
+ MsgSetAPPNAME(pMsg, (char*)pBuf);
+ }
+
+ /* PROCID */
+ if(bContParse) {
+ parseRFCField(&p2parse, pBuf, &lenMsg);
+ MsgSetPROCID(pMsg, (char*)pBuf);
+ }
+
+ /* MSGID */
+ if(bContParse) {
+ parseRFCField(&p2parse, pBuf, &lenMsg);
+ MsgSetMSGID(pMsg, (char*)pBuf);
+ }
+
+ /* STRUCTURED-DATA */
+ if(bContParse) {
+ parseRFCStructuredData(&p2parse, pBuf, &lenMsg);
+ MsgSetStructuredData(pMsg, (char*)pBuf);
+ }
+
+ /* MSG */
+ MsgSetMSGoffs(pMsg, p2parse - pMsg->pszRawMsg);
+
+finalize_it:
+ if(pBuf != NULL)
+ free(pBuf);
+ENDparse
+
+
+BEGINmodExit
+CODESTARTmodExit
+ /* release what we no longer need */
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(parser, CORE_COMPONENT);
+ objRelease(datetime, CORE_COMPONENT);
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_PMOD_QUERIES
+CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit(pmrfc5424)
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(parser, CORE_COMPONENT));
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
+
+ dbgprintf("rfc5424 parser init called\n");
+ dbgprintf("GetParserName addr %p\n", GetParserName);
+ENDmodInit
+
+/* vim:set ai:
+ */
diff --git a/tools/pmrfc5424.h b/tools/pmrfc5424.h
new file mode 100644
index 00000000..df2a1c81
--- /dev/null
+++ b/tools/pmrfc5424.h
@@ -0,0 +1,33 @@
+/* pmrfc5424.h
+ * These are the definitions for the RFCC5424 parser module.
+ *
+ * File begun on 2009-11-03 by RGerhards
+ *
+ * Copyright 2009 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#ifndef PMRFC54254_H_INCLUDED
+#define PMRFC54254_H_INCLUDED 1
+
+/* prototypes */
+rsRetVal modInitpmrfc5424(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*);
+
+#endif /* #ifndef PMRFC54254_H_INCLUDED */
+/* vi:set ai:
+ */
diff --git a/tools/recover_qi.pl b/tools/recover_qi.pl
new file mode 100755
index 00000000..4e2cf9d5
--- /dev/null
+++ b/tools/recover_qi.pl
@@ -0,0 +1,207 @@
+#!/usr/bin/perl -w
+# recover rsyslog disk queue index (.qi) from queue files (.nnnnnnnn).
+#
+# See:
+# runtime/queue.c: qqueuePersist()
+# runtime/queue.c: qqueueTryLoadPersistedInfo()
+#
+# kaiwang.chen@gmail.com 2012-03-14
+#
+use strict;
+use Getopt::Long;
+
+my %opt = ();
+GetOptions(\%opt,"spool|w=s","basename|f=s","digits|d=i","help!");
+if ($opt{help}) {
+ print "Usage:
+\t$0 -w WorkDirectory -f QueueFileName -d 8 > QueueFileName.qi
+";
+ exit;
+}
+
+# runtime/queue.c: qConstructDisk()
+my $iMaxFiles = 10000000; # 0+"1".( "0"x($opt{digits} - 1));
+
+# get the list of queue files, spool directory excluded
+my $re = qr/^\Q$opt{basename}\E\.\d{$opt{digits}}$/;
+opendir(DIR, $opt{spool}) or die "can’t open spool: $!";
+my @qf = grep { /$re/ && -f "$opt{spool}/$_" } readdir(DIR);
+closedir DIR;
+
+# ensure order and continuity
+@qf = sort @qf;
+my ($head) = ($qf[0] =~ /(\d+)$/);
+my ($tail) = ($qf[-1] =~ /(\d+)$/);
+$head += 0;
+$tail += 0;
+if ($tail-$head+1 != @qf || $tail > $iMaxFiles) {
+ die "broken queue: missing file(s) or wrong tail\n";
+}
+
+# collect some counters about the queue, assuming all are unprocessed entries.
+my $sizeOnDisk = 0;
+my $iQueueSize = 0;
+chdir($opt{spool}) or die "can't chdir to spool: $!";
+print STDERR "traversing ". @qf ." files, please wait...\n";
+for (@qf) {
+ open FH, "<", $_ or die "can't read queue file $_\n";
+ $sizeOnDisk += (stat FH)[7];
+ while (<FH>) {
+ $iQueueSize++ if /^<Obj/; # runtime/msg.c: MsgSerialize()
+ }
+ close FH;
+}
+# happen to reuse last stat
+my $iCurrOffs_Write = (stat(_))[7];
+
+# runtime/queue.c: qqueuePersist()
+my $qqueue = Rsyslog::OPB->new("qqueue",1);
+$qqueue->property("iQueueSize", "INT", $iQueueSize);
+$qqueue->property("tVars.disk.sizeOnDisk", "INT64", $sizeOnDisk);
+$qqueue->property("tVars.disk.bytesRead", "INT64", 0);
+
+# runtime/stream.h: strmType_t
+my $STREAMTYPE_FILE_CIRCULAR = 1;
+# runtime/stream.h: strmMode_t
+my $STREAMMODE_READ = 1;
+my $STREAMMODE_WRITE_APPEND = 4;
+
+# runtime/stream.c: strmSerialize()
+# write to end
+my $strm_Write = Rsyslog::Obj->new("strm",1);
+$strm_Write->property( "iCurrFNum", "INT", $tail);
+$strm_Write->property( "pszFName", "PSZ", $opt{basename});
+$strm_Write->property( "iMaxFiles", "INT", $iMaxFiles);
+$strm_Write->property( "bDeleteOnClose", "INT", 0);
+$strm_Write->property( "sType", "INT", $STREAMTYPE_FILE_CIRCULAR);
+$strm_Write->property("tOperationsMode", "INT", $STREAMMODE_WRITE_APPEND);
+$strm_Write->property( "tOpenMode", "INT", 0600);
+$strm_Write->property( "iCurrOffs","INT64", $iCurrOffs_Write);
+# read from head
+my $strm_ReadDel = Rsyslog::Obj->new("strm",1);
+$strm_ReadDel->property( "iCurrFNum", "INT", $head);
+$strm_ReadDel->property( "pszFName", "PSZ", $opt{basename});
+$strm_ReadDel->property( "iMaxFiles", "INT", $iMaxFiles);
+$strm_ReadDel->property( "bDeleteOnClose", "INT", 1);
+$strm_ReadDel->property( "sType", "INT", $STREAMTYPE_FILE_CIRCULAR);
+$strm_ReadDel->property("tOperationsMode", "INT", $STREAMMODE_READ);
+$strm_ReadDel->property( "tOpenMode", "INT", 0600);
+$strm_ReadDel->property( "iCurrOffs","INT64", 0);
+
+# .qi
+print $qqueue->serialize();
+print $strm_Write->serialize();
+print $strm_ReadDel->serialize();
+
+exit;
+#-----------------------------------------------------------------------------
+
+package Rsyslog::Serializable;
+# runtime/obj.c
+sub COOKIE_OBJLINE { '<' }
+sub COOKIE_PROPLINE { '+' }
+sub COOKIE_ENDLINE { '>' }
+sub COOKIE_BLANKLINE { '.' }
+# VARTYPE(short_ptype)
+sub VARTYPE {
+ my ($t) = @_;
+ # runtime/obj-types.h: propType_t
+ my $ptype = "PROPTYPE_".$t;
+ # runtime/var.h: varType_t
+ my %vm = (
+ VARTYPE_NONE => 0,
+ VARTYPE_STR => 1,
+ VARTYPE_NUMBER => 2,
+ VARTYPE_SYSLOGTIME => 3,
+ );
+ # runtime/obj.c: SerializeProp()
+ my %p2v = (
+ #PROPTYPE_NONE => "",
+ PROPTYPE_PSZ => "VARTYPE_STR",
+ PROPTYPE_SHORT => "VARTYPE_NUMBER",
+ PROPTYPE_INT => "VARTYPE_NUMBER",
+ PROPTYPE_LONG => "VARTYPE_NUMBER",
+ PROPTYPE_INT64 => "VARTYPE_NUMBER",
+ PROPTYPE_CSTR => "VARTYPE_STR",
+ #PROPTYPE_SYSLOGTIME => "VARTYPE_SYSLOGTIME",
+ );
+ my $vtype = $p2v{$ptype};
+ unless ($vtype) {
+ die "property type $t is not supported!\n";
+ }
+ return $vm{$vtype};
+}
+sub serialize {
+ my $self = shift;
+ # runtime/obj.c: objSerializeHeader()
+ my $x = COOKIE_OBJLINE();
+ $x .= join(":", $self->type(), $self->cver(), $self->id(), $self->version());
+ $x .= ":\n";
+ for ( values %{$self->{props}} ) {
+ # runtime/obj.c: SerializeProp()
+ $x .= COOKIE_PROPLINE();
+ $x .= join(":",
+ $_->{name},
+ VARTYPE($_->{type}),
+ length($_->{value}),
+ $_->{value});
+ $x .= ":\n";
+ }
+ # runtime/obj.c: EndSerialize()
+ $x .= COOKIE_ENDLINE() . "End\n";
+ $x .= COOKIE_BLANKLINE() . "\n";
+}
+# constructor: new(id,version)
+sub new {
+ my ($class, $id, $version) = @_;
+ $class = ref $class if ref $class;
+ bless {
+ id => $id,
+ version => $version,
+ props => {},
+ }, $class;
+}
+sub id {
+ my $self = shift;
+ if (@_) {
+ my $x = $self->{id};
+ $self->{id} = shift;
+ return $x;
+ }
+ return $self->{id};
+}
+sub version {
+ my $self = shift;
+ if (@_) {
+ my $x = $self->{version};
+ $self->{version} = shift;
+ return $x;
+ }
+ return $self->{version};
+}
+# property(name, type, value)
+sub property {
+ my $self = shift;
+ my $name = shift;
+ if (@_) {
+ my $x = $self->{props}{$name};
+ $self->{props}{$name}{name} = $name;
+ $self->{props}{$name}{type} = shift;
+ $self->{props}{$name}{value} = shift;
+ return $x;
+ }
+ return $self->{props}{$name};
+}
+1;
+package Rsyslog::OPB;
+use base qw(Rsyslog::Serializable);
+sub type { 'OPB' }
+sub cver { 1 }
+sub new { shift->SUPER::new(@_) }
+1;
+package Rsyslog::Obj;
+use base qw(Rsyslog::Serializable);
+sub type { 'Obj' }
+sub cver { 1 }
+sub new { shift->SUPER::new(@_) }
+1;
diff --git a/tools/regexp.c b/tools/regexp.c
new file mode 100644
index 00000000..e8bba4f4
--- /dev/null
+++ b/tools/regexp.c
@@ -0,0 +1,73 @@
+/* A simple regular expression checker for rsyslog test and debug.
+ * Regular expressions have shown to turn out to be a hot support topic.
+ * While I have done an online tool at http://www.rsyslog.com/tool-regex
+ * there are still some situations where one wants to check against the
+ * actual clib api calls. This is what this small test program does,
+ * it takes its command line arguments (re first, then sample data) and
+ * pushes them into the API and then shows the result. This should be
+ * considered the ultimate reference for any questions arising.
+ *
+ * Copyright 2008 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <regex.h>
+
+int main(int argc, char *argv[])
+{
+ regex_t preg;
+ size_t nmatch = 10;
+ regmatch_t pmatch[10];
+ char *pstr;
+ int i;
+
+ if(argc != 3) {
+ fprintf(stderr, "usage: regex regexp sample-data\n");
+ exit(1);
+ }
+
+ pstr = strdup(argv[2]); /* get working copy */
+
+ i = regcomp(&preg, argv[1], REG_EXTENDED);
+ printf("regcomp returns %d\n", i);
+ i = regexec(&preg, pstr, nmatch, pmatch, 0);
+ printf("regexec returns %d\n", i);
+ if(i == REG_NOMATCH) {
+ printf("found no match!\n");
+ return 1;
+ }
+
+ printf("returned substrings:\n");
+ for(i = 0 ; i < 10 ; i++) {
+ printf("%d: so %d, eo %d", i, pmatch[i].rm_so, pmatch[i].rm_eo);
+ if(pmatch[i].rm_so != -1) {
+ int j;
+ printf(", text: '");
+ for(j = pmatch[i].rm_so ; j < pmatch[i].rm_eo ; ++j)
+ putchar(pstr[j]);
+ putchar('\'');
+ }
+ putchar('\n');
+ }
+ return 0;
+}
diff --git a/tools/rscryutil.c b/tools/rscryutil.c
new file mode 100644
index 00000000..2591b2cc
--- /dev/null
+++ b/tools/rscryutil.c
@@ -0,0 +1,512 @@
+/* This is a tool for processing rsyslog encrypted log files.
+ *
+ * Copyright 2013 Adiscon GmbH
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either exprs or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <gcrypt.h>
+
+#include "rsyslog.h"
+#include "libgcry.h"
+
+
+static enum { MD_DECRYPT, MD_WRITE_KEYFILE
+} mode = MD_DECRYPT;
+static int verbose = 0;
+static gcry_cipher_hd_t gcry_chd;
+static size_t blkLength;
+
+static char *keyfile = NULL;
+static char *keyprog = NULL;
+static int randomKeyLen = -1;
+static char *cry_key = NULL;
+static unsigned cry_keylen = 0;
+static int cry_algo = GCRY_CIPHER_AES128;
+static int cry_mode = GCRY_CIPHER_MODE_CBC;
+static int optionForce = 0;
+
+/* rectype/value must be EIF_MAX_*_LEN+1 long!
+ * returns 0 on success or something else on error/EOF
+ */
+static int
+eiGetRecord(FILE *eifp, char *rectype, char *value)
+{
+ int r;
+ unsigned short i, j;
+ char buf[EIF_MAX_RECTYPE_LEN+EIF_MAX_VALUE_LEN+128];
+ /* large enough for any valid record */
+
+ if(fgets(buf, sizeof(buf), eifp) == NULL) {
+ r = 1; goto done;
+ }
+
+ for(i = 0 ; i < EIF_MAX_RECTYPE_LEN && buf[i] != ':' ; ++i)
+ if(buf[i] == '\0') {
+ r = 2; goto done;
+ } else
+ rectype[i] = buf[i];
+ rectype[i] = '\0';
+ j = 0;
+ for(++i ; i < EIF_MAX_VALUE_LEN && buf[i] != '\n' ; ++i, ++j)
+ if(buf[i] == '\0') {
+ r = 3; goto done;
+ } else
+ value[j] = buf[i];
+ value[j] = '\0';
+ r = 0;
+done: return r;
+}
+
+static int
+eiCheckFiletype(FILE *eifp)
+{
+ char rectype[EIF_MAX_RECTYPE_LEN+1];
+ char value[EIF_MAX_VALUE_LEN+1];
+ int r;
+
+ if((r = eiGetRecord(eifp, rectype, value)) != 0) goto done;
+ if(strcmp(rectype, "FILETYPE") || strcmp(value, RSGCRY_FILETYPE_NAME)) {
+ fprintf(stderr, "invalid filetype \"cookie\" in encryption "
+ "info file\n");
+ fprintf(stderr, "\trectype: '%s', value: '%s'\n", rectype, value);
+ r = 1; goto done;
+ }
+ r = 0;
+done: return r;
+}
+
+static int
+eiGetIV(FILE *eifp, char *iv, size_t leniv)
+{
+ char rectype[EIF_MAX_RECTYPE_LEN+1];
+ char value[EIF_MAX_VALUE_LEN+1];
+ size_t valueLen;
+ unsigned short i, j;
+ int r;
+ unsigned char nibble;
+
+ if((r = eiGetRecord(eifp, rectype, value)) != 0) goto done;
+ if(strcmp(rectype, "IV")) {
+ fprintf(stderr, "no IV record found when expected, record type "
+ "seen is '%s'\n", rectype);
+ r = 1; goto done;
+ }
+ valueLen = strlen(value);
+ if(valueLen/2 != leniv) {
+ fprintf(stderr, "length of IV is %d, expected %d\n",
+ valueLen/2, leniv);
+ r = 1; goto done;
+ }
+
+ for(i = j = 0 ; i < valueLen ; ++i) {
+ if(value[i] >= '0' && value[i] <= '9')
+ nibble = value[i] - '0';
+ else if(value[i] >= 'a' && value[i] <= 'f')
+ nibble = value[i] - 'a' + 10;
+ else {
+ fprintf(stderr, "invalid IV '%s'\n", value);
+ r = 1; goto done;
+ }
+ if(i % 2 == 0)
+ iv[j] = nibble << 4;
+ else
+ iv[j++] |= nibble;
+ }
+ r = 0;
+done: return r;
+}
+
+static int
+eiGetEND(FILE *eifp, off64_t *offs)
+{
+ char rectype[EIF_MAX_RECTYPE_LEN+1];
+ char value[EIF_MAX_VALUE_LEN+1];
+ int r;
+
+ if((r = eiGetRecord(eifp, rectype, value)) != 0) goto done;
+ if(strcmp(rectype, "END")) {
+ fprintf(stderr, "no END record found when expected, record type "
+ "seen is '%s'\n", rectype);
+ r = 1; goto done;
+ }
+ *offs = atoll(value);
+ r = 0;
+done: return r;
+}
+
+static int
+initCrypt(FILE *eifp)
+{
+ int r = 0;
+ gcry_error_t gcryError;
+ char iv[4096];
+
+ blkLength = gcry_cipher_get_algo_blklen(cry_algo);
+ if(blkLength > sizeof(iv)) {
+ fprintf(stderr, "internal error[%s:%d]: block length %d too large for "
+ "iv buffer\n", __FILE__, __LINE__, blkLength);
+ r = 1; goto done;
+ }
+ if((r = eiGetIV(eifp, iv, blkLength)) != 0) goto done;
+
+ size_t keyLength = gcry_cipher_get_algo_keylen(cry_algo);
+ if(strlen(cry_key) != keyLength) {
+ fprintf(stderr, "invalid key length; key is %u characters, but "
+ "exactly %u characters are required\n", cry_keylen,
+ keyLength);
+ r = 1; goto done;
+ }
+
+ gcryError = gcry_cipher_open(&gcry_chd, cry_algo, cry_mode, 0);
+ if (gcryError) {
+ printf("gcry_cipher_open failed: %s/%s\n",
+ gcry_strsource(gcryError),
+ gcry_strerror(gcryError));
+ r = 1; goto done;
+ }
+
+ gcryError = gcry_cipher_setkey(gcry_chd, cry_key, keyLength);
+ if (gcryError) {
+ printf("gcry_cipher_setkey failed: %s/%s\n",
+ gcry_strsource(gcryError),
+ gcry_strerror(gcryError));
+ r = 1; goto done;
+ }
+
+ gcryError = gcry_cipher_setiv(gcry_chd, iv, blkLength);
+ if (gcryError) {
+ printf("gcry_cipher_setiv failed: %s/%s\n",
+ gcry_strsource(gcryError),
+ gcry_strerror(gcryError));
+ r = 1; goto done;
+ }
+done: return r;
+}
+
+static inline void
+removePadding(char *buf, size_t *plen)
+{
+ unsigned len = (unsigned) *plen;
+ unsigned iSrc, iDst;
+ char *frstNUL;
+
+ frstNUL = memchr(buf, 0x00, *plen);
+ if(frstNUL == NULL)
+ goto done;
+ iDst = iSrc = frstNUL - buf;
+
+ while(iSrc < len) {
+ if(buf[iSrc] != 0x00)
+ buf[iDst++] = buf[iSrc];
+ ++iSrc;
+ }
+
+ *plen = iDst;
+done: return;
+}
+
+static void
+decryptBlock(FILE *fpin, FILE *fpout, off64_t blkEnd, off64_t *pCurrOffs)
+{
+ gcry_error_t gcryError;
+ size_t nRead, nWritten;
+ size_t toRead;
+ size_t leftTillBlkEnd;
+ char buf[64*1024];
+
+ leftTillBlkEnd = blkEnd - *pCurrOffs;
+ while(1) {
+ toRead = sizeof(buf) <= leftTillBlkEnd ? sizeof(buf) : leftTillBlkEnd;
+ toRead = toRead - toRead % blkLength;
+ nRead = fread(buf, 1, toRead, fpin);
+ if(nRead == 0)
+ break;
+ leftTillBlkEnd -= nRead, *pCurrOffs += nRead;
+ gcryError = gcry_cipher_decrypt(
+ gcry_chd, // gcry_cipher_hd_t
+ buf, // void *
+ nRead, // size_t
+ NULL, // const void *
+ 0); // size_t
+ if (gcryError) {
+ fprintf(stderr, "gcry_cipher_decrypt failed: %s/%s\n",
+ gcry_strsource(gcryError),
+ gcry_strerror(gcryError));
+ return;
+ }
+ removePadding(buf, &nRead);
+ nWritten = fwrite(buf, 1, nRead, fpout);
+ if(nWritten != nRead) {
+ perror("fpout");
+ return;
+ }
+ }
+}
+
+
+static int
+doDecrypt(FILE *logfp, FILE *eifp, FILE *outfp)
+{
+ off64_t blkEnd;
+ off64_t currOffs = 0;
+ int r;
+
+ while(1) {
+ /* process block */
+ if(initCrypt(eifp) != 0)
+ goto done;
+ if((r = eiGetEND(eifp, &blkEnd)) != 0) goto done;
+ decryptBlock(logfp, outfp, blkEnd, &currOffs);
+ gcry_cipher_close(gcry_chd);
+ }
+ r = 0;
+done: return r;
+}
+
+static void
+decrypt(char *name)
+{
+ FILE *logfp = NULL, *eifp = NULL;
+ int r = 0;
+ char eifname[4096];
+
+ if(!strcmp(name, "-")) {
+ fprintf(stderr, "decrypt mode cannot work on stdin\n");
+ goto err;
+ } else {
+ if((logfp = fopen(name, "r")) == NULL) {
+ perror(name);
+ goto err;
+ }
+ snprintf(eifname, sizeof(eifname), "%s%s", name, ENCINFO_SUFFIX);
+ eifname[sizeof(eifname)-1] = '\0';
+ if((eifp = fopen(eifname, "r")) == NULL) {
+ perror(eifname);
+ goto err;
+ }
+ if(eiCheckFiletype(eifp) != 0)
+ goto err;
+ }
+
+ doDecrypt(logfp, eifp, stdout);
+
+ fclose(logfp); logfp = NULL;
+ fclose(eifp); eifp = NULL;
+ return;
+
+err:
+ fprintf(stderr, "error %d processing file %s\n", r, name);
+ if(logfp != NULL)
+ fclose(logfp);
+}
+
+static void
+write_keyfile(char *fn)
+{
+ int fd;
+ int r;
+ mode_t fmode;
+
+ fmode = O_WRONLY|O_CREAT;
+ if(!optionForce)
+ fmode |= O_EXCL;
+ if((fd = open(fn, fmode, S_IRUSR)) == -1) {
+ fprintf(stderr, "error opening keyfile ");
+ perror(fn);
+ exit(1);
+ }
+ if((r = write(fd, cry_key, cry_keylen)) != (ssize_t)cry_keylen) {
+ fprintf(stderr, "error writing keyfile (ret=%d) ", r);
+ perror(fn);
+ exit(1);
+ }
+ close(fd);
+}
+
+static void
+getKeyFromFile(char *fn)
+{
+ int r;
+ r = gcryGetKeyFromFile(fn, &cry_key, &cry_keylen);
+ if(r != 0) {
+ fprintf(stderr, "Error %d reading key from file '%s'\n", r, fn);
+ exit(1);
+ }
+}
+
+static void
+getRandomKey(void)
+{
+ int fd;
+ cry_keylen = randomKeyLen;
+ cry_key = malloc(randomKeyLen); /* do NOT zero-out! */
+ /* if we cannot obtain data from /dev/urandom, we use whatever
+ * is present at the current memory location as random data. Of
+ * course, this is very weak and we should consider a different
+ * option, especially when not running under Linux (for Linux,
+ * unavailability of /dev/urandom is just a theoretic thing, it
+ * will always work...). -- TODO -- rgerhards, 2013-03-06
+ */
+ if((fd = open("/dev/urandom", O_RDONLY)) > 0) {
+ if(read(fd, cry_key, randomKeyLen)) {}; /* keep compiler happy */
+ close(fd);
+ }
+}
+
+
+static void
+setKey()
+{
+ if(randomKeyLen != -1)
+ getRandomKey();
+ else if(keyfile != NULL)
+ getKeyFromFile(keyfile);
+ else if(keyprog != NULL)
+ gcryGetKeyFromProg(keyprog, &cry_key, &cry_keylen);
+ if(cry_key == NULL) {
+ fprintf(stderr, "ERROR: key must be set via some method\n");
+ exit(1);
+ }
+}
+
+static struct option long_options[] =
+{
+ {"verbose", no_argument, NULL, 'v'},
+ {"version", no_argument, NULL, 'V'},
+ {"decrypt", no_argument, NULL, 'd'},
+ {"force", no_argument, NULL, 'f'},
+ {"write-keyfile", required_argument, NULL, 'W'},
+ {"key", required_argument, NULL, 'K'},
+ {"generate-random-key", required_argument, NULL, 'r'},
+ {"keyfile", required_argument, NULL, 'k'},
+ {"key-program", required_argument, NULL, 'p'},
+ {"algo", required_argument, NULL, 'a'},
+ {"mode", required_argument, NULL, 'm'},
+ {NULL, 0, NULL, 0}
+};
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ int opt;
+ int temp;
+ char *newKeyFile = NULL;
+
+ while(1) {
+ opt = getopt_long(argc, argv, "a:dfk:K:m:p:r:vVW:", long_options, NULL);
+ if(opt == -1)
+ break;
+ switch(opt) {
+ case 'd':
+ mode = MD_DECRYPT;
+ break;
+ case 'W':
+ mode = MD_WRITE_KEYFILE;
+ newKeyFile = optarg;
+ break;
+ case 'k':
+ keyfile = optarg;
+ break;
+ case 'p':
+ keyprog = optarg;
+ break;
+ case 'f':
+ optionForce = 1;
+ break;
+ case 'r':
+ randomKeyLen = atoi(optarg);
+ if(randomKeyLen > 64*1024) {
+ fprintf(stderr, "ERROR: keys larger than 64KiB are "
+ "not supported\n");
+ exit(1);
+ }
+ break;
+ case 'K':
+ fprintf(stderr, "WARNING: specifying the actual key "
+ "via the command line is highly insecure\n"
+ "Do NOT use this for PRODUCTION use.\n");
+ cry_key = optarg;
+ cry_keylen = strlen(cry_key);
+ break;
+ case 'a':
+ temp = rsgcryAlgoname2Algo(optarg);
+ if(temp == GCRY_CIPHER_NONE) {
+ fprintf(stderr, "ERROR: algorithm \"%s\" is not "
+ "kown/supported\n", optarg);
+ exit(1);
+ }
+ cry_algo = temp;
+ break;
+ case 'm':
+ temp = rsgcryModename2Mode(optarg);
+ if(temp == GCRY_CIPHER_MODE_NONE) {
+ fprintf(stderr, "ERROR: cipher mode \"%s\" is not "
+ "kown/supported\n", optarg);
+ exit(1);
+ }
+ cry_mode = temp;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'V':
+ fprintf(stderr, "rsgtutil " VERSION "\n");
+ exit(0);
+ break;
+ case '?':
+ break;
+ default:fprintf(stderr, "getopt_long() returns unknown value %d\n", opt);
+ return 1;
+ }
+ }
+
+ setKey();
+
+ if(mode == MD_WRITE_KEYFILE) {
+ if(optind != argc) {
+ fprintf(stderr, "ERROR: no file parameters permitted in "
+ "--write-keyfile mode\n");
+ exit(1);
+ }
+ write_keyfile(newKeyFile);
+ } else {
+ if(optind == argc)
+ decrypt("-");
+ else {
+ for(i = optind ; i < argc ; ++i)
+ decrypt(argv[i]);
+ }
+ }
+
+ memset(cry_key, 0, cry_keylen); /* zero-out key store */
+ cry_keylen = 0;
+ return 0;
+}
diff --git a/tools/rscryutil.rst b/tools/rscryutil.rst
new file mode 100644
index 00000000..dfd447d2
--- /dev/null
+++ b/tools/rscryutil.rst
@@ -0,0 +1,199 @@
+=========
+rscryutil
+=========
+
+--------------------------
+Manage Encrypted Log Files
+--------------------------
+
+:Author: Rainer Gerhards <rgerhards@adiscon.com>
+:Date: 2013-04-15
+:Manual section: 1
+
+SYNOPSIS
+========
+
+::
+
+ rscryutil [OPTIONS] [FILE] ...
+
+
+DESCRIPTION
+===========
+
+This tool performs various operations on encrypted log files.
+Most importantly, it provides the ability to decrypt them.
+
+
+OPTIONS
+=======
+
+-d, --decrypt
+ Select decryption mode. This is the default mode.
+
+-W, --write-keyfile <file>
+ Utility function to write a key to a keyfile. The key can be obtained
+ via any method.
+
+-v, --verbose
+ Select verbose mode.
+
+-f, --force
+ Forces operations that otherwise would fail.
+
+-k, --keyfile <file>
+ Reads the key from <file>. File _must_ contain the key, only, no headers
+ or other meta information. Keyfiles can be generated via the
+ *--write-keyfile* option.
+
+-p, --key-program <path-to-program>
+ In this mode, the key is provided by a so-called "key program". This program
+ is executed and must return the key to (as well as some meta information)
+ via stdout. The core idea of key programs is that using this interface the
+ user can implement as complex (and secure) method to obtain keys as
+ desired, all without the need to make modifications to rsyslog.
+
+-K, --key <KEY>
+ TESTING AID, NOT FOR PRODUCTION USE. This uses the KEY specified
+ on the command line. This is the actual key, and as such this mode
+ is highly insecure. However, it can be useful for intial testing
+ steps. This option may be removed in the future.
+
+-a, --algo <algo>
+ Sets the encryption algorightm (cipher) to be used. See below
+ for supported algorithms. The default is "AES128".
+
+-m, --mode <mode>
+ Sets the ciphermode to be used. See below for supported modes.
+ The default is "CBC".
+
+-r, --generate-random-key <bytes>
+ Generates a random key of length <bytes>. This option is
+ meant to be used together with *--write-keyfile* (and it is hard
+ to envision any other valid use for it).
+
+OPERATION MODES
+===============
+
+The operation mode specifies what exactly the tool does with the provided
+files. The default operation mode is "dump", but this may change in the future.
+Thus, it is recommended to always set the operations mode explicitely. If
+multiple operations mode are set on the command line, results are
+unpredictable.
+
+decrypt
+-------
+
+The provided log files are decrypted. Note that the *.encinfo* side files
+must exist and be accessible in order for decryption to to work.
+
+write-keyfile
+-------------
+
+In this mode no log files are processed; thus it is an error to specify
+any on the command line. The specified keyfile is written. The key itself
+is obtained via the usual key commands. If *--keyfile* is used, that
+file is effectively copied.
+
+For security reasons, existing key files are _not_ overwritten. To permit
+this, specify the *--force* option. When doing so, keep in mind that lost
+keys cannot be recovered and data encrypted with them may also be considered
+lost.
+
+Keyfiles are always created with 0400 permission, that is read access for only
+the user. An exception is when an existing file is overwritten via the
+*--force* option, in which case the former permissions still apply.
+
+EXIT CODES
+==========
+
+The command returns an exit code of 0 if everything went fine, and some
+other code in case of failures.
+
+
+SUPPORTED ALGORITHMS
+====================
+
+We basically support what libgcrypt supports. This is:
+
+ 3DES
+ CAST5
+ BLOWFISH
+ AES128
+ AES192
+ AES256
+ TWOFISH
+ TWOFISH128
+ ARCFOUR
+ DES
+ SERPENT128
+ SERPENT192
+ SERPENT256
+ RFC2268_40
+ SEED
+ CAMELLIA128
+ CAMELLIA192
+ CAMELLIA256
+
+
+SUPPORTED CIPHER MODES
+======================
+
+We basically support what libgcrypt supports. This is:
+
+ ECB
+ CFB
+ CBC
+ STREAM
+ OFB
+ CTR
+ AESWRAP
+
+EXAMPLES
+========
+
+**rscryutil logfile**
+
+Decrypts "logfile" and sends data to stdout.
+
+
+**rscryutil --generate-random-key 16 --keyfile /some/secured/path/keyfile**
+
+Generates random key and stores it in the specified keyfile.
+
+LOG SIGNATURES
+==============
+
+Encrypted log files can be used together with signing. To verify such a file,
+it must be decrypted first, and the verification tool **rsgtutil(1)** must be
+run on the decrypted file.
+
+SECURITY CONSIDERATIONS
+=======================
+
+Specifying keys directly on the command line (*--key* option) is very
+insecure and should
+not be done, except for testing purposes with test keys. Even then it is
+recommended to use keyfiles, which are also easy to handle during testing.
+Keep in mind that command history is usally be kept by bash and can also
+easily be monitored.
+
+Local keyfiles are also a security risk. At a minimum, they should be
+used with very restrictive file permissions. For this reason,
+the *rscryutil* tool creates them with read permissions for the user,
+only, no matter what umask is set to.
+
+When selecting cipher algorithms and modes, care needs to be taken. The
+defaults should be reasonable safe to use, but this tends to change over
+time. Keep up with the most current crypto recommendations.
+
+
+SEE ALSO
+========
+**rsgtutil(1)**, **rsyslogd(8)**
+
+COPYRIGHT
+=========
+
+This page is part of the *rsyslog* project, and is available under
+LGPLv2.
diff --git a/tools/rsgtutil.c b/tools/rsgtutil.c
new file mode 100644
index 00000000..095b8066
--- /dev/null
+++ b/tools/rsgtutil.c
@@ -0,0 +1,431 @@
+/* This is a tool for dumpoing the content of GuardTime TLV
+ * files in a (somewhat) human-readable manner.
+ *
+ * Copyright 2013 Adiscon GmbH
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either exprs or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <gt_base.h>
+#include <gt_http.h>
+#include <getopt.h>
+
+#include "librsgt.h"
+
+typedef unsigned char uchar;
+
+static enum { MD_DUMP, MD_DETECT_FILE_TYPE, MD_SHOW_SIGBLK_PARAMS,
+ MD_VERIFY, MD_EXTEND
+} mode = MD_DUMP;
+static int verbose = 0;
+
+static void
+dumpFile(char *name)
+{
+ FILE *fp;
+ uchar hdr[9];
+ void *obj;
+ tlvrecord_t rec;
+ int r = -1;
+
+ if(!strcmp(name, "-"))
+ fp = stdin;
+ else {
+ printf("Processing file %s:\n", name);
+ if((fp = fopen(name, "r")) == NULL) {
+ perror(name);
+ goto err;
+ }
+ }
+ if((r = rsgt_tlvrdHeader(fp, hdr)) != 0) goto err;
+ printf("File Header: '%s'\n", hdr);
+ while(1) { /* we will err out on EOF */
+ if((r = rsgt_tlvrd(fp, &rec, &obj)) != 0) {
+ if(feof(fp))
+ break;
+ else
+ goto err;
+ }
+ rsgt_tlvprint(stdout, rec.tlvtype, obj, verbose);
+ rsgt_objfree(rec.tlvtype, obj);
+ }
+
+ if(fp != stdin)
+ fclose(fp);
+ return;
+err: fprintf(stderr, "error %d processing file %s\n", r, name);
+}
+
+static void
+showSigblkParams(char *name)
+{
+ FILE *fp;
+ block_sig_t *bs;
+ uint8_t bHasRecHashes, bHasIntermedHashes;
+ uint64_t blkCnt = 0;
+ int r = -1;
+
+ if(!strcmp(name, "-"))
+ fp = stdin;
+ else {
+ if((fp = fopen(name, "r")) == NULL) {
+ perror(name);
+ goto err;
+ }
+ }
+ if((r = rsgt_chkFileHdr(fp, "LOGSIG10")) != 0) goto err;
+
+ while(1) { /* we will err out on EOF */
+ if((r = rsgt_getBlockParams(fp, 0, &bs, &bHasRecHashes,
+ &bHasIntermedHashes)) != 0)
+ goto err;
+ ++blkCnt;
+ rsgt_printBLOCK_SIG(stdout, bs, verbose);
+ printf("\t***META INFORMATION:\n");
+ printf("\tBlock Nbr in File...: %llu\n", blkCnt);
+ printf("\tHas Record Hashes...: %d\n", bHasRecHashes);
+ printf("\tHas Tree Hashes.....: %d\n", bHasIntermedHashes);
+ }
+
+ if(fp != stdin)
+ fclose(fp);
+ return;
+err:
+ if(r != RSGTE_EOF)
+ fprintf(stderr, "error %d processing file %s\n", r, name);
+}
+
+static void
+detectFileType(char *name)
+{
+ FILE *fp;
+ char *typeName;
+ char hdr[9];
+ int r = -1;
+
+ if(!strcmp(name, "-"))
+ fp = stdin;
+ else {
+ if((fp = fopen(name, "r")) == NULL) {
+ perror(name);
+ goto err;
+ }
+ }
+ if((r = rsgt_tlvrdHeader(fp, (uchar*)hdr)) != 0) goto err;
+ if(!strcmp(hdr, "LOGSIG10"))
+ typeName = "Log Signature File, Version 10";
+ else if(!strcmp(hdr, "GTSTAT10"))
+ typeName = "rsyslog GuardTime Signature State File, Version 10";
+ else
+ typeName = "unknown";
+
+ printf("%s: %s [%s]\n", name, hdr, typeName);
+
+ if(fp != stdin)
+ fclose(fp);
+ return;
+err: fprintf(stderr, "error %d processing file %s\n", r, name);
+}
+
+static inline int
+doVerifyRec(FILE *logfp, FILE *sigfp, FILE *nsigfp,
+ block_sig_t *bs, gtfile gf, gterrctx_t *ectx, uint8_t bInBlock)
+{
+ int r;
+ size_t lenRec;
+ char line[128*1024];
+
+ if(fgets(line, sizeof(line), logfp) == NULL) {
+ if(feof(logfp)) {
+ r = RSGTE_EOF;
+ } else {
+ perror("log file input");
+ r = RSGTE_IO;
+ }
+ goto done;
+ }
+ lenRec = strlen(line);
+ if(line[lenRec-1] == '\n') {
+ line[lenRec-1] = '\0';
+ --lenRec;
+ rsgt_errctxSetErrRec(ectx, line);
+ }
+
+ /* we need to preserve the first line (record) of each block for
+ * error-reporting purposes (bInBlock==0 meanst start of block)
+ */
+ if(bInBlock == 0)
+ rsgt_errctxFrstRecInBlk(ectx, line);
+
+ r = rsgt_vrfy_nextRec(bs, gf, sigfp, nsigfp, (unsigned char*)line, lenRec, ectx);
+done:
+ return r;
+}
+
+/* We handle both verify and extend with the same function as they
+ * are very similiar.
+ *
+ * note: here we need to have the LOG file name, not signature!
+ */
+static void
+verify(char *name)
+{
+ FILE *logfp = NULL, *sigfp = NULL, *nsigfp = NULL;
+ block_sig_t *bs = NULL;
+ gtfile gf;
+ uint8_t bHasRecHashes, bHasIntermedHashes;
+ uint8_t bInBlock;
+ int r = 0;
+ char sigfname[4096];
+ char oldsigfname[4096];
+ char nsigfname[4096];
+ gterrctx_t ectx;
+
+ if(!strcmp(name, "-")) {
+ fprintf(stderr, "%s mode cannot work on stdin\n",
+ mode == MD_VERIFY ? "verify" : "extend");
+ goto err;
+ } else {
+ snprintf(sigfname, sizeof(sigfname), "%s.gtsig", name);
+ sigfname[sizeof(sigfname)-1] = '\0';
+ if((logfp = fopen(name, "r")) == NULL) {
+ perror(name);
+ goto err;
+ }
+ if((sigfp = fopen(sigfname, "r")) == NULL) {
+ perror(sigfname);
+ goto err;
+ }
+ if(mode == MD_EXTEND) {
+ snprintf(nsigfname, sizeof(nsigfname), "%s.gtsig.new", name);
+ nsigfname[sizeof(nsigfname)-1] = '\0';
+ if((nsigfp = fopen(nsigfname, "w")) == NULL) {
+ perror(nsigfname);
+ goto err;
+ }
+ snprintf(oldsigfname, sizeof(oldsigfname),
+ "%s.gtsig.old", name);
+ oldsigfname[sizeof(oldsigfname)-1] = '\0';
+ }
+ }
+
+ rsgtInit("rsyslog rsgtutil " VERSION);
+ rsgt_errctxInit(&ectx);
+ ectx.verbose = verbose;
+ ectx.fp = stderr;
+ ectx.filename = strdup(sigfname);
+
+ if((r = rsgt_chkFileHdr(sigfp, "LOGSIG10")) != 0) goto done;
+ if(mode == MD_EXTEND) {
+ if(fwrite("LOGSIG10", 8, 1, nsigfp) != 1) {
+ perror(nsigfname);
+ r = RSGTE_IO;
+ goto done;
+ }
+ }
+ gf = rsgt_vrfyConstruct_gf();
+ if(gf == NULL) {
+ fprintf(stderr, "error initializing signature file structure\n");
+ goto done;
+ }
+
+ bInBlock = 0;
+ ectx.blkNum = 0;
+ ectx.recNumInFile = 0;
+
+ while(!feof(logfp)) {
+ if(bInBlock == 0) {
+ if(bs != NULL)
+ rsgt_objfree(0x0902, bs);
+ if((r = rsgt_getBlockParams(sigfp, 1, &bs, &bHasRecHashes,
+ &bHasIntermedHashes)) != 0)
+ goto done;
+ rsgt_vrfyBlkInit(gf, bs, bHasRecHashes, bHasIntermedHashes);
+ ectx.recNum = 0;
+ ++ectx.blkNum;
+ }
+ ++ectx.recNum, ++ectx.recNumInFile;
+ if((r = doVerifyRec(logfp, sigfp, nsigfp, bs, gf, &ectx, bInBlock)) != 0)
+ goto done;
+ if(ectx.recNum == bs->recCount) {
+ if((r = verifyBLOCK_SIG(bs, gf, sigfp, nsigfp,
+ (mode == MD_EXTEND) ? 1 : 0, &ectx)) != 0)
+ goto done;
+ bInBlock = 0;
+ } else bInBlock = 1;
+ }
+
+done:
+ if(r != RSGTE_EOF)
+ goto err;
+
+ fclose(logfp); logfp = NULL;
+ fclose(sigfp); sigfp = NULL;
+ if(nsigfp != NULL) {
+ fclose(nsigfp); nsigfp = NULL;
+ }
+
+ /* everything went fine, so we rename files if we updated them */
+ if(mode == MD_EXTEND) {
+ if(unlink(oldsigfname) != 0) {
+ if(errno != ENOENT) {
+ perror("unlink oldsig");
+ r = RSGTE_IO;
+ goto err;
+ }
+ }
+ if(link(sigfname, oldsigfname) != 0) {
+ perror("link oldsig");
+ r = RSGTE_IO;
+ goto err;
+ }
+ if(unlink(sigfname) != 0) {
+ perror("unlink cursig");
+ r = RSGTE_IO;
+ goto err;
+ }
+ if(link(nsigfname, sigfname) != 0) {
+ perror("link newsig");
+ fprintf(stderr, "WARNING: current sig file has been "
+ "renamed to %s - you need to manually recover "
+ "it.\n", oldsigfname);
+ r = RSGTE_IO;
+ goto err;
+ }
+ if(unlink(nsigfname) != 0) {
+ perror("unlink newsig");
+ fprintf(stderr, "WARNING: current sig file has been "
+ "renamed to %s - you need to manually recover "
+ "it.\n", oldsigfname);
+ r = RSGTE_IO;
+ goto err;
+ }
+ }
+ rsgtExit();
+ rsgt_errctxExit(&ectx);
+ return;
+
+err:
+ fprintf(stderr, "error %d processing file %s\n", r, name);
+ if(logfp != NULL)
+ fclose(logfp);
+ if(sigfp != NULL)
+ fclose(sigfp);
+ if(nsigfp != NULL) {
+ fclose(nsigfp);
+ unlink(nsigfname);
+ }
+ rsgtExit();
+ rsgt_errctxExit(&ectx);
+}
+
+static void
+processFile(char *name)
+{
+ switch(mode) {
+ case MD_DETECT_FILE_TYPE:
+ detectFileType(name);
+ break;
+ case MD_DUMP:
+ dumpFile(name);
+ break;
+ case MD_SHOW_SIGBLK_PARAMS:
+ showSigblkParams(name);
+ break;
+ case MD_VERIFY:
+ case MD_EXTEND:
+ verify(name);
+ break;
+ }
+}
+
+
+static struct option long_options[] =
+{
+ {"dump", no_argument, NULL, 'D'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"version", no_argument, NULL, 'V'},
+ {"detect-file-type", no_argument, NULL, 'T'},
+ {"show-sigblock-params", no_argument, NULL, 'B'},
+ {"verify", no_argument, NULL, 't'}, /* 't' as in "test signatures" */
+ {"extend", no_argument, NULL, 'e'},
+ {"publications-server", optional_argument, NULL, 'P'},
+ {"show-verified", no_argument, NULL, 's'},
+ {NULL, 0, NULL, 0}
+};
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ int opt;
+
+ while(1) {
+ opt = getopt_long(argc, argv, "DvVTBtPs", long_options, NULL);
+ if(opt == -1)
+ break;
+ switch(opt) {
+ case 'v':
+ verbose = 1;
+ break;
+ case 's':
+ rsgt_read_showVerified = 1;
+ break;
+ case 'V':
+ fprintf(stderr, "rsgtutil " VERSION "\n");
+ exit(0);
+ case 'D':
+ mode = MD_DUMP;
+ break;
+ case 'B':
+ mode = MD_SHOW_SIGBLK_PARAMS;
+ break;
+ case 'P':
+ rsgt_read_puburl = optarg;
+ break;
+ case 'T':
+ mode = MD_DETECT_FILE_TYPE;
+ break;
+ case 't':
+ mode = MD_VERIFY;
+ break;
+ case 'e':
+ mode = MD_EXTEND;
+ break;
+ case '?':
+ break;
+ default:fprintf(stderr, "getopt_long() returns unknown value %d\n", opt);
+ return 1;
+ }
+ }
+
+ if(optind == argc)
+ processFile("-");
+ else {
+ for(i = optind ; i < argc ; ++i)
+ processFile(argv[i]);
+ }
+
+ return 0;
+}
diff --git a/tools/rsgtutil.rst b/tools/rsgtutil.rst
new file mode 100644
index 00000000..37958450
--- /dev/null
+++ b/tools/rsgtutil.rst
@@ -0,0 +1,177 @@
+========
+rsgtutil
+========
+
+-----------------------------------
+Manage (GuardTime) Signed Log Files
+-----------------------------------
+
+:Author: Rainer Gerhards <rgerhards@adiscon.com>
+:Date: 2013-03-25
+:Manual section: 1
+
+SYNOPSIS
+========
+
+::
+
+ rsgtutil [OPTIONS] [FILE] ...
+
+
+DESCRIPTION
+===========
+
+This tool performs various maintenance operations on signed log files.
+It specifically supports the GuardTime signature provider.
+
+The *rsgtutil* tool is the primary tool to verify log file signatures,
+dump signature file contents and carry out other maintenance operations.
+The tool offers different operation modes, which are selected via
+command line options.
+
+The processing of multiple files is permitted. Depending on operation
+mode, either the signature file or the base log file must be specified.
+Within a single call, only a single operations mode is permitted. To
+use different modes on different files, multiple calles, one for each
+mode, must be made.
+
+If no file is specified on the command line, stdin is used instead. Note
+that not all operation modes support stdin.
+
+OPTIONS
+=======
+
+-D, --dump
+ Select "dump" operations mode.
+
+-t, --verify
+ Select "verify" operations mode.
+
+-T, --detect-file-type
+ Select "detect-file-type" operations mode.
+
+-B, --show-sigblock-params
+ Select "show-sigblock-params" operations mode.
+
+-s, --show-verified
+ Prints out information about correctly verified blocks (by default, only
+ errors are printed).
+
+-v, --verbose
+ Select verbose mode. Most importantly, hashes and signatures are printed
+ in full length (can be **very** lengthy) rather than the usual abbreviation.
+
+-e, --extend
+ Select extend mode. This extends the RFC3161 signatures. Note that this
+ mode also implies a full verification. If there are verify errors, extending
+ will also fail.
+
+-P <URL>, --publications-server <URL>
+ Sets the publications server. If not set but required by the operation a
+ default server is used. The default server is not necessarily optimal
+ in regard to performance and reliability.
+
+
+OPERATION MODES
+===============
+
+The operation mode specifies what exactly the tool does with the provided
+files. The default operation mode is "dump", but this may change in the future.
+Thus, it is recommended to always set the operations mode explicitely. If
+multiple operations mode are set on the command line, results are
+unpredictable.
+
+dump
+----
+
+The provided *signature* files are dumped. For each top-level record, the*u
+type code is printed as well as q short description. If there is additional
+information available, it will be printed in tab-indented lines below the
+main record dump. The actual *log* files need not to be present.
+
+verify
+------
+
+This mode does not work with stdin. On the command line, the *log* file names
+are specified. The corresponding *signature* files (ending on ".gtsig") must also
+be preset at the same location as the log file. In verify mode, both the log
+and signature file is read and the validity of the log file checked. If verification
+errors are detected these are printed and processing of the file aborted. By default,
+each file is verified individually, without taking cross-file hash chains into
+account (so the order of files on the command line does not matter).
+
+Note that the actual amount of what can be verified depends on the parameters with
+which the signature file was written. If record and tree hashes are present, they
+will be verified and thus fine-granular error reporting is possible. If they are
+not present, only the block signature itself is verified.
+
+By default, only errors are printed. To also print successful verifications, use the
+**--show-verified** option.
+
+
+extend
+------
+This extends the RFC3161 signatures. This includes a full verification
+of the file. If there are verification errors, extending will also fail.
+Note that a signature can only be extended when the required hash has been
+published. Currently, these hashes are created at the 15th of each month at
+0:00hrs UTC. It takes another few days to get them finally published. As such,
+it can be assumed that extending is only possible after this happend (which
+means it may take slightly above a month).
+
+To prevent data corruption, a copy of the signature file is created during
+extension. So there must be enough disk space available for both files,
+otherwise the operation will fail. If the log file is named logfile, the
+signature file is logfile.gtsig and the temporary work file is named
+logfile.gtsig.new. When extending finished successfully, the original
+signature file (logfile.gtsig in our example) is renamed with the .old
+postfix (logfile.gtsig.old) and the temporary file written under the
+original name. The .old file can be deleted. It is just kept as a
+precaution to prevent signature loss. Note that any already existing
+.old or .new files are overwritten by these operations.
+
+
+detect-file-type
+----------------
+This mode is used to detect the type of some well-know files used inside the
+signature system. The detection is based on the file header. This mode is
+primarily a debug aid.
+
+
+show-sigblock-params
+--------------------
+This mode is used to print signature block parameters. It is similar to *dump*
+mode, but will ignore everything except signature blocks. Also, some additional
+meta information is printed. This mode is primarily a debug aid.
+
+EXIT CODES
+==========
+
+The command returns an exit code of 0 if everything went fine, and some
+other code in case of failures.
+
+
+EXAMPLES
+========
+
+**rsgtutil --verify logfile**
+
+This verifies the file "logfile" via its associated signature file
+"logfile.gtsig". If errors are detected, these are reported to stderr.
+Otherwise, rsgtutil terminates without messages.
+
+**rsgtutil --dump logfile.gtsig**
+
+This dumps the content of the signature file "logfile.gtsig". The
+actual log file is not being processed and does not even need to be
+present.
+
+SEE ALSO
+========
+**rsyslogd(8)**
+
+COPYRIGHT
+=========
+
+This page is part of the *rsyslog* project, and is available under
+LGPLv2.
diff --git a/tools/rsyslog.conf.5 b/tools/rsyslog.conf.5
new file mode 100644
index 00000000..07da6ffd
--- /dev/null
+++ b/tools/rsyslog.conf.5
@@ -0,0 +1,771 @@
+.\" rsyslog.conf - rsyslogd(8) configuration file
+.\" Copyright 2003-2008 Rainer Gerhards and Adiscon GmbH.
+.\"
+.\" This file is part of the rsyslog package, an enhanced system log daemon.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 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 General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+.\"
+.TH RSYSLOG.CONF 5 "22 October 2012" "Version 7.2.0" "Linux System Administration"
+.SH NAME
+rsyslog.conf \- rsyslogd(8) configuration file
+.SH DESCRIPTION
+The
+.I rsyslog.conf
+file is the main configuration file for the
+.BR rsyslogd (8)
+which logs system messages on *nix systems. This file specifies rules
+for logging. For special features see the
+.BR rsyslogd (8)
+manpage. Rsyslog.conf is backward-compatible with sysklogd's syslog.conf file. So if you migrate
+from sysklogd you can rename it and it should work.
+
+.B Note that this version of rsyslog ships with extensive documentation in html format.
+This is provided in the ./doc subdirectory and probably
+in a separate package if you installed rsyslog via a packaging system.
+To use rsyslog's advanced features, you
+.B need
+to look at the html documentation, because the man pages only cover
+basic aspects of operation.
+
+
+.SH MODULES
+
+Rsyslog has a modular design. Consequently, there is a growing number
+of modules. See the html documentation for their full description.
+
+.TP
+.I omsnmp
+SNMP trap output module
+.TP
+.I omgssapi
+Output module for GSS-enabled syslog
+.TP
+.I ommysql
+Output module for MySQL
+.TP
+.I omrelp
+Output module for the reliable RELP protocol (prevents message loss).
+For details, see below at imrelp and the html documentation.
+It can be used like this:
+.IP
+*.* :omrelp:server:port
+.IP
+*.* :omrelp:192.168.0.1:2514 # actual sample
+.TP
+.I ompgsql
+Output module for PostgreSQL
+.TP
+.I omlibdbi
+Generic database output module (Firebird/Interbase, MS SQL, Sybase,
+SQLite, Ingres, Oracle, mSQL)
+.TP
+.I imfile
+Input module for text files
+.TP
+.I imudp
+Input plugin for UDP syslog. Replaces the deprecated -r option. Can be
+used like this:
+.IP
+$ModLoad imudp
+.IP
+$UDPServerRun 514
+.TP
+.I imtcp
+Input plugin for plain TCP syslog. Replaces the deprecated -t
+option. Can be used like this:
+.IP
+$ModLoad imtcp
+.IP
+$InputTCPServerRun 514
+.TP
+.TP
+.I imrelp
+Input plugin for the RELP protocol. RELP can be used instead
+of UDP or plain TCP syslog to provide reliable delivery of
+syslog messages. Please note that plain TCP syslog does NOT
+provide truly reliable delivery, with it messages may be lost
+when there is a connection problem or the server shuts down.
+RELP prevents message loss in those cases.
+It can be used like this:
+.IP
+$ModLoad imrelp
+.IP
+$InputRELPServerRun 2514
+.TP
+.I imgssapi
+Input plugin for plain TCP and GSS-enable syslog
+.TP
+.I immark
+Support for mark messages
+.TP
+.I imklog
+Kernel logging. To include kernel log messages, you need to do
+.IP
+$ModLoad imklog
+
+Please note that the klogd daemon is no longer necessary and consequently
+no longer provided by the rsyslog package.
+.TP
+.I imuxsock
+Unix sockets, including the system log socket. You need to specify
+.IP
+$ModLoad imuxsock
+
+in order to receive log messages from local system processes. This
+config directive should only left out if you know exactly what you
+are doing.
+
+
+.SH BASIC STRUCTURE
+
+Lines starting with a hash mark ('#') and empty lines are ignored.
+Rsyslog.conf should contain following sections (sorted by recommended order in file):
+
+.TP
+Global directives
+Global directives set some global properties of whole rsyslog daemon, for example size of main
+message queue ($MainMessageQueueSize), loading external modules ($ModLoad) and so on.
+All global directives need to be specified on a line by their own and must start with
+a dollar-sign. The complete list of global directives can be found in html documentation in doc
+directory or online on web pages.
+
+.TP
+Templates
+Templates allow you to specify format of the logged message. They are also used for dynamic
+file name generation. They have to be defined before they are used in rules. For more info
+about templates see TEMPLATES section of this manpage.
+
+.TP
+Output channels
+Output channels provide an umbrella for any type of output that the user might want.
+They have to be defined before they are used in rules. For more info about output channels
+see OUTPUT CHANNELS section of this manpage.
+
+.TP
+Rules (selector + action)
+Every rule line consists of two fields, a selector field and an action field. These
+two fields are separated by one or more spaces or tabs. The selector field specifies
+a pattern of facilities and priorities belonging to the specified action.
+
+.SH SELECTORS
+
+The selector field itself again consists of two parts, a facility and a
+priority, separated by a period ('.'). Both parts are case insensitive and can
+also be specified as decimal numbers, but don't do that, you have been warned.
+Both facilities and priorities are described in syslog(3). The names mentioned
+below correspond to the similar LOG_-values in /usr/include/syslog.h.
+
+The facility is one of the following keywords: auth, authpriv, cron, daemon,
+kern, lpr, mail, mark, news, security (same as auth), syslog, user, uucp and
+local0 through local7. The keyword security should not be used anymore and mark
+is only for internal use and therefore should not be used in applications.
+Anyway, you may want to specify and redirect these messages here. The facility
+specifies the subsystem that produced the message, i.e. all mail programs log
+with the mail facility (LOG_MAIL) if they log using syslog.
+
+The priority is one of the following keywords, in ascending order: debug, info,
+notice, warning, warn (same as warning), err, error (same as err), crit, alert,
+emerg, panic (same as emerg). The keywords error, warn and panic are deprecated
+and should not be used anymore. The priority defines the severity of the message.
+
+The behavior of the original BSD syslogd is that all messages of the specified
+priority and higher are logged according to the given action. Rsyslogd behaves
+the same, but has some extensions.
+
+In addition to the above mentioned names the rsyslogd(8) understands the
+following extensions: An asterisk ('*') stands for all facilities or all
+priorities, depending on where it is used (before or after the period). The
+keyword none stands for no priority of the given facility.
+
+You can specify multiple facilities with the same priority pattern in one
+statement using the comma (',') operator. You may specify as much facilities as
+you want. Remember that only the facility part from such a statement is taken, a
+priority part would be skipped.
+
+Multiple selectors may be specified for a single action using the semicolon
+(';') separator. Remember that each selector in the selector field is capable
+to overwrite the preceding ones. Using this behavior you can exclude some
+priorities from the pattern.
+
+Rsyslogd has a syntax extension to the original BSD source, that makes its use
+more intuitively. You may precede every priority with an equals sign ('=') to
+specify only this single priority and not any of the above. You may also (both
+is valid, too) precede the priority with an exclamation mark ('!') to ignore
+all that priorities, either exact this one or this and any higher priority. If
+you use both extensions than the exclamation mark must occur before the equals
+sign, just use it intuitively.
+
+.SH ACTIONS
+The action field of a rule describes what to do with the message. In general, message content
+is written to a kind of "logfile". But also other actions might be done, like writing to a
+database table or forwarding to another host.
+
+.SS Regular file
+Typically messages are logged to real files. The file has to be specified with full pathname,
+beginning with a slash ('/').
+
+.B Example:
+.RS
+*.* /var/log/traditionalfile.log;RSYSLOG_TraditionalFileFormat # log to a file in the traditional format
+.RE
+
+Note: if you would like to use high-precision timestamps in your log files,
+just remove the ";RSYSLOG_TraditionalFormat". That will select the default
+template, which, if not changed, uses RFC 3339 timestamps.
+
+.B Example:
+.RS
+*.* /var/log/file.log # log to a file with RFC3339 timestamps
+.RE
+
+.SS Named pipes
+This version of rsyslogd(8) has support for logging output to named pipes (fifos). A fifo or
+named pipe can be used as a destination for log messages by prepending a pipe symbol ('|')
+to the name of the file. This is handy for debugging. Note that the fifo must be created with
+the mkfifo(1) command before rsyslogd(8) is started.
+
+.SS Terminal and console
+If the file you specified is a tty, special tty-handling is done, same with /dev/console.
+
+.SS Remote machine
+There are three ways to forward message: the traditional UDP transport, which is extremely
+lossy but standard, the plain TCP based transport which loses messages only during certain
+situations but is widely available and the RELP transport which does not lose messages
+but is currently available only as part of rsyslogd 3.15.0 and above.
+
+To forward messages to another host via UDP, prepend the hostname with the at sign ("@").
+To forward it via plain tcp, prepend two at signs ("@@"). To forward via RELP, prepend the
+string ":omrelp:" in front of the hostname.
+
+.B Example:
+.RS
+*.* @192.168.0.1
+.RE
+.sp
+In the example above, messages are forwarded via UDP to the machine 192.168.0.1, the destination
+port defaults to 514. Due to the nature of UDP, you will probably lose some messages in transit.
+If you expect high traffic volume, you can expect to lose a quite noticeable number of messages
+(the higher the traffic, the more likely and severe is message loss).
+
+.B If you would like to prevent message loss, use RELP:
+.RS
+*.* :omrelp:192.168.0.1:2514
+.RE
+.sp
+Note that a port number was given as there is no standard port for relp.
+
+Keep in mind that you need to load the correct input and output plugins (see "Modules" above).
+
+Please note that rsyslogd offers a variety of options in regarding to remote
+forwarding. For full details, please see the html documentation.
+
+.SS List of users
+Usually critical messages are also directed to ``root'' on that machine. You
+can specify a list
+of users that shall get the message by simply writing ":omusrmsg:" followed
+by the login name. You may specify more than one
+user by separating them with commas (','). If they're logged in they
+get the message (for example: ":omusrmsg:root,user1,user2").
+
+.SS Everyone logged on
+Emergency messages often go to all users currently online to notify them that something strange
+is happening with the system. To specify this wall(1)-feature use an ":omusrmsg:*".
+
+.SS Database table
+This allows logging of the message to a database table.
+By default, a MonitorWare-compatible schema is required for this to work. You can
+create that schema with the createDB.SQL file that came with the rsyslog package. You can also
+use any other schema of your liking - you just need to define a proper template and assign this
+template to the action.
+
+See the html documentation for further details on database logging.
+
+.SS Discard
+If the discard action is carried out, the received message is immediately discarded. Discard
+can be highly effective if you want to filter out some annoying messages that otherwise would
+fill your log files. To do that, place the discard actions early in your log files.
+This often plays well with property-based filters, giving you great freedom in specifying
+what you do not want.
+
+Discard is just the single tilde character with no further parameters.
+.sp
+.B Example:
+.RS
+*.* ~ # discards everything.
+.RE
+
+
+.SS Output channel
+Binds an output channel definition (see there for details) to this action. Output channel actions
+must start with a $-sign, e.g. if you would like to bind your output channel definition "mychannel"
+to the action, use "$mychannel". Output channels support template definitions like all all other
+actions.
+
+.SS Shell execute
+This executes a program in a subshell. The program is passed the template-generated message as the
+only command line parameter. Rsyslog waits until the program terminates and only then continues to run.
+
+.B Example:
+.RS
+^program-to-execute;template
+.RE
+
+The program-to-execute can be any valid executable. It receives the template string as a single parameter
+(argv[1]).
+
+.SH FILTER CONDITIONS
+Rsyslog offers three different types "filter conditions":
+.sp 0
+ * "traditional" severity and facility based selectors
+.sp 0
+ * property-based filters
+.sp 0
+ * expression-based filters
+.RE
+
+.SS Selectors
+.B Selectors are the traditional way of filtering syslog messages.
+They have been kept in rsyslog with their original syntax, because it is well-known, highly
+effective and also needed for compatibility with stock syslogd configuration files. If you just
+need to filter based on priority and facility, you should do this with selector lines. They are
+not second-class citizens in rsyslog and offer the best performance for this job.
+
+.SS Property-Based Filters
+Property-based filters are unique to rsyslogd. They allow to filter on any property, like HOSTNAME,
+syslogtag and msg.
+
+A property-based filter must start with a colon in column 0. This tells rsyslogd that it is the new
+filter type. The colon must be followed by the property name, a comma, the name of the compare
+operation to carry out, another comma and then the value to compare against. This value must be quoted.
+There can be spaces and tabs between the commas. Property names and compare operations are
+case-sensitive, so "msg" works, while "MSG" is an invalid property name. In brief, the syntax is as follows:
+.sp
+.RS
+:property, [!]compare-operation, "value"
+.RE
+
+The following compare-operations are currently supported:
+.sp
+.RS
+.B contains
+.RS
+Checks if the string provided in value is contained in the property
+.RE
+.sp
+.B isequal
+.RS
+Compares the "value" string provided and the property contents. These two values must be exactly equal to match.
+.RE
+.sp
+.B startswith
+.RS
+Checks if the value is found exactly at the beginning of the property value
+.RE
+.sp
+.B regex
+.RS
+Compares the property against the provided regular expression.
+.RE
+
+.SS Expression-Based Filters
+See the html documentation for this feature.
+
+
+.SH TEMPLATES
+
+Every output in rsyslog uses templates - this holds true for files, user
+messages and so on. Templates compatible with the stock syslogd
+formats are hardcoded into rsyslogd. If no template is specified, we use
+one of these hardcoded templates. Search for "template_" in syslogd.c and
+you will find the hardcoded ones.
+
+A template consists of a template directive, a name, the actual template text
+and optional options. A sample is:
+
+.RS
+.B $template MyTemplateName,"\\\\7Text %property% some more text\\\\n",<options>
+.RE
+
+The "$template" is the template directive. It tells rsyslog that this line
+contains a template. The backslash is an escape character. For example, \\7 rings the
+bell (this is an ASCII value), \\n is a new line. The set in rsyslog is a bit restricted
+currently.
+
+All text in the template is used literally, except for things within percent
+signs. These are properties and allow you access to the contents of the syslog
+message. Properties are accessed via the property replacer and it can for example
+pick a substring or do date-specific formatting. More on this is the PROPERTY REPLACER
+section of this manpage.
+
+To escape:
+.sp 0
+ % = \\%
+.sp 0
+ \\ = \\\\ --> '\\' is used to escape (as in C)
+.sp 0
+$template TraditionalFormat,"%timegenerated% %HOSTNAME% %syslogtag%%msg%\\n"
+
+Properties can be accessed by the property replacer (see there for details).
+
+.B Please note that templates can also by used to generate selector lines with dynamic file names.
+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:
+
+.RS
+.B $template DynFile,"/var/log/system-%HOSTNAME%.log"
+.RE
+
+This template can then be used when defining an output selector line. It will
+result in something like "/var/log/system-localhost.log"
+
+.SS Template options
+The <options> part is optional. It carries options influencing the template as whole.
+See details below. Be sure NOT to mistake template options with property options - the
+later ones are processed by the property replacer and apply to a SINGLE property, only
+(and not the whole template).
+
+Template options are case-insensitive. Currently defined are:
+
+.RS
+.TP
+sql
+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 NO_BACKSLASH_ESCAPES
+mode must be turned off for this format to work (this is the default).
+
+.TP
+stdsql
+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 NO_BACKSLASH_ESCAPES
+is turned on.
+.RE
+
+Either the
+.B sql
+or
+.B stdsql
+option
+.B MUST
+be specified when a template is used for writing to a database,
+otherwise injection might occur. Please note that due to the unfortunate fact
+that several vendors have violated the sql standard and introduced their own
+escape methods, it is impossible to have a single option doing all the work.
+So you yourself must make sure you are using the right format.
+.B If you choose the wrong one, you are still vulnerable to sql injection.
+
+Please note that the database writer *checks* that the sql option is present
+in the template. If it is not present, the write database action is disabled.
+This is to guard you against accidental forgetting it and then becoming
+vulnerable to SQL injection. The sql option can also be useful with files -
+especially if you want to import them into a database on another machine for
+performance reasons. However, do NOT use it if you do not have a real need for
+it - among others, it takes some toll on the processing time. Not much, but on
+a really busy system you might notice it ;)
+
+The default template for the write to database action has the sql option set.
+
+.SS Template examples
+Please note that the samples are split across multiple lines. A template MUST
+NOT actually be split across multiple lines.
+
+A template that resembles traditional syslogd file output:
+.sp
+.RS
+$template TraditionalFormat,"%timegenerated% %HOSTNAME%
+.sp 0
+%syslogtag%%msg:::drop-last-lf%\\n"
+.RE
+
+A template that tells you a little more about the message:
+.sp
+.RS
+$template precise,"%syslogpriority%,%syslogfacility%,%timegenerated%,%HOSTNAME%,
+.sp 0
+%syslogtag%,%msg%\\n"
+.RE
+
+A template for RFC 3164 format:
+.sp
+.RS
+$template RFC3164fmt,"<%PRI%>%TIMESTAMP% %HOSTNAME% %syslogtag%%msg%"
+.RE
+
+A template for the format traditionally used for user messages:
+.sp
+.RS
+$template usermsg," XXXX%syslogtag%%msg%\\n\\r"
+.RE
+
+And a template with the traditional wall-message format:
+.sp
+.RS
+$template wallmsg,"\\r\\n\\7Message from syslogd@%HOSTNAME% at %timegenerated%"
+.RE
+
+.B A template that can be used for writing to a database (please note the SQL template option)
+.sp
+.RS
+.ad l
+$template MySQLInsert,"insert iut, message, receivedat values
+('%iut%', '%msg:::UPPERCASE%', '%timegenerated:::date-mysql%')
+into systemevents\\r\\n", SQL
+
+NOTE 1: This template is embedded into core application under name
+.B StdDBFmt
+, so you don't need to define it.
+.sp
+NOTE 2: You have to have MySQL module installed to use this template.
+.ad
+.RE
+
+.SH OUTPUT CHANNELS
+
+Output Channels are a new concept first introduced in rsyslog 0.9.0. As of this writing,
+it is most likely that they will be replaced by something different in the future.
+So if you use them, be prepared to change you configuration file syntax when you upgrade
+to a later release.
+
+Output channels are defined via an $outchannel directive. It's syntax is as follows:
+.sp
+.RS
+.B $outchannel name,file-name,max-size,action-on-max-size
+.RE
+
+name is the name of the output channel (not the file), file-name is the file name to be
+written to, max-size the maximum allowed size and action-on-max-size a command to be issued
+when the max size is reached. This command always has exactly one parameter. The binary is
+that part of action-on-max-size before the first space, its parameter is everything behind
+that space.
+
+Keep in mind that $outchannel just defines a channel with "name". It does not activate it.
+To do so, you must use a selector line (see below). That selector line includes the channel
+name plus ":omfile:$" in front of it. A sample might be:
+.sp
+.RS
+*.* :omfile:$mychannel
+.RE
+
+.SH PROPERTY REPLACER
+The property replacer is a core component in rsyslogd's output system. A syslog message has
+a number of well-defined properties (see below). Each of this properties can be accessed and
+manipulated by the property replacer. With it, it is easy to use only part of a property value
+or manipulate the value, e.g. by converting all characters to lower case.
+
+.SS Accessing Properties
+Syslog message properties are used inside templates. They are accessed by putting them between
+percent signs. Properties can be modified by the property replacer. The full syntax is as follows:
+.sp
+.RS
+.B %propname:fromChar:toChar:options%
+.RE
+
+propname is the name of the property to access.
+.B It is case-sensitive.
+
+.SS Available Properties
+.TP
+.B msg
+the MSG part of the message (aka "the message" ;))
+.TP
+.B rawmsg
+the message exactly as it was received from the socket. Should be useful for debugging.
+.TP
+.B HOSTNAME
+hostname from the message
+.TP
+.B FROMHOST
+hostname of the system the message was received from (in a relay chain, this is the system immediately
+in front of us and not necessarily the original sender)
+.TP
+.B syslogtag
+TAG from the message
+.TP
+.B programname
+the "static" part of the tag, as defined by BSD syslogd. For example, when TAG is "named[12345]",
+programname is "named".
+.TP
+.B PRI
+PRI part of the message - undecoded (single value)
+.TP
+.B PRI-text
+the PRI part of the message in a textual form (e.g. "syslog.info")
+.TP
+.B IUT
+the monitorware InfoUnitType - used when talking to a MonitorWare backend (also for phpLogCon)
+.TP
+.B syslogfacility
+the facility from the message - in numerical form
+.TP
+.B syslogfacility-text
+the facility from the message - in text form
+.TP
+.B syslogseverity
+severity from the message - in numerical form
+.TP
+.B syslogseverity-text
+severity from the message - in text form
+.TP
+.B timegenerated
+timestamp when the message was RECEIVED. Always in high resolution
+.TP
+.B timereported
+timestamp from the message. Resolution depends on what was provided in the message (in most cases, only seconds)
+.TP
+.B TIMESTAMP
+alias for timereported
+.TP
+.B PROTOCOL-VERSION
+The contents of the PROTOCOL-VERSION field from IETF draft draft-ietf-syslog-protocol
+.TP
+.B STRUCTURED-DATA
+The contents of the STRUCTURED-DATA field from IETF draft draft-ietf-syslog-protocol
+.TP
+.B APP-NAME
+The contents of the APP-NAME field from IETF draft draft-ietf-syslog-protocol
+.TP
+.B PROCID
+The contents of the PROCID field from IETF draft draft-ietf-syslog-protocol
+.TP
+.B MSGID
+The contents of the MSGID field from IETF draft draft-ietf-syslog-protocol
+.TP
+.B $NOW
+The current date stamp in the format YYYY-MM-DD
+.TP
+.B $YEAR
+The current year (4-digit)
+.TP
+.B $MONTH
+The current month (2-digit)
+.TP
+.B $DAY
+The current day of the month (2-digit)
+.TP
+.B $HOUR
+The current hour in military (24 hour) time (2-digit)
+.TP
+.B $MINUTE
+The current minute (2-digit)
+
+.P
+Properties starting with a $-sign are so-called system properties. These do NOT stem from the
+message but are rather internally-generated.
+
+.SS Character Positions
+FromChar and toChar 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 obtain the first 2 characters of
+the message text, you can use this syntax: "%msg:1:2%". If you do not wish to specify from and to,
+but you want to specify options, you still need to include the colons. For example, if you would
+like to convert the full message text to lower case, use "%msg:::lowercase%". If you would like to
+extract from a position until the end of the string, you can place a dollar-sign ("$") in toChar
+(e.g. %msg:10:$%, which will extract from position 10 to the end of the string).
+
+There is also support for
+.B regular expressions.
+To use them, you need to place a "R" into FromChar.
+This tells rsyslog that a regular expression instead of position-based extraction is desired. The
+actual regular expression
+.B must
+then be provided in toChar. The regular expression must be followed
+by the string "--end". It denotes the end of the regular expression and will not become part of it.
+If you are using regular expressions, the property replacer will return the part of the property text
+that matches the regular expression. An example for a property replacer sequence with a regular
+expression is: "%msg:R:.*Sev:. \\(.*\\) \\[.*--end%"
+
+Also, extraction can be done based on so-called "fields". To do so, place a "F" into FromChar. A field
+in its current definition is anything that is delimited by a delimiter character. The delimiter by
+default is TAB (US-ASCII value 9). However, if can be changed to any other US-ASCII character by
+specifying a comma and the decimal US-ASCII value of the delimiter immediately after the "F". For example,
+to use comma (",") as a delimiter, use this field specifier: "F,44". If your syslog data is delimited,
+this is a quicker way to extract than via regular expressions (actually, a *much* quicker way). Field
+counting starts at 1. Field zero is accepted, but will always lead to a "field not found" error. The same
+happens if a field number higher than the number of 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%".
+
+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 to error
+messages and will NOT provide the intended result).
+
+.SS Property Options
+Property options are case-insensitive. Currently, the following options are defined:
+.TP
+uppercase
+convert property to lowercase only
+.TP
+lowercase
+convert property text to uppercase only
+.TP
+drop-last-lf
+The last LF in the message (if any), is dropped. Especially useful for PIX.
+.TP
+date-mysql
+format as mysql date
+.TP
+date-rfc3164
+format as RFC 3164 date
+.TP
+date-rfc3339
+format as RFC 3339 date
+.TP
+escape-cc
+replace control characters (ASCII value 127 and values less then 32) with an escape sequence. The sequence is "#<charval>" where charval is the 3-digit decimal value of the control character. For example, a tabulator would be replaced by "#009".
+.TP
+space-cc
+replace control characters by spaces
+.TP
+drop-cc
+drop control characters - the resulting string will neither contain control characters, escape sequences nor any other replacement character like space.
+
+.SH QUEUED OPERATIONS
+Rsyslogd supports queued operations to handle offline outputs
+(like remote syslogd's or database servers being down). When running in
+queued mode, rsyslogd buffers messages to memory and optionally to disk
+(on an as-needed basis). Queues survive rsyslogd restarts.
+
+It is highly suggested to use remote forwarding and database writing
+in queued mode, only.
+
+To learn more about queued operations, see the html documentation.
+
+.SH FILES
+.PD 0
+.TP
+.I /etc/rsyslog.conf
+Configuration file for
+.B rsyslogd
+
+.SH SEE ALSO
+.BR rsyslogd (8),
+.BR logger (1),
+.BR syslog (3)
+
+The complete documentation can be found in the doc folder of the rsyslog distribution or online at
+
+.RS
+.B http://www.rsyslog.com/doc
+
+.RE
+Please note that the man page reflects only a subset of the configuration options. Be sure to read
+the html documentation for all features and details. This is especially vital if you plan to set
+up a more-then-extremely-simple system.
+
+.SH AUTHORS
+.B rsyslogd
+is taken from sysklogd sources, which have been heavily modified
+by Rainer Gerhards (rgerhards@adiscon.com) and others.
diff --git a/tools/rsyslogd.8 b/tools/rsyslogd.8
new file mode 100644
index 00000000..ac732b88
--- /dev/null
+++ b/tools/rsyslogd.8
@@ -0,0 +1,401 @@
+.\" Copyright 2004-2008 Rainer Gerhards and Adiscon for the rsyslog modifications
+.\" May be distributed under the GNU General Public License
+.\"
+.TH RSYSLOGD 8 "16 October 2012" "Version 6.4.3" "Linux System Administration"
+.SH NAME
+rsyslogd \- reliable and extended syslogd
+.SH SYNOPSIS
+.B rsyslogd
+.RB [ " \-4 " ]
+.RB [ " \-6 " ]
+.RB [ " \-A " ]
+.RB [ " \-d " ]
+.RB [ " \-D " ]
+.RB [ " \-f "
+.I config file
+]
+.br
+.RB [ " \-i "
+.I pid file
+]
+.RB [ " \-l "
+.I hostlist
+]
+.RB [ " \-n " ]
+.RB [ " \-N "
+.I level
+]
+.br
+.RB [ " \-q " ]
+.RB [ " \-Q " ]
+.RB [ " \-s "
+.I domainlist
+]
+.RB [ " \-u "
+.I userlevel
+]
+.RB [ " \-v " ]
+.RB [ " \-w " ]
+.RB [ " \-x " ]
+.LP
+.SH DESCRIPTION
+.B Rsyslogd
+is a system utility providing support for message logging.
+Support of both internet and
+unix domain sockets enables this utility to support both local
+and remote logging.
+
+.B Note that this version of rsyslog ships with extensive documentation in html format.
+This is provided in the ./doc subdirectory and probably
+in a separate package if you installed rsyslog via a packaging system.
+To use rsyslog's advanced features, you
+.B need
+to look at the html documentation, because the man pages only cover
+basic aspects of operation.
+.B For details and configuration examples, see the rsyslog.conf (5)
+.B man page and the online documentation at http://www.rsyslog.com/doc
+
+.BR Rsyslogd (8)
+is derived from the sysklogd package which in turn is derived from the
+stock BSD sources.
+
+.B Rsyslogd
+provides a kind of logging that many modern programs use. Every logged
+message contains at least a time and a hostname field, normally a
+program name field, too, but that depends on how trusty the logging
+program is. The rsyslog package supports free definition of output formats
+via templates. It also supports precise timestamps and writing directly
+to databases. If the database option is used, tools like phpLogCon can
+be used to view the log data.
+
+While the
+.B rsyslogd
+sources have been heavily modified a couple of notes
+are in order. First of all there has been a systematic attempt to
+ensure that rsyslogd follows its default, standard BSD behavior. Of course,
+some configuration file changes are necessary in order to support the
+template system. However, rsyslogd should be able to use a standard
+syslog.conf and act like the original syslogd. However, an original syslogd
+will not work correctly with a rsyslog-enhanced configuration file. At
+best, it will generate funny looking file names.
+The second important concept to note is that this version of rsyslogd
+interacts transparently with the version of syslog found in the
+standard libraries. If a binary linked to the standard shared
+libraries fails to function correctly we would like an example of the
+anomalous behavior.
+
+The main configuration file
+.I /etc/rsyslog.conf
+or an alternative file, given with the
+.B "\-f"
+option, is read at startup. Any lines that begin with the hash mark
+(``#'') and empty lines are ignored. If an error occurs during parsing
+the error element is ignored. It is tried to parse the rest of the line.
+
+.LP
+.SH OPTIONS
+.TP
+.BI "\-A"
+When sending UDP messages, there are potentially multiple paths to
+the target destination. By default,
+.B rsyslogd
+only sends to the first target it can successfully send to. If -A
+is given, messages are sent to all targets. This may improve
+reliability, but may also cause message duplication. This option
+should be enabled only if it is fully understood.
+.TP
+.BI "\-4"
+Causes
+.B rsyslogd
+to listen to IPv4 addresses only.
+If neither -4 nor -6 is given,
+.B rsyslogd
+listens to all configured addresses of the system.
+.TP
+.BI "\-6"
+Causes
+.B rsyslogd
+to listen to IPv6 addresses only.
+If neither -4 nor -6 is given,
+.B rsyslogd
+listens to all configured addresses of the system.
+.TP
+.BI "\-c " "version"
+This option has been obsoleted and has no function any longer. It is still
+accepted in order not to break existing scripts. However, future versions
+may not support it.
+.TP
+.B "\-D"
+Runs the Bison config parser in debug mode. This may help when hard to find
+syntax errors are reported. Please note that the output generated is deeply
+technical and orignally targeted towards developers.
+.TP
+.B "\-d"
+Turns on debug mode. Using this the daemon will not proceed a
+.BR fork (2)
+to set itself in the background, but opposite to that stay in the
+foreground and write much debug information on the current tty. See the
+DEBUGGING section for more information.
+.TP
+.BI "\-f " "config file"
+Specify an alternative configuration file instead of
+.IR /etc/rsyslog.conf ","
+which is the default.
+.TP
+.BI "\-i " "pid file"
+Specify an alternative pid file instead of the default one.
+This option must be used if multiple instances of rsyslogd should
+run on a single machine.
+.TP
+.BI "\-l " "hostlist"
+Specify a hostname that should be logged only with its simple hostname
+and not the fqdn. Multiple hosts may be specified using the colon
+(``:'') separator.
+.TP
+.B "\-n"
+Avoid auto-backgrounding. This is needed especially if the
+.B rsyslogd
+is started and controlled by
+.BR init (8).
+.TP
+.B "\-N " "level"
+Do a coNfig check. Do NOT run in regular mode, just check configuration
+file correctness.
+This option is meant to verify a config file. To do so, run rsyslogd
+interactively in foreground, specifying -f <config-file> and -N level.
+The level argument modifies behaviour. Currently, 0 is the same as
+not specifying the -N option at all (so this makes limited sense) and
+1 actually activates the code. Later, higher levels will mean more
+verbosity (this is a forward-compatibility option).
+.B rsyslogd
+is started and controlled by
+.BR init (8).
+.TP
+.BI "\-q " "add hostname if DNS fails during ACL processing"
+During ACL processing, hostnames are resolved to IP addresses for
+performance reasons. If DNS fails during that process, the hostname
+is added as wildcard text, which results in proper, but somewhat
+slower operation once DNS is up again.
+.TP
+.BI "\-Q " "do not resolve hostnames during ACL processing"
+Do not resolve hostnames to IP addresses during ACL processing.
+.TP
+.BI "\-s " "domainlist"
+Specify a domainname that should be stripped off before
+logging. Multiple domains may be specified using the colon (``:'')
+separator.
+Please be advised that no sub-domains may be specified but only entire
+domains. For example if
+.B "\-s north.de"
+is specified and the host logging resolves to satu.infodrom.north.de
+no domain would be cut, you will have to specify two domains like:
+.BR "\-s north.de:infodrom.north.de" .
+.TP
+.BI "\-S ip_address" "local client source IP"
+rsyslogd uses ip_address as local client address while connecting
+to remote logserver. Currently used by omrelp only and only with tcp.
+.TP
+.BI "\-u " "userlevel"
+This is a "catch all" option for some very seldomly-used user settings.
+The "userlevel" variable selects multiple things. Add the specific values
+to get the combined effect of them.
+A value of 1 prevents rsyslogd from parsing hostnames and tags inside
+messages.
+A value of 2 prevents rsyslogd from changing to the root directory. This
+is almost never a good idea in production use. This option was introduced
+in support of the internal testbed.
+To combine these two features, use a userlevel of 3 (1+2). Whenever you use
+an -u option, make sure you really understand what you do and why you do it.
+.TP
+.B "\-v"
+Print version and exit.
+.TP
+.B "\-w"
+Suppress warnings issued when messages are received from non-authorized
+machines (those, that are in no AllowedSender list).
+.TP
+.B "\-x"
+Disable DNS for remote messages.
+.LP
+.SH SIGNALS
+.B Rsyslogd
+reacts to a set of signals. You may easily send a signal to
+.B rsyslogd
+using the following:
+.IP
+.nf
+kill -SIGNAL $(cat /var/run/rsyslogd.pid)
+.fi
+.PP
+Note that -SIGNAL must be replaced with the actual signal
+you are trying to send, e.g. with HUP. So it then becomes:
+.IP
+.nf
+kill -HUP $(cat /var/run/rsyslogd.pid)
+.fi
+.PP
+.TP
+.B HUP
+This lets
+.B rsyslogd
+perform close all open files.
+Also, in v3 a full restart will be done in order to read changed configuration files.
+Note that this means a full rsyslogd restart is done. This has, among others,
+the consequence that TCP and other connections are torn down. Also, if any
+queues are not running in disk assisted mode or are not set to persist data
+on shutdown, queue data is lost. HUPing rsyslogd is an extremely expensive
+operation and should only be done when actually necessary. Actually, it is
+a rsyslgod stop immediately followed by a restart. Future versions will remove
+this restart functionality of HUP (it will go away in v5). So it is advised to use
+HUP only for closing files, and a "real restart" (e.g. /etc/rc.d/rsyslogd restart)
+to activate configuration changes.
+.TP
+.B TERM ", " INT ", " QUIT
+.B Rsyslogd
+will die.
+.TP
+.B USR1
+Switch debugging on/off. This option can only be used if
+.B rsyslogd
+is started with the
+.B "\-d"
+debug option.
+.TP
+.B CHLD
+Wait for childs if some were born, because of wall'ing messages.
+.LP
+.SH SECURITY THREATS
+There is the potential for the rsyslogd daemon to be
+used as a conduit for a denial of service attack.
+A rogue program(mer) could very easily flood the rsyslogd daemon with
+syslog messages resulting in the log files consuming all the remaining
+space on the filesystem. Activating logging over the inet domain
+sockets will of course expose a system to risks outside of programs or
+individuals on the local machine.
+
+There are a number of methods of protecting a machine:
+.IP 1.
+Implement kernel firewalling to limit which hosts or networks have
+access to the 514/UDP socket.
+.IP 2.
+Logging can be directed to an isolated or non-root filesystem which,
+if filled, will not impair the machine.
+.IP 3.
+The ext2 filesystem can be used which can be configured to limit a
+certain percentage of a filesystem to usage by root only. \fBNOTE\fP
+that this will require rsyslogd to be run as a non-root process.
+\fBALSO NOTE\fP that this will prevent usage of remote logging on the default port since
+rsyslogd will be unable to bind to the 514/UDP socket.
+.IP 4.
+Disabling inet domain sockets will limit risk to the local machine.
+.SS Message replay and spoofing
+If remote logging is enabled, messages can easily be spoofed and replayed.
+As the messages are transmitted in clear-text, an attacker might use
+the information obtained from the packets for malicious things. Also, an
+attacker might replay recorded messages or spoof a sender's IP address,
+which could lead to a wrong perception of system activity. These can
+be prevented by using GSS-API authentication and encryption. Be sure
+to think about syslog network security before enabling it.
+.LP
+.SH DEBUGGING
+When debugging is turned on using
+.B "\-d"
+option then
+.B rsyslogd
+will be very verbose by writing much of what it does on stdout.
+.SH FILES
+.PD 0
+.TP
+.I /etc/rsyslog.conf
+Configuration file for
+.BR rsyslogd .
+See
+.BR rsyslog.conf (5)
+for exact information.
+.TP
+.I /dev/log
+The Unix domain socket to from where local syslog messages are read.
+.TP
+.I /var/run/rsyslogd.pid
+The file containing the process id of
+.BR rsyslogd .
+.TP
+.I prefix/lib/rsyslog
+Default directory for
+.B rsyslogd
+modules. The
+.I prefix
+is specified during compilation (e.g. /usr/local).
+.SH ENVIRONMENT
+.TP
+.B RSYSLOG_DEBUG
+Controls runtime debug support.It contains an option string with the
+following options possible (all are case insensitive):
+
+.RS
+.IP LogFuncFlow
+Print out the logical flow of functions (entering and exiting them)
+.IP FileTrace
+Specifies which files to trace LogFuncFlow. If not set (the
+default), a LogFuncFlow trace is provided for all files. Set to
+limit it to the files specified.FileTrace may be specified multiple
+times, one file each (e.g. export RSYSLOG_DEBUG="LogFuncFlow
+FileTrace=vm.c FileTrace=expr.c"
+.IP PrintFuncDB
+Print the content of the debug function database whenever debug
+information is printed (e.g. abort case)!
+.IP PrintAllDebugInfoOnExit
+Print all debug information immediately before rsyslogd exits
+(currently not implemented!)
+.IP PrintMutexAction
+Print mutex action as it happens. Useful for finding deadlocks and
+such.
+.IP NoLogTimeStamp
+Do not prefix log lines with a timestamp (default is to do that).
+.IP NoStdOut
+Do not emit debug messages to stdout. If RSYSLOG_DEBUGLOG is not
+set, this means no messages will be displayed at all.
+.IP Help
+Display a very short list of commands - hopefully a life saver if
+you can't access the documentation...
+.RE
+
+.TP
+.B RSYSLOG_DEBUGLOG
+If set, writes (almost) all debug message to the specified log file
+in addition to stdout.
+.TP
+.B RSYSLOG_MODDIR
+Provides the default directory in which loadable modules reside.
+.PD
+.SH BUGS
+Please review the file BUGS for up-to-date information on known
+bugs and annoyances.
+.SH Further Information
+Please visit
+.BR http://www.rsyslog.com/doc
+for additional information, tutorials and a support forum.
+.SH SEE ALSO
+.BR rsyslog.conf (5),
+.BR logger (1),
+.BR syslog (2),
+.BR syslog (3),
+.BR services (5),
+.BR savelog (8)
+.LP
+.SH COLLABORATORS
+.B rsyslogd
+is derived from sysklogd sources, which in turn was taken from
+the BSD sources. Special thanks to Greg Wettstein (greg@wind.enjellic.com)
+and Martin Schulze (joey@linux.de) for the fine sysklogd package.
+
+.PD 0
+.TP
+Rainer Gerhards
+.TP
+Adiscon GmbH
+.TP
+Grossrinderfeld, Germany
+.TP
+rgerhards@adiscon.com
+.PD
diff --git a/tools/smfile.c b/tools/smfile.c
new file mode 100644
index 00000000..1e0bf091
--- /dev/null
+++ b/tools/smfile.c
@@ -0,0 +1,136 @@
+/* smfile.c
+ * This is a strgen module for the traditional file format.
+ *
+ * Format generated:
+ * "%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\n"
+ * Note that this is the same as smtradfile.c, except that we do have a RFC3339 timestamp. However,
+ * we have copied over the code from there, it is too simple to go through all the hassle
+ * of having a single code base.
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * File begun on 2010-06-01 by RGerhards
+ *
+ * Copyright 2010 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include "syslogd.h"
+#include "conf.h"
+#include "syslogd-types.h"
+#include "template.h"
+#include "msg.h"
+#include "module-template.h"
+#include "unicode-helper.h"
+
+MODULE_TYPE_STRGEN
+MODULE_TYPE_NOKEEP
+STRGEN_NAME("RSYSLOG_FileFormat")
+
+/* internal structures
+ */
+DEF_SMOD_STATIC_DATA
+
+
+/* config data */
+
+
+/* This strgen tries to minimize the amount of reallocs be first obtaining pointers to all strings
+ * needed (including their length) and then calculating the actual space required. So when we
+ * finally copy, we know exactly what we need. So we do at most one alloc.
+ */
+BEGINstrgen
+ register int iBuf;
+ uchar *pTimeStamp;
+ size_t lenTimeStamp;
+ uchar *pHOSTNAME;
+ size_t lenHOSTNAME;
+ uchar *pTAG;
+ int lenTAG;
+ uchar *pMSG;
+ size_t lenMSG;
+ size_t lenTotal;
+CODESTARTstrgen
+ /* first obtain all strings and their length (if not fixed) */
+ pTimeStamp = (uchar*) getTimeReported(pMsg, tplFmtRFC3339Date);
+ lenTimeStamp = ustrlen(pTimeStamp);
+ pHOSTNAME = (uchar*) getHOSTNAME(pMsg);
+ lenHOSTNAME = getHOSTNAMELen(pMsg);
+ getTAG(pMsg, &pTAG, &lenTAG);
+ pMSG = getMSG(pMsg);
+ lenMSG = getMSGLen(pMsg);
+
+ /* calculate len, constants for spaces and similar fixed strings */
+ lenTotal = lenTimeStamp + 1 + lenHOSTNAME + 1 + lenTAG + lenMSG + 2;
+ if(pMSG[0] != ' ')
+ ++lenTotal; /* then we need to introduce one additional space */
+
+ /* now make sure buffer is large enough */
+ if(lenTotal >= *pLenBuf)
+ CHKiRet(ExtendBuf(ppBuf, pLenBuf, lenTotal));
+
+ /* and concatenate the resulting string */
+ memcpy(*ppBuf, pTimeStamp, lenTimeStamp);
+ iBuf = lenTimeStamp;
+ *(*ppBuf + iBuf++) = ' ';
+
+ memcpy(*ppBuf + iBuf, pHOSTNAME, lenHOSTNAME);
+ iBuf += lenHOSTNAME;
+ *(*ppBuf + iBuf++) = ' ';
+
+ memcpy(*ppBuf + iBuf, pTAG, lenTAG);
+ iBuf += lenTAG;
+
+ if(pMSG[0] != ' ')
+ *(*ppBuf + iBuf++) = ' ';
+ memcpy(*ppBuf + iBuf, pMSG, lenMSG);
+ iBuf += lenMSG;
+
+ /* trailer */
+ *(*ppBuf + iBuf++) = '\n';
+ *(*ppBuf + iBuf) = '\0';
+
+finalize_it:
+ENDstrgen
+
+
+BEGINmodExit
+CODESTARTmodExit
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_SMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit(smfile)
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+
+ dbgprintf("rsyslog standard file format strgen init called, compiled with version %s\n", VERSION);
+ENDmodInit
diff --git a/tools/smfile.h b/tools/smfile.h
new file mode 100644
index 00000000..10946db5
--- /dev/null
+++ b/tools/smfile.h
@@ -0,0 +1,31 @@
+/* smfile.h
+ * These are the definitions for the traditional file format stringen module.
+ *
+ * File begun on 2010-06-04 by RGerhards
+ *
+ * Copyright 2010 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#ifndef SMFILE_H_INCLUDED
+#define SMFILE_H_INCLUDED 1
+
+/* prototypes */
+rsRetVal modInitsmfile(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*);
+
+#endif /* #ifndef SMFILE_H_INCLUDED */
diff --git a/tools/smfwd.c b/tools/smfwd.c
new file mode 100644
index 00000000..60fe94a7
--- /dev/null
+++ b/tools/smfwd.c
@@ -0,0 +1,143 @@
+/* smfwd.c
+ * This is a strgen module for the traditional (network) forwarding format.
+ *
+ * Format generated:
+ * "<%PRI%>%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag:1:32%%msg:::sp-if-no-1st-sp%%msg%"
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * File begun on 2010-06-01 by RGerhards
+ *
+ * Copyright 2010 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include "syslogd.h"
+#include "conf.h"
+#include "syslogd-types.h"
+#include "template.h"
+#include "msg.h"
+#include "module-template.h"
+#include "unicode-helper.h"
+
+MODULE_TYPE_STRGEN
+MODULE_TYPE_NOKEEP
+STRGEN_NAME("RSYSLOG_ForwardFormat")
+
+/* internal structures
+ */
+DEF_SMOD_STATIC_DATA
+
+
+/* config data */
+
+
+/* This strgen tries to minimize the amount of reallocs be first obtaining pointers to all strings
+ * needed (including their length) and then calculating the actual space required. So when we
+ * finally copy, we know exactly what we need. So we do at most one alloc.
+ */
+BEGINstrgen
+ register int iBuf;
+ char *pPRI;
+ size_t lenPRI;
+ uchar *pTimeStamp;
+ size_t lenTimeStamp;
+ uchar *pHOSTNAME;
+ size_t lenHOSTNAME;
+ uchar *pTAG;
+ int lenTAG;
+ uchar *pMSG;
+ size_t lenMSG;
+ size_t lenTotal;
+CODESTARTstrgen
+ /* first obtain all strings and their length (if not fixed) */
+ pPRI = getPRI(pMsg);
+ lenPRI = strlen(pPRI);
+ pTimeStamp = (uchar*) getTimeReported(pMsg, tplFmtRFC3339Date);
+ lenTimeStamp = ustrlen(pTimeStamp);
+ pHOSTNAME = (uchar*) getHOSTNAME(pMsg);
+ lenHOSTNAME = getHOSTNAMELen(pMsg);
+ getTAG(pMsg, &pTAG, &lenTAG);
+ if(lenTAG > 32)
+ lenTAG = 32; /* for forwarding, a max of 32 chars is permitted (RFC!) */
+ pMSG = getMSG(pMsg);
+ lenMSG = getMSGLen(pMsg);
+
+ /* calculate len, constants for spaces and similar fixed strings */
+ lenTotal = 1 + lenPRI + 1 + lenTimeStamp + 1 + lenHOSTNAME + 1 + lenTAG + lenMSG + 1;
+ if(pMSG[0] != ' ')
+ ++lenTotal; /* then we need to introduce one additional space */
+
+ /* now make sure buffer is large enough */
+ if(lenTotal >= *pLenBuf)
+ CHKiRet(ExtendBuf(ppBuf, pLenBuf, lenTotal));
+
+ /* and concatenate the resulting string */
+ **ppBuf = '<';
+ memcpy(*ppBuf + 1, pPRI, lenPRI);
+ iBuf = lenPRI + 1;
+ *(*ppBuf + iBuf++) = '>';
+
+ memcpy(*ppBuf + iBuf, pTimeStamp, lenTimeStamp);
+ iBuf += lenTimeStamp;
+ *(*ppBuf + iBuf++) = ' ';
+
+ memcpy(*ppBuf + iBuf, pHOSTNAME, lenHOSTNAME);
+ iBuf += lenHOSTNAME;
+ *(*ppBuf + iBuf++) = ' ';
+
+ memcpy(*ppBuf + iBuf, pTAG, lenTAG);
+ iBuf += lenTAG;
+
+ if(pMSG[0] != ' ')
+ *(*ppBuf + iBuf++) = ' ';
+ memcpy(*ppBuf + iBuf, pMSG, lenMSG);
+ iBuf += lenMSG;
+
+ /* string terminator */
+ *(*ppBuf + iBuf) = '\0';
+
+finalize_it:
+ENDstrgen
+
+
+BEGINmodExit
+CODESTARTmodExit
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_SMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit(smfwd)
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+
+ dbgprintf("rsyslog standard (network) forward format strgen init called, compiled with version %s\n", VERSION);
+ENDmodInit
diff --git a/tools/smfwd.h b/tools/smfwd.h
new file mode 100644
index 00000000..191a6bf1
--- /dev/null
+++ b/tools/smfwd.h
@@ -0,0 +1,30 @@
+/* smfwd.h
+ *
+ * File begun on 2010-06-04 by RGerhards
+ *
+ * Copyright 2010 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#ifndef SMFWD_H_INCLUDED
+#define SMFWD_H_INCLUDED 1
+
+/* prototypes */
+rsRetVal modInitsmfwd(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*);
+
+#endif /* #ifndef SMFWD_H_INCLUDED */
diff --git a/tools/smtradfile.c b/tools/smtradfile.c
new file mode 100644
index 00000000..5484f7be
--- /dev/null
+++ b/tools/smtradfile.c
@@ -0,0 +1,129 @@
+/* smtradfile.c
+ * This is a strgen module for the traditional file format.
+ *
+ * Format generated:
+ * "%TIMESTAMP% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\n"
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * File begun on 2010-06-01 by RGerhards
+ *
+ * Copyright 2010 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include "syslogd.h"
+#include "conf.h"
+#include "syslogd-types.h"
+#include "template.h"
+#include "msg.h"
+#include "module-template.h"
+#include "unicode-helper.h"
+
+MODULE_TYPE_STRGEN
+MODULE_TYPE_NOKEEP
+STRGEN_NAME("RSYSLOG_TraditionalFileFormat")
+
+/* internal structures
+ */
+DEF_SMOD_STATIC_DATA
+
+
+/* config data */
+
+
+/* This strgen tries to minimize the amount of reallocs be first obtaining pointers to all strings
+ * needed (including their length) and then calculating the actual space required. So when we
+ * finally copy, we know exactly what we need. So we do at most one alloc.
+ */
+BEGINstrgen
+ register int iBuf;
+ uchar *pTimeStamp;
+ uchar *pHOSTNAME;
+ size_t lenHOSTNAME;
+ uchar *pTAG;
+ int lenTAG;
+ uchar *pMSG;
+ size_t lenMSG;
+ size_t lenTotal;
+CODESTARTstrgen
+ /* first obtain all strings and their length (if not fixed) */
+ pTimeStamp = (uchar*) getTimeReported(pMsg, tplFmtRFC3164Date);
+ pHOSTNAME = (uchar*) getHOSTNAME(pMsg);
+ lenHOSTNAME = getHOSTNAMELen(pMsg);
+ getTAG(pMsg, &pTAG, &lenTAG);
+ pMSG = getMSG(pMsg);
+ lenMSG = getMSGLen(pMsg);
+
+ /* calculate len, constants for spaces and similar fixed strings */
+ lenTotal = CONST_LEN_TIMESTAMP_3164 + 1 + lenHOSTNAME + 1 + lenTAG + lenMSG + 2;
+ if(pMSG[0] != ' ')
+ ++lenTotal; /* then we need to introduce one additional space */
+
+ /* now make sure buffer is large enough */
+ if(lenTotal >= *pLenBuf)
+ CHKiRet(ExtendBuf(ppBuf, pLenBuf, lenTotal));
+
+ /* and concatenate the resulting string */
+ memcpy(*ppBuf, pTimeStamp, CONST_LEN_TIMESTAMP_3164);
+ *(*ppBuf + CONST_LEN_TIMESTAMP_3164) = ' ';
+
+ memcpy(*ppBuf + CONST_LEN_TIMESTAMP_3164 + 1, pHOSTNAME, lenHOSTNAME);
+ iBuf = CONST_LEN_TIMESTAMP_3164 + 1 + lenHOSTNAME;
+ *(*ppBuf + iBuf++) = ' ';
+
+ memcpy(*ppBuf + iBuf, pTAG, lenTAG);
+ iBuf += lenTAG;
+
+ if(pMSG[0] != ' ')
+ *(*ppBuf + iBuf++) = ' ';
+ memcpy(*ppBuf + iBuf, pMSG, lenMSG);
+ iBuf += lenMSG;
+
+ /* trailer */
+ *(*ppBuf + iBuf++) = '\n';
+ *(*ppBuf + iBuf) = '\0';
+
+finalize_it:
+ENDstrgen
+
+
+BEGINmodExit
+CODESTARTmodExit
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_SMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit(smtradfile)
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ dbgprintf("traditional file format strgen init called, compiled with version %s\n", VERSION);
+ENDmodInit
diff --git a/tools/smtradfile.h b/tools/smtradfile.h
new file mode 100644
index 00000000..afc737ed
--- /dev/null
+++ b/tools/smtradfile.h
@@ -0,0 +1,31 @@
+/* smtradfile.h
+ * These are the definitions for the traditional file format stringen module.
+ *
+ * File begun on 2010-06-01 by RGerhards
+ *
+ * Copyright 2010 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#ifndef SMTRADFILE_H_INCLUDED
+#define SMTRADFILE_H_INCLUDED 1
+
+/* prototypes */
+rsRetVal modInitsmtradfile(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*);
+
+#endif /* #ifndef SMTRADFILE_H_INCLUDED */
diff --git a/tools/smtradfwd.c b/tools/smtradfwd.c
new file mode 100644
index 00000000..37717434
--- /dev/null
+++ b/tools/smtradfwd.c
@@ -0,0 +1,140 @@
+/* smtradfwd.c
+ * This is a strgen module for the traditional forwarding format.
+ *
+ * Format generated:
+ * "<%PRI%>%TIMESTAMP% %HOSTNAME% %syslogtag:1:32%%msg:::sp-if-no-1st-sp%%msg%"
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * File begun on 2010-06-01 by RGerhards
+ *
+ * Copyright 2010 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include "syslogd.h"
+#include "conf.h"
+#include "syslogd-types.h"
+#include "template.h"
+#include "msg.h"
+#include "module-template.h"
+#include "unicode-helper.h"
+
+MODULE_TYPE_STRGEN
+MODULE_TYPE_NOKEEP
+STRGEN_NAME("RSYSLOG_TraditionalForwardFormat")
+
+/* internal structures
+ */
+DEF_SMOD_STATIC_DATA
+
+
+/* config data */
+
+
+/* This strgen tries to minimize the amount of reallocs be first obtaining pointers to all strings
+ * needed (including their length) and then calculating the actual space required. So when we
+ * finally copy, we know exactly what we need. So we do at most one alloc.
+ */
+BEGINstrgen
+ register int iBuf;
+ char *pPRI;
+ size_t lenPRI;
+ uchar *pTimeStamp;
+ uchar *pHOSTNAME;
+ size_t lenHOSTNAME;
+ uchar *pTAG;
+ int lenTAG;
+ uchar *pMSG;
+ size_t lenMSG;
+ size_t lenTotal;
+CODESTARTstrgen
+ /* first obtain all strings and their length (if not fixed) */
+ pPRI = getPRI(pMsg);
+ lenPRI = strlen(pPRI);
+ pTimeStamp = (uchar*) getTimeReported(pMsg, tplFmtRFC3164Date);
+ pHOSTNAME = (uchar*) getHOSTNAME(pMsg);
+ lenHOSTNAME = getHOSTNAMELen(pMsg);
+ getTAG(pMsg, &pTAG, &lenTAG);
+ if(lenTAG > 32)
+ lenTAG = 32; /* for forwarding, a max of 32 chars is permitted (RFC!) */
+ pMSG = getMSG(pMsg);
+ lenMSG = getMSGLen(pMsg);
+
+ /* calculate len, constants for spaces and similar fixed strings */
+ lenTotal = 1 + lenPRI + 1 + CONST_LEN_TIMESTAMP_3164 + 1 + lenHOSTNAME + 1 + lenTAG + lenMSG + 1;
+ if(pMSG[0] != ' ')
+ ++lenTotal; /* then we need to introduce one additional space */
+
+ /* now make sure buffer is large enough */
+ if(lenTotal >= *pLenBuf)
+ CHKiRet(ExtendBuf(ppBuf, pLenBuf, lenTotal));
+
+ /* and concatenate the resulting string */
+ **ppBuf = '<';
+ memcpy(*ppBuf + 1, pPRI, lenPRI);
+ iBuf = lenPRI + 1;
+ *(*ppBuf + iBuf++) = '>';
+
+ memcpy(*ppBuf + iBuf, pTimeStamp, CONST_LEN_TIMESTAMP_3164);
+ iBuf += CONST_LEN_TIMESTAMP_3164;
+ *(*ppBuf + iBuf++) = ' ';
+
+ memcpy(*ppBuf + iBuf, pHOSTNAME, lenHOSTNAME);
+ iBuf += lenHOSTNAME;
+ *(*ppBuf + iBuf++) = ' ';
+
+ memcpy(*ppBuf + iBuf, pTAG, lenTAG);
+ iBuf += lenTAG;
+
+ if(pMSG[0] != ' ')
+ *(*ppBuf + iBuf++) = ' ';
+ memcpy(*ppBuf + iBuf, pMSG, lenMSG);
+ iBuf += lenMSG;
+
+ /* string terminator */
+ *(*ppBuf + iBuf) = '\0';
+
+finalize_it:
+ENDstrgen
+
+
+BEGINmodExit
+CODESTARTmodExit
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_SMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit(smtradfwd)
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ dbgprintf("rsyslog traditional (network) forward format strgen init called, compiled with version %s\n", VERSION);
+ENDmodInit
diff --git a/tools/smtradfwd.h b/tools/smtradfwd.h
new file mode 100644
index 00000000..9ff0ab54
--- /dev/null
+++ b/tools/smtradfwd.h
@@ -0,0 +1,30 @@
+/* smtradfwd.h
+ *
+ * File begun on 2010-06-04 by RGerhards
+ *
+ * Copyright 2010 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#ifndef SMTRADFWD_H_INCLUDED
+#define SMTRADFWD_H_INCLUDED 1
+
+/* prototypes */
+rsRetVal modInitsmtradfwd(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*);
+
+#endif /* #ifndef SMTRADFWD_H_INCLUDED */
diff --git a/tools/syncdemo.c b/tools/syncdemo.c
new file mode 100644
index 00000000..89a5c6cc
--- /dev/null
+++ b/tools/syncdemo.c
@@ -0,0 +1,440 @@
+/* syncdemo - a program to demonstrate the performance and validity of different
+ * synchronization methods as well as some timing properties.
+ *
+ * The task to be done is very simple: a single gloabl integer is to to incremented
+ * by multiple threads. All this is done in a very-high concurrency environment. Note that
+ * the test is unfair to mechanisms likes spinlocks, because we have almost only wait
+ * time but no real processing time between the waits. However, the test provides
+ * some good insight into atomic instructions vs. other synchronisation methods.
+ * It also proves that garbling variables by not doing proper synchronisation is
+ * highly likely. For best results, this program should be executed on a
+ * multiprocessor machine (on a uniprocessor, it will probably not display the
+ * problems caused by missing synchronisation).
+ *
+ * Note: partitioned processing mode means that all computation is first done
+ * locally and the final result is then combined doing proper synchronization.
+ * This mode is used as a baseline for uninterrupted processing.
+ *
+ * compile with $ gcc -O1 -o syncdemo -lpthread syncdemo.c
+ *
+ * Alternatively, you may use -O0, but not a higher level. Note that
+ * the gcc code generator does in neither case generate code really
+ * suitable to compare "part" and "none" modes. If you absolutely need
+ * to do that, you need to use inline assembly. However, the results should
+ * be fairly OK when consitently using either -O0 or -O1. If you see a big loss
+ * of performance when you compare "none" and "part", be sure to run
+ * "none" with -t1 and watch out for the results! In any case, looking at the generated
+ * assembly code is vital to interpret results correctly. Review of generated assembly
+ * done on 2010-05-05 indicates that -O0 is probably the best choice. Note that we
+ * use the volatile attribute in one spot. This is used because it results in the
+ * best comparable result for our gcc 4.4.3, not really to invoke the volatile semantics.
+ *
+ * use "gcc -g -Wa,-ahl=syncdemo.s -lpthread syncdemo.c" to obtain a mixed code/assembly listing.
+ *
+ * This program REQUIRES linux. With slight modification, it may run on Solaris.
+ * Note that gcc on Sparc does NOT offer atomic instruction support!
+ *
+ * Copyright (C) 2010 by Rainer Gerhards <rgerhards@hq.adiscon.com>
+ * Released under the GNU GPLv3.
+ *
+ * Inspired by (retrieved 2010-04-13)
+ * http://www.alexonlinux.com/multithreaded-simple-data-type-access-and-atomic-variables
+ */
+#define _GNU_SOURCE
+#include <sched.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <semaphore.h>
+#include <stdlib.h>
+#include <linux/unistd.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <getopt.h>
+
+
+typedef enum { part, none, atomic, cas, spinlock, mutex, semaphore } syncType_t;
+static syncType_t syncTypes[] = { part, none, atomic, cas, spinlock, mutex, semaphore };
+
+/* config settings */
+static int bCPUAffinity = 0;
+static int procs = 0; /* number of processors */
+static int numthrds = 0; /* if zero, => equal num of processors */
+static unsigned goal = 50000000; /* 50 million */
+static int bCSV = 0; /* generate CSV output? */
+static int numIterations = 1; /* number of iterations */
+static int dummyLoad = 0; /* number of dummy load iterations to generate */
+syncType_t syncType;
+static int bAllSyncTypes = 0;
+
+static int global_int = 0; /* our global counter */
+static unsigned thrd_WorkToDo; /* number of computations each thread must do */
+static volatile int bStartRun = 0; /* indicate to flag when threads should start */
+
+static struct timeval tvStart, tvEnd; /* used for timing one testing iteration */
+
+/* statistic counters */
+static long long totalRuntime;
+static unsigned minRuntime = 999999999;
+static unsigned maxRuntime = 0;
+
+/* sync objects (if needed) */
+static pthread_mutex_t mut;
+static pthread_spinlock_t spin;
+static sem_t sem;
+
+static char*
+getSyncMethName(syncType_t st)
+{
+ switch(st) {
+ case part : return "partition";
+ case none : return "none";
+ case atomic : return "atomic op";
+ case spinlock : return "spin lock";
+ case mutex : return "mutex";
+ case semaphore: return "semaphore";
+ case cas : return "cas";
+ }
+}
+
+
+static pid_t
+gettid()
+{
+ return syscall( __NR_gettid );
+}
+
+
+void *workerThread( void *arg )
+{
+ int i, j;
+ volatile int partval = 0; /* use volatile so that gcc generates code similar to global var */
+ int *partptr;
+ int oldval, newval; /* for CAS sync mode */
+ int thrd_num = (int)(long)arg;
+ cpu_set_t set;
+
+ CPU_ZERO(&set);
+ CPU_SET(thrd_num % procs, &set);
+ if(syncType == part) {
+ partval = 0;
+ }
+
+ /* if enabled, try to put thread on a fixed CPU (the one that corresponds to the
+ * thread ID). This may
+ */
+ if(bCPUAffinity) {
+ if (sched_setaffinity( gettid(), sizeof( cpu_set_t ), &set )) {
+ perror( "sched_setaffinity" );
+ return NULL;
+ }
+ }
+
+ /* wait for "go" */
+ while(bStartRun == 0)
+ /*WAIT!*/;
+
+ for (i = 0; i < thrd_WorkToDo; i++) {
+ switch(syncType) {
+ case part:
+ ///* one needs to use inline assembly to get this right... */
+ //asm("addl $1, global_int(%rip)");
+ partval++;
+ break;
+ case none:
+ global_int++;
+ break;
+ case atomic:
+ __sync_fetch_and_add(&global_int,1);
+ break;
+ case cas:
+ do {
+ oldval = global_int;
+ newval = oldval + 1;
+ } while(!__sync_bool_compare_and_swap(&global_int, oldval, newval));
+ break;
+ case mutex:
+ pthread_mutex_lock(&mut);
+ global_int++;
+ pthread_mutex_unlock(&mut);
+ break;
+ case spinlock:
+ pthread_spin_lock(&spin);
+ global_int++;
+ pthread_spin_unlock(&spin);
+ break;
+ case semaphore:
+ sem_wait(&sem);
+ global_int++;
+ sem_post(&sem);
+ break;
+ }
+
+ /* we now generate "dummy load" if instructed to do so. The idea is that
+ * we do some other work, as in real life, so that we have a better
+ * ratio of sync vs. actual work to do.
+ */
+ for(j = 0 ; j < dummyLoad ; ++j) {
+ /* be careful: compiler may optimize loop out! */;
+ }
+ }
+
+ if(syncType == part) {
+ pthread_mutex_lock(&mut);
+ global_int += partval;
+ pthread_mutex_unlock(&mut);
+ }
+
+ return NULL;
+}
+
+
+static void beginTiming(void)
+{
+ if(!(bCSV || bAllSyncTypes)) {
+ printf("Test Parameters:\n");
+ printf("\tNumber of Cores.........: %d\n", procs);
+ printf("\tNumber of Threads.......: %d\n", numthrds);
+ printf("\tSet Affinity............: %s\n", bCPUAffinity ? "yes" : "no");
+ printf("\tCount to................: %u\n", goal);
+ printf("\tWork for each Thread....: %u\n", thrd_WorkToDo);
+ printf("\tDummy Load Counter......: %d\n", dummyLoad);
+ printf("\tSync Method used........: %s\n", getSyncMethName(syncType));
+ }
+ gettimeofday(&tvStart, NULL);
+}
+
+
+static void endTiming(void)
+{
+ unsigned delta;
+ long sec, usec;
+ long runtime;
+
+ gettimeofday(&tvEnd, NULL);
+ if(tvStart.tv_usec > tvEnd.tv_usec) {
+ tvEnd.tv_sec--;
+ tvEnd.tv_usec += 1000000;
+ }
+
+ sec = tvEnd.tv_sec - tvStart.tv_sec;
+ usec = tvEnd.tv_usec - tvStart.tv_usec;
+
+ delta = thrd_WorkToDo * numthrds - global_int;
+ if(!bAllSyncTypes) {
+ if(bCSV) {
+ printf("%s,%d,%d,%d,%u,%u,%ld.%06.6ld\n",
+ getSyncMethName(syncType), procs, numthrds, bCPUAffinity, goal, delta, sec, usec);
+ } else {
+ printf("measured (sytem time) runtime is %ld.% 6.6ld seconds\n", sec, usec);
+ if(delta == 0) {
+ printf("Computation was done correctly.\n");
+ } else {
+ printf("Computation INCORRECT,\n"
+ "\texpected %9u\n"
+ "\treal %9u\n"
+ "\toff by %9u\n",
+ thrd_WorkToDo * numthrds,
+ global_int,
+ delta);
+ }
+ }
+ }
+
+ runtime = sec * 1000 + (usec / 1000);
+ totalRuntime += runtime;
+ if(runtime < minRuntime)
+ minRuntime = runtime;
+ if(runtime > maxRuntime)
+ maxRuntime = runtime;
+}
+
+
+static void
+usage(void)
+{
+ fprintf(stderr, "Usage: syncdemo -a -c<num> -t<num>\n");
+ fprintf(stderr, "\t-a set CPU affinity\n");
+ fprintf(stderr, "\t-i number of iterations\n");
+ fprintf(stderr, "\t-c<num> count to <num>\n");
+ fprintf(stderr, "\t-d<num> dummy load, <num> iterations\n");
+ fprintf(stderr, "\t-t<num> number of threads to use\n");
+ fprintf(stderr, "\t-s<type> sync-type to use (none, atomic, mutex, spin, semaphore)\n");
+ fprintf(stderr, "\t-C generate CSV output\n");
+ fprintf(stderr, "\t-A test ALL sync types\n");
+ exit(2);
+}
+
+
+/* carry out the actual test (one iteration)
+ */
+static void
+singleTest(void)
+{
+ int i;
+ pthread_t *thrs;
+
+ global_int = 0;
+ bStartRun = 0;
+
+ thrs = malloc(sizeof(pthread_t) * numthrds);
+ if (thrs == NULL) {
+ perror( "malloc" );
+ exit(1);
+ }
+
+ thrd_WorkToDo = goal / numthrds;
+
+ for (i = 0; i < numthrds; i++) {
+ if(pthread_create( &thrs[i], NULL, workerThread, (void *)(long)i )) {
+ perror( "pthread_create" );
+ procs = i;
+ break;
+ }
+ }
+
+ beginTiming();
+ bStartRun = 1; /* start the threads (they are busy-waiting so far!) */
+
+ for (i = 0; i < numthrds; i++)
+ pthread_join( thrs[i], NULL );
+
+ endTiming();
+
+ free( thrs );
+
+}
+
+
+/* display an unsigned ms runtime count as string. Note that the
+ * string is inside a dynamically allocated buffer, which the caller
+ * must free to prevent a memory leak.
+ */
+char *
+dispRuntime(unsigned rt)
+{
+ static char *fmtbuf;
+
+ fmtbuf = malloc(32 * sizeof(char));
+ snprintf(fmtbuf, 32, "%u.%03.3u",
+ rt / 1000, rt % 1000);
+ return(fmtbuf);
+}
+
+
+doTest(syncType_t st)
+{
+ int i;
+
+ syncType = st;
+ totalRuntime = 0;
+ minRuntime = 999999999;
+ maxRuntime = 0;
+ for(i = 0 ; i < numIterations ; ++i) {
+ //printf("starting iteration %d\n", i);
+ singleTest();
+ }
+
+ /* we have a memory leak due to calling dispRuntime(), but we don't
+ * care as we terminate immediately.
+ */
+ printf("%-10s: total runtime %6ld.%3.3u, avg %s, min %s, max %s\n",
+ getSyncMethName(st),
+ (long)totalRuntime/1000, (unsigned)(totalRuntime % 1000),
+ dispRuntime((unsigned) (totalRuntime / numIterations)),
+ dispRuntime(minRuntime),
+ dispRuntime(maxRuntime));
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ int opt;
+
+ while((opt = getopt(argc, argv, "ac:d:i:t:s:CA")) != EOF) {
+ switch((char)opt) {
+ case 'A':
+ bAllSyncTypes = 1;
+ break;
+ case 'a':
+ bCPUAffinity = 1;
+ break;
+ case 'c':
+ goal = (unsigned) atol(optarg);
+ break;
+ case 'd':
+ dummyLoad = atoi(optarg);
+ break;
+ case 'i':
+ numIterations = atoi(optarg);
+ break;
+ case 't':
+ numthrds = atoi(optarg);
+ break;
+ case 'C':
+ bCSV = 1;
+ break;
+ case 's':
+ if(!strcmp(optarg, "none"))
+ syncType = none;
+ else if(!strcmp(optarg, "part"))
+ syncType = part;
+ else if(!strcmp(optarg, "atomic"))
+ syncType = atomic;
+ else if(!strcmp(optarg, "cas"))
+ syncType = cas;
+ else if(!strcmp(optarg, "mutex")) {
+ syncType = mutex;
+ pthread_mutex_init(&mut, NULL);
+ } else if(!strcmp(optarg, "spin")) {
+ syncType = spinlock;
+ } else if(!strcmp(optarg, "semaphore")) {
+ syncType = semaphore;
+ sem_init(&sem, 0, 1);
+ } else {
+ fprintf(stderr, "error: invalid sync mode '%s'\n", optarg);
+ usage();
+ }
+ break;
+ default:usage();
+ break;
+ }
+ }
+
+ /* for simplicity, we init all sync helpers no matter if we need them */
+ pthread_mutex_init(&mut, NULL);
+ pthread_spin_init(&spin, PTHREAD_PROCESS_PRIVATE);
+ sem_init(&sem, 0, 1);
+
+ /* Getting number of CPUs */
+ procs = (int)sysconf(_SC_NPROCESSORS_ONLN);
+ if(procs < 0) {
+ perror("sysconf");
+ return -1;
+ }
+
+ if(numthrds < 1) {
+ numthrds = procs;
+ }
+
+ if(bAllSyncTypes) {
+ for(i = 0 ; i < sizeof(syncTypes) / sizeof(syncType_t) ; ++i) {
+ doTest(syncTypes[i]);
+ }
+ printf("Done running tests, result based on:\n");
+ printf("\tNumber of Cores.........: %d\n", procs);
+ printf("\tNumber of Threads.......: %d\n", numthrds);
+ printf("\tSet CPU Affinity........: %s\n", bCPUAffinity ? "yes" : "no");
+ printf("\tCount to................: %u\n", goal);
+ printf("\tWork for each Thread....: %u\n", thrd_WorkToDo);
+ printf("\tDummy Load Counter......: %d\n", dummyLoad);
+ printf("\tIterations..............: %d\n", numIterations);
+ } else {
+ doTest(syncType);
+ }
+
+ return 0;
+}
diff --git a/tools/syslogd.c b/tools/syslogd.c
new file mode 100644
index 00000000..2f0f64c3
--- /dev/null
+++ b/tools/syslogd.c
@@ -0,0 +1,2079 @@
+/**
+ * \brief This is the main file of the rsyslogd daemon.
+ *
+ * Please visit the rsyslog project at
+ *
+ * http://www.rsyslog.com
+ *
+ * to learn more about it and discuss any questions you may have.
+ *
+ * rsyslog had initially been forked from the sysklogd project.
+ * I would like to express my thanks to the developers of the sysklogd
+ * package - without it, I would have had a much harder start...
+ *
+ * Please note that while rsyslog started from the sysklogd code base,
+ * it nowadays has almost nothing left in common with it. Allmost all
+ * parts of the code have been rewritten.
+ *
+ * This Project was intiated and is maintained by
+ * Rainer Gerhards <rgerhards@hq.adiscon.com>.
+ *
+ * For further information, please see http://www.rsyslog.com
+ *
+ * rsyslog - An Enhanced syslogd Replacement.
+ * Copyright 2003-2012 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include "rsyslog.h"
+
+#define DEFUPRI (LOG_USER|LOG_NOTICE)
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <limits.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+#include <assert.h>
+
+#ifdef OS_SOLARIS
+# include <errno.h>
+# include <fcntl.h>
+# include <stropts.h>
+# include <sys/termios.h>
+# include <sys/types.h>
+#else
+# include <libgen.h>
+# include <sys/errno.h>
+#endif
+
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <sys/file.h>
+#include <sys/resource.h>
+#include <grp.h>
+
+#if HAVE_SYS_TIMESPEC_H
+# include <sys/timespec.h>
+#endif
+
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+
+#include <signal.h>
+
+#if HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+#ifdef USE_NETZIP
+#include <zlib.h>
+#endif
+
+extern int yydebug; /* interface to flex */
+
+#include <netdb.h>
+
+#include "pidfile.h"
+#include "srUtils.h"
+#include "stringbuf.h"
+#include "syslogd-types.h"
+#include "template.h"
+#include "outchannel.h"
+#include "syslogd.h"
+
+#include "msg.h"
+#include "modules.h"
+#include "action.h"
+#include "iminternal.h"
+#include "cfsysline.h"
+#include "threads.h"
+#include "wti.h"
+#include "queue.h"
+#include "stream.h"
+#include "conf.h"
+#include "errmsg.h"
+#include "datetime.h"
+#include "parser.h"
+#include "batch.h"
+#include "unicode-helper.h"
+#include "ruleset.h"
+#include "net.h"
+#include "prop.h"
+#include "rsconf.h"
+#include "dnscache.h"
+#include "sd-daemon.h"
+#include "rainerscript.h"
+#include "ratelimit.h"
+
+/* definitions for objects we access */
+DEFobjCurrIf(obj)
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(datetime) /* TODO: make go away! */
+DEFobjCurrIf(conf)
+DEFobjCurrIf(module)
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(ruleset)
+DEFobjCurrIf(prop)
+DEFobjCurrIf(parser)
+DEFobjCurrIf(rsconf)
+DEFobjCurrIf(net) /* TODO: make go away! */
+
+
+/* forward definitions */
+static rsRetVal GlobalClassExit(void);
+static rsRetVal queryLocalHostname(void);
+
+
+#ifndef _PATH_LOGCONF
+#define _PATH_LOGCONF "/etc/rsyslog.conf"
+#endif
+
+#ifndef _PATH_MODDIR
+# if defined(__FreeBSD__)
+# define _PATH_MODDIR "/usr/local/lib/rsyslog/"
+# else
+# define _PATH_MODDIR "/lib/rsyslog/"
+# endif
+#endif
+
+#if defined(SYSLOGD_PIDNAME)
+# undef _PATH_LOGPID
+# if defined(FSSTND)
+# ifdef OS_BSD
+# define _PATH_VARRUN "/var/run/"
+# endif
+# if defined(__sun) || defined(__hpux)
+# define _PATH_VARRUN "/var/run/"
+# endif
+# define _PATH_LOGPID _PATH_VARRUN SYSLOGD_PIDNAME
+# else
+# define _PATH_LOGPID "/etc/" SYSLOGD_PIDNAME
+# endif
+#else
+# ifndef _PATH_LOGPID
+# if defined(__sun) || defined(__hpux)
+# define _PATH_VARRUN "/var/run/"
+# endif
+# if defined(FSSTND)
+# define _PATH_LOGPID _PATH_VARRUN "rsyslogd.pid"
+# else
+# define _PATH_LOGPID "/etc/rsyslogd.pid"
+# endif
+# endif
+#endif
+
+#ifndef _PATH_TTY
+# define _PATH_TTY "/dev/tty"
+#endif
+
+rsconf_t *ourConf; /* our config object */
+
+static prop_t *pInternalInputName = NULL; /* there is only one global inputName for all internally-generated messages */
+static uchar *ConfFile = (uchar*) _PATH_LOGCONF; /* read-only after startup */
+static char *PidFile = _PATH_LOGPID; /* read-only after startup */
+
+/* mypid is read-only after the initial fork() */
+static int bHadHUP = 0; /* did we have a HUP? */
+
+static int bFinished = 0; /* used by termination signal handler, read-only except there
+ * is either 0 or the number of the signal that requested the
+ * termination.
+ */
+int iConfigVerify = 0; /* is this just a config verify run? */
+
+#define LIST_DELIMITER ':' /* delimiter between two hosts */
+
+static pid_t ppid; /* This is a quick and dirty hack used for spliting main/startup thread */
+
+struct queuefilenames_s {
+ struct queuefilenames_s *next;
+ uchar *name;
+} *queuefilenames = NULL;
+
+
+static ratelimit_t *dflt_ratelimiter = NULL; /* ratelimiter for submits without explicit one */
+static ratelimit_t *internalMsg_ratelimiter = NULL; /* ratelimiter for rsyslog-own messages */
+int MarkInterval = 20 * 60; /* interval between marks in seconds - read-only after startup */
+int send_to_all = 0; /* send message to all IPv4/IPv6 addresses */
+static int doFork = 1; /* fork - run in daemon mode - read-only after startup */
+int bHaveMainQueue = 0;/* set to 1 if the main queue - in queueing mode - is available
+ * If the main queue is either not yet ready or not running in
+ * queueing mode (mode DIRECT!), then this is set to 0.
+ */
+
+extern int errno;
+
+/* main message queue and its configuration parameters */
+qqueue_t *pMsgQueue = NULL; /* the main message queue */
+
+
+/* up to the next comment, prototypes that should be removed by reordering */
+/* Function prototypes. */
+static char **crunch_list(char *list);
+static void reapchild();
+static void debug_switch();
+static void sighup_handler();
+
+
+static int usage(void)
+{
+ fprintf(stderr, "usage: rsyslogd [-46AdnqQvwx] [-l<hostlist>] [-s<domainlist>]\n"
+ " [-f<conffile>] [-i<pidfile>] [-N<level>] [-M<module load path>]\n"
+ " [-u<number>]\n"
+ "For further information see http://www.rsyslog.com/doc\n");
+ exit(1); /* "good" exit - done to terminate usage() */
+}
+
+
+/* ------------------------------ some support functions for imdiag ------------------------------ *
+ * This is a bit dirty, but the only way to do it, at least with reasonable effort.
+ * rgerhards, 2009-05-25
+ */
+
+/* return back the approximate current number of messages in the main message queue
+ * This number includes the messages that reside in an associated DA queue (if
+ * it exists) -- rgerhards, 2009-10-14
+ */
+rsRetVal
+diagGetMainMsgQSize(int *piSize)
+{
+ DEFiRet;
+ assert(piSize != NULL);
+ *piSize = (pMsgQueue->pqDA != NULL) ? pMsgQueue->pqDA->iQueueSize : 0;
+ *piSize += pMsgQueue->iQueueSize;
+ RETiRet;
+}
+
+
+/* ------------------------------ end support functions for imdiag ------------------------------ */
+
+
+/* rgerhards, 2005-10-24: crunch_list is called only during option processing. So
+ * it is never called once rsyslogd is running. This code
+ * contains some exits, but they are considered safe because they only happen
+ * during startup. Anyhow, when we review the code here, we might want to
+ * reconsider the exit()s.
+ */
+static char **crunch_list(char *list)
+{
+ int count, i;
+ char *p, *q;
+ char **result = NULL;
+
+ p = list;
+
+ /* strip off trailing delimiters */
+ while (p[strlen(p)-1] == LIST_DELIMITER) {
+ count--;
+ p[strlen(p)-1] = '\0';
+ }
+ /* cut off leading delimiters */
+ while (p[0] == LIST_DELIMITER) {
+ count--;
+ p++;
+ }
+
+ /* count delimiters to calculate elements */
+ for (count=i=0; p[i]; i++)
+ if (p[i] == LIST_DELIMITER) count++;
+
+ if ((result = (char **)MALLOC(sizeof(char *) * (count+2))) == NULL) {
+ printf ("Sorry, can't get enough memory, exiting.\n");
+ exit(0); /* safe exit, because only called during startup */
+ }
+
+ /*
+ * We now can assume that the first and last
+ * characters are different from any delimiters,
+ * so we don't have to care about this.
+ */
+ count = 0;
+ while ((q=strchr(p, LIST_DELIMITER))) {
+ result[count] = (char *) MALLOC((q - p + 1) * sizeof(char));
+ if (result[count] == NULL) {
+ printf ("Sorry, can't get enough memory, exiting.\n");
+ exit(0); /* safe exit, because only called during startup */
+ }
+ strncpy(result[count], p, q - p);
+ result[count][q - p] = '\0';
+ p = q; p++;
+ count++;
+ }
+ if ((result[count] = \
+ (char *)MALLOC(sizeof(char) * strlen(p) + 1)) == NULL) {
+ printf ("Sorry, can't get enough memory, exiting.\n");
+ exit(0); /* safe exit, because only called during startup */
+ }
+ strcpy(result[count],p);
+ result[++count] = NULL;
+
+#if 0
+ count=0;
+ while (result[count])
+ DBGPRINTF("#%d: %s\n", count, StripDomains[count++]);
+#endif
+ return result;
+}
+
+
+void untty(void)
+#ifdef HAVE_SETSID
+{
+ if(!Debug) {
+ setsid();
+ }
+ return;
+}
+#else
+{
+ int i;
+ pid_t pid;
+
+ if(!Debug) {
+ pid = getpid();
+ if (setpgid(pid, pid) < 0) {
+ perror("setpgid");
+ exit(1);
+ }
+
+ i = open(_PATH_TTY, O_RDWR|O_CLOEXEC);
+ if (i >= 0) {
+# if !defined(__hpux)
+ (void) ioctl(i, (int) TIOCNOTTY, NULL);
+# else
+ /* TODO: we need to implement something for HP UX! -- rgerhards, 2008-03-04 */
+ /* actually, HP UX should have setsid, so the code directly above should
+ * trigger. So the actual question is why it doesn't do that...
+ */
+# endif
+ close(i);
+ }
+ }
+}
+#endif
+
+
+/* This takes a received message that must be decoded and submits it to
+ * the main message queue. This is a legacy function which is being provided
+ * to aid older input plugins that do not support message creation via
+ * the new interfaces themselves. It is not recommended to use this
+ * function for new plugins. -- rgerhards, 2009-10-12
+ */
+rsRetVal
+parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int flags, flowControl_t flowCtlType,
+ prop_t *pInputName, struct syslogTime *stTime, time_t ttGenTime, ruleset_t *pRuleset)
+{
+ prop_t *pProp = NULL;
+ msg_t *pMsg;
+ DEFiRet;
+
+ /* we now create our own message object and submit it to the queue */
+ if(stTime == NULL) {
+ CHKiRet(msgConstruct(&pMsg));
+ } else {
+ CHKiRet(msgConstructWithTime(&pMsg, stTime, ttGenTime));
+ }
+ if(pInputName != NULL)
+ MsgSetInputName(pMsg, pInputName);
+ MsgSetRawMsg(pMsg, (char*)msg, len);
+ MsgSetFlowControlType(pMsg, flowCtlType);
+ MsgSetRuleset(pMsg, pRuleset);
+ pMsg->msgFlags = flags | NEEDS_PARSING;
+
+ MsgSetRcvFromStr(pMsg, hname, ustrlen(hname), &pProp);
+ CHKiRet(prop.Destruct(&pProp));
+ CHKiRet(MsgSetRcvFromIPStr(pMsg, hnameIP, ustrlen(hnameIP), &pProp));
+ CHKiRet(prop.Destruct(&pProp));
+ CHKiRet(submitMsg2(pMsg));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* this is a special function used to submit an error message. This
+ * function is also passed to the runtime library as the generic error
+ * message handler. -- rgerhards, 2008-04-17
+ */
+rsRetVal
+submitErrMsg(int iErr, uchar *msg)
+{
+ DEFiRet;
+ iRet = logmsgInternal(iErr, LOG_SYSLOG|LOG_ERR, msg, 0);
+ RETiRet;
+}
+
+
+static inline rsRetVal
+submitMsgWithDfltRatelimiter(msg_t *pMsg)
+{
+ return ratelimitAddMsg(dflt_ratelimiter, NULL, pMsg);
+}
+
+/* rgerhards 2004-11-09: the following is a function that can be used
+ * to log a message orginating from the syslogd itself.
+ */
+rsRetVal
+logmsgInternal(int iErr, int pri, uchar *msg, int flags)
+{
+ uchar pszTag[33];
+ msg_t *pMsg;
+ DEFiRet;
+
+ CHKiRet(msgConstruct(&pMsg));
+ MsgSetInputName(pMsg, pInternalInputName);
+ MsgSetRawMsgWOSize(pMsg, (char*)msg);
+ MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName(), ustrlen(glbl.GetLocalHostName()));
+ MsgSetRcvFrom(pMsg, glbl.GetLocalHostNameProp());
+ MsgSetRcvFromIP(pMsg, glbl.GetLocalHostIP());
+ MsgSetMSGoffs(pMsg, 0);
+ /* check if we have an error code associated and, if so,
+ * adjust the tag. -- rgerhards, 2008-06-27
+ */
+ if(iErr == NO_ERRCODE) {
+ MsgSetTAG(pMsg, UCHAR_CONSTANT("rsyslogd:"), sizeof("rsyslogd:") - 1);
+ } else {
+ size_t len = snprintf((char*)pszTag, sizeof(pszTag), "rsyslogd%d:", iErr);
+ pszTag[32] = '\0'; /* just to make sure... */
+ MsgSetTAG(pMsg, pszTag, len);
+ }
+ pMsg->iFacility = LOG_FAC(pri);
+ pMsg->iSeverity = LOG_PRI(pri);
+ flags |= INTERNAL_MSG;
+ pMsg->msgFlags = flags;
+
+ /* we now check if we should print internal messages out to stderr. This was
+ * suggested by HKS as a way to help people troubleshoot rsyslog configuration
+ * (by running it interactively. This makes an awful lot of sense, so I add
+ * it here. -- rgerhards, 2008-07-28
+ * Note that error messages can not be disable during a config verify. This
+ * permits us to process unmodified config files which otherwise contain a
+ * supressor statement.
+ */
+ if(((Debug == DEBUG_FULL || !doFork) && ourConf->globals.bErrMsgToStderr) || iConfigVerify) {
+ if(LOG_PRI(pri) == LOG_ERR)
+ fprintf(stderr, "rsyslogd: %s\n", msg);
+ }
+
+ if(bHaveMainQueue == 0) { /* not yet in queued mode */
+ iminternalAddMsg(pMsg);
+ } else {
+ /* we have the queue, so we can simply provide the
+ * message to the queue engine.
+ */
+ ratelimitAddMsg(internalMsg_ratelimiter, NULL, pMsg);
+ //submitMsgWithDfltRatelimiter(pMsg);
+ }
+finalize_it:
+ RETiRet;
+}
+
+
+/* preprocess a batch of messages, that is ready them for actual processing. This is done
+ * as a first stage and totally in parallel to any other worker active in the system. So
+ * it helps us keep up the overall concurrency level.
+ * rgerhards, 2010-06-09
+ */
+static inline rsRetVal
+preprocessBatch(batch_t *pBatch) {
+ prop_t *ip;
+ prop_t *fqdn;
+ prop_t *localName;
+ prop_t *propFromHost = NULL;
+ prop_t *propFromHostIP = NULL;
+ int bSingleRuleset;
+ ruleset_t *batchRuleset; /* the ruleset used for all message inside the batch, if there is a single one */
+ int bIsPermitted;
+ msg_t *pMsg;
+ int i;
+ rsRetVal localRet;
+ DEFiRet;
+
+ bSingleRuleset = 1;
+ batchRuleset = (pBatch->nElem > 0) ? pBatch->pElem[0].pMsg->pRuleset : NULL;
+
+ for(i = 0 ; i < pBatch->nElem && !*(pBatch->pbShutdownImmediate) ; i++) {
+ pMsg = pBatch->pElem[i].pMsg;
+ if((pMsg->msgFlags & NEEDS_ACLCHK_U) != 0) {
+ DBGPRINTF("msgConsumer: UDP ACL must be checked for message (hostname-based)\n");
+ if(net.cvthname(pMsg->rcvFrom.pfrominet, &localName, &fqdn, &ip) != RS_RET_OK)
+ continue;
+ bIsPermitted = net.isAllowedSender2((uchar*)"UDP",
+ (struct sockaddr *)pMsg->rcvFrom.pfrominet, (char*)propGetSzStr(fqdn), 1);
+ if(!bIsPermitted) {
+ DBGPRINTF("Message from '%s' discarded, not a permitted sender host\n",
+ propGetSzStr(fqdn));
+ pBatch->eltState[i] = BATCH_STATE_DISC;
+ } else {
+ /* save some of the info we obtained */
+ MsgSetRcvFrom(pMsg, localName);
+ CHKiRet(MsgSetRcvFromIP(pMsg, ip));
+ pMsg->msgFlags &= ~NEEDS_ACLCHK_U;
+ }
+ }
+ if((pMsg->msgFlags & NEEDS_PARSING) != 0) {
+ if((localRet = parser.ParseMsg(pMsg)) != RS_RET_OK) {
+ DBGPRINTF("Message discarded, parsing error %d\n", localRet);
+ pBatch->eltState[i] = BATCH_STATE_DISC;
+ }
+ }
+ if(pMsg->pRuleset != batchRuleset)
+ bSingleRuleset = 0;
+ }
+
+ batchSetSingleRuleset(pBatch, bSingleRuleset);
+
+finalize_it:
+ if(propFromHost != NULL)
+ prop.Destruct(&propFromHost);
+ if(propFromHostIP != NULL)
+ prop.Destruct(&propFromHostIP);
+ RETiRet;
+}
+
+/* The consumer of dequeued messages. This function is called by the
+ * queue engine on dequeueing of a message. It runs on a SEPARATE
+ * THREAD. It receives an array of pointers, which it must iterate
+ * over. We do not do any further batching, as this is of no benefit
+ * for the main queue.
+ */
+static rsRetVal
+msgConsumer(void __attribute__((unused)) *notNeeded, batch_t *pBatch, int *pbShutdownImmediate)
+{
+ DEFiRet;
+ assert(pBatch != NULL);
+ pBatch->pbShutdownImmediate = pbShutdownImmediate; /* TODO: move this to batch creation! */
+ preprocessBatch(pBatch);
+ ruleset.ProcessBatch(pBatch);
+//TODO: the BATCH_STATE_COMM must be set somewhere down the road, but we
+//do not have this yet and so we emulate -- 2010-06-10
+int i;
+ for(i = 0 ; i < pBatch->nElem && !*pbShutdownImmediate ; i++) {
+ pBatch->eltState[i] = BATCH_STATE_COMM;
+ }
+ RETiRet;
+}
+
+
+/* submit a message to the main message queue. This is primarily
+ * a hook to prevent the need for callers to know about the main message queue
+ * rgerhards, 2008-02-13
+ */
+rsRetVal
+submitMsg2(msg_t *pMsg)
+{
+ qqueue_t *pQueue;
+ ruleset_t *pRuleset;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pMsg, msg);
+
+ pRuleset = MsgGetRuleset(pMsg);
+ pQueue = (pRuleset == NULL) ? pMsgQueue : ruleset.GetRulesetQueue(pRuleset);
+
+ /* if a plugin logs a message during shutdown, the queue may no longer exist */
+ if(pQueue == NULL) {
+ DBGPRINTF("submitMsg2() could not submit message - "
+ "queue does (no longer?) exist - ignored\n");
+ FINALIZE;
+ }
+
+ qqueueEnqMsg(pQueue, pMsg->flowCtlType, pMsg);
+
+finalize_it:
+ RETiRet;
+}
+
+rsRetVal
+submitMsg(msg_t *pMsg)
+{
+ return submitMsgWithDfltRatelimiter(pMsg);
+}
+
+
+/* submit multiple messages at once, very similar to submitMsg, just
+ * for multi_submit_t. All messages need to go into the SAME queue!
+ * rgerhards, 2009-06-16
+ */
+rsRetVal
+multiSubmitMsg2(multi_submit_t *pMultiSub)
+{
+ qqueue_t *pQueue;
+ ruleset_t *pRuleset;
+ DEFiRet;
+ assert(pMultiSub != NULL);
+
+ if(pMultiSub->nElem == 0)
+ FINALIZE;
+
+ pRuleset = MsgGetRuleset(pMultiSub->ppMsgs[0]);
+ pQueue = (pRuleset == NULL) ? pMsgQueue : ruleset.GetRulesetQueue(pRuleset);
+
+ /* if a plugin logs a message during shutdown, the queue may no longer exist */
+ if(pQueue == NULL) {
+ DBGPRINTF("multiSubmitMsg() could not submit message - "
+ "queue does (no longer?) exist - ignored\n");
+ FINALIZE;
+ }
+
+ iRet = pQueue->MultiEnq(pQueue, pMultiSub);
+ pMultiSub->nElem = 0;
+
+finalize_it:
+ RETiRet;
+}
+rsRetVal
+multiSubmitMsg(multi_submit_t *pMultiSub) /* backward compat. level */
+{
+ return multiSubmitMsg2(pMultiSub);
+}
+
+
+/* flush multiSubmit, e.g. at end of read records */
+rsRetVal
+multiSubmitFlush(multi_submit_t *pMultiSub)
+{
+ DEFiRet;
+ if(pMultiSub->nElem > 0) {
+ iRet = multiSubmitMsg2(pMultiSub);
+ }
+ RETiRet;
+}
+
+
+static void
+reapchild()
+{
+ int saved_errno = errno;
+ struct sigaction sigAct;
+
+ memset(&sigAct, 0, sizeof (sigAct));
+ sigemptyset(&sigAct.sa_mask);
+ sigAct.sa_handler = reapchild;
+ sigaction(SIGCHLD, &sigAct, NULL); /* reset signal handler -ASP */
+
+ while(waitpid(-1, NULL, WNOHANG) > 0);
+ errno = saved_errno;
+}
+
+
+static void debug_switch()
+{
+ time_t tTime;
+ struct tm tp;
+ struct sigaction sigAct;
+
+ datetime.GetTime(&tTime);
+ localtime_r(&tTime, &tp);
+ if(debugging_on == 0) {
+ debugging_on = 1;
+ dbgprintf("\n");
+ dbgprintf("\n");
+ dbgprintf("********************************************************************************\n");
+ dbgprintf("Switching debugging_on to true at %2.2d:%2.2d:%2.2d\n",
+ tp.tm_hour, tp.tm_min, tp.tm_sec);
+ dbgprintf("********************************************************************************\n");
+ } else {
+ dbgprintf("********************************************************************************\n");
+ dbgprintf("Switching debugging_on to false at %2.2d:%2.2d:%2.2d\n",
+ tp.tm_hour, tp.tm_min, tp.tm_sec);
+ dbgprintf("********************************************************************************\n");
+ dbgprintf("\n");
+ dbgprintf("\n");
+ debugging_on = 0;
+ }
+
+ memset(&sigAct, 0, sizeof (sigAct));
+ sigemptyset(&sigAct.sa_mask);
+ sigAct.sa_handler = debug_switch;
+ sigaction(SIGUSR1, &sigAct, NULL);
+}
+
+
+/* doDie() is a signal handler. If called, it sets the bFinished variable
+ * to indicate the program should terminate. However, it does not terminate
+ * it itself, because that causes issues with multi-threading. The actual
+ * termination is then done on the main thread. This solution might introduce
+ * a minimal delay, but it is much cleaner than the approach of doing everything
+ * inside the signal handler.
+ * rgerhards, 2005-10-26
+ * Note:
+ * - we do not call DBGPRINTF() as this may cause us to block in case something
+ * with the threading is wrong.
+ * - we do not really care about the return state of write(), but we need this
+ * strange check we do to silence compiler warnings (thanks, Ubuntu!)
+ */
+static void doDie(int sig)
+{
+# define MSG1 "DoDie called.\n"
+# define MSG2 "DoDie called 5 times - unconditional exit\n"
+ static int iRetries = 0; /* debug aid */
+ dbgprintf(MSG1);
+ if(Debug == DEBUG_FULL) {
+ if(write(1, MSG1, sizeof(MSG1) - 1)) {}
+ }
+ if(iRetries++ == 4) {
+ if(Debug == DEBUG_FULL) {
+ if(write(1, MSG2, sizeof(MSG2) - 1)) {}
+ }
+ abort();
+ }
+ bFinished = sig;
+# undef MSG1
+# undef MSG2
+}
+
+
+/* Finalize and destruct all actions.
+ */
+static inline void
+destructAllActions(void)
+{
+ ruleset.DestructAllActions(runConf);
+ bHaveMainQueue = 0; // flag that internal messages need to be temporarily stored
+}
+
+
+/* die() is called when the program shall end. This typically only occurs
+ * during sigterm or during the initialization.
+ * As die() is intended to shutdown rsyslogd, it is
+ * safe to call exit() here. Just make sure that die() itself is not called
+ * at inapropriate places. As a general rule of thumb, it is a bad idea to add
+ * any calls to die() in new code!
+ * rgerhards, 2005-10-24
+ */
+static void
+die(int sig)
+{
+ char buf[256];
+
+ DBGPRINTF("exiting on signal %d\n", sig);
+
+ /* IMPORTANT: we should close the inputs first, and THEN send our termination
+ * message. If we do it the other way around, logmsgInternal() may block on
+ * a full queue and the inputs still fill up that queue. Depending on the
+ * scheduling order, we may end up with logmsgInternal being held for a quite
+ * long time. When the inputs are terminated first, that should not happen
+ * because the queue is drained in parallel. The situation could only become
+ * an issue with extremely long running actions in a queue full environment.
+ * However, such actions are at least considered poorly written, if not
+ * outright wrong. So we do not care about this very remote problem.
+ * rgerhards, 2008-01-11
+ */
+
+ /* close the inputs */
+ DBGPRINTF("Terminating input threads...\n");
+ glbl.SetGlobalInputTermination();
+ thrdTerminateAll();
+
+ /* and THEN send the termination log message (see long comment above) */
+ if(sig && runConf->globals.bLogStatusMsgs) {
+ (void) snprintf(buf, sizeof(buf) / sizeof(char),
+ " [origin software=\"rsyslogd\" " "swVersion=\"" VERSION \
+ "\" x-pid=\"%d\" x-info=\"http://www.rsyslog.com\"]" " exiting on signal %d.",
+ (int) glblGetOurPid(), sig);
+ errno = 0;
+ logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)buf, 0);
+ }
+ /* we sleep for 50ms to give the queue a chance to pick up the exit message;
+ * otherwise we have seen cases where the message did not make it to log
+ * files, even on idle systems.
+ */
+ srSleep(0, 50);
+
+ /* drain queue (if configured so) and stop main queue worker thread pool */
+ DBGPRINTF("Terminating main queue...\n");
+ qqueueDestruct(&pMsgQueue);
+ pMsgQueue = NULL;
+
+ /* Free ressources and close connections. This includes flushing any remaining
+ * repeated msgs.
+ */
+ DBGPRINTF("Terminating outputs...\n");
+ 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.
+ */
+ /* de-init some modules */
+ modExitIminternal();
+
+ /*dbgPrintAllDebugInfo(); / * this is the last spot where this can be done - below output modules are unloaded! */
+
+ /* the following line cleans up CfSysLineHandlers that were not based on loadable
+ * modules. As such, they are not yet cleared.
+ */
+ unregCfSysLineHdlrs();
+
+ /* destruct our global properties */
+ if(pInternalInputName != NULL)
+ prop.Destruct(&pInternalInputName);
+
+ /* terminate the remaining classes */
+ GlobalClassExit();
+
+ module.UnloadAndDestructAll(eMOD_LINK_ALL);
+
+ DBGPRINTF("Clean shutdown completed, bye\n");
+ /* dbgClassExit MUST be the last one, because it de-inits the debug system */
+ dbgClassExit();
+
+ /* 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()] */
+}
+
+/*
+ * Signal handler to terminate the parent process.
+ * rgerhards, 2005-10-24: this is only called during forking of the
+ * detached syslogd. I consider this method to be safe.
+ */
+static void doexit()
+{
+ exit(0); /* "good" exit, only during child-creation */
+}
+
+#if 0 /* TODO: re-enable, currently not used */
+/* helper to generateConfigDAG, to print out all actions via
+ * the llExecFunc() facility.
+ * rgerhards, 2007-08-02
+ */
+struct dag_info {
+ FILE *fp; /* output file */
+ int iActUnit; /* current action unit number */
+ int iAct; /* current action in unit */
+ int bDiscarded; /* message discarded (config error) */
+ };
+DEFFUNC_llExecFunc(generateConfigDAGAction)
+{
+ action_t *pAction;
+ uchar *pszModName;
+ uchar *pszVertexName;
+ struct dag_info *pDagInfo;
+ DEFiRet;
+
+ pDagInfo = (struct dag_info*) pParam;
+ pAction = (action_t*) pData;
+
+ pszModName = module.GetStateName(pAction->pMod);
+
+ /* vertex */
+ if(pAction->pszName == NULL) {
+ if(!strcmp((char*)pszModName, "builtin-discard"))
+ pszVertexName = (uchar*)"discard";
+ else
+ pszVertexName = pszModName;
+ } else {
+ pszVertexName = pAction->pszName;
+ }
+
+ fprintf(pDagInfo->fp, "\tact%d_%d\t\t[label=\"%s\"%s%s]\n",
+ pDagInfo->iActUnit, pDagInfo->iAct, pszVertexName,
+ pDagInfo->bDiscarded ? " style=dotted color=red" : "",
+ (pAction->pQueue->qType == QUEUETYPE_DIRECT) ? "" : " shape=hexagon"
+ );
+
+ /* edge */
+ if(pDagInfo->iAct == 0) {
+ } else {
+ fprintf(pDagInfo->fp, "\tact%d_%d -> act%d_%d[%s%s]\n",
+ pDagInfo->iActUnit, pDagInfo->iAct - 1,
+ pDagInfo->iActUnit, pDagInfo->iAct,
+ pDagInfo->bDiscarded ? " style=dotted color=red" : "",
+ pAction->bExecWhenPrevSusp ? " label=\"only if\\nsuspended\"" : "" );
+ }
+
+ /* check for discard */
+ if(!strcmp((char*) pszModName, "builtin-discard")) {
+ fprintf(pDagInfo->fp, "\tact%d_%d\t\t[shape=box]\n",
+ pDagInfo->iActUnit, pDagInfo->iAct);
+ pDagInfo->bDiscarded = 1;
+ }
+
+
+ ++pDagInfo->iAct;
+
+ RETiRet;
+}
+
+
+/* create config DAG
+ * This functions takes a rsyslog config and produces a .dot file for use
+ * with graphviz (http://www.graphviz.org). This is done in an effort to
+ * document, and also potentially troubleshoot, configurations. Plus, I
+ * consider it a nice feature to explain some concepts. Note that the
+ * current version only produces a graph with relatively little information.
+ * This is a foundation that may be later expanded (if it turns out to be
+ * useful enough).
+ * rgerhards, 2009-05-11
+ */
+static rsRetVal
+generateConfigDAG(uchar *pszDAGFile)
+{
+ //rule_t *f;
+ FILE *fp;
+ int iActUnit = 1;
+ //int bHasFilter = 0; /* filter associated with this action unit? */
+ //int bHadFilter;
+ //int i;
+ struct dag_info dagInfo;
+ //char *pszFilterName;
+ char szConnectingNode[64];
+ DEFiRet;
+
+ assert(pszDAGFile != NULL);
+
+ logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)
+ "Configuration graph generation is unfortunately disabled "
+ "in the current code base.", 0);
+ ABORT_FINALIZE(RS_RET_FILENAME_INVALID);
+
+ if((fp = fopen((char*) pszDAGFile, "w")) == NULL) {
+ logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)
+ "configuraton graph output file could not be opened, none generated", 0);
+ ABORT_FINALIZE(RS_RET_FILENAME_INVALID);
+ }
+
+ dagInfo.fp = fp;
+
+ /* from here on, we assume writes go well. This here is a really
+ * unimportant utility function and if something goes wrong, it has
+ * almost no effect. So let's not overdo this...
+ */
+ fprintf(fp, "# graph created by rsyslog " VERSION "\n\n"
+ "# use the dot tool from http://www.graphviz.org to visualize!\n"
+ "digraph rsyslogConfig {\n"
+ "\tinputs [shape=tripleoctagon]\n"
+ "\tinputs -> act0_0\n"
+ "\tact0_0 [label=\"main\\nqueue\" shape=hexagon]\n"
+ /*"\tmainq -> act1_0\n"*/
+ );
+ strcpy(szConnectingNode, "act0_0");
+ dagInfo.bDiscarded = 0;
+
+/* TODO: re-enable! */
+#if 0
+ for(f = Files; f != NULL ; f = f->f_next) {
+ /* BSD-Style filters are currently ignored */
+ bHadFilter = bHasFilter;
+ if(f->f_filter_type == FILTER_PRI) {
+ bHasFilter = 0;
+ for (i = 0; i <= LOG_NFACILITIES; i++)
+ if (f->f_filterData.f_pmask[i] != 0xff) {
+ bHasFilter = 1;
+ break;
+ }
+ } else {
+ bHasFilter = 1;
+ }
+
+ /* we know we have a filter, so it can be false */
+ switch(f->f_filter_type) {
+ case FILTER_PRI:
+ pszFilterName = "pri filter";
+ break;
+ case FILTER_PROP:
+ pszFilterName = "property filter";
+ break;
+ case FILTER_EXPR:
+ pszFilterName = "script filter";
+ break;
+ }
+
+ /* write action unit node */
+ if(bHasFilter) {
+ fprintf(fp, "\t%s -> act%d_end\t[label=\"%s:\\nfalse\"]\n",
+ szConnectingNode, iActUnit, pszFilterName);
+ fprintf(fp, "\t%s -> act%d_0\t[label=\"%s:\\ntrue\"]\n",
+ szConnectingNode, iActUnit, pszFilterName);
+ fprintf(fp, "\tact%d_end\t\t\t\t[shape=point]\n", iActUnit);
+ snprintf(szConnectingNode, sizeof(szConnectingNode), "act%d_end", iActUnit);
+ } else {
+ fprintf(fp, "\t%s -> act%d_0\t[label=\"no filter\"]\n",
+ szConnectingNode, iActUnit);
+ snprintf(szConnectingNode, sizeof(szConnectingNode), "act%d_0", iActUnit);
+ }
+
+ /* draw individual nodes */
+ dagInfo.iActUnit = iActUnit;
+ dagInfo.iAct = 0;
+ dagInfo.bDiscarded = 0;
+ llExecFunc(&f->llActList, generateConfigDAGAction, &dagInfo); /* actions */
+
+ /* finish up */
+ if(bHasFilter && !dagInfo.bDiscarded) {
+ fprintf(fp, "\tact%d_%d -> %s\n",
+ iActUnit, dagInfo.iAct - 1, szConnectingNode);
+ }
+
+ ++iActUnit;
+ }
+#endif
+
+ fprintf(fp, "\t%s -> act%d_0\n", szConnectingNode, iActUnit);
+ fprintf(fp, "\tact%d_0\t\t[label=discard shape=box]\n"
+ "}\n", iActUnit);
+ fclose(fp);
+
+finalize_it:
+ RETiRet;
+}
+#endif
+
+
+/* create a main message queue, now also used for ruleset queues. This function
+ * needs to be moved to some other module, but it is considered acceptable for
+ * the time being (remember that we want to restructure config processing at large!).
+ * rgerhards, 2009-10-27
+ */
+rsRetVal createMainQueue(qqueue_t **ppQueue, uchar *pszQueueName, struct nvlst *lst)
+{
+ struct queuefilenames_s *qfn;
+ uchar *qfname = NULL;
+ static int qfn_renamenum = 0;
+ uchar qfrenamebuf[1024];
+ DEFiRet;
+
+ /* create message queue */
+ CHKiRet_Hdlr(qqueueConstruct(ppQueue, ourConf->globals.mainQ.MainMsgQueType, ourConf->globals.mainQ.iMainMsgQueueNumWorkers, ourConf->globals.mainQ.iMainMsgQueueSize, msgConsumer)) {
+ /* no queue is fatal, we need to give up in that case... */
+ errmsg.LogError(0, iRet, "could not create (ruleset) main message queue"); \
+ }
+ /* name our main queue object (it's not fatal if it fails...) */
+ obj.SetName((obj_t*) (*ppQueue), pszQueueName);
+
+ if(lst == NULL) { /* use legacy parameters? */
+ /* ... set some properties ... */
+ # define setQPROP(func, directive, data) \
+ CHKiRet_Hdlr(func(*ppQueue, data)) { \
+ errmsg.LogError(0, NO_ERRCODE, "Invalid " #directive ", error %d. Ignored, running with default setting", iRet); \
+ }
+ # define setQPROPstr(func, directive, data) \
+ CHKiRet_Hdlr(func(*ppQueue, data, (data == NULL)? 0 : strlen((char*) data))) { \
+ errmsg.LogError(0, NO_ERRCODE, "Invalid " #directive ", error %d. Ignored, running with default setting", iRet); \
+ }
+
+ if(ourConf->globals.mainQ.pszMainMsgQFName != NULL) {
+ /* check if the queue file name is unique, else emit an error */
+ for(qfn = queuefilenames ; qfn != NULL ; qfn = qfn->next) {
+ dbgprintf("check queue file name '%s' vs '%s'\n", qfn->name, ourConf->globals.mainQ.pszMainMsgQFName );
+ if(!ustrcmp(qfn->name, ourConf->globals.mainQ.pszMainMsgQFName)) {
+ snprintf((char*)qfrenamebuf, sizeof(qfrenamebuf), "%d-%s-%s",
+ ++qfn_renamenum, ourConf->globals.mainQ.pszMainMsgQFName,
+ (pszQueueName == NULL) ? "NONAME" : (char*)pszQueueName);
+ qfname = ustrdup(qfrenamebuf);
+ errmsg.LogError(0, NO_ERRCODE, "Error: queue file name '%s' already in use "
+ " - using '%s' instead", ourConf->globals.mainQ.pszMainMsgQFName, qfname);
+ break;
+ }
+ }
+ if(qfname == NULL)
+ qfname = ustrdup(ourConf->globals.mainQ.pszMainMsgQFName);
+ qfn = malloc(sizeof(struct queuefilenames_s));
+ qfn->name = qfname;
+ qfn->next = queuefilenames;
+ queuefilenames = qfn;
+ }
+
+ setQPROP(qqueueSetMaxFileSize, "$MainMsgQueueFileSize", ourConf->globals.mainQ.iMainMsgQueMaxFileSize);
+ setQPROP(qqueueSetsizeOnDiskMax, "$MainMsgQueueMaxDiskSpace", ourConf->globals.mainQ.iMainMsgQueMaxDiskSpace);
+ setQPROP(qqueueSetiDeqBatchSize, "$MainMsgQueueDequeueBatchSize", ourConf->globals.mainQ.iMainMsgQueDeqBatchSize);
+ setQPROPstr(qqueueSetFilePrefix, "$MainMsgQueueFileName", qfname);
+ setQPROP(qqueueSetiPersistUpdCnt, "$MainMsgQueueCheckpointInterval", ourConf->globals.mainQ.iMainMsgQPersistUpdCnt);
+ setQPROP(qqueueSetbSyncQueueFiles, "$MainMsgQueueSyncQueueFiles", ourConf->globals.mainQ.bMainMsgQSyncQeueFiles);
+ setQPROP(qqueueSettoQShutdown, "$MainMsgQueueTimeoutShutdown", ourConf->globals.mainQ.iMainMsgQtoQShutdown );
+ setQPROP(qqueueSettoActShutdown, "$MainMsgQueueTimeoutActionCompletion", ourConf->globals.mainQ.iMainMsgQtoActShutdown);
+ setQPROP(qqueueSettoWrkShutdown, "$MainMsgQueueWorkerTimeoutThreadShutdown", ourConf->globals.mainQ.iMainMsgQtoWrkShutdown);
+ setQPROP(qqueueSettoEnq, "$MainMsgQueueTimeoutEnqueue", ourConf->globals.mainQ.iMainMsgQtoEnq);
+ setQPROP(qqueueSetiHighWtrMrk, "$MainMsgQueueHighWaterMark", ourConf->globals.mainQ.iMainMsgQHighWtrMark);
+ setQPROP(qqueueSetiLowWtrMrk, "$MainMsgQueueLowWaterMark", ourConf->globals.mainQ.iMainMsgQLowWtrMark);
+ setQPROP(qqueueSetiDiscardMrk, "$MainMsgQueueDiscardMark", ourConf->globals.mainQ.iMainMsgQDiscardMark);
+ setQPROP(qqueueSetiDiscardSeverity, "$MainMsgQueueDiscardSeverity", ourConf->globals.mainQ.iMainMsgQDiscardSeverity);
+ setQPROP(qqueueSetiMinMsgsPerWrkr, "$MainMsgQueueWorkerThreadMinimumMessages", ourConf->globals.mainQ.iMainMsgQWrkMinMsgs);
+ setQPROP(qqueueSetbSaveOnShutdown, "$MainMsgQueueSaveOnShutdown", ourConf->globals.mainQ.bMainMsgQSaveOnShutdown);
+ setQPROP(qqueueSetiDeqSlowdown, "$MainMsgQueueDequeueSlowdown", ourConf->globals.mainQ.iMainMsgQDeqSlowdown);
+ setQPROP(qqueueSetiDeqtWinFromHr, "$MainMsgQueueDequeueTimeBegin", ourConf->globals.mainQ.iMainMsgQueueDeqtWinFromHr);
+ setQPROP(qqueueSetiDeqtWinToHr, "$MainMsgQueueDequeueTimeEnd", ourConf->globals.mainQ.iMainMsgQueueDeqtWinToHr);
+
+ # undef setQPROP
+ # undef setQPROPstr
+ } else { /* use new style config! */
+ qqueueSetDefaultsRulesetQueue(*ppQueue);
+ qqueueApplyCnfParam(*ppQueue, lst);
+ }
+
+ /* ... and finally start the queue! */
+ CHKiRet_Hdlr(qqueueStart(*ppQueue)) {
+ /* no queue is fatal, we need to give up in that case... */
+ errmsg.LogError(0, iRet, "could not start (ruleset) main message queue"); \
+ }
+ RETiRet;
+}
+
+
+/* INIT -- Initialize syslogd
+ * Note that if iConfigVerify is set, only the config file is verified but nothing
+ * else happens. -- rgerhards, 2008-07-28
+ */
+static rsRetVal
+init(void)
+{
+ char bufStartUpMsg[512];
+ struct sigaction sigAct;
+ DEFiRet;
+
+ memset(&sigAct, 0, sizeof (sigAct));
+ sigemptyset(&sigAct.sa_mask);
+ sigAct.sa_handler = sighup_handler;
+ sigaction(SIGHUP, &sigAct, NULL);
+
+ CHKiRet(rsconf.Activate(ourConf));
+ DBGPRINTF(" started.\n");
+
+ /* we now generate the startup message. It now includes everything to
+ * identify this instance. -- rgerhards, 2005-08-17
+ */
+ if(ourConf->globals.bLogStatusMsgs) {
+ snprintf(bufStartUpMsg, sizeof(bufStartUpMsg)/sizeof(char),
+ " [origin software=\"rsyslogd\" " "swVersion=\"" VERSION \
+ "\" x-pid=\"%d\" x-info=\"http://www.rsyslog.com\"] start",
+ (int) glblGetOurPid());
+ logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)bufStartUpMsg, 0);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/*
+ * The following function is resposible for handling a SIGHUP signal. Since
+ * we are now doing mallocs/free as part of init we had better not being
+ * doing this during a signal handler. Instead this function simply sets
+ * a flag variable which will tells the main loop to do "the right thing".
+ */
+void sighup_handler()
+{
+ struct sigaction sigAct;
+
+ bHadHUP = 1;
+
+ memset(&sigAct, 0, sizeof (sigAct));
+ sigemptyset(&sigAct.sa_mask);
+ sigAct.sa_handler = sighup_handler;
+ sigaction(SIGHUP, &sigAct, NULL);
+}
+
+void sigttin_handler()
+{
+}
+
+/* this function pulls all internal messages from the buffer
+ * and puts them into the processing engine.
+ * We can only do limited error handling, as this would not
+ * really help us. TODO: add error messages?
+ * rgerhards, 2007-08-03
+ */
+static inline void processImInternal(void)
+{
+ msg_t *pMsg;
+
+ while(iminternalRemoveMsg(&pMsg) == RS_RET_OK) {
+ submitMsgWithDfltRatelimiter(pMsg);
+ }
+}
+
+
+/* helper to doHUP(), this "HUPs" each action. The necessary locking
+ * is done inside the action class and nothing we need to take care of.
+ * rgerhards, 2008-10-22
+ */
+DEFFUNC_llExecFunc(doHUPActions)
+{
+ BEGINfunc
+ actionCallHUPHdlr((action_t*) pData);
+ ENDfunc
+ return RS_RET_OK; /* we ignore errors, we can not do anything either way */
+}
+
+
+/* This function processes a HUP after one has been detected. Note that this
+ * is *NOT* the sighup handler. The signal is recorded by the handler, that record
+ * detected inside the mainloop and then this function is called to do the
+ * real work. -- rgerhards, 2008-10-22
+ * Note: there is a VERY slim chance of a data race when the hostname is reset.
+ * We prefer to take this risk rather than sync all accesses, because to the best
+ * of my analysis it can not really hurt (the actual property is reference-counted)
+ * but the sync would require some extra CPU for *each* message processed.
+ * rgerhards, 2012-04-11
+ */
+static inline void
+doHUP(void)
+{
+ char buf[512];
+
+ if(ourConf->globals.bLogStatusMsgs) {
+ snprintf(buf, sizeof(buf) / sizeof(char),
+ " [origin software=\"rsyslogd\" " "swVersion=\"" VERSION
+ "\" x-pid=\"%d\" x-info=\"http://www.rsyslog.com\"] rsyslogd was HUPed",
+ (int) glblGetOurPid());
+ errno = 0;
+ logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)buf, 0);
+ }
+
+ queryLocalHostname(); /* re-read our name */
+ ruleset.IterateAllActions(ourConf, doHUPActions, NULL);
+}
+
+
+/* This is the main processing loop. It is called after successful initialization.
+ * When it returns, the syslogd terminates.
+ * Its sole function is to provide some housekeeping things. The real work is done
+ * by the other threads spawned.
+ */
+static void
+mainloop(void)
+{
+ struct timeval tvSelectTimeout;
+
+ BEGINfunc
+ /* first check if we have any internal messages queued and spit them out. We used
+ * to do that on any loop iteration, but that is no longer necessry. The reason
+ * is that once we reach this point here, we always run on multiple threads and
+ * thus the main queue is properly initialized. -- rgerhards, 2008-06-09
+ */
+ processImInternal();
+
+ while(!bFinished){
+ /* this is now just a wait - please note that we do use a near-"eternal"
+ * timeout of 1 day. This enables us to help safe the environment
+ * by not unnecessarily awaking rsyslog on a regular tick (just think
+ * powertop, for example). In that case, we primarily wait for a signal,
+ * but a once-a-day wakeup should be quite acceptable. -- rgerhards, 2008-06-09
+ */
+ tvSelectTimeout.tv_sec = 86400 /*1 day*/;
+ tvSelectTimeout.tv_usec = 0;
+ select(1, NULL, NULL, NULL, &tvSelectTimeout);
+ if(bFinished)
+ break; /* exit as quickly as possible */
+
+ if(bHadHUP) {
+ doHUP();
+ bHadHUP = 0;
+ continue;
+ }
+ }
+ ENDfunc
+}
+
+/* print version and compile-time setting information.
+ */
+static void printVersion(void)
+{
+ printf("rsyslogd %s, ", VERSION);
+ printf("compiled with:\n");
+#ifdef FEATURE_REGEXP
+ printf("\tFEATURE_REGEXP:\t\t\t\tYes\n");
+#else
+ printf("\tFEATURE_REGEXP:\t\t\t\tNo\n");
+#endif
+#if defined(_LARGE_FILES) || (defined (_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS >= 64)
+ printf("\tFEATURE_LARGEFILE:\t\t\tYes\n");
+#else
+ printf("\tFEATURE_LARGEFILE:\t\t\tNo\n");
+#endif
+#if defined(SYSLOG_INET) && defined(USE_GSSAPI)
+ printf("\tGSSAPI Kerberos 5 support:\t\tYes\n");
+#else
+ printf("\tGSSAPI Kerberos 5 support:\t\tNo\n");
+#endif
+#ifndef NDEBUG
+ printf("\tFEATURE_DEBUG (debug build, slow code):\tYes\n");
+#else
+ printf("\tFEATURE_DEBUG (debug build, slow code):\tNo\n");
+#endif
+#ifdef HAVE_ATOMIC_BUILTINS
+ printf("\t32bit Atomic operations supported:\tYes\n");
+#else
+ printf("\t32bit Atomic operations supported:\tNo\n");
+#endif
+#ifdef HAVE_ATOMIC_BUILTINS_64BIT
+ printf("\t64bit Atomic operations supported:\tYes\n");
+#else
+ printf("\t64bit Atomic operations supported:\tNo\n");
+#endif
+#ifdef RTINST
+ printf("\tRuntime Instrumentation (slow code):\tYes\n");
+#else
+ printf("\tRuntime Instrumentation (slow code):\tNo\n");
+#endif
+#ifdef USE_LIBUUID
+ printf("\tuuid support:\t\t\t\tYes\n");
+#else
+ printf("\tuuid support:\t\t\t\tNo\n");
+#endif
+ printf("\nSee http://www.rsyslog.com for more information.\n");
+}
+
+
+/* Method to initialize all global classes and use the objects that we need.
+ * rgerhards, 2008-01-04
+ * rgerhards, 2008-04-16: the actual initialization is now carried out by the runtime
+ */
+static rsRetVal
+InitGlobalClasses(void)
+{
+ DEFiRet;
+ char *pErrObj; /* tells us which object failed if that happens (useful for troubleshooting!) */
+
+ /* Intialize the runtime system */
+ pErrObj = "rsyslog runtime"; /* set in case the runtime errors before setting an object */
+ CHKiRet(rsrtInit(&pErrObj, &obj));
+ CHKiRet(rsrtSetErrLogger(submitErrMsg)); /* set out error handler */
+
+ /* Now tell the system which classes we need ourselfs */
+ pErrObj = "glbl";
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ pErrObj = "errmsg";
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ pErrObj = "module";
+ CHKiRet(objUse(module, CORE_COMPONENT));
+ pErrObj = "datetime";
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
+ pErrObj = "ruleset";
+ CHKiRet(objUse(ruleset, CORE_COMPONENT));
+ pErrObj = "conf";
+ CHKiRet(objUse(conf, CORE_COMPONENT));
+ pErrObj = "prop";
+ CHKiRet(objUse(prop, CORE_COMPONENT));
+ pErrObj = "parser";
+ CHKiRet(objUse(parser, CORE_COMPONENT));
+ pErrObj = "rsconf";
+ CHKiRet(objUse(rsconf, CORE_COMPONENT));
+
+ /* intialize some dummy classes that are not part of the runtime */
+ pErrObj = "action";
+ CHKiRet(actionClassInit());
+ pErrObj = "template";
+ CHKiRet(templateInit());
+
+ /* TODO: the dependency on net shall go away! -- rgerhards, 2008-03-07 */
+ pErrObj = "net";
+ CHKiRet(objUse(net, LM_NET_FILENAME));
+ dnscacheInit();
+ initRainerscript();
+ ratelimitModInit();
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ /* we know we are inside the init sequence, so we can safely emit
+ * messages to stderr. -- rgerhards, 2008-04-02
+ */
+ fprintf(stderr, "Error during class init for object '%s' - failing...\n", pErrObj);
+ }
+
+ RETiRet;
+}
+
+
+/* Method to exit all global classes. We do not do any error checking here,
+ * because that wouldn't help us at all. So better try to deinit blindly
+ * as much as succeeds (which usually means everything will). We just must
+ * be careful to do the de-init in the opposite order of the init, because
+ * of the dependencies. However, its not as important this time, because
+ * we have reference counting.
+ * rgerhards, 2008-03-10
+ */
+static rsRetVal
+GlobalClassExit(void)
+{
+ DEFiRet;
+
+ /* first, release everything we used ourself */
+ objRelease(net, LM_NET_FILENAME);/* TODO: the dependency on net shall go away! -- rgerhards, 2008-03-07 */
+ objRelease(prop, CORE_COMPONENT);
+ objRelease(conf, CORE_COMPONENT);
+ objRelease(ruleset, CORE_COMPONENT);
+ parserClassExit(); /* this is hack, currently core_modules do not get this automatically called */
+ rsconfClassExit(); /* this is hack, currently core_modules do not get this automatically called */
+ objRelease(datetime, CORE_COMPONENT);
+
+ /* TODO: implement the rest of the deinit */
+ /* dummy "classes */
+ strExit();
+ ratelimitModExit();
+
+#if 0
+ CHKiRet(objGetObjInterface(&obj)); /* this provides the root pointer for all other queries */
+ /* the following classes were intialized by objClassInit() */
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(module, CORE_COMPONENT));
+#endif
+ dnscacheDeinit();
+ rsrtExit(); /* *THIS* *MUST/SHOULD?* always be the first class initilizer being called (except debug)! */
+
+ RETiRet;
+}
+
+
+/* query our host and domain names - we need to do this early as we may emit
+ * rgerhards, 2012-04-11
+ */
+static rsRetVal
+queryLocalHostname(void)
+{
+ uchar *LocalHostName;
+ uchar *LocalDomain;
+ uchar *LocalFQDNName;
+ uchar *p;
+ struct hostent *hent;
+ DEFiRet;
+
+ net.getLocalHostname(&LocalFQDNName);
+ CHKmalloc(LocalHostName = (uchar*) strdup((char*)LocalFQDNName));
+ glbl.SetLocalFQDNName(LocalFQDNName); /* set the FQDN before we modify it */
+ if((p = (uchar*)strchr((char*)LocalHostName, '.'))) {
+ *p++ = '\0';
+ LocalDomain = p;
+ } else {
+ LocalDomain = (uchar*)"";
+
+ /* It's not clearly defined whether gethostname()
+ * should return the simple hostname or the fqdn. A
+ * good piece of software should be aware of both and
+ * we want to distribute good software. Joey
+ *
+ * Good software also always checks its return values...
+ * If syslogd starts up before DNS is up & /etc/hosts
+ * doesn't have LocalHostName listed, gethostbyname will
+ * return NULL.
+ */
+ /* TODO: gethostbyname() is not thread-safe, but replacing it is
+ * not urgent as we do not run on multiple threads here. rgerhards, 2007-09-25
+ */
+ hent = gethostbyname((char*)LocalHostName);
+ if(hent) {
+ int i = 0;
+
+ if(hent->h_aliases) {
+ size_t hnlen;
+
+ hnlen = strlen((char *) LocalHostName);
+
+ for (i = 0; hent->h_aliases[i]; i++) {
+ if (!strncmp(hent->h_aliases[i], (char *) LocalHostName, hnlen)
+ && hent->h_aliases[i][hnlen] == '.') {
+ /* found a matching hostname */
+ break;
+ }
+ }
+ }
+
+ free(LocalHostName);
+ if(hent->h_aliases && hent->h_aliases[i]) {
+ CHKmalloc(LocalHostName = (uchar*)strdup(hent->h_aliases[i]));
+ } else {
+ CHKmalloc(LocalHostName = (uchar*)strdup(hent->h_name));
+ }
+
+ if((p = (uchar*)strchr((char*)LocalHostName, '.')))
+ {
+ *p++ = '\0';
+ LocalDomain = p;
+ }
+ }
+ }
+
+ /* LocalDomain is "" or part of LocalHostName, allocate a new string */
+ CHKmalloc(LocalDomain = (uchar*)strdup((char*)LocalDomain));
+
+ /* Convert to lower case to recognize the correct domain laterly */
+ for(p = LocalDomain ; *p ; p++)
+ *p = (char)tolower((int)*p);
+
+ /* we now have our hostname and can set it inside the global vars.
+ * TODO: think if all of this would better be a runtime function
+ * rgerhards, 2008-04-17
+ */
+ glbl.SetLocalHostName(LocalHostName);
+ glbl.SetLocalDomain(LocalDomain);
+
+ if ( strlen((char*)LocalDomain) ) {
+ CHKmalloc(LocalFQDNName = (uchar*)malloc(strlen((char*)LocalDomain)+strlen((char*)LocalHostName)+2));/* one for dot, one for NUL! */
+ if ( sprintf((char*)LocalFQDNName,"%s.%s",(char*)LocalHostName,(char*)LocalDomain) )
+ glbl.SetLocalFQDNName(LocalFQDNName);
+ }
+
+ glbl.GenerateLocalHostNameProperty(); /* must be redone after conf processing, FQDN setting may have changed */
+finalize_it:
+ RETiRet;
+}
+
+
+/* some support for command line option parsing. Any non-trivial options must be
+ * buffered until the complete command line has been parsed. This is necessary to
+ * prevent dependencies between the options. That, in turn, means we need to have
+ * something that is capable of buffering options and there values. The follwing
+ * functions handle that.
+ * rgerhards, 2008-04-04
+ */
+typedef struct bufOpt {
+ struct bufOpt *pNext;
+ char optchar;
+ char *arg;
+} bufOpt_t;
+static bufOpt_t *bufOptRoot = NULL;
+static bufOpt_t *bufOptLast = NULL;
+
+/* add option buffer */
+static rsRetVal
+bufOptAdd(char opt, char *arg)
+{
+ DEFiRet;
+ bufOpt_t *pBuf;
+
+ if((pBuf = MALLOC(sizeof(bufOpt_t))) == NULL)
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+
+ pBuf->optchar = opt;
+ pBuf->arg = arg;
+ pBuf->pNext = NULL;
+
+ if(bufOptLast == NULL) {
+ bufOptRoot = pBuf; /* then there is also no root! */
+ } else {
+ bufOptLast->pNext = pBuf;
+ }
+ bufOptLast = pBuf;
+
+finalize_it:
+ RETiRet;
+}
+
+
+
+/* remove option buffer from top of list, return values and destruct buffer itself.
+ * returns RS_RET_END_OF_LINKEDLIST when no more options are present.
+ * (we use int *opt instead of char *opt to keep consistent with getopt())
+ */
+static rsRetVal
+bufOptRemove(int *opt, char **arg)
+{
+ DEFiRet;
+ bufOpt_t *pBuf;
+
+ if(bufOptRoot == NULL)
+ ABORT_FINALIZE(RS_RET_END_OF_LINKEDLIST);
+ pBuf = bufOptRoot;
+
+ *opt = pBuf->optchar;
+ *arg = pBuf->arg;
+
+ bufOptRoot = pBuf->pNext;
+ free(pBuf);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* global initialization, to be done only once and before the mainloop is started.
+ * rgerhards, 2008-07-28 (extracted from realMain())
+ */
+static rsRetVal
+doGlblProcessInit(void)
+{
+ struct sigaction sigAct;
+ int num_fds;
+ int i;
+ DEFiRet;
+
+ thrdInit();
+
+ if(doFork) {
+ DBGPRINTF("Checking pidfile '%s'.\n", PidFile);
+ if (!check_pid(PidFile))
+ {
+ memset(&sigAct, 0, sizeof (sigAct));
+ sigemptyset(&sigAct.sa_mask);
+ sigAct.sa_handler = doexit;
+ sigaction(SIGTERM, &sigAct, NULL);
+
+ /* stop writing debug messages to stdout (if debugging is on) */
+ stddbg = -1;
+
+ dbgprintf("ready for forking\n");
+ if (fork()) {
+ /* Parent process
+ */
+ dbgprintf("parent process going to sleep for 60 secs\n");
+ sleep(60);
+ /* Not reached unless something major went wrong. 1
+ * minute should be a fair amount of time to wait.
+ * The parent should not exit before rsyslogd is
+ * properly initilized (at least almost) or the init
+ * system may get a wrong impression of our readyness.
+ * Note that we exit before being completely initialized,
+ * but at this point it is very, very unlikely that something
+ * bad can happen. We do this here, because otherwise we would
+ * need to have much more code to handle priv drop (which we
+ * don't consider worth for the init system, especially as it
+ * is going away on the majority of distros).
+ */
+ exit(1); /* "good" exit - after forking, not diasabling anything */
+ }
+
+ num_fds = getdtablesize();
+ close(0);
+ /* we keep stdout and stderr open in case we have to emit something */
+ i = 3;
+ dbgprintf("in child, finalizing initialization\n");
+
+ /* if (sd_booted()) */ {
+ const char *e;
+ char buf[24] = { '\0' };
+ char *p = NULL;
+ unsigned long l;
+ int sd_fds;
+
+ /* fork & systemd socket activation:
+ * fetch listen pid and update to ours,
+ * when it is set to pid of our parent.
+ */
+ if ( (e = getenv("LISTEN_PID"))) {
+ errno = 0;
+ l = strtoul(e, &p, 10);
+ if (errno == 0 && l > 0 && (!p || !*p)) {
+ if (getppid() == (pid_t)l) {
+ snprintf(buf, sizeof(buf), "%d",
+ getpid());
+ setenv("LISTEN_PID", buf, 1);
+ }
+ }
+ }
+
+ /*
+ * close only all further fds, except
+ * of the fds provided by systemd.
+ */
+ sd_fds = sd_listen_fds(0);
+ if (sd_fds > 0)
+ i = SD_LISTEN_FDS_START + sd_fds;
+ }
+ for ( ; i < num_fds; i++)
+ if(i != dbgGetDbglogFd())
+ close(i);
+
+ untty();
+ } else {
+ fputs(" Already running. If you want to run multiple instances, you need "
+ "to specify different pid files (use -i option)\n", stderr);
+ exit(1); /* "good" exit, done if syslogd is already running */
+ }
+ }
+
+ /* tuck my process id away */
+ DBGPRINTF("Writing pidfile '%s'.\n", PidFile);
+ if (!check_pid(PidFile))
+ {
+ if (!write_pid(PidFile))
+ {
+ fputs("Can't write pid.\n", stderr);
+ exit(1); /* exit during startup - questionable */
+ }
+ }
+ else
+ {
+ fputs("Pidfile (and pid) already exist.\n", stderr);
+ exit(1); /* exit during startup - questionable */
+ }
+ glblSetOurPid(getpid());
+
+ memset(&sigAct, 0, sizeof (sigAct));
+ sigemptyset(&sigAct.sa_mask);
+
+ sigAct.sa_handler = sigsegvHdlr;
+ sigaction(SIGSEGV, &sigAct, NULL);
+ sigAct.sa_handler = sigsegvHdlr;
+ sigaction(SIGABRT, &sigAct, NULL);
+ sigAct.sa_handler = doDie;
+ sigaction(SIGTERM, &sigAct, NULL);
+ sigAct.sa_handler = Debug ? doDie : SIG_IGN;
+ sigaction(SIGINT, &sigAct, NULL);
+ sigaction(SIGQUIT, &sigAct, NULL);
+ sigAct.sa_handler = reapchild;
+ sigaction(SIGCHLD, &sigAct, NULL);
+ sigAct.sa_handler = Debug ? debug_switch : SIG_IGN;
+ sigaction(SIGUSR1, &sigAct, NULL);
+ sigAct.sa_handler = sigttin_handler;
+ sigaction(SIGTTIN, &sigAct, NULL); /* (ab)used to interrupt input threads */
+ sigAct.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &sigAct, NULL);
+ sigaction(SIGXFSZ, &sigAct, NULL); /* do not abort if 2gig file limit is hit */
+
+ RETiRet;
+}
+
+
+/* This is the main entry point into rsyslogd. Over time, we should try to
+ * modularize it a bit more...
+ */
+int realMain(int argc, char **argv)
+{
+ rsRetVal localRet;
+ int ch;
+ extern int optind;
+ extern char *optarg;
+ int bEOptionWasGiven = 0;
+ int iHelperUOpt;
+ int bChDirRoot = 1; /* change the current working directory to "/"? */
+ char *arg; /* for command line option processing */
+ char cwdbuf[128]; /* buffer to obtain/display current working directory */
+ DEFiRet;
+
+ /* first, parse the command line options. We do not carry out any actual work, just
+ * see what we should do. This relieves us from certain anomalies and we can process
+ * the parameters down below in the correct order. For example, we must know the
+ * value of -M before we can do the init, but at the same time we need to have
+ * the base classes init before we can process most of the options. Now, with the
+ * split of functionality, this is no longer a problem. Thanks to varmofekoj for
+ * suggesting this algo.
+ * Note: where we just need to set some flags and can do so without knowledge
+ * of other options, we do this during the inital option processing.
+ * rgerhards, 2008-04-04
+ */
+ while((ch = getopt(argc, argv, "46a:Ac:dDef:g:hi:l:m:M:nN:op:qQr::s:S:t:T:u:vwx")) != EOF) {
+ switch((char)ch) {
+ case '4':
+ case '6':
+ case 'A':
+ case 'a':
+ case 'f': /* configuration file */
+ case 'h':
+ case 'i': /* pid file name */
+ case 'l':
+ case 'm': /* mark interval */
+ case 'n': /* don't fork */
+ case 'N': /* enable config verify mode */
+ case 'o':
+ case 'p':
+ case 'q': /* add hostname if DNS resolving has failed */
+ case 'Q': /* dont resolve hostnames in ACL to IPs */
+ case 's':
+ case 'S': /* Source IP for local client to be used on multihomed host */
+ case 'T': /* chroot on startup (primarily for testing) */
+ case 'u': /* misc user settings */
+ case 'w': /* disable disallowed host warnings */
+ case 'x': /* disable dns for remote messages */
+ case 'g': /* enable tcp gssapi logging */
+ case 'r': /* accept remote messages */
+ case 't': /* enable tcp logging */
+ CHKiRet(bufOptAdd(ch, optarg));
+ break;
+ case 'c': /* compatibility mode */
+ fprintf(stderr, "rsyslogd: error: option -c is no longer supported - ignored\n");
+ break;
+ case 'd': /* debug - must be handled now, so that debug is active during init! */
+ debugging_on = 1;
+ Debug = 1;
+ yydebug = 1;
+ break;
+ case 'D': /* BISON debug */
+ yydebug = 1;
+ break;
+ case 'e': /* log every message (no repeat message supression) */
+ bEOptionWasGiven = 1;
+ break;
+ case 'M': /* default module load path -- this MUST be carried out immediately! */
+ glblModPath = (uchar*) optarg;
+ break;
+ case 'v': /* MUST be carried out immediately! */
+ printVersion();
+ exit(0); /* exit for -v option - so this is a "good one" */
+ case '?':
+ default:
+ usage();
+ }
+ }
+
+ if(argc - optind)
+ usage();
+
+ DBGPRINTF("rsyslogd %s startup, module path '%s', cwd:%s\n",
+ VERSION, glblModPath == NULL ? "" : (char*)glblModPath,
+ getcwd(cwdbuf, sizeof(cwdbuf)));
+
+ /* we are done with the initial option parsing and processing. Now we init the system. */
+
+ ppid = getpid();
+
+ CHKiRet_Hdlr(InitGlobalClasses()) {
+ fprintf(stderr, "rsyslogd initializiation failed - global classes could not be initialized.\n"
+ "Did you do a \"make install\"?\n"
+ "Suggested action: run rsyslogd with -d -n options to see what exactly "
+ "fails.\n");
+ FINALIZE;
+ }
+
+ /* doing some core initializations */
+
+ /* we need to create the inputName property (only once during our lifetime) */
+ CHKiRet(prop.Construct(&pInternalInputName));
+ CHKiRet(prop.SetString(pInternalInputName, UCHAR_CONSTANT("rsyslogd"), sizeof("rsyslogd") - 1));
+ CHKiRet(prop.ConstructFinalize(pInternalInputName));
+
+ /* get our host and domain names - we need to do this early as we may emit
+ * error log messages, which need the correct hostname. -- rgerhards, 2008-04-04
+ */
+ queryLocalHostname();
+
+ /* initialize the objects */
+ if((iRet = modInitIminternal()) != RS_RET_OK) {
+ fprintf(stderr, "fatal error: could not initialize errbuf object (error code %d).\n",
+ iRet);
+ exit(1); /* "good" exit, leaving at init for fatal error */
+ }
+
+
+ /* END core initializations - we now come back to carrying out command line options*/
+
+ while((iRet = bufOptRemove(&ch, &arg)) == RS_RET_OK) {
+ DBGPRINTF("deque option %c, optarg '%s'\n", ch, (arg == NULL) ? "" : arg);
+ switch((char)ch) {
+ case '4':
+ glbl.SetDefPFFamily(PF_INET);
+ break;
+ case '6':
+ glbl.SetDefPFFamily(PF_INET6);
+ break;
+ case 'A':
+ send_to_all++;
+ break;
+ case 'a':
+ fprintf(stderr, "rsyslogd: error -a is no longer supported, use module imuxsock instead");
+ break;
+ case 'S': /* Source IP for local client to be used on multihomed host */
+ if(glbl.GetSourceIPofLocalClient() != NULL) {
+ fprintf (stderr, "rsyslogd: Only one -S argument allowed, the first one is taken.\n");
+ } else {
+ glbl.SetSourceIPofLocalClient((uchar*)arg);
+ }
+ break;
+ case 'f': /* configuration file */
+ ConfFile = (uchar*) arg;
+ break;
+ case 'g': /* enable tcp gssapi logging */
+ fprintf(stderr, "rsyslogd: -g option no longer supported - ignored\n");
+ case 'h':
+ fprintf(stderr, "rsyslogd: error -h is no longer supported - ignored");
+ break;
+ case 'i': /* pid file name */
+ PidFile = arg;
+ break;
+ case 'l':
+ if(glbl.GetLocalHosts() != NULL) {
+ fprintf (stderr, "rsyslogd: Only one -l argument allowed, the first one is taken.\n");
+ } else {
+ glbl.SetLocalHosts(crunch_list(arg));
+ }
+ break;
+ case 'm': /* mark interval */
+ fprintf(stderr, "rsyslogd: error -m is no longer supported - use immark instead");
+ break;
+ case 'n': /* don't fork */
+ doFork = 0;
+ break;
+ case 'N': /* enable config verify mode */
+ iConfigVerify = atoi(arg);
+ break;
+ case 'o':
+ fprintf(stderr, "error -o is no longer supported, use module imuxsock instead");
+ break;
+ case 'p':
+ fprintf(stderr, "error -p is no longer supported, use module imuxsock instead");
+ break;
+ case 'q': /* add hostname if DNS resolving has failed */
+ *(net.pACLAddHostnameOnFail) = 1;
+ break;
+ case 'Q': /* dont resolve hostnames in ACL to IPs */
+ *(net.pACLDontResolve) = 1;
+ break;
+ case 'r': /* accept remote messages */
+ fprintf(stderr, "rsyslogd: error option -r is no longer supported - ignored");
+ break;
+ case 's':
+ if(glbl.GetStripDomains() != NULL) {
+ fprintf (stderr, "rsyslogd: Only one -s argument allowed, the first one is taken.\n");
+ } else {
+ glbl.SetStripDomains(crunch_list(arg));
+ }
+ break;
+ case 't': /* enable tcp logging */
+ fprintf(stderr, "rsyslogd: error option -t is no longer supported - ignored");
+ break;
+ case 'T':/* chroot() immediately at program startup, but only for testing, NOT security yet */
+ if(chroot(arg) != 0) {
+ perror("chroot");
+ exit(1);
+ }
+ break;
+ case 'u': /* misc user settings */
+ iHelperUOpt = atoi(arg);
+ if(iHelperUOpt & 0x01)
+ glbl.SetParseHOSTNAMEandTAG(0);
+ if(iHelperUOpt & 0x02)
+ bChDirRoot = 0;
+ break;
+ case 'w': /* disable disallowed host warnigs */
+ glbl.SetOption_DisallowWarning(0);
+ break;
+ case 'x': /* disable dns for remote messages */
+ glbl.SetDisableDNS(1);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+
+ if(iRet != RS_RET_END_OF_LINKEDLIST)
+ FINALIZE;
+
+ if(iConfigVerify) {
+ fprintf(stderr, "rsyslogd: version %s, config validation run (level %d), master config %s\n",
+ VERSION, iConfigVerify, ConfFile);
+ }
+
+ localRet = rsconf.Load(&ourConf, ConfFile);
+ queryLocalHostname(); /* need to re-query to pick up a changed hostname due to config */
+
+ if(localRet == RS_RET_NONFATAL_CONFIG_ERR) {
+ if(loadConf->globals.bAbortOnUncleanConfig) {
+ fprintf(stderr, "rsyslogd: $AbortOnUncleanConfig is set, and config is not clean.\n"
+ "Check error log for details, fix errors and restart. As a last\n"
+ "resort, you may want to remove $AbortOnUncleanConfig to permit a\n"
+ "startup with a dirty config.\n");
+ exit(2);
+ }
+ if(iConfigVerify) {
+ /* a bit dirty, but useful... */
+ exit(1);
+ }
+ localRet = RS_RET_OK;
+ }
+ CHKiRet(localRet);
+
+ CHKiRet(ratelimitNew(&dflt_ratelimiter, "rsyslogd", "dflt"));
+ /* TODO: add linux-type limiting capability */
+ CHKiRet(ratelimitNew(&internalMsg_ratelimiter, "rsyslogd", "internal_messages"));
+ ratelimitSetLinuxLike(internalMsg_ratelimiter, 5, 500);
+ /* TODO: make internalMsg ratelimit settings configurable */
+
+ if(bChDirRoot) {
+ if(chdir("/") != 0)
+ fprintf(stderr, "Can not do 'cd /' - still trying to run\n");
+ }
+
+ /* process compatibility mode settings */
+ if(bEOptionWasGiven) {
+ errmsg.LogError(0, NO_ERRCODE, "WARNING: \"message repeated n times\" feature MUST be turned on in "
+ "rsyslog.conf - CURRENTLY EVERY MESSAGE WILL BE LOGGED. Visit "
+ "http://www.rsyslog.com/rptdmsgreduction to learn "
+ "more and cast your vote if you want us to keep this feature.");
+ }
+
+ if(!iConfigVerify)
+ CHKiRet(doGlblProcessInit());
+
+ /* Send a signal to the parent so it can terminate. */
+ if(glblGetOurPid() != ppid)
+ kill(ppid, SIGTERM);
+
+ CHKiRet(init());
+
+ if(Debug && debugging_on) {
+ dbgprintf("Debugging enabled, SIGUSR1 to turn off debugging.\n");
+ }
+
+ /* END OF INTIALIZATION */
+ DBGPRINTF("initialization completed, transitioning to regular run mode\n");
+
+ /* close stderr and stdout if they are kept open during a fork. Note that this
+ * may introduce subtle security issues: if we are in a jail, one may break out of
+ * it via these descriptors. But if I close them earlier, error messages will (once
+ * again) not be emitted to the user that starts the daemon. As root jail support
+ * is still in its infancy (and not really done), we currently accept this issue.
+ * rgerhards, 2009-06-29
+ */
+ if(doFork) {
+ close(1);
+ close(2);
+ ourConf->globals.bErrMsgToStderr = 0;
+ }
+
+ sd_notify(0, "READY=1");
+
+ mainloop();
+
+ /* do any de-init's that need to be done AFTER this comment */
+
+ die(bFinished);
+
+ thrdExit();
+
+finalize_it:
+ if(iRet == RS_RET_VALIDATION_RUN) {
+ fprintf(stderr, "rsyslogd: End of config validation run. Bye.\n");
+ } else if(iRet != RS_RET_OK) {
+ fprintf(stderr, "rsyslogd: run failed with error %d (see rsyslog.h "
+ "or try http://www.rsyslog.com/e/%d to learn what that number means)\n", iRet, iRet*-1);
+ exit(1);
+ }
+
+ ENDfunc
+ return 0;
+}
+
+
+/* This is the main entry point into rsyslogd. This must be a function in its own
+ * right in order to intialize the debug system in a portable way (otherwise we would
+ * need to have a statement before variable definitions.
+ * rgerhards, 20080-01-28
+ */
+int main(int argc, char **argv)
+{
+ dbgClassInit();
+ return realMain(argc, argv);
+}
+/* vim:set ai:
+ */
diff --git a/tools/syslogd.h b/tools/syslogd.h
new file mode 100644
index 00000000..88bbd5f3
--- /dev/null
+++ b/tools/syslogd.h
@@ -0,0 +1,38 @@
+/* common header for syslogd
+ * Copyright 2007-2012 Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef SYSLOGD_H_INCLUDED
+#define SYSLOGD_H_INCLUDED 1
+
+#include "syslogd-types.h"
+#include "objomsr.h"
+#include "modules.h"
+#include "template.h"
+#include "action.h"
+#include "linkedlist.h"
+
+/* the following prototypes should go away once we have an input
+ * module interface -- rgerhards, 2007-12-12
+ */
+extern int NoHops;
+extern int send_to_all;
+extern int Debug;
+#include "dirty.h"
+
+#endif /* #ifndef SYSLOGD_H_INCLUDED */
diff --git a/tools/zpipe.c b/tools/zpipe.c
new file mode 100644
index 00000000..38069425
--- /dev/null
+++ b/tools/zpipe.c
@@ -0,0 +1,262 @@
+/* zpipe.c: example of proper use of zlib's inflate() and deflate()
+ Not copyrighted -- provided to the public domain
+ Version 1.5 11 December 2005 Mark Adler
+ Version 2.0 03 June 2009 Rainer Gerhards */
+
+/* RSYSLOG NOTE:
+ * This file is primarily been used as a testing aid for rsyslog. We do NOT
+ * properly maintain it and it has been brought to our attention that it may
+ * have some security issues. However, we prefer not to remove the file as it
+ * may turn out to be useful for further testing. All users are advised NOT
+ * to base any development on this version here, but rather look for the
+ * original zpipe.c by the authors mentioned above.
+ *
+ * This file is beeing distributed as part of rsyslog, but is just an
+ * add-on. Most importantly, rsyslog's copyright does not apply but
+ * rather the (non-) copyright stated above.
+ */
+
+/* Version history:
+ 1.0 30 Oct 2004 First version
+ 1.1 8 Nov 2004 Add void casting for unused return values
+ Use switch statement for inflate() return values
+ 1.2 9 Nov 2004 Add assertions to document zlib guarantees
+ 1.3 6 Apr 2005 Remove incorrect assertion in inf()
+ 1.4 11 Dec 2005 Add hack to avoid MSDOS end-of-line conversions
+ Avoid some compiler warnings for input and output buffers
+ 2.0 03 Jun 2009 Add hack to support multiple deflate records inside a single
+ file on inflate. This is needed in order to support reading
+ files created by rsyslog's zip output writer.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "zlib.h"
+
+#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
+# include <fcntl.h>
+# include <io.h>
+# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
+#else
+# define SET_BINARY_MODE(file)
+#endif
+
+#define CHUNK 16384
+
+/* Compress from file source to file dest until EOF on source.
+ def() returns Z_OK on success, Z_MEM_ERROR if memory could not be
+ allocated for processing, Z_STREAM_ERROR if an invalid compression
+ level is supplied, Z_VERSION_ERROR if the version of zlib.h and the
+ version of the library linked do not match, or Z_ERRNO if there is
+ an error reading or writing the files. */
+int def(FILE *source, FILE *dest, int level)
+{
+ int ret, flush;
+ unsigned have;
+ z_stream strm;
+ unsigned char in[CHUNK];
+ unsigned char out[CHUNK];
+
+ /* allocate deflate state */
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ ret = deflateInit(&strm, level);
+ if (ret != Z_OK)
+ return ret;
+
+ /* compress until end of file */
+ do {
+ strm.avail_in = fread(in, 1, CHUNK, source);
+ if (ferror(source)) {
+ (void)deflateEnd(&strm);
+ return Z_ERRNO;
+ }
+ flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
+ strm.next_in = in;
+
+ /* run deflate() on input until output buffer not full, finish
+ compression if all of source has been read in */
+ do {
+ strm.avail_out = CHUNK;
+ strm.next_out = out;
+ ret = deflate(&strm, flush); /* no bad return value */
+ assert(ret != Z_STREAM_ERROR); /* state not clobbered */
+ have = CHUNK - strm.avail_out;
+ if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
+ (void)deflateEnd(&strm);
+ return Z_ERRNO;
+ }
+ } while (strm.avail_out == 0);
+ assert(strm.avail_in == 0); /* all input will be used */
+
+ /* done when last data in file processed */
+ } while (flush != Z_FINISH);
+ assert(ret == Z_STREAM_END); /* stream will be complete */
+
+ /* clean up and return */
+ (void)deflateEnd(&strm);
+ return Z_OK;
+}
+
+
+/* initialize stream for deflating (we need this in case of
+ * multiple records.
+ * rgerhards, 2009-06-03
+ */
+int doInflateInit(z_stream *strm)
+{
+ int ret;
+
+ /* allocate inflate state */
+ strm->zalloc = Z_NULL;
+ strm->zfree = Z_NULL;
+ strm->opaque = Z_NULL;
+ strm->avail_in = 0;
+ strm->next_in = Z_NULL;
+ ret = inflateInit(strm);
+ return ret;
+}
+
+
+/* Decompress from file source to file dest until stream ends or EOF.
+ inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be
+ allocated for processing, Z_DATA_ERROR if the deflate data is
+ invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and
+ the version of the library linked do not match, or Z_ERRNO if there
+ is an error reading or writing the files. */
+int inf(FILE *source, FILE *dest)
+{
+ int ret;
+ unsigned have;
+ z_stream strm;
+ unsigned char in[CHUNK];
+ int len;
+ unsigned char *next_in_save;
+ unsigned char out[CHUNK];
+
+ ret = doInflateInit(&strm);
+ if (ret != Z_OK)
+ return ret;
+
+ /* decompress until deflate stream ends or end of file */
+ do {
+ len = fread(in, 1, CHUNK, source);
+ if (ferror(source)) {
+ (void)inflateEnd(&strm);
+ return Z_ERRNO;
+ }
+ if (len == 0) {
+ break;
+ }
+ strm.avail_in = len;
+ strm.next_in = in;
+
+ /* run inflate() on input until output buffer not full */
+ strm.avail_out = CHUNK;
+ strm.next_out = out;
+ do {
+ /* fprintf(stderr, "---inner LOOP---, avail_in %d, avail_out %d Byte 0: %x, 1: %x\n", strm.avail_in, strm.avail_out, *strm.next_in, *(strm.next_in+1));*/
+ do {
+ ret = inflate(&strm, Z_NO_FLUSH);
+ assert(ret != Z_STREAM_ERROR); /* state not clobbered */
+ switch (ret) {
+ case Z_NEED_DICT:
+ ret = Z_DATA_ERROR; /* and fall through */
+ case Z_DATA_ERROR:
+ case Z_MEM_ERROR:
+ (void)inflateEnd(&strm);
+ return ret;
+ }
+ have = CHUNK - strm.avail_out;
+ if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
+ (void)inflateEnd(&strm);
+ return Z_ERRNO;
+ }
+ } while (strm.avail_out == 0);
+ /* handle the case that more than one deflate record is contained
+ * in a single file. -- rgerhards, 2009-06-03
+ */
+ if(ret == Z_STREAM_END) {
+ len -= strm.total_in;
+ if(len > 0) {
+ next_in_save = strm.next_in;
+ (void)inflateEnd(&strm);
+ ret = doInflateInit(&strm);
+ if (ret != Z_OK)
+ return ret;
+ strm.avail_in = len;
+ strm.next_in = next_in_save;
+ strm.avail_out = CHUNK;
+ strm.next_out = out;
+ ret = Z_OK; /* continue outer loop */
+ }
+ }
+ } while (strm.avail_in > 0);
+
+ /* done when inflate() says it's done */
+ } while (ret != Z_STREAM_END);
+
+ /* clean up and return */
+ (void)inflateEnd(&strm);
+ return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
+}
+
+/* report a zlib or i/o error */
+void zerr(int ret)
+{
+ fputs("zpipe: ", stdout);
+ switch (ret) {
+ case Z_ERRNO:
+ if (ferror(stdin))
+ fputs("error reading stdin\n", stdout);
+ if (ferror(stdout))
+ fputs("error writing stdout\n", stdout);
+ break;
+ case Z_STREAM_ERROR:
+ fputs("invalid compression level\n", stdout);
+ break;
+ case Z_DATA_ERROR:
+ fputs("invalid or incomplete deflate data\n", stdout);
+ break;
+ case Z_MEM_ERROR:
+ fputs("out of memory\n", stdout);
+ break;
+ case Z_VERSION_ERROR:
+ fputs("zlib version mismatch!\n", stdout);
+ }
+}
+
+/* compress or decompress from stdin to stdout */
+int main(int argc, char **argv)
+{
+ int ret;
+
+ /* avoid end-of-line conversions */
+ SET_BINARY_MODE(stdin);
+ SET_BINARY_MODE(stdout);
+
+ /* do compression if no arguments */
+ if (argc == 1) {
+ ret = def(stdin, stdout, Z_DEFAULT_COMPRESSION);
+ if (ret != Z_OK)
+ zerr(ret);
+ return ret;
+ }
+
+ /* do decompression if -d specified */
+ else if (argc == 2 && strcmp(argv[1], "-d") == 0) {
+ ret = inf(stdin, stdout);
+ if (ret != Z_OK)
+ zerr(ret);
+ return ret;
+ }
+
+ /* otherwise, report usage */
+ else {
+ fputs("zpipe usage: zpipe [-d] < source > dest\n", stdout);
+ return 1;
+ }
+}