summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile.am38
-rw-r--r--tools/logctl.c4
-rw-r--r--tools/logsigner.c159
-rw-r--r--tools/omdiscard.c8
-rw-r--r--tools/omfile.c301
-rw-r--r--tools/omfwd.c5
-rw-r--r--tools/pidfile.c6
-rw-r--r--tools/pmrfc3164.c2
-rwxr-xr-x[-rw-r--r--]tools/recover_qi.pl0
-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.519
-rw-r--r--tools/rsyslogd.88
-rw-r--r--tools/syslogd.c404
16 files changed, 1989 insertions, 284 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am
index e634076c..6832494e 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -1,5 +1,7 @@
sbin_PROGRAMS =
bin_PROGRAMS =
+CLEANFILES =
+man1_MANS =
man_MANS = rsyslogd.8 rsyslog.conf.5
sbin_PROGRAMS += rsyslogd
@@ -36,13 +38,20 @@ rsyslogd_SOURCES = \
pidfile.h \
\
../dirty.h
-rsyslogd_CPPFLAGS = $(PTHREADS_CFLAGS) $(RSRT_CFLAGS) $(CNF_LIBS)
+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) $(LIBEE_LIBS) $(LIBLOGNORM_LIBS) $(LIBUUID_LIBS)
+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
@@ -58,7 +67,26 @@ 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
-
-EXTRA_DIST = $(man_MANS) \
- recover_qi.pl
diff --git a/tools/logctl.c b/tools/logctl.c
index df332bc2..1ab8ead0 100644
--- a/tools/logctl.c
+++ b/tools/logctl.c
@@ -143,7 +143,6 @@ struct ofields* get_data(struct results *res)
struct ofields *fields;
const char *msg;
const char *prog;
- const char *level;
const char *syslog_tag;
gint64 date_r;
bson_cursor *c;
@@ -263,7 +262,7 @@ struct select_doc* create_select()
struct query_doc* create_query(struct queryopt *opt)
{
struct query_doc *qu_doc;
- bson *query_what, *order_what, *order_how, *msg_what, *date_what;
+ bson *query_what, *order_what, *msg_what, *date_what;
struct tm tm;
time_t t;
gint64 ts;
@@ -417,7 +416,6 @@ int main (int argc, char *argv[])
struct queryopt opt;
struct ofields *fields;
- struct bson_doc *doc;
struct select_doc *s_doc;
struct query_doc *qu_doc;
struct db_connect *db_conn;
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/omdiscard.c b/tools/omdiscard.c
index 182c4b63..15c6ea82 100644
--- a/tools/omdiscard.c
+++ b/tools/omdiscard.c
@@ -35,6 +35,7 @@
#include "syslogd-types.h"
#include "omdiscard.h"
#include "module-template.h"
+#include "errmsg.h"
MODULE_TYPE_OUTPUT
MODULE_TYPE_NOKEEP
@@ -42,6 +43,7 @@ MODULE_TYPE_NOKEEP
/* internal structures
*/
DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg);
typedef struct _instanceData {
EMPTY_STRUCT
@@ -92,8 +94,10 @@ CODE_STD_STRING_REQUESTparseSelectorAct(0)
p = *pp;
if(*p == '~') {
- /* TODO: check the rest of the selector line - error reporting */
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;
}
@@ -103,6 +107,7 @@ ENDparseSelectorAct
BEGINmodExit
CODESTARTmodExit
+ objRelease(errmsg, CORE_COMPONENT);
ENDmodExit
@@ -116,6 +121,7 @@ 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/omfile.c b/tools/omfile.c
index 715b218c..ba9f7f70 100644
--- a/tools/omfile.c
+++ b/tools/omfile.c
@@ -17,7 +17,7 @@
* pipes. These have been moved to ompipe, to reduced the entanglement
* between the two different functionalities. -- rgerhards
*
- * Copyright 2007-2012 Adiscon GmbH.
+ * Copyright 2007-2013 Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -68,6 +68,9 @@
#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
@@ -81,6 +84,7 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
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
@@ -116,6 +120,7 @@ getClockFileAccess(void)
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;
@@ -141,6 +146,18 @@ typedef struct _instanceData {
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 */
+ void *cryprovFileData;/* opaque data ptr for file instance */
+ 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 */
@@ -156,6 +173,13 @@ typedef struct _instanceData {
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;
@@ -205,6 +229,7 @@ static struct cnfparamdescr actpdescr[] = {
{ "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 */
@@ -218,7 +243,9 @@ static struct cnfparamdescr actpdescr[] = {
{ "sync", eCmdHdlrBinary, 0 }, /* legacy: actionfileenablesync */
{ "file", eCmdHdlrString, 0 }, /* either "file" or ... */
{ "dynafile", eCmdHdlrString, 0 }, /* "dynafile" MUST be present */
- { "template", eCmdHdlrGetWord, 0 },
+ { "sig.provider", eCmdHdlrGetWord, 0 },
+ { "cry.provider", eCmdHdlrGetWord, 0 },
+ { "template", eCmdHdlrGetWord, 0 }
};
static struct cnfparamblk actpblk =
{ CNFPARAMBLK_VERSION,
@@ -269,7 +296,8 @@ CODESTARTdbgPrintInstInfo
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 ? "yes" : "no");
+ 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",
@@ -292,7 +320,7 @@ setLegacyDfltTpl(void __attribute__((unused)) *pVal, uchar* newVal)
if(loadModConf != NULL && loadModConf->tplName != NULL) {
free(newVal);
- errmsg.LogError(0, RS_RET_ERR, "omfile default template already set via module "
+ 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);
}
@@ -405,15 +433,16 @@ finalize_it:
* if the entry should be d_free()ed and 0 if not.
*/
static rsRetVal
-dynaFileDelCacheEntry(dynaFileCacheEntry **pCache, int iEntry, int bFreeEntry)
+dynaFileDelCacheEntry(instanceData *pData, int iEntry, int bFreeEntry)
{
+ dynaFileCacheEntry **pCache = pData->dynCache;
DEFiRet;
ASSERT(pCache != NULL);
if(pCache[iEntry] == NULL)
FINALIZE;
- DBGPRINTF("Removed entry %d for file '%s' from dynaCache.\n", iEntry,
+ 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) {
@@ -423,8 +452,10 @@ dynaFileDelCacheEntry(dynaFileCacheEntry **pCache, int iEntry, int bFreeEntry)
if(pCache[iEntry]->pStrm != NULL) {
strm.Destruct(&pCache[iEntry]->pStrm);
- if(pCache[iEntry]->pStrm != NULL) /* safety check -- TODO: remove if no longer necessary */
- abort();
+ if(pData->useSigprov) {
+ pData->sigprov.OnFileClose(pCache[iEntry]->sigprovFileData);
+ pCache[iEntry]->sigprovFileData = NULL;
+ }
}
if(bFreeEntry) {
@@ -449,7 +480,7 @@ dynaFileFreeCacheEntries(instanceData *pData)
BEGINfunc;
for(i = 0 ; i < pData->iCurrCacheSize ; ++i) {
- dynaFileDelCacheEntry(pData->dynCache, i, 1);
+ dynaFileDelCacheEntry(pData, i, 1);
}
pData->iCurrElt = -1; /* invalidate current element */
ENDfunc;
@@ -470,6 +501,29 @@ static void dynaFileFreeCache(instanceData *pData)
}
+/* 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
@@ -536,12 +590,17 @@ prepareFile(instanceData *pData, uchar *newFileName)
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
@@ -551,11 +610,14 @@ prepareFile(instanceData *pData, uchar *newFileName)
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) {
- strm.Destruct(&pData->pStrm);
+ closeFile(pData);
}
}
RETiRet;
@@ -586,14 +648,13 @@ prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts)
pCache = pData->dynCache;
- /* first check, if we still have the current file
- * I *hope* this will be a performance enhancement.
- */
+ /* 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();
- // LRU needs only a strictly monotonically increasing counter, so such a one could do
+ STATSCOUNTER_INC(pData->ctrLevel0, pData->mutCtrLevel0);
+ /* LRU needs only a strictly monotonically increasing counter, so such a one could do */
FINALIZE;
}
@@ -609,9 +670,11 @@ prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts)
if(iFirstFree == -1)
iFirstFree = i;
} else { /* got an element, let's see if it matches */
- if(!ustrcmp(newFileName, pCache[i]->pName)) { // RG: name == NULL?
+ 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;
@@ -625,6 +688,7 @@ prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts)
}
/* 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
@@ -637,11 +701,12 @@ prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts)
* but it could be triggered in the common case of a failed open() system call.
* rgerhards, 2010-03-22
*/
- pData->pStrm = NULL;
+ 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,
@@ -649,13 +714,11 @@ prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts)
* The cache array is only updated after the open was successful. -- rgerhards, 2010-03-21
*/
if(iFirstFree == -1) {
- dynaFileDelCacheEntry(pCache, iOldest, 0);
+ 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 */
- /* TODO: performance note: we could alloc all entries on startup, thus saving malloc
- * overhead -- this may be something to consider in v5...
- */
CHKmalloc(pCache[iFirstFree] = (dynaFileCacheEntry*) calloc(1, sizeof(dynaFileCacheEntry)));
}
@@ -678,10 +741,12 @@ prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts)
}
if((pCache[iFirstFree]->pName = ustrdup(newFileName)) == NULL) {
- strm.Destruct(&pData->pStrm); /* need to free failed entry! */
+ 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);
@@ -706,7 +771,9 @@ doWrite(instanceData *pData, uchar *pszBuf, int lenBuf)
DBGPRINTF("write to stream, pData->pStrm %p, lenBuf %d\n", pData->pStrm, lenBuf);
if(pData->pStrm != NULL){
CHKiRet(strm.Write(pData->pStrm, pszBuf, lenBuf));
- FINALIZE;
+ if(pData->useSigprov) {
+ CHKiRet(pData->sigprov.OnRecordWrite(pData->sigprovFileData, pszBuf, lenBuf));
+ }
}
finalize_it:
@@ -714,10 +781,7 @@ finalize_it:
}
-/* rgerhards 2004-11-11: write to a file output. This
- * will be called for all outputs using file semantics,
- * for example also for pipes.
- */
+/* rgerhards 2004-11-11: write to a file output. */
static rsRetVal
writeFile(uchar **ppString, unsigned iMsgOpts, instanceData *pData)
{
@@ -825,7 +889,21 @@ CODESTARTfreeInstance
if(pData->bDynamicName) {
dynaFileFreeCache(pData);
} else if(pData->pStrm != NULL)
- strm.Destruct(&pData->pStrm);
+ 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
@@ -843,7 +921,12 @@ BEGINendTransaction
CODESTARTendTransaction
/* Note: pStrm may be NULL if there was an error opening the stream */
if(pData->bFlushOnTXEnd && pData->pStrm != NULL) {
- CHKiRet(strm.Flush(pData->pStrm));
+ /* 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
@@ -851,7 +934,10 @@ ENDendTransaction
BEGINdoAction
CODESTARTdoAction
- DBGPRINTF("file to log to: %s\n", pData->f_fname);
+ 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));
@@ -878,14 +964,143 @@ setInstParamDefaults(instanceData *pData)
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));
+
+ 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");
@@ -914,6 +1129,8 @@ CODESTARTnewActInst
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")) {
@@ -948,6 +1165,10 @@ CODESTARTnewActInst
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);
@@ -960,7 +1181,16 @@ CODESTARTnewActInst
ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
}
- CHKiRet(OMSRsetEntry(*ppOMSR, 0, ustrdup(getDfltTpl()), OMSR_NO_RQD_TPL_OPTS));
+ 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
@@ -970,10 +1200,11 @@ CODESTARTnewActInst
// TODO: create unified code for this (legacy+v6 system)
/* we now allocate the cache table */
CHKmalloc(pData->dynCache = (dynaFileCacheEntry**)
- calloc(cs.iDynaFileCacheSize, sizeof(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);
@@ -1062,6 +1293,8 @@ CODESTARTparseSelectorAct
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
@@ -1088,7 +1321,6 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
cs.bUseAsyncWriter = USE_ASYNCWRITER_DFLT;
free(pszFileDfltTplName);
pszFileDfltTplName = NULL;
-
return RS_RET_OK;
}
@@ -1099,8 +1331,7 @@ CODESTARTdoHUP
dynaFileFreeCacheEntries(pData);
} else {
if(pData->pStrm != NULL) {
- strm.Destruct(&pData->pStrm);
- pData->pStrm = NULL;
+ closeFile(pData);
}
}
ENDdoHUP
@@ -1110,6 +1341,7 @@ BEGINmodExit
CODESTARTmodExit
objRelease(errmsg, CORE_COMPONENT);
objRelease(strm, CORE_COMPONENT);
+ objRelease(statsobj, CORE_COMPONENT);
DESTROY_ATOMIC_HELPER_MUT(mutClock);
ENDmodExit
@@ -1132,6 +1364,7 @@ CODEmodInit_QueryRegCFSLineHdlr
INITLegCnfVars
CHKiRet(objUse(errmsg, CORE_COMPONENT));
CHKiRet(objUse(strm, CORE_COMPONENT));
+ CHKiRet(objUse(statsobj, CORE_COMPONENT));
INIT_ATOMIC_HELPER_MUT(mutClock);
diff --git a/tools/omfwd.c b/tools/omfwd.c
index 2fd24bdf..129392d2 100644
--- a/tools/omfwd.c
+++ b/tools/omfwd.c
@@ -138,6 +138,7 @@ static struct cnfparamdescr actpdescr[] = {
{ "streamdriverauthmode", eCmdHdlrGetWord, 0 },
{ "streamdriverpermittedpeers", eCmdHdlrGetWord, 0 },
{ "resendlastmsgonreconnect", eCmdHdlrBinary, 0 },
+ { "template", eCmdHdlrGetWord, 0 },
};
static struct cnfparamblk actpblk =
{ CNFPARAMBLK_VERSION,
@@ -760,6 +761,7 @@ setInstParamDefaults(instanceData *pData)
BEGINnewActInst
struct cnfparamvals *pvals;
+ uchar *tplToUse;
int i;
rsRetVal localRet;
CODESTARTnewActInst
@@ -881,7 +883,8 @@ CODESTARTnewActInst
}
CODE_STD_STRING_REQUESTnewActInst(1)
- CHKiRet(OMSRsetEntry(*ppOMSR, 0, ustrdup(getDfltTpl()), OMSR_NO_RQD_TPL_OPTS));
+ tplToUse = ustrdup((pData->tplName == NULL) ? getDfltTpl() : pData->tplName);
+ CHKiRet(OMSRsetEntry(*ppOMSR, 0, tplToUse, OMSR_NO_RQD_TPL_OPTS));
CHKiRet(initTCP(pData));
CODE_STD_FINALIZERnewActInst
diff --git a/tools/pidfile.c b/tools/pidfile.c
index e9601232..8298b94e 100644
--- a/tools/pidfile.c
+++ b/tools/pidfile.c
@@ -55,7 +55,8 @@ int read_pid (char *pidfile)
if (!(f=fopen(pidfile,"r")))
return 0;
- fscanf(f,"%d", &pid);
+ if(fscanf(f,"%d", &pid) != 1)
+ pid = 0;
fclose(f);
return pid;
}
@@ -113,7 +114,8 @@ int write_pid (char *pidfile)
#if HAVE_FLOCK
if (flock(fd, LOCK_EX|LOCK_NB) == -1) {
- fscanf(f, "%d", &pid);
+ if(fscanf(f, "%d", &pid) != 1)
+ pid = 0;
fclose(f);
printf("Can't lock, lock is held by pid %d.\n", pid);
return 0;
diff --git a/tools/pmrfc3164.c b/tools/pmrfc3164.c
index bcded428..5dfa74f0 100644
--- a/tools/pmrfc3164.c
+++ b/tools/pmrfc3164.c
@@ -138,7 +138,7 @@ CODESTARTparse
*/
if(lenMsg > 0 && pMsg->msgFlags & PARSE_HOSTNAME) {
i = 0;
- while(i < lenMsg && (isalnum(p2parse[i]) || p2parse[i] == '.' || p2parse[i] == '.'
+ while(i < lenMsg && (isalnum(p2parse[i]) || p2parse[i] == '.'
|| p2parse[i] == '_' || p2parse[i] == '-') && i < (CONF_HOSTNAME_MAXSIZE - 1)) {
bufParseHOSTNAME[i] = p2parse[i];
++i;
diff --git a/tools/recover_qi.pl b/tools/recover_qi.pl
index 4e2cf9d5..4e2cf9d5 100644..100755
--- a/tools/recover_qi.pl
+++ b/tools/recover_qi.pl
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
index dcc9b7c7..07da6ffd 100644
--- a/tools/rsyslog.conf.5
+++ b/tools/rsyslog.conf.5
@@ -17,7 +17,7 @@
.\" 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 "11 July 2008" "Version 3.18.0" "Linux System Administration"
+.TH RSYSLOG.CONF 5 "22 October 2012" "Version 7.2.0" "Linux System Administration"
.SH NAME
rsyslog.conf \- rsyslogd(8) configuration file
.SH DESCRIPTION
@@ -218,7 +218,7 @@ beginning with a slash ('/').
.B Example:
.RS
-*.* /var/log/traditionalfile.log;RSYSLOG_TraditionalFormat # log to a file in the traditional format
+*.* /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,
@@ -335,13 +335,6 @@ Rsyslog offers three different types "filter conditions":
* expression-based filters
.RE
-.SS Blocks
-Rsyslogd supports BSD-style blocks inside rsyslog.conf. Each block of lines is separated from
-the previous block by a program or hostname specification. A block will only log messages
-corresponding to the most recent program and hostname specifications given. Thus, a block which
-selects "ppp" as the program, directly followed by a block that selects messages from the
-hostname "dialhost", then the second block will only log messages from the ppp program on dialhost.
-
.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
@@ -422,7 +415,7 @@ To escape:
.sp 0
\\ = \\\\ --> '\\' is used to escape (as in C)
.sp 0
-$template TraditionalFormat,"%timegenerated% %HOSTNAME% %syslogtag%%msg%\n"
+$template TraditionalFormat,"%timegenerated% %HOSTNAME% %syslogtag%%msg%\\n"
Properties can be accessed by the property replacer (see there for details).
@@ -494,7 +487,7 @@ A template that resembles traditional syslogd file output:
.RS
$template TraditionalFormat,"%timegenerated% %HOSTNAME%
.sp 0
-%syslogtag%%msg:::drop-last-lf%\n"
+%syslogtag%%msg:::drop-last-lf%\\n"
.RE
A template that tells you a little more about the message:
@@ -502,7 +495,7 @@ A template that tells you a little more about the message:
.RS
$template precise,"%syslogpriority%,%syslogfacility%,%timegenerated%,%HOSTNAME%,
.sp 0
-%syslogtag%,%msg%\n"
+%syslogtag%,%msg%\\n"
.RE
A template for RFC 3164 format:
@@ -514,7 +507,7 @@ $template RFC3164fmt,"<%PRI%>%TIMESTAMP% %HOSTNAME% %syslogtag%%msg%"
A template for the format traditionally used for user messages:
.sp
.RS
-$template usermsg," XXXX%syslogtag%%msg%\n\r"
+$template usermsg," XXXX%syslogtag%%msg%\\n\\r"
.RE
And a template with the traditional wall-message format:
diff --git a/tools/rsyslogd.8 b/tools/rsyslogd.8
index 36f29769..620006f2 100644
--- a/tools/rsyslogd.8
+++ b/tools/rsyslogd.8
@@ -10,6 +10,7 @@ rsyslogd \- reliable and extended syslogd
.RB [ " \-6 " ]
.RB [ " \-A " ]
.RB [ " \-d " ]
+.RB [ " \-D " ]
.RB [ " \-f "
.I config file
]
@@ -120,10 +121,15 @@ If neither -4 nor -6 is given,
listens to all configured addresses of the system.
.TP
.BI "\-c " "version"
-This option has been obsolted and has no function any longer. It is still
+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)
diff --git a/tools/syslogd.c b/tools/syslogd.c
index a89c7e57..a8a733d6 100644
--- a/tools/syslogd.c
+++ b/tools/syslogd.c
@@ -44,7 +44,6 @@
#include "rsyslog.h"
#define DEFUPRI (LOG_USER|LOG_NOTICE)
-#define TIMERINTVL 30 /* interval for checking flush, mark */
#include <unistd.h>
#include <stdlib.h>
@@ -52,7 +51,6 @@
#include <stddef.h>
#include <ctype.h>
#include <limits.h>
-#define GNU_SOURCE
#include <string.h>
#include <stdarg.h>
#include <time.h>
@@ -93,6 +91,8 @@
#include <zlib.h>
#endif
+extern int yydebug; /* interface to flex */
+
#include <netdb.h>
#include "pidfile.h"
@@ -119,13 +119,13 @@
#include "batch.h"
#include "unicode-helper.h"
#include "ruleset.h"
-#include "rule.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)
@@ -134,7 +134,6 @@ DEFobjCurrIf(datetime) /* TODO: make go away! */
DEFobjCurrIf(conf)
DEFobjCurrIf(module)
DEFobjCurrIf(errmsg)
-DEFobjCurrIf(rule)
DEFobjCurrIf(ruleset)
DEFobjCurrIf(prop)
DEFobjCurrIf(parser)
@@ -195,7 +194,6 @@ static prop_t *pInternalInputName = NULL; /* there is only one global inputName
static uchar *ConfFile = (uchar*) _PATH_LOGCONF; /* read-only after startup */
static char *PidFile = _PATH_LOGPID; /* read-only after startup */
-static pid_t myPid; /* our pid for use in self-generated messages, e.g. on startup */
/* mypid is read-only after the initial fork() */
static int bHadHUP = 0; /* did we have a HUP? */
@@ -205,13 +203,6 @@ static int bFinished = 0; /* used by termination signal handler, read-only excep
*/
int iConfigVerify = 0; /* is this just a config verify run? */
-/* Intervals at which we flush out "message repeated" messages,
- * in seconds after previous message is logged. After each flush,
- * we move to the next interval until we reach the largest.
- * TODO: this shall go into action object! -- rgerhards, 2008-01-29
- */
-int repeatinterval[2] = { 30, 60 }; /* # of secs before flush */
-
#define LIST_DELIMITER ':' /* delimiter between two hosts */
static pid_t ppid; /* This is a quick and dirty hack used for spliting main/startup thread */
@@ -222,9 +213,11 @@ struct queuefilenames_s {
} *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 NoFork = 0; /* don't fork - don't run in daemon mode - read-only after startup */
+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.
@@ -413,7 +406,7 @@ parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int fla
CHKiRet(prop.Destruct(&pProp));
CHKiRet(MsgSetRcvFromIPStr(pMsg, hnameIP, ustrlen(hnameIP), &pProp));
CHKiRet(prop.Destruct(&pProp));
- CHKiRet(submitMsg(pMsg));
+ CHKiRet(submitMsg2(pMsg));
finalize_it:
RETiRet;
@@ -433,6 +426,12 @@ submitErrMsg(int iErr, uchar *msg)
}
+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.
*/
@@ -473,7 +472,7 @@ logmsgInternal(int iErr, int pri, uchar *msg, int flags)
* permits us to process unmodified config files which otherwise contain a
* supressor statement.
*/
- if(((Debug == DEBUG_FULL || NoFork) && ourConf->globals.bErrMsgToStderr) || iConfigVerify) {
+ if(((Debug == DEBUG_FULL || !doFork) && ourConf->globals.bErrMsgToStderr) || iConfigVerify) {
if(LOG_PRI(pri) == LOG_ERR)
fprintf(stderr, "rsyslogd: %s\n", msg);
}
@@ -484,50 +483,13 @@ logmsgInternal(int iErr, int pri, uchar *msg, int flags)
/* we have the queue, so we can simply provide the
* message to the queue engine.
*/
- submitMsg(pMsg);
+ ratelimitAddMsg(internalMsg_ratelimiter, NULL, pMsg);
+ //submitMsgWithDfltRatelimiter(pMsg);
}
finalize_it:
RETiRet;
}
-/* check message against ACL set
- * rgerhards, 2009-11-16
- */
-#if 0
-static inline rsRetVal
-chkMsgAgainstACL() {
- /* if we reach this point, we had a good receive and can process the packet received */
- /* check if we have a different sender than before, if so, we need to query some new values */
- if(net.CmpHost(&frominet, frominetPrev, socklen) != 0) {
- CHKiRet(net.cvthname(&frominet, fromHost, fromHostFQDN, fromHostIP));
- memcpy(frominetPrev, &frominet, socklen); /* update cache indicator */
- /* Here we check if a host is permitted to send us
- * syslog messages. If it isn't, we do not further
- * process the message but log a warning (if we are
- * configured to do this).
- * rgerhards, 2005-09-26
- */
- *pbIsPermitted = net.isAllowedSender((uchar*)"UDP",
- (struct sockaddr *)&frominet, (char*)fromHostFQDN);
-
- if(!*pbIsPermitted) {
- DBGPRINTF("%s is not an allowed sender\n", (char*)fromHostFQDN);
- if(glbl.GetOption_DisallowWarning) {
- time_t tt;
-
- datetime.GetTime(&tt);
- if(tt > ttLastDiscard + 60) {
- ttLastDiscard = tt;
- errmsg.LogError(0, NO_ERRCODE,
- "UDP message from disallowed sender %s discarded",
- (char*)fromHost);
- }
- }
- }
- }
-}
-#endif
-
/* 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
@@ -536,9 +498,9 @@ chkMsgAgainstACL() {
*/
static inline rsRetVal
preprocessBatch(batch_t *pBatch) {
- uchar fromHost[NI_MAXHOST];
- uchar fromHostIP[NI_MAXHOST];
- uchar fromHostFQDN[NI_MAXHOST];
+ prop_t *ip;
+ prop_t *fqdn;
+ prop_t *localName;
prop_t *propFromHost = NULL;
prop_t *propFromHostIP = NULL;
int bSingleRuleset;
@@ -550,31 +512,31 @@ preprocessBatch(batch_t *pBatch) {
DEFiRet;
bSingleRuleset = 1;
- batchRuleset = (pBatch->nElem > 0) ? ((msg_t*) pBatch->pElem[0].pUsrp)->pRuleset : NULL;
+ batchRuleset = (pBatch->nElem > 0) ? pBatch->pElem[0].pMsg->pRuleset : NULL;
for(i = 0 ; i < pBatch->nElem && !*(pBatch->pbShutdownImmediate) ; i++) {
- pMsg = (msg_t*) pBatch->pElem[i].pUsrp;
+ 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, fromHost, fromHostFQDN, fromHostIP) != RS_RET_OK)
+ if(net.cvthname(pMsg->rcvFrom.pfrominet, &localName, &fqdn, &ip) != RS_RET_OK)
continue;
bIsPermitted = net.isAllowedSender2((uchar*)"UDP",
- (struct sockaddr *)pMsg->rcvFrom.pfrominet, (char*)fromHostFQDN, 1);
+ (struct sockaddr *)pMsg->rcvFrom.pfrominet, (char*)propGetSzStr(fqdn), 1);
if(!bIsPermitted) {
DBGPRINTF("Message from '%s' discarded, not a permitted sender host\n",
- fromHostFQDN);
- pBatch->pElem[i].state = BATCH_STATE_DISC;
+ propGetSzStr(fqdn));
+ pBatch->eltState[i] = BATCH_STATE_DISC;
} else {
/* save some of the info we obtained */
- MsgSetRcvFromStr(pMsg, fromHost, ustrlen(fromHost), &propFromHost);
- CHKiRet(MsgSetRcvFromIPStr(pMsg, fromHostIP, ustrlen(fromHostIP), &propFromHostIP));
+ 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->pElem[i].state = BATCH_STATE_DISC;
+ pBatch->eltState[i] = BATCH_STATE_DISC;
}
}
if(pMsg->pRuleset != batchRuleset)
@@ -609,7 +571,7 @@ msgConsumer(void __attribute__((unused)) *notNeeded, batch_t *pBatch, int *pbShu
//do not have this yet and so we emulate -- 2010-06-10
int i;
for(i = 0 ; i < pBatch->nElem && !*pbShutdownImmediate ; i++) {
- pBatch->pElem[i].state = BATCH_STATE_COMM;
+ pBatch->eltState[i] = BATCH_STATE_COMM;
}
RETiRet;
}
@@ -620,7 +582,7 @@ int i;
* rgerhards, 2008-02-13
*/
rsRetVal
-submitMsg(msg_t *pMsg)
+submitMsg2(msg_t *pMsg)
{
qqueue_t *pQueue;
ruleset_t *pRuleset;
@@ -633,26 +595,31 @@ submitMsg(msg_t *pMsg)
/* if a plugin logs a message during shutdown, the queue may no longer exist */
if(pQueue == NULL) {
- DBGPRINTF("submitMsg() could not submit message - "
+ DBGPRINTF("submitMsg2() could not submit message - "
"queue does (no longer?) exist - ignored\n");
FINALIZE;
}
- qqueueEnqObj(pQueue, pMsg->flowCtlType, (void*) pMsg);
+ 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
-multiSubmitMsg(multi_submit_t *pMultiSub)
+multiSubmitMsg2(multi_submit_t *pMultiSub)
{
- int i;
qqueue_t *pQueue;
ruleset_t *pRuleset;
DEFiRet;
@@ -677,8 +644,23 @@ multiSubmitMsg(multi_submit_t *pMultiSub)
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
@@ -697,43 +679,6 @@ reapchild()
}
-/* helper to doFlushRptdMsgs() to flush the individual action links via llExecFunc
- * rgerhards, 2007-08-02
- */
-DEFFUNC_llExecFunc(flushRptdMsgsActions)
-{
- action_t *pAction = (action_t*) pData;
- assert(pAction != NULL);
-
- BEGINfunc
- d_pthread_mutex_lock(&pAction->mutAction);
- /* TODO: time() performance: the call below could be moved to
- * the beginn of the llExec(). This makes it slightly less correct, but
- * in an acceptable way. -- rgerhards, 2008-09-16
- */
- if (pAction->f_prevcount && datetime.GetTime(NULL) >= REPEATTIME(pAction)) {
- DBGPRINTF("flush %s: repeated %d times, %d sec.\n",
- module.GetStateName(pAction->pMod), pAction->f_prevcount,
- repeatinterval[pAction->f_repeatcount]);
- actionWriteToAction(pAction);
- BACKOFF(pAction);
- }
- d_pthread_mutex_unlock(&pAction->mutAction);
-
- ENDfunc
- return RS_RET_OK; /* we ignore errors, we can not do anything either way */
-}
-
-
-/* This method flushes repeat messages.
- */
-static void
-doFlushRptdMsgs(void)
-{
- ruleset.IterateAllActions(runConf, flushRptdMsgsActions, NULL);
-}
-
-
static void debug_switch()
{
time_t tTime;
@@ -774,8 +719,11 @@ static void debug_switch()
* 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.
+ * 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)
{
@@ -783,11 +731,13 @@ static void doDie(int sig)
# define MSG2 "DoDie called 5 times - unconditional exit\n"
static int iRetries = 0; /* debug aid */
dbgprintf(MSG1);
- if(Debug == DEBUG_FULL)
- write(1, MSG1, sizeof(MSG1) - 1);
+ if(Debug == DEBUG_FULL) {
+ if(write(1, MSG1, sizeof(MSG1) - 1)) {}
+ }
if(iRetries++ == 4) {
- if(Debug == DEBUG_FULL)
- write(1, MSG2, sizeof(MSG2) - 1);
+ if(Debug == DEBUG_FULL) {
+ if(write(1, MSG2, sizeof(MSG2) - 1)) {}
+ }
abort();
}
bFinished = sig;
@@ -843,7 +793,7 @@ die(int sig)
(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) myPid, sig);
+ (int) glblGetOurPid(), sig);
errno = 0;
logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)buf, 0);
}
@@ -1106,7 +1056,7 @@ finalize_it:
* the time being (remember that we want to restructure config processing at large!).
* rgerhards, 2009-10-27
*/
-rsRetVal createMainQueue(qqueue_t **ppQueue, uchar *pszQueueName)
+rsRetVal createMainQueue(qqueue_t **ppQueue, uchar *pszQueueName, struct cnfparamvals *queueParams)
{
struct queuefilenames_s *qfn;
uchar *qfname = NULL;
@@ -1122,60 +1072,65 @@ rsRetVal createMainQueue(qqueue_t **ppQueue, uchar *pszQueueName)
/* name our main queue object (it's not fatal if it fails...) */
obj.SetName((obj_t*) (*ppQueue), pszQueueName);
- /* ... 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(queueParams == 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(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;
}
- 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
+ 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, queueParams);
+ }
/* ... and finally start the queue! */
CHKiRet_Hdlr(qqueueStart(*ppQueue)) {
@@ -1212,7 +1167,7 @@ init(void)
snprintf(bufStartUpMsg, sizeof(bufStartUpMsg)/sizeof(char),
" [origin software=\"rsyslogd\" " "swVersion=\"" VERSION \
"\" x-pid=\"%d\" x-info=\"http://www.rsyslog.com\"] start",
- (int) myPid);
+ (int) glblGetOurPid());
logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)bufStartUpMsg, 0);
}
@@ -1254,7 +1209,7 @@ static inline void processImInternal(void)
msg_t *pMsg;
while(iminternalRemoveMsg(&pMsg) == RS_RET_OK) {
- submitMsg(pMsg);
+ submitMsgWithDfltRatelimiter(pMsg);
}
}
@@ -1291,7 +1246,7 @@ doHUP(void)
snprintf(buf, sizeof(buf) / sizeof(char),
" [origin software=\"rsyslogd\" " "swVersion=\"" VERSION
"\" x-pid=\"%d\" x-info=\"http://www.rsyslog.com\"] rsyslogd was HUPed",
- (int) myPid);
+ (int) glblGetOurPid());
errno = 0;
logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)buf, 0);
}
@@ -1321,49 +1276,22 @@ mainloop(void)
while(!bFinished){
/* this is now just a wait - please note that we do use a near-"eternal"
- * timeout of 1 day if we do not have repeated message reduction turned on
- * (which it is not by default). This enables us to help safe the environment
+ * 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 = (runConf->globals.bReduceRepeatMsgs == 1) ? TIMERINTVL : 86400 /*1 day*/;
- //tvSelectTimeout.tv_sec = TIMERINTVL; /* TODO: change this back to the above code when we have a better solution for apc */
+ tvSelectTimeout.tv_sec = 86400 /*1 day*/;
tvSelectTimeout.tv_usec = 0;
select(1, NULL, NULL, NULL, &tvSelectTimeout);
if(bFinished)
- break; /* exit as quickly as possible - see long comment below */
-
- /* If we received a HUP signal, we call doFlushRptdMsgs() a bit early. This
- * doesn't matter, because doFlushRptdMsgs() checks timestamps. What may happen,
- * however, is that the too-early call may lead to a bit too-late output
- * of "last message repeated n times" messages. But that is quite acceptable.
- * rgerhards, 2007-12-21
- * ... and just to explain, we flush here because that is exactly what the mainloop
- * shall do - provide a periodic interval in which not-yet-flushed messages will
- * be flushed. Be careful, there is a potential race condition: doFlushRptdMsgs()
- * needs to aquire a lock on the action objects. If, however, long-running consumers
- * cause the main queue worker threads to lock them for a long time, we may receive
- * a starvation condition, resulting in the mainloop being held on lock for an extended
- * period of time. That, in turn, could lead to unresponsiveness to termination
- * requests. It is especially important that the bFinished flag is checked before
- * doFlushRptdMsgs() is called (I know because I ran into that situation). I am
- * not yet sure if the remaining probability window of a termination-related
- * problem is large enough to justify changing the code - I would consider it
- * extremely unlikely that the problem ever occurs in practice. Fixing it would
- * require not only a lot of effort but would cost considerable performance. So
- * for the time being, I think the remaining risk can be accepted.
- * rgerhards, 2008-01-10
- */
- if(runConf->globals.bReduceRepeatMsgs == 1)
- doFlushRptdMsgs();
+ break; /* exit as quickly as possible */
if(bHadHUP) {
doHUP();
bHadHUP = 0;
continue;
}
- // TODO: remove execScheduled(); /* handle Apc calls (if any) */
}
ENDfunc
}
@@ -1409,6 +1337,11 @@ static void printVersion(void)
#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");
}
@@ -1437,8 +1370,6 @@ InitGlobalClasses(void)
CHKiRet(objUse(module, CORE_COMPONENT));
pErrObj = "datetime";
CHKiRet(objUse(datetime, CORE_COMPONENT));
- pErrObj = "rule";
- CHKiRet(objUse(rule, CORE_COMPONENT));
pErrObj = "ruleset";
CHKiRet(objUse(ruleset, CORE_COMPONENT));
pErrObj = "conf";
@@ -1461,6 +1392,7 @@ InitGlobalClasses(void)
CHKiRet(objUse(net, LM_NET_FILENAME));
dnscacheInit();
initRainerscript();
+ ratelimitModInit();
finalize_it:
if(iRet != RS_RET_OK) {
@@ -1492,7 +1424,6 @@ GlobalClassExit(void)
objRelease(prop, CORE_COMPONENT);
objRelease(conf, CORE_COMPONENT);
objRelease(ruleset, CORE_COMPONENT);
- objRelease(rule, 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);
@@ -1500,6 +1431,7 @@ GlobalClassExit(void)
/* 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 */
@@ -1595,6 +1527,13 @@ queryLocalHostname(void)
*/
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;
@@ -1681,8 +1620,7 @@ doGlblProcessInit(void)
thrdInit();
- if( !(Debug == DEBUG_FULL || NoFork) )
- {
+ if(doFork) {
DBGPRINTF("Checking pidfile '%s'.\n", PidFile);
if (!check_pid(PidFile))
{
@@ -1694,16 +1632,23 @@ doGlblProcessInit(void)
/* stop writing debug messages to stdout (if debugging is on) */
stddbg = -1;
+ dbgprintf("ready for forking\n");
if (fork()) {
/* Parent process
*/
- sleep(300);
- /* Not reached unless something major went wrong. 5
- * minutes should be a fair amount of time to wait.
- * Please note that this procedure is important since
- * the father must not exit before syslogd isn't
- * initialized or the klogd won't be able to flush its
- * logs. -Joey
+ 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 */
}
@@ -1712,6 +1657,7 @@ doGlblProcessInit(void)
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;
@@ -1745,7 +1691,8 @@ doGlblProcessInit(void)
i = SD_LISTEN_FDS_START + sd_fds;
}
for ( ; i < num_fds; i++)
- (void) close(i);
+ if(i != dbgGetDbglogFd())
+ close(i);
untty();
} else {
@@ -1770,7 +1717,7 @@ doGlblProcessInit(void)
fputs("Pidfile (and pid) already exist.\n", stderr);
exit(1); /* exit during startup - questionable */
}
- myPid = getpid(); /* save our pid for further testing (also used for messages) */
+ glblSetOurPid(getpid());
memset(&sigAct, 0, sizeof (sigAct));
sigemptyset(&sigAct.sa_mask);
@@ -1825,7 +1772,7 @@ int realMain(int argc, char **argv)
* of other options, we do this during the inital option processing.
* rgerhards, 2008-04-04
*/
- while((ch = getopt(argc, argv, "46a:Ac:def:g:hi:l:m:M:nN:op:qQr::s:t:T:u:vwx")) != EOF) {
+ while((ch = getopt(argc, argv, "46a:Ac:dDef:g:hi:l:m:M:nN:op:qQr::s:t:T:u:vwx")) != EOF) {
switch((char)ch) {
case '4':
case '6':
@@ -1853,11 +1800,15 @@ int realMain(int argc, char **argv)
CHKiRet(bufOptAdd(ch, optarg));
break;
case 'c': /* compatibility mode */
- fprintf(stderr, "rsyslogd: error: option -c is no longer supported - ignored");
+ 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;
@@ -1952,7 +1903,7 @@ int realMain(int argc, char **argv)
fprintf(stderr, "rsyslogd: error -m is no longer supported - use immark instead");
break;
case 'n': /* don't fork */
- NoFork = 1;
+ doFork = 0;
break;
case 'N': /* enable config verify mode */
iConfigVerify = atoi(arg);
@@ -2034,6 +1985,12 @@ int realMain(int argc, char **argv)
}
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");
@@ -2050,17 +2007,16 @@ int realMain(int argc, char **argv)
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");
}
- /* Send a signal to the parent so it can terminate. */
- if(myPid != ppid)
- kill(ppid, SIGTERM);
-
-
/* END OF INTIALIZATION */
DBGPRINTF("initialization completed, transitioning to regular run mode\n");
@@ -2071,12 +2027,14 @@ int realMain(int argc, char **argv)
* is still in its infancy (and not really done), we currently accept this issue.
* rgerhards, 2009-06-29
*/
- if(!(Debug == DEBUG_FULL || NoFork)) {
+ 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 */