summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRainer Gerhards <rgerhards@adiscon.com>2009-06-16 13:59:09 +0200
committerRainer Gerhards <rgerhards@adiscon.com>2009-06-16 13:59:09 +0200
commitb5f3387357ffa11e238ddfe0fa38af4fffba6081 (patch)
tree80447feb5aac91ca4d515ae373167efca4dea9ca
parentcf1289737659a8a807419b51566613c9fb776005 (diff)
parentaef1a38fe8c7472362904b2f90c67113b21034ab (diff)
downloadrsyslog-b5f3387357ffa11e238ddfe0fa38af4fffba6081.tar.gz
rsyslog-b5f3387357ffa11e238ddfe0fa38af4fffba6081.tar.xz
rsyslog-b5f3387357ffa11e238ddfe0fa38af4fffba6081.zip
Merge branch 'omfile' into v5-devel
Note that this was NOT a trivial merge, and there may be some issues. This needs to be seen when we continue developing. Conflicts: runtime/msg.h runtime/obj.h runtime/queue.c runtime/srUtils.h runtime/stream.c runtime/stream.h runtime/wti.c tests/Makefile.am tools/omfile.c tools/syslogd.c
-rw-r--r--ChangeLog15
-rw-r--r--action.c4
-rw-r--r--configure.ac1
-rw-r--r--dirty.h5
-rw-r--r--doc/property_replacer.html4
-rw-r--r--doc/queues.html6
-rw-r--r--doc/rsyslog_conf_global.html19
-rw-r--r--outchannel.c20
-rw-r--r--parse.c16
-rw-r--r--plugins/imdiag/imdiag.c2
-rw-r--r--plugins/imfile/imfile.c53
-rw-r--r--plugins/imklog/imklog.c4
-rw-r--r--plugins/imtcp/imtcp.c31
-rw-r--r--plugins/imudp/imudp.c35
-rw-r--r--plugins/omgssapi/omgssapi.c1
-rw-r--r--plugins/ommail/ommail.c2
-rw-r--r--plugins/ommysql/ommysql.c2
-rw-r--r--plugins/ompgsql/ompgsql.c2
-rw-r--r--plugins/omprog/omprog.c2
-rw-r--r--plugins/omrelp/omrelp.c2
-rw-r--r--plugins/omsnmp/omsnmp.c2
-rw-r--r--plugins/omstdout/omstdout.c2
-rw-r--r--plugins/omtemplate/omtemplate.c2
-rw-r--r--runtime/Makefile.am17
-rw-r--r--runtime/apc.c400
-rw-r--r--runtime/apc.h56
-rw-r--r--runtime/cfsysline.c18
-rw-r--r--runtime/conf.c157
-rw-r--r--runtime/conf.h6
-rw-r--r--runtime/ctok.c4
-rw-r--r--runtime/datetime.c5
-rw-r--r--runtime/linkedlist.c2
-rw-r--r--runtime/msg.c183
-rw-r--r--runtime/msg.h56
-rw-r--r--runtime/nsd_gtls.c8
-rw-r--r--runtime/obj-types.h9
-rw-r--r--runtime/obj.c109
-rw-r--r--runtime/obj.h2
-rw-r--r--runtime/parser.c36
-rw-r--r--runtime/queue.c130
-rw-r--r--runtime/queue.h2
-rw-r--r--runtime/rsyslog.c30
-rw-r--r--runtime/rsyslog.h29
-rw-r--r--runtime/rule.c450
-rw-r--r--runtime/rule.h77
-rw-r--r--runtime/ruleset.c451
-rw-r--r--runtime/ruleset.h60
-rw-r--r--runtime/srUtils.h4
-rw-r--r--runtime/srutils.c27
-rw-r--r--runtime/stream.c687
-rw-r--r--runtime/stream.h117
-rw-r--r--runtime/stringbuf.c227
-rw-r--r--runtime/stringbuf.h27
-rw-r--r--runtime/sysvar.c2
-rw-r--r--runtime/unicode-helper.h25
-rw-r--r--runtime/vm.c2
-rw-r--r--runtime/vmop.c2
-rw-r--r--runtime/zlibw.c125
-rw-r--r--runtime/zlibw.h46
-rw-r--r--tcps_sess.c61
-rw-r--r--tcpsrv.c19
-rw-r--r--tcpsrv.h4
-rw-r--r--template.c54
-rw-r--r--tests/Makefile.am12
-rwxr-xr-xtests/diag.sh3
-rwxr-xr-xtests/diskqueue-fsync.sh16
-rwxr-xr-xtests/diskqueue.sh1
-rw-r--r--tests/ourtail.c2
-rw-r--r--tests/runtime-dummy.c4
-rw-r--r--tests/testsuites/diskqueue-fsync.conf17
-rwxr-xr-xtests/validation-run.sh7
-rw-r--r--tools/Makefile.am4
-rw-r--r--tools/omfile.c538
-rw-r--r--tools/omfwd.c1
-rw-r--r--tools/omshell.c2
-rw-r--r--tools/omusrmsg.c2
-rw-r--r--tools/syslogd.c573
-rw-r--r--tools/syslogd.h60
-rw-r--r--tools/zpipe.c254
79 files changed, 3847 insertions, 1607 deletions
diff --git a/ChangeLog b/ChangeLog
index 1e954a2a..e052dfed 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -33,6 +33,9 @@ increase.
output module interface
---------------------------------------------------------------------------
Version 4.3.2 [DEVEL] (rgerhards), 2009-??-??
+- improved config error messages: now contain a copy of the config line
+ that (most likely) caused the error
+- removed long-obsoleted property UxTradMsg
- added a generic network stream server (in addition to rather specific
syslog tcp server)
- added ability for the UDP output action to rebind its send socket after
@@ -42,6 +45,18 @@ Version 4.3.2 [DEVEL] (rgerhards), 2009-??-??
- bugfix: imdiag/imtcp had a race condition
- improved testbench (now much better code design and reuse)
- added config switch --enable-testbench=no to turn off testbench
+- reduced max value for $DynaFileCacheSize to 1,000 (the former maximum
+ of 10,000 really made no sense, even 1,000 is very high, but we like
+ to keep the user in control ;)).
+- added capability to fsync() queue disk files for enhanced reliability
+ (also add's speed, because you do no longer need to run the whole file
+ system in sync mode)
+- added configuration commands (see doc for explanations)
+ * $OMFileZipLevel
+ * $OMFileIOBufferSize
+ * $OMFileFlushOnTXEnd
+ * $MainMsgQueueSyncQueueFiles
+ * $ActionQueueSyncQueueFiles
---------------------------------------------------------------------------
Version 4.3.1 [DEVEL] (rgerhards), 2009-05-25
- added capability to run multiple tcp listeners (on different ports)
diff --git a/action.c b/action.c
index 424cb00b..c252126b 100644
--- a/action.c
+++ b/action.c
@@ -77,6 +77,7 @@ static int iActionQueueNumWorkers = 1; /* number of worker threads for the mm
static uchar *pszActionQFName = NULL; /* prefix for the main message queue file */
static int64 iActionQueMaxFileSize = 1024*1024;
static int iActionQPersistUpdCnt = 0; /* persist queue info every n updates */
+static int bActionQSyncQeueFiles = 0; /* sync queue files */
static int iActionQtoQShutdown = 0; /* queue shutdown */
static int iActionQtoActShutdown = 1000; /* action shutdown (in phase 2) */
static int iActionQtoEnq = 2000; /* timeout for queue enque */
@@ -157,6 +158,7 @@ actionResetQueueParams(void)
iActionQueueNumWorkers = 1; /* number of worker threads for the mm queue above */
iActionQueMaxFileSize = 1024*1024;
iActionQPersistUpdCnt = 0; /* persist queue info every n updates */
+ bActionQSyncQeueFiles = 0;
iActionQtoQShutdown = 0; /* queue shutdown */
iActionQtoActShutdown = 1000; /* action shutdown (in phase 2) */
iActionQtoEnq = 2000; /* timeout for queue enque */
@@ -281,6 +283,7 @@ actionConstructFinalize(action_t *pThis)
setQPROP(qqueueSetMaxFileSize, "$ActionQueueFileSize", iActionQueMaxFileSize);
setQPROPstr(qqueueSetFilePrefix, "$ActionQueueFileName", pszActionQFName);
setQPROP(qqueueSetiPersistUpdCnt, "$ActionQueueCheckpointInterval", iActionQPersistUpdCnt);
+ setQPROP(qqueueSetbSyncQueueFiles, "$ActionQueueSyncQueueFiles", bActionQSyncQeueFiles);
setQPROP(qqueueSettoQShutdown, "$ActionQueueTimeoutShutdown", iActionQtoQShutdown );
setQPROP(qqueueSettoActShutdown, "$ActionQueueTimeoutActionCompletion", iActionQtoActShutdown);
setQPROP(qqueueSettoWrkShutdown, "$ActionQueueWorkerTimeoutThreadShutdown", iActionQtoWrkShutdown);
@@ -1257,6 +1260,7 @@ actionAddCfSysLineHdrl(void)
CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuediscardmark", 0, eCmdHdlrInt, NULL, &iActionQDiscardMark, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuediscardseverity", 0, eCmdHdlrInt, NULL, &iActionQDiscardSeverity, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuecheckpointinterval", 0, eCmdHdlrInt, NULL, &iActionQPersistUpdCnt, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuesyncqueuefiles", 0, eCmdHdlrBinary, NULL, &bActionQSyncQeueFiles, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuetype", 0, eCmdHdlrGetWord, setActionQueType, NULL, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionqueueworkerthreads", 0, eCmdHdlrInt, NULL, &iActionQueueNumWorkers, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionqueuetimeoutshutdown", 0, eCmdHdlrInt, NULL, &iActionQtoQShutdown, NULL));
diff --git a/configure.ac b/configure.ac
index 06ceec3c..ed45c414 100644
--- a/configure.ac
+++ b/configure.ac
@@ -186,6 +186,7 @@ AC_ARG_ENABLE(zlib,
esac],
[enable_zlib=yes]
)
+AM_CONDITIONAL(ENABLE_ZLIB, test x$enable_zlib = xyes)
if test "$enable_zlib" = "yes"; then
AC_CHECK_HEADER(zlib.h, [zlib_header="yes"], [zlib_header="no" enable_zlib="false"])
if test "$zlib_header" = "yes"; then
diff --git a/dirty.h b/dirty.h
index 6d585753..513886b5 100644
--- a/dirty.h
+++ b/dirty.h
@@ -33,10 +33,7 @@ rsRetVal parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len
int parseRFCSyslogMsg(msg_t *pMsg, int flags);
int parseLegacySyslogMsg(msg_t *pMsg, int flags);
rsRetVal diagGetMainMsgQSize(int *piSize); /* for imdiag */
-
-/* TODO: the following 2 need to go in conf obj interface... */
-rsRetVal cflineParseTemplateName(uchar** pp, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts, uchar *dfltTplName);
-rsRetVal cflineParseFileName(uchar* p, uchar *pFileName, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts, uchar *pszTpl);
+char* getFIOPName(unsigned iFIOP);
/* Intervals at which we flush out "message repeated" messages,
* in seconds after previous message is logged. After each flush,
diff --git a/doc/property_replacer.html b/doc/property_replacer.html
index a6e9b518..7b604ea0 100644
--- a/doc/property_replacer.html
+++ b/doc/property_replacer.html
@@ -30,10 +30,6 @@ Currently supported are:</p>
socket. Should be useful for debugging.</td>
</tr>
<tr>
-<td><b>uxtradmsg</b></td>
-<td>will disappear soon - do NOT use!</td>
-</tr>
-<tr>
<td><b>hostname</b></td>
<td>hostname from the message</td>
</tr>
diff --git a/doc/queues.html b/doc/queues.html
index f063e87c..75b70fbf 100644
--- a/doc/queues.html
+++ b/doc/queues.html
@@ -115,7 +115,11 @@ isolation. This is currently selected by specifying different <i>$WorkDirectory<
config directives before the queue creation statement.</p>
<p>To create a disk queue, use the "<i>$&lt;object&gt;QueueType Disk</i>" config
directive. Checkpoint intervals can be specified via "<i>$&lt;object&gt;QueueCheckpointInterval</i>",
-with 0 meaning no checkpoints. </p>
+with 0 meaning no checkpoints. Note that disk-based queues can be made very reliable
+by issuing a (f)sync after each write operation. Starting with version 4.3.2, this can
+be requested via "<i>&lt;object&gt;QueueSyncQueueFiles on/off</i> with the
+default being off. Activating this option has a performance penalty, so it should
+not be turned on without reason.</p>
<h2>In-Memory Queues</h2>
<p>In-memory queue mode is what most people have on their mind when they think
about computing queues. Here, the enqueued data elements are held in memory.
diff --git a/doc/rsyslog_conf_global.html b/doc/rsyslog_conf_global.html
index c7574e5d..6a8af77a 100644
--- a/doc/rsyslog_conf_global.html
+++ b/doc/rsyslog_conf_global.html
@@ -109,6 +109,9 @@ that no rebind is done. This directive is useful for use with load-balancers.</l
<li>$DefaultNetstreamDriver &lt;drivername&gt;, the default <a href="netstream.html">network stream driver</a> to use. Defaults to&nbsp;ptcp.$DefaultNetstreamDriverCAFile &lt;/path/to/cafile.pem&gt;</li>
<li>$DefaultNetstreamDriverCertFile &lt;/path/to/certfile.pem&gt;</li>
<li>$DefaultNetstreamDriverKeyFile &lt;/path/to/keyfile.pem&gt;</li>
+<li><b>$DefaultRuleset</b> <i>name</i> - changes the default ruleset for unbound inputs to
+the provided <i>name</i> (the default default ruleset is named
+&quot;RSYSLOG_DefaultRuleset&quot;).
<li><b>$CreateDirs</b> [<b>on</b>/off] - create directories on an as-needed basis</li>
<li><a href="rsconf1_dircreatemode.html">$DirCreateMode</a></li>
<li><a href="rsconf1_dirgroup.html">$DirGroup</a></li>
@@ -191,6 +194,17 @@ supported in order to be compliant to the upcoming new syslog RFC series.
<li><a href="rsconf1_maxopenfiles.html">$MaxOpenFiles</a></li>
<li><a href="rsconf1_moddir.html">$ModDir</a></li>
<li><a href="rsconf1_modload.html">$ModLoad</a></li>
+<li><b>$OMFileZipLevel</b> 0..9 [default 0] - if greater 0, turns on gzip compression
+of the output file. The higher the number, the better the compression, but also the
+more CPU is required for zipping.</li>
+<li><b>$OMFileIOBufferSize</b> &lt;size_nbr&gt;, default 4k, size of the buffer used to writing output data. The larger the buffer, the potentially better performance is. The default of 4k is quite conservative, it is useful to go up to 64k, and 128K if you used gzip compression (then, even higher sizes may make sense)</li>
+<li><b>$OMFileFlushOnTXEnd</b> &lt;[on/<b>off</b>]&gt;, default off, by default, omfile
+writes output using a buffered writer. Disk writes are only done when the buffer is
+full. So if an error happens during that write, data is potentially lost. In cases where
+this is unacceptable, set $OMFileFlushOnTXEnd to on. Then, data is written at the end
+of each transaction (for pre-v5 this means after <b>each</b> log message) and the usual
+error recovery thus can handle write errors without data loss. Note that this option
+severely reduces the effect of zip compression.</li>
<li><b>$RepeatedMsgContainsOriginalMsg</b> [on/<b>off</b>] - "last message repeated n times" messages, if generated,
have a different format that contains the message that is being repeated.
Note that only the first "n" characters are included, with n to be at least 80 characters, most
@@ -199,6 +213,11 @@ line is that n is large enough to get a good idea which message was repeated but
large enough for the whole message. (Introduced with 4.1.5). Once set, it affects all following actions.</li>
<li><a href="rsconf1_repeatedmsgreduction.html">$RepeatedMsgReduction</a></li>
<li><a href="rsconf1_resetconfigvariables.html">$ResetConfigVariables</a></li>
+<li><b>$Ruleset</b> <i>name</i> - starts a new ruleset or switches back to one already defined.
+All following actions belong to that new rule set.
+the <i>name</i> does not yet exist, it is created. To swith back to rsyslog's
+default ruleset, specify &quot;RSYSLOG_DefaultRuleset&quot;) as the name.
+All following actions belong to that new rule set.</li>
<li><b>$OptimizeForUniprocessor</b> [on/<b>off</b>] - turns on optimizatons which lead to better
performance on uniprocessors. If you run on multicore-machiens, turning this off lessens CPU load. The
default may change as uniprocessor systems become less common. [available since 4.1.0]</li>
diff --git a/outchannel.c b/outchannel.c
index 5c348b63..4f8abb32 100644
--- a/outchannel.c
+++ b/outchannel.c
@@ -105,22 +105,22 @@ static rsRetVal get_Field(uchar **pp, uchar **pField)
skip_Comma((char**)pp);
p = *pp;
- CHKiRet(rsCStrConstruct(&pStrB));
+ CHKiRet(cstrConstruct(&pStrB));
rsCStrSetAllocIncrement(pStrB, 32);
/* copy the field */
while(*p && *p != ' ' && *p != ',') {
- CHKiRet(rsCStrAppendChar(pStrB, *p++));
+ CHKiRet(cstrAppendChar(pStrB, *p++));
}
*pp = p;
- CHKiRet(rsCStrFinish(pStrB));
- CHKiRet(rsCStrConvSzStrAndDestruct(pStrB, pField, 0));
+ CHKiRet(cstrFinalize(pStrB));
+ CHKiRet(cstrConvSzStrAndDestruct(pStrB, pField, 0));
finalize_it:
if(iRet != RS_RET_OK) {
if(pStrB != NULL)
- rsCStrDestruct(&pStrB);
+ cstrDestruct(&pStrB);
}
RETiRet;
@@ -174,22 +174,22 @@ static inline rsRetVal get_restOfLine(uchar **pp, uchar **pBuf)
skip_Comma((char**)pp);
p = *pp;
- CHKiRet(rsCStrConstruct(&pStrB));
+ CHKiRet(cstrConstruct(&pStrB));
rsCStrSetAllocIncrement(pStrB, 32);
/* copy the field */
while(*p) {
- CHKiRet(rsCStrAppendChar(pStrB, *p++));
+ CHKiRet(cstrAppendChar(pStrB, *p++));
}
*pp = p;
- CHKiRet(rsCStrFinish(pStrB));
- CHKiRet(rsCStrConvSzStrAndDestruct(pStrB, pBuf, 0));
+ CHKiRet(cstrFinalize(pStrB));
+ CHKiRet(cstrConvSzStrAndDestruct(pStrB, pBuf, 0));
finalize_it:
if(iRet != RS_RET_OK) {
if(pStrB != NULL)
- rsCStrDestruct(&pStrB);
+ cstrDestruct(&pStrB);
}
RETiRet;
diff --git a/parse.c b/parse.c
index 58458d62..87c67676 100644
--- a/parse.c
+++ b/parse.c
@@ -268,7 +268,7 @@ rsRetVal parsDelimCStr(rsParsObj *pThis, cstr_t **ppCStr, char cDelim, int bTrim
/* We got the string, now take it and see if we need to
* remove anything at its end.
*/
- CHKiRet(rsCStrFinish(pCStr));
+ CHKiRet(cstrFinalize(pCStr));
if(bTrimTrailing) {
CHKiRet(rsCStrTrimTrailingWhiteSpace(pCStr));
@@ -344,7 +344,7 @@ rsRetVal parsQuotedCStr(rsParsObj *pThis, cstr_t **ppCStr)
}
/* We got the string, let's finish it... */
- CHKiRet(rsCStrFinish(pCStr));
+ CHKiRet(cstrFinalize(pCStr));
/* done! */
*ppCStr = pCStr;
@@ -380,7 +380,7 @@ rsRetVal parsAddrWithBits(rsParsObj *pThis, struct NetAddr **pIP, int *pBits)
assert(pIP != NULL);
assert(pBits != NULL);
- CHKiRet(rsCStrConstruct(&pCStr));
+ CHKiRet(cstrConstruct(&pCStr));
parsSkipWhitespace(pThis);
pC = rsCStrGetBufBeg(pThis->pCStr) + pThis->iCurrPos;
@@ -390,8 +390,8 @@ rsRetVal parsAddrWithBits(rsParsObj *pThis, struct NetAddr **pIP, int *pBits)
*/
while(pThis->iCurrPos < rsCStrLen(pThis->pCStr)
&& *pC != '/' && *pC != ',' && !isspace((int)*pC)) {
- if((iRet = rsCStrAppendChar(pCStr, *pC)) != RS_RET_OK) {
- rsCStrDestruct (&pCStr);
+ if((iRet = cstrAppendChar(pCStr, *pC)) != RS_RET_OK) {
+ cstrDestruct (&pCStr);
FINALIZE;
}
++pThis->iCurrPos;
@@ -399,15 +399,15 @@ rsRetVal parsAddrWithBits(rsParsObj *pThis, struct NetAddr **pIP, int *pBits)
}
/* We got the string, let's finish it... */
- if((iRet = rsCStrFinish(pCStr)) != RS_RET_OK) {
- rsCStrDestruct (&pCStr);
+ if((iRet = cstrFinalize(pCStr)) != RS_RET_OK) {
+ cstrDestruct(&pCStr);
FINALIZE;
}
/* now we have the string and must check/convert it to
* an NetAddr structure.
*/
- CHKiRet(rsCStrConvSzStrAndDestruct(pCStr, &pszIP, 0));
+ CHKiRet(cstrConvSzStrAndDestruct(pCStr, &pszIP, 0));
*pIP = calloc(1, sizeof(struct NetAddr));
diff --git a/plugins/imdiag/imdiag.c b/plugins/imdiag/imdiag.c
index cb1c1686..501077eb 100644
--- a/plugins/imdiag/imdiag.c
+++ b/plugins/imdiag/imdiag.c
@@ -207,7 +207,7 @@ doInjectMsg(int iNum)
CHKiRet(msgConstructWithTime(&pMsg, &stTime, ttGenTime));
CHKmalloc(pMsg->pszRawMsg = ustrdup(szMsg));
pMsg->iLenRawMsg = ustrlen(szMsg);
- MsgSetInputName(pMsg, UCHAR_CONSTANT("imdiag"));
+ MsgSetInputName(pMsg, UCHAR_CONSTANT("imdiag"), sizeof("imdiag")-1);
MsgSetFlowControlType(pMsg, eFLOWCTL_NO_DELAY);
pMsg->msgFlags = NEEDS_PARSING | PARSE_HOSTNAME;
pMsg->bParseHOSTNAME = 1;
diff --git a/plugins/imfile/imfile.c b/plugins/imfile/imfile.c
index 92fd30c3..86270e2d 100644
--- a/plugins/imfile/imfile.c
+++ b/plugins/imfile/imfile.c
@@ -56,6 +56,7 @@ DEF_IMOD_STATIC_DATA /* must be present, starts static data */
DEFobjCurrIf(errmsg)
DEFobjCurrIf(glbl)
DEFobjCurrIf(datetime)
+DEFobjCurrIf(strm)
typedef struct fileInfo_s {
uchar *pszFileName;
@@ -95,8 +96,7 @@ static rsRetVal enqLine(fileInfo_t *pInfo, cstr_t *cstrLine)
CHKiRet(msgConstruct(&pMsg));
MsgSetFlowControlType(pMsg, eFLOWCTL_FULL_DELAY);
- MsgSetInputName(pMsg, UCHAR_CONSTANT("imfile"));
- MsgSetUxTradMsg(pMsg, (char*)rsCStrGetSzStr(cstrLine));
+ MsgSetInputName(pMsg, UCHAR_CONSTANT("imfile"), sizeof("imfile")-1);
MsgSetRawMsg(pMsg, (char*)rsCStrGetSzStr(cstrLine));
MsgSetMSG(pMsg, (char*)rsCStrGetSzStr(cstrLine));
MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName());
@@ -139,16 +139,16 @@ openFile(fileInfo_t *pThis)
/* If we reach this point, we have a .si file */
- CHKiRet(strmConstruct(&psSF));
- CHKiRet(strmSettOperationsMode(psSF, STREAMMODE_READ));
- CHKiRet(strmSetsType(psSF, STREAMTYPE_FILE_SINGLE));
- CHKiRet(strmSetFName(psSF, pszSFNam, lenSFNam));
- CHKiRet(strmConstructFinalize(psSF));
+ CHKiRet(strm.Construct(&psSF));
+ CHKiRet(strm.SettOperationsMode(psSF, STREAMMODE_READ));
+ CHKiRet(strm.SetsType(psSF, STREAMTYPE_FILE_SINGLE));
+ CHKiRet(strm.SetFName(psSF, pszSFNam, lenSFNam));
+ CHKiRet(strm.ConstructFinalize(psSF));
/* read back in the object */
CHKiRet(obj.Deserialize(&pThis->pStrm, (uchar*) "strm", psSF, NULL, pThis));
- CHKiRet(strmSeekCurrOffs(pThis->pStrm));
+ CHKiRet(strm.SeekCurrOffs(pThis->pStrm));
/* OK, we could successfully read the file, so we now can request that it be deleted.
* If we need it again, it will be written on the next shutdown.
@@ -157,14 +157,14 @@ openFile(fileInfo_t *pThis)
finalize_it:
if(psSF != NULL)
- strmDestruct(&psSF);
+ strm.Destruct(&psSF);
if(iRet != RS_RET_OK) {
- CHKiRet(strmConstruct(&pThis->pStrm));
- CHKiRet(strmSettOperationsMode(pThis->pStrm, STREAMMODE_READ));
- CHKiRet(strmSetsType(pThis->pStrm, STREAMTYPE_FILE_MONITOR));
- CHKiRet(strmSetFName(pThis->pStrm, pThis->pszFileName, strlen((char*) pThis->pszFileName)));
- CHKiRet(strmConstructFinalize(pThis->pStrm));
+ CHKiRet(strm.Construct(&pThis->pStrm));
+ CHKiRet(strm.SettOperationsMode(pThis->pStrm, STREAMMODE_READ));
+ CHKiRet(strm.SetsType(pThis->pStrm, STREAMTYPE_FILE_MONITOR));
+ CHKiRet(strm.SetFName(pThis->pStrm, pThis->pszFileName, strlen((char*) pThis->pszFileName)));
+ CHKiRet(strm.ConstructFinalize(pThis->pStrm));
}
RETiRet;
@@ -203,7 +203,7 @@ static rsRetVal pollFile(fileInfo_t *pThis, int *pbHadFileData)
/* loop below will be exited when strmReadLine() returns EOF */
while(1) {
- CHKiRet(strmReadLine(pThis->pStrm, &pCStr));
+ CHKiRet(strm.ReadLine(pThis->pStrm, &pCStr));
*pbHadFileData = 1; /* this is just a flag, so set it and forget it */
CHKiRet(enqLine(pThis, pCStr)); /* process line */
rsCStrDestruct(&pCStr); /* discard string (must be done by us!) */
@@ -354,21 +354,20 @@ persistStrmState(fileInfo_t *pInfo)
ASSERT(pInfo != NULL);
/* TODO: create a function persistObj in obj.c? */
- CHKiRet(strmConstruct(&psSF));
- CHKiRet(strmSetDir(psSF, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir())));
- CHKiRet(strmSettOperationsMode(psSF, STREAMMODE_WRITE));
- CHKiRet(strmSetiAddtlOpenFlags(psSF, O_TRUNC));
- CHKiRet(strmSetsType(psSF, STREAMTYPE_FILE_SINGLE));
- CHKiRet(strmSetFName(psSF, pInfo->pszStateFile, strlen((char*) pInfo->pszStateFile)));
- CHKiRet(strmConstructFinalize(psSF));
+ CHKiRet(strm.Construct(&psSF));
+ CHKiRet(strm.SetDir(psSF, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir())));
+ CHKiRet(strm.SettOperationsMode(psSF, STREAMMODE_WRITE_TRUNC));
+ CHKiRet(strm.SetsType(psSF, STREAMTYPE_FILE_SINGLE));
+ CHKiRet(strm.SetFName(psSF, pInfo->pszStateFile, strlen((char*) pInfo->pszStateFile)));
+ CHKiRet(strm.ConstructFinalize(psSF));
- CHKiRet(strmSerialize(pInfo->pStrm, psSF));
+ CHKiRet(strm.Serialize(pInfo->pStrm, psSF));
- CHKiRet(strmDestruct(&psSF));
+ CHKiRet(strm.Destruct(&psSF));
finalize_it:
if(psSF != NULL)
- strmDestruct(&psSF);
+ strm.Destruct(&psSF);
RETiRet;
}
@@ -388,7 +387,7 @@ CODESTARTafterRun
for(i = 0 ; i < iFilPtr ; ++i) {
if(files[i].pStrm != NULL) { /* stream open? */
persistStrmState(&files[i]);
- strmDestruct(&(files[i].pStrm));
+ strm.Destruct(&(files[i].pStrm));
}
}
ENDafterRun
@@ -401,6 +400,7 @@ ENDafterRun
BEGINmodExit
CODESTARTmodExit
/* release objects we used */
+ objRelease(strm, CORE_COMPONENT);
objRelease(datetime, CORE_COMPONENT);
objRelease(glbl, CORE_COMPONENT);
objRelease(errmsg, CORE_COMPONENT);
@@ -512,6 +512,7 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(errmsg, CORE_COMPONENT));
CHKiRet(objUse(glbl, CORE_COMPONENT));
CHKiRet(objUse(datetime, CORE_COMPONENT));
+ CHKiRet(objUse(strm, CORE_COMPONENT));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfilename", 0, eCmdHdlrGetWord,
NULL, &pszFileName, STD_LOADABLE_MODULE_ID));
diff --git a/plugins/imklog/imklog.c b/plugins/imklog/imklog.c
index ecb6c100..420ebbf1 100644
--- a/plugins/imklog/imklog.c
+++ b/plugins/imklog/imklog.c
@@ -95,9 +95,7 @@ enqMsg(uchar *msg, uchar* pszTag, int iFacility, int iSeverity)
CHKiRet(msgConstruct(&pMsg));
MsgSetFlowControlType(pMsg, eFLOWCTL_LIGHT_DELAY);
- MsgSetInputName(pMsg, UCHAR_CONSTANT("imklog"));
- MsgSetRawMsg(pMsg, (char*)msg);
- MsgSetUxTradMsg(pMsg, (char*)msg);
+ MsgSetInputName(pMsg, UCHAR_CONSTANT("imklog"), sizeof("imklog")-1);
MsgSetRawMsg(pMsg, (char*)msg);
MsgSetMSG(pMsg, (char*)msg);
MsgSetRcvFrom(pMsg, glbl.GetLocalHostName());
diff --git a/plugins/imtcp/imtcp.c b/plugins/imtcp/imtcp.c
index 7300a799..e1f513c8 100644
--- a/plugins/imtcp/imtcp.c
+++ b/plugins/imtcp/imtcp.c
@@ -61,6 +61,7 @@
#include "netstrm.h"
#include "errmsg.h"
#include "tcpsrv.h"
+#include "ruleset.h"
#include "net.h" /* for permittedPeers, may be removed when this is removed */
MODULE_TYPE_INPUT
@@ -72,6 +73,7 @@ DEFobjCurrIf(tcps_sess)
DEFobjCurrIf(net)
DEFobjCurrIf(netstrm)
DEFobjCurrIf(errmsg)
+DEFobjCurrIf(ruleset)
/* Module static data */
static tcpsrv_t *pOurTcpsrv = NULL; /* our TCP server(listener) TODO: change for multiple instances */
@@ -84,6 +86,7 @@ static int iStrmDrvrMode = 0; /* mode for stream driver, driver-dependent (0 mos
static int iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER; /* addtl frame delimiter, e.g. for netscreen, default none */
static uchar *pszStrmDrvrAuthMode = NULL; /* authentication mode to use */
static uchar *pszInputName = NULL; /* value for inputname property, NULL is OK and handled by core engine */
+static ruleset_t *pBindRuleset = NULL; /* ruleset to bind listener to (use system default if unspecified) */
/* callbacks */
@@ -157,6 +160,27 @@ finalize_it:
}
+/* accept a new ruleset to bind. Checks if it exists and complains, if not */
+static rsRetVal setRuleset(void __attribute__((unused)) *pVal, uchar *pszName)
+{
+ ruleset_t *pRuleset;
+ rsRetVal localRet;
+ DEFiRet;
+
+ localRet = ruleset.GetRuleset(&pRuleset, pszName);
+ if(localRet == RS_RET_NOT_FOUND) {
+ errmsg.LogError(0, NO_ERRCODE, "error: ruleset '%s' not found - ignored", pszName);
+ }
+ CHKiRet(localRet);
+ pBindRuleset = pRuleset;
+ DBGPRINTF("imtcp current bind ruleset %p: '%s'\n", pRuleset, pszName);
+
+finalize_it:
+ free(pszName); /* no longer needed */
+ RETiRet;
+}
+
+
static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVal)
{
DEFiRet;
@@ -180,7 +204,8 @@ static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVa
}
}
- /* initialized, now add socket */
+ /* initialized, now add socket and listener params */
+ CHKiRet(tcpsrv.SetRuleset(pOurTcpsrv, pBindRuleset));
CHKiRet(tcpsrv.SetInputName(pOurTcpsrv, pszInputName == NULL ?
UCHAR_CONSTANT("imtcp") : pszInputName));
tcpsrv.configureTCPListen(pOurTcpsrv, pNewVal);
@@ -240,6 +265,7 @@ CODESTARTmodExit
objRelease(tcps_sess, LM_TCPSRV_FILENAME);
objRelease(tcpsrv, LM_TCPSRV_FILENAME);
objRelease(errmsg, CORE_COMPONENT);
+ objRelease(ruleset, CORE_COMPONENT);
ENDmodExit
@@ -275,6 +301,7 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(tcps_sess, LM_TCPSRV_FILENAME));
CHKiRet(objUse(tcpsrv, LM_TCPSRV_FILENAME));
CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(ruleset, CORE_COMPONENT));
/* register config file handlers */
CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverrun"), 0, eCmdHdlrGetWord,
@@ -291,6 +318,8 @@ CODEmodInit_QueryRegCFSLineHdlr
NULL, &iAddtlFrameDelim, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverinputname"), 0,
eCmdHdlrGetWord, NULL, &pszInputName, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverbindruleset"), 0,
+ eCmdHdlrGetWord, setRuleset, NULL, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("resetconfigvariables"), 1, eCmdHdlrCustomHandler,
resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
diff --git a/plugins/imudp/imudp.c b/plugins/imudp/imudp.c
index a486b818..97e66e8e 100644
--- a/plugins/imudp/imudp.c
+++ b/plugins/imudp/imudp.c
@@ -6,7 +6,7 @@
*
* File begun on 2007-12-21 by RGerhards (extracted from syslogd.c)
*
- * Copyright 2007 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007-2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -68,6 +68,7 @@ static uchar *pRcvBuf = NULL; /* receive buffer (for a single packet). We use a
* it so that we can check available memory in willRun() and request
* termination if we can not get it. -- rgerhards, 2007-12-27
*/
+// TODO: static ruleset_t *pBindRuleset = NULL; /* ruleset to bind listener to (use system default if unspecified) */
#define TIME_REQUERY_DFLT 2
static int iTimeRequery = TIME_REQUERY_DFLT;/* how often is time to be queried inside tight recv loop? 0=always */
@@ -97,7 +98,7 @@ static rsRetVal addListner(void __attribute__((unused)) *pVal, uchar *pNewVal)
else
bindAddr = pszBindAddr;
- dbgprintf("Trying to open syslog UDP ports at %s:%s.\n",
+ DBGPRINTF("Trying to open syslog UDP ports at %s:%s.\n",
(bindAddr == NULL) ? (uchar*)"*" : bindAddr, pNewVal);
newSocks = net.create_udp_socket(bindAddr, (pNewVal == NULL || *pNewVal == '\0') ? (uchar*) "514" : pNewVal, 1);
@@ -137,6 +138,30 @@ finalize_it:
}
+#if 0 /* TODO: implement when tehre is time, requires restructure of socket array! */
+/* accept a new ruleset to bind. Checks if it exists and complains, if not */
+static rsRetVal
+setRuleset(void __attribute__((unused)) *pVal, uchar *pszName)
+{
+ ruleset_t *pRuleset;
+ rsRetVal localRet;
+ DEFiRet;
+
+ localRet = ruleset.GetRuleset(&pRuleset, pszName);
+ if(localRet == RS_RET_NOT_FOUND) {
+ errmsg.LogError(0, NO_ERRCODE, "error: ruleset '%s' not found - ignored", pszName);
+ }
+ CHKiRet(localRet);
+ pBindRuleset = pRuleset;
+ DBGPRINTF("imudp current bind ruleset %p: '%s'\n", pRuleset, pszName);
+
+finalize_it:
+ free(pszName); /* no longer needed */
+ RETiRet;
+}
+#endif
+
+
/* This function is a helper to runInput. I have extracted it
* from the main loop just so that we do not have that large amount of code
* in a single place. This function takes a socket and pulls messages from
@@ -220,7 +245,7 @@ processSocket(int fd, struct sockaddr_storage *frominetPrev, int *pbIsPermitted,
CHKmalloc(pMsg->pszRawMsg = malloc(sizeof(uchar)* lenRcvBuf));
memcpy(pMsg->pszRawMsg, pRcvBuf, lenRcvBuf);
pMsg->iLenRawMsg = lenRcvBuf;
- MsgSetInputName(pMsg, UCHAR_CONSTANT("imudp"));
+ MsgSetInputName(pMsg, UCHAR_CONSTANT("imudp"), sizeof("imudp")-1);
MsgSetFlowControlType(pMsg, eFLOWCTL_NO_DELAY);
pMsg->msgFlags = NEEDS_PARSING | PARSE_HOSTNAME;
pMsg->bParseHOSTNAME = 1;
@@ -386,6 +411,10 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(net, LM_NET_FILENAME));
/* register config file handlers */
+ /* TODO: add - but this requires more changes, no time right now...
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"udpserverbindruleset", 0, eCmdHdlrGetWord,
+ setRuleset, NULL, STD_LOADABLE_MODULE_ID));
+ */
CHKiRet(omsdRegCFSLineHdlr((uchar *)"udpserverrun", 0, eCmdHdlrGetWord,
addListner, NULL, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"udpserveraddress", 0, eCmdHdlrGetWord,
diff --git a/plugins/omgssapi/omgssapi.c b/plugins/omgssapi/omgssapi.c
index 361f657f..7b5a46e1 100644
--- a/plugins/omgssapi/omgssapi.c
+++ b/plugins/omgssapi/omgssapi.c
@@ -44,6 +44,7 @@
#include <pthread.h>
#include <gssapi/gssapi.h>
#include "dirty.h"
+#include "conf.h"
#include "syslogd-types.h"
#include "srUtils.h"
#include "net.h"
diff --git a/plugins/ommail/ommail.c b/plugins/ommail/ommail.c
index 5faadce3..3a7669c9 100644
--- a/plugins/ommail/ommail.c
+++ b/plugins/ommail/ommail.c
@@ -44,7 +44,7 @@
#include <netdb.h>
#include <time.h>
#include <sys/socket.h>
-#include "dirty.h"
+#include "conf.h"
#include "syslogd-types.h"
#include "srUtils.h"
#include "cfsysline.h"
diff --git a/plugins/ommysql/ommysql.c b/plugins/ommysql/ommysql.c
index ecf738a9..d6870a7b 100644
--- a/plugins/ommysql/ommysql.c
+++ b/plugins/ommysql/ommysql.c
@@ -36,7 +36,7 @@
#include <errno.h>
#include <time.h>
#include <mysql.h>
-#include "dirty.h"
+#include "conf.h"
#include "syslogd-types.h"
#include "srUtils.h"
#include "template.h"
diff --git a/plugins/ompgsql/ompgsql.c b/plugins/ompgsql/ompgsql.c
index 003cf6a8..cb6b6a4d 100644
--- a/plugins/ompgsql/ompgsql.c
+++ b/plugins/ompgsql/ompgsql.c
@@ -40,7 +40,7 @@
#include <errno.h>
#include <time.h>
#include <libpq-fe.h>
-#include "dirty.h"
+#include "conf.h"
#include "syslogd-types.h"
#include "srUtils.h"
#include "template.h"
diff --git a/plugins/omprog/omprog.c b/plugins/omprog/omprog.c
index 2a078a6d..01fa7cea 100644
--- a/plugins/omprog/omprog.c
+++ b/plugins/omprog/omprog.c
@@ -36,7 +36,7 @@
#include <errno.h>
#include <unistd.h>
#include <wait.h>
-#include "dirty.h"
+#include "conf.h"
#include "syslogd-types.h"
#include "srUtils.h"
#include "template.h"
diff --git a/plugins/omrelp/omrelp.c b/plugins/omrelp/omrelp.c
index 8d74c82f..d5ef8b4f 100644
--- a/plugins/omrelp/omrelp.c
+++ b/plugins/omrelp/omrelp.c
@@ -36,7 +36,7 @@
#include <errno.h>
#include <ctype.h>
#include <librelp.h>
-#include "dirty.h"
+#include "conf.h"
#include "syslogd-types.h"
#include "srUtils.h"
#include "cfsysline.h"
diff --git a/plugins/omsnmp/omsnmp.c b/plugins/omsnmp/omsnmp.c
index 72fa8d64..4db60e62 100644
--- a/plugins/omsnmp/omsnmp.c
+++ b/plugins/omsnmp/omsnmp.c
@@ -36,7 +36,7 @@
#include <netdb.h>
#include <ctype.h>
#include <assert.h>
-#include "dirty.h"
+#include "conf.h"
#include "syslogd-types.h"
#include "cfsysline.h"
#include "module-template.h"
diff --git a/plugins/omstdout/omstdout.c b/plugins/omstdout/omstdout.c
index 181895a4..b9125f19 100644
--- a/plugins/omstdout/omstdout.c
+++ b/plugins/omstdout/omstdout.c
@@ -35,7 +35,7 @@
#include <signal.h>
#include <errno.h>
#include <unistd.h>
-#include "dirty.h"
+#include "conf.h"
#include "syslogd-types.h"
#include "srUtils.h"
#include "template.h"
diff --git a/plugins/omtemplate/omtemplate.c b/plugins/omtemplate/omtemplate.c
index e35968ad..5577f8c6 100644
--- a/plugins/omtemplate/omtemplate.c
+++ b/plugins/omtemplate/omtemplate.c
@@ -36,7 +36,7 @@
#include <signal.h>
#include <errno.h>
#include <time.h>
-#include "dirty.h"
+#include "conf.h"
#include "syslogd-types.h"
#include "srUtils.h"
#include "template.h"
diff --git a/runtime/Makefile.am b/runtime/Makefile.am
index 838c15bd..d274d9e4 100644
--- a/runtime/Makefile.am
+++ b/runtime/Makefile.am
@@ -40,6 +40,8 @@ librsyslog_la_SOURCES = \
obj.h \
modules.c \
modules.h \
+ apc.c \
+ apc.h \
sync.c \
sync.h \
expr.c \
@@ -68,6 +70,10 @@ librsyslog_la_SOURCES = \
vmop.h \
queue.c \
queue.h \
+ ruleset.c \
+ ruleset.h \
+ rule.c \
+ rule.h \
cfsysline.c \
cfsysline.h \
\
@@ -106,6 +112,17 @@ lmregexp_la_LDFLAGS = -module -avoid-version
lmregexp_la_LIBADD =
endif
+#
+# zlib support
+#
+if ENABLE_ZLIB
+pkglib_LTLIBRARIES += lmzlibw.la
+lmzlibw_la_SOURCES = zlibw.c zlibw.h
+lmzlibw_la_CPPFLAGS = $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
+lmzlibw_la_LDFLAGS = -module -avoid-version
+lmzlibw_la_LIBADD =
+endif
+
if ENABLE_INET
pkglib_LTLIBRARIES += lmnet.la lmnetstrms.la
#
diff --git a/runtime/apc.c b/runtime/apc.c
new file mode 100644
index 00000000..b0b5f298
--- /dev/null
+++ b/runtime/apc.c
@@ -0,0 +1,400 @@
+/* apc.c - asynchronous procedure call support
+ *
+ * An asynchronous procedure call (APC) is a procedure call (guess what) that is potentially run
+ * asynchronously to its main thread. It can be scheduled to occur at a caller-provided time.
+ * As long as the procedure has not been called, the APC entry may be modified by the caller
+ * or deleted. It is the caller's purpose to make sure proper synchronization is in place.
+ * The APC object only case about APC's own control structures (which *are* properly
+ * guarded by synchronization primitives).
+ *
+ * Module begun 2009-06-15 by Rainer Gerhards
+ *
+ * Copyright 2009 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of the rsyslog runtime library.
+ *
+ * The rsyslog runtime library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The rsyslog runtime library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <pthread.h>
+
+#include "rsyslog.h"
+#include "obj.h"
+#include "apc.h"
+#include "srUtils.h"
+
+/* static data */
+DEFobjStaticHelpers
+
+/* following is a used to implement a monotonically increasing id for the apcs. That
+ * ID can be used to cancel an apc request. Note that the ID is generated with modulo
+ * arithmetic, so at some point, it will wrap. Howerver, this happens at 2^32-1 at
+ * earliest, so this is not considered a problem.
+ */
+apc_id_t apcID = 0;
+
+/* private data structures */
+
+/* the apc list and its entries
+ * This is a doubly-linked list as we need to be able to do inserts
+ * and deletes right in the middle of the list. It is inspired by the
+ * Unix callout mechanism.
+ * Note that we support two generic caller-provided parameters as
+ * experience shows that at most two are often used. This causes very
+ * little overhead, but simplifies caller code in cases where exactly
+ * two parameters are needed. We hope this is a useful optimizaton.
+ * rgerhards, 2009-06-15
+ */
+typedef struct apc_list_s {
+ struct apc_list_s *pNext;
+ struct apc_list_s *pPrev;
+ apc_id_t id;
+ apc_t *pApc; /* pointer to the APC object to be scheduled */
+} apc_list_t;
+
+apc_list_t *apcListRoot = NULL;
+apc_list_t *apcListTail = NULL;
+pthread_mutex_t listMutex; /* needs to be locked for all list operations */
+
+
+/* destructor for the apc object */
+BEGINobjDestruct(apc) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDestruct(apc)
+ENDobjDestruct(apc)
+
+
+/* ------------------------------ APC list handling functions ------------------------------ */
+
+/* Function that handles changes to the list root. Most importantly, this function
+ * needs to schedule a new timer. It is OK to call this function with an empty list.
+ */
+static rsRetVal
+listRootChanged(void)
+{
+ DEFiRet;
+
+ if(apcListRoot == NULL)
+ FINALIZE;
+
+ // TODO: implement!
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* insert an apc entry into the APC list. The same entry MUST NOT already be present!
+ */
+static rsRetVal
+insertApc(apc_t *pThis, apc_id_t *pID)
+{
+ apc_list_t *pCurr;
+ apc_list_t *pNew;
+ DEFiRet;
+
+ CHKmalloc(pNew = (apc_list_t*) calloc(1, sizeof(apc_list_t)));
+ pNew->pApc = pThis;
+ pNew->id = *pID = apcID++;
+dbgprintf("insert apc %p, id %ld\n", pThis, pNew->id);
+
+ /* find right list location */
+ if(apcListRoot == NULL) {
+ /* no need to search, list is empty */
+ apcListRoot = pNew;
+ apcListTail = pNew;
+ CHKiRet(listRootChanged());
+ } else {
+ for(pCurr = apcListRoot ; pCurr != NULL ; pCurr = pCurr->pNext) {
+ if(pCurr->pApc->ttExec > pThis->ttExec)
+ break;
+ }
+
+ if(pCurr == NULL) {
+ /* insert at tail */
+ pNew->pPrev = apcListTail;
+ apcListTail->pNext = pNew;
+ apcListTail = pNew;
+ } else {
+ if(pCurr == apcListRoot) {
+ /* new first entry */
+ pCurr->pPrev = pNew;
+ pNew->pNext = pCurr;
+ apcListRoot = pNew;
+ CHKiRet(listRootChanged());
+ } else {
+ /* in the middle of the list */
+ pCurr->pPrev = pNew;
+ pNew->pNext = pCurr;
+ }
+ }
+ }
+
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Delete an apc entry from the APC list. It is OK if the entry is not found,
+ * in this case we assume it already has been processed.
+ */
+static rsRetVal
+deleteApc(apc_id_t id)
+{
+ apc_list_t *pCurr;
+ DEFiRet;
+
+dbgprintf("trying to delete apc %ld\n", id);
+ for(pCurr = apcListRoot ; pCurr != NULL ; pCurr = pCurr->pNext) {
+ if(pCurr->id == id) {
+RUNLOG_STR("apc id found, now deleting!\n");
+ if(pCurr == apcListRoot) {
+ apcListRoot = pCurr->pNext;
+ CHKiRet(listRootChanged());
+ } else {
+ pCurr->pPrev->pNext = pCurr->pNext;
+ }
+ if(pCurr->pNext == NULL) {
+ apcListTail = pCurr->pPrev;
+ } else {
+ pCurr->pNext->pPrev = pCurr->pPrev;
+ }
+ free(pCurr);
+ pCurr = NULL;
+ break;
+ }
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* unlist all elements up to the current timestamp. Return this as a seperate list
+ * to the caller. Returns an empty (NULL ptr) list if there are no such elements.
+ * The caller must handle that gracefully. The list is returned in the parameter.
+ */
+static rsRetVal
+unlistCurrent(apc_list_t **ppList)
+{
+ apc_list_t *pCurr;
+ time_t tCurr;
+ DEFiRet;
+ assert(ppList != NULL);
+
+ time(&tCurr);
+
+ if(apcListRoot == NULL || apcListRoot->pApc->ttExec > tCurr) {
+ *ppList = NULL;
+ FINALIZE;
+ }
+
+ *ppList = apcListRoot;
+ /* now search up to which entry we need to execute */
+ for(pCurr = apcListRoot ; pCurr != NULL && pCurr->pApc->ttExec <= tCurr ; pCurr = pCurr->pNext) {
+ /*JUST SKIP TO LAST ELEMENT*/;
+ }
+
+ if(pCurr == NULL) {
+ /* all elements can be unlisted */
+ apcListRoot = NULL;
+ apcListTail = NULL;
+ } else {
+ /* need to set a new root */
+ pCurr->pPrev->pNext = NULL; /* terminate newly unlisted list */
+ pCurr->pPrev = NULL; /* we are the new root */
+ apcListRoot = pCurr;
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* ------------------------------ END APC list handling functions ------------------------------ */
+
+
+/* execute all list elements that are currently scheduled for execution. We do this in two phases.
+ * In the first phase, we look the list mutex and move everything from the head of the queue to
+ * the current timestamp to a new to-be-executed list. Then we unlock the mutex and do the actual
+ * exec (which may take some time).
+ * Note that the caller is responsible for proper
+ * caller-level synchronization. The caller may schedule another Apc, this module must
+ * ensure that (and it does so by not locking the list mutex while we call the Apc).
+ * Note: this function "consumes" the apc_t, so it is no longer existing after this
+ * function returns.
+ */
+// TODO make static and associated with our own pthread-based timer
+rsRetVal
+execScheduled(void)
+{
+ apc_list_t *pExecList;
+ apc_list_t *pCurr;
+ apc_list_t *pNext;
+ DEFVARS_mutexProtection_uncond;
+ DEFiRet;
+
+ BEGIN_MTX_PROTECTED_OPERATIONS_UNCOND(&listMutex);
+ iRet = unlistCurrent(&pExecList);
+ END_MTX_PROTECTED_OPERATIONS_UNCOND(&listMutex);
+ CHKiRet(iRet);
+
+ DBGPRINTF("running apc scheduler - we have %s to execute\n",
+ pExecList == NULL ? "nothing" : "something");
+ for(pCurr = pExecList ; pCurr != NULL ; pCurr = pNext) {
+dbgprintf("executing apc list entry %p, apc %p\n", pCurr, pCurr->pApc);
+ pNext = pCurr->pNext;
+ pCurr->pApc->pProc(pCurr->pApc->param1, pCurr->pApc->param2);
+ apcDestruct(&pCurr->pApc);
+ free(pCurr);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Standard-Constructor
+ */
+BEGINobjConstruct(apc) /* be sure to specify the object type also in END macro! */
+ENDobjConstruct(apc)
+
+
+/* ConstructionFinalizer
+ * Note that we use a non-standard calling interface: pID returns the current APC
+ * id. This is the only way to handle the situation without the need for extra
+ * locking.
+ * rgerhards, 2008-01-09
+ */
+static rsRetVal
+apcConstructFinalize(apc_t *pThis, apc_id_t *pID)
+{
+ DEFVARS_mutexProtection_uncond;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, apc);
+ assert(pID != NULL);
+ BEGIN_MTX_PROTECTED_OPERATIONS_UNCOND(&listMutex);
+ insertApc(pThis, pID);
+ END_MTX_PROTECTED_OPERATIONS_UNCOND(&listMutex);
+RUNLOG_STR("apcConstructFinalize post mutex unlock\n");
+ RETiRet;
+}
+
+
+/* some set methods */
+static rsRetVal
+SetProcedure(apc_t *pThis, void (*pProc)(void*, void*))
+{
+ ISOBJ_TYPE_assert(pThis, apc);
+ pThis->pProc = pProc;
+ return RS_RET_OK;
+}
+static rsRetVal
+SetParam1(apc_t *pThis, void *param1)
+{
+ ISOBJ_TYPE_assert(pThis, apc);
+ pThis->param1 = param1;
+ return RS_RET_OK;
+}
+static rsRetVal
+SetParam2(apc_t *pThis, void *param2)
+{
+ ISOBJ_TYPE_assert(pThis, apc);
+ pThis->param1 = param2;
+ return RS_RET_OK;
+}
+
+
+/* cancel an Apc request, ID is provided. It is OK if the ID can not be found, this may
+ * happen if the Apc was executed in the mean time. So it is safe to call CancelApc() at
+ * any time.
+ */
+static rsRetVal
+CancelApc(apc_id_t id)
+{
+ DEFVARS_mutexProtection_uncond;
+
+ BEGIN_MTX_PROTECTED_OPERATIONS_UNCOND(&listMutex);
+ deleteApc(id);
+ END_MTX_PROTECTED_OPERATIONS_UNCOND(&listMutex);
+ return RS_RET_OK;
+}
+
+
+/* debugprint for the apc object */
+BEGINobjDebugPrint(apc) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDebugPrint(apc)
+ dbgoprint((obj_t*) pThis, "APC module, currently no state info available\n");
+ENDobjDebugPrint(apc)
+
+
+/* queryInterface function
+ */
+BEGINobjQueryInterface(apc)
+CODESTARTobjQueryInterface(apc)
+ if(pIf->ifVersion != apcCURR_IF_VERSION) { /* check for current version, increment on each change */
+ ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED);
+ }
+
+ /* ok, we have the right interface, so let's fill it
+ * Please note that we may also do some backwards-compatibility
+ * work here (if we can support an older interface version - that,
+ * of course, also affects the "if" above).
+ */
+ pIf->Construct = apcConstruct;
+ pIf->ConstructFinalize = apcConstructFinalize;
+ pIf->Destruct = apcDestruct;
+ pIf->DebugPrint = apcDebugPrint;
+ pIf->CancelApc = CancelApc;
+ pIf->SetProcedure = SetProcedure;
+ pIf->SetParam1 = SetParam1;
+ pIf->SetParam2 = SetParam2;
+finalize_it:
+ENDobjQueryInterface(apc)
+
+
+/* Exit the apc class.
+ * rgerhards, 2009-04-06
+ */
+BEGINObjClassExit(apc, OBJ_IS_CORE_MODULE) /* class, version */
+ //objRelease(apcstk, CORE_COMPONENT);
+ pthread_mutex_destroy(&listMutex);
+ENDObjClassExit(apc)
+
+
+/* Initialize the apc class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-02-19
+ */
+BEGINObjClassInit(apc, 1, OBJ_IS_CORE_MODULE) /* class, version */
+ /* request objects we use */
+ //CHKiRet(objUse(apcstk, CORE_COMPONENT));
+
+ /* set our own handlers */
+ OBJSetMethodHandler(objMethod_DEBUGPRINT, apcDebugPrint);
+ OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, apcConstructFinalize);
+
+ /* do other initializations */
+ pthread_mutex_init(&listMutex, NULL);
+ENDObjClassInit(apc)
+
+/* vi:set ai:
+ */
diff --git a/runtime/apc.h b/runtime/apc.h
new file mode 100644
index 00000000..7c679b97
--- /dev/null
+++ b/runtime/apc.h
@@ -0,0 +1,56 @@
+/* The apc object.
+ *
+ * See apc.c for more information.
+ *
+ * Copyright 2009 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of the rsyslog runtime library.
+ *
+ * The rsyslog runtime library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The rsyslog runtime library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
+ */
+#ifndef INCLUDED_APC_H
+#define INCLUDED_APC_H
+
+/* the apc object */
+typedef struct apc_s {
+ BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
+ time_t ttExec; /* when to call procedure (so far seconds...) */
+ void (*pProc)(void*, void*); /* which procedure to call */
+ void *param1; /* user-supplied parameters */
+ void *param2; /* user-supplied parameters */
+} apc_t;
+
+typedef unsigned long apc_id_t; /* monotonically incrementing apc ID */
+
+/* interfaces */
+BEGINinterface(apc) /* name must also be changed in ENDinterface macro! */
+ INTERFACEObjDebugPrint(apc);
+ rsRetVal (*Construct)(apc_t **ppThis);
+ rsRetVal (*ConstructFinalize)(apc_t *pThis, apc_id_t *);
+ rsRetVal (*Destruct)(apc_t **ppThis);
+ rsRetVal (*SetProcedure)(apc_t *pThis, void (*pProc)(void*, void*));
+ rsRetVal (*SetParam1)(apc_t *pThis, void *);
+ rsRetVal (*SetParam2)(apc_t *pThis, void *);
+ rsRetVal (*CancelApc)(apc_id_t);
+ENDinterface(apc)
+#define apcCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+
+
+/* prototypes */
+PROTOTYPEObj(apc);
+
+#endif /* #ifndef INCLUDED_APC_H */
diff --git a/runtime/cfsysline.c b/runtime/cfsysline.c
index e1e4a6a4..184c0d87 100644
--- a/runtime/cfsysline.c
+++ b/runtime/cfsysline.c
@@ -462,7 +462,7 @@ getWord(uchar **pp, cstr_t **ppStrB)
ASSERT(*pp != NULL);
ASSERT(ppStrB != NULL);
- CHKiRet(rsCStrConstruct(ppStrB));
+ CHKiRet(cstrConstruct(ppStrB));
skipWhiteSpace(pp); /* skip over any whitespace */
@@ -470,9 +470,9 @@ getWord(uchar **pp, cstr_t **ppStrB)
p = *pp;
while(*p && !isspace((int) *p)) {
- CHKiRet(rsCStrAppendChar(*ppStrB, *p++));
+ CHKiRet(cstrAppendChar(*ppStrB, *p++));
}
- CHKiRet(rsCStrFinish(*ppStrB));
+ CHKiRet(cstrFinalize(*ppStrB));
*pp = p;
@@ -506,7 +506,7 @@ static rsRetVal doGetWord(uchar **pp, rsRetVal (*pSetHdlr)(void*, uchar*), void
ASSERT(*pp != NULL);
CHKiRet(getWord(pp, &pStrB));
- CHKiRet(rsCStrConvSzStrAndDestruct(pStrB, &pNewVal, 0));
+ CHKiRet(cstrConvSzStrAndDestruct(pStrB, &pNewVal, 0));
pStrB = NULL;
/* we got the word, now set it */
@@ -525,7 +525,7 @@ static rsRetVal doGetWord(uchar **pp, rsRetVal (*pSetHdlr)(void*, uchar*), void
finalize_it:
if(iRet != RS_RET_OK) {
if(pStrB != NULL)
- rsCStrDestruct(&pStrB);
+ cstrDestruct(&pStrB);
}
RETiRet;
@@ -548,7 +548,7 @@ doSyslogName(uchar **pp, rsRetVal (*pSetHdlr)(void*, int), void *pVal, syslogNam
ASSERT(*pp != NULL);
CHKiRet(getWord(pp, &pStrB)); /* get word */
- iNewVal = decodeSyslogName(rsCStrGetSzStr(pStrB), pNameTable);
+ iNewVal = decodeSyslogName(cstrGetSzStr(pStrB), pNameTable);
if(pSetHdlr == NULL) {
/* we should set value directly to var */
@@ -814,7 +814,7 @@ rsRetVal regCfSysLineHdlr(uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlTy
CHKiRet(cslcConstruct(&pThis, bChainingPermitted));
CHKiRet_Hdlr(cslcAddHdlr(pThis, eType, pHdlr, pData, pOwnerCookie)) {
cslcDestruct(pThis);
- goto finalize_it;
+ FINALIZE;
}
/* important: add to list, AFTER everything else is OK. Else
* we mess up things in the error case.
@@ -825,7 +825,7 @@ rsRetVal regCfSysLineHdlr(uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlTy
}
CHKiRet_Hdlr(llAppend(&llCmdList, pMyCmdName, (void*) pThis)) {
cslcDestruct(pThis);
- goto finalize_it;
+ FINALIZE;
}
} else {
/* command already exists, are we allowed to chain? */
@@ -834,7 +834,7 @@ rsRetVal regCfSysLineHdlr(uchar *pCmdName, int bChainingPermitted, ecslCmdHdrlTy
}
CHKiRet_Hdlr(cslcAddHdlr(pThis, eType, pHdlr, pData, pOwnerCookie)) {
cslcDestruct(pThis);
- goto finalize_it;
+ FINALIZE;
}
}
diff --git a/runtime/conf.c b/runtime/conf.c
index 0d0a8bd3..538af7d6 100644
--- a/runtime/conf.c
+++ b/runtime/conf.c
@@ -69,13 +69,16 @@
#include "expr.h"
#include "ctok.h"
#include "ctok_token.h"
+#include "rule.h"
+#include "ruleset.h"
+#include "unicode-helper.h"
#ifdef OS_SOLARIS
# define NAME_MAX MAXNAMELEN
#endif
/* forward definitions */
-static rsRetVal cfline(uchar *line, selector_t **pfCurr);
+static rsRetVal cfline(uchar *line, rule_t **pfCurr);
static rsRetVal processConfFile(uchar *pConfFile);
@@ -87,6 +90,8 @@ DEFobjCurrIf(ctok_token)
DEFobjCurrIf(module)
DEFobjCurrIf(errmsg)
DEFobjCurrIf(net)
+DEFobjCurrIf(rule)
+DEFobjCurrIf(ruleset)
static int iNbrActions; /* number of actions the running config has. Needs to be init on ReInitConf() */
@@ -392,15 +397,17 @@ finalize_it:
static rsRetVal
processConfFile(uchar *pConfFile)
{
- DEFiRet;
int iLnNbr = 0;
FILE *cf;
- selector_t *fCurr = NULL;
+ rule_t *pCurrRule = NULL;
uchar *p;
uchar cbuf[CFGLNSIZ];
uchar *cline;
int i;
int bHadAnError = 0;
+ uchar *pszOrgLine = NULL;
+ size_t lenLine;
+ DEFiRet;
ASSERT(pConfFile != NULL);
if((cf = fopen((char*)pConfFile, "r")) == NULL) {
@@ -413,9 +420,12 @@ processConfFile(uchar *pConfFile)
while (fgets((char*)cline, sizeof(cbuf) - (cline - cbuf), cf) != NULL) {
++iLnNbr;
/* drop LF - TODO: make it better, replace fgets(), but its clean as it is */
- if(cline[strlen((char*)cline)-1] == '\n') {
- cline[strlen((char*)cline) -1] = '\0';
+ lenLine = ustrlen(cline);
+ if(cline[lenLine-1] == '\n') {
+ cline[lenLine-1] = '\0';
}
+ free(pszOrgLine);
+ pszOrgLine = ustrdup(cline); /* save if needed for errmsg, NULL ptr is OK */
/* check for end-of-section, comments, strip off trailing
* spaces and newline character.
*/
@@ -429,7 +439,6 @@ processConfFile(uchar *pConfFile)
* TODO: review the code at whole - this is highly suspect (but will go away
* once we do the rest of RainerScript).
*/
- /* was: strcpy((char*)cline, (char*)p); */
for( i = 0 ; p[i] != '\0' ; ++i) {
cline[i] = p[i];
}
@@ -453,7 +462,7 @@ processConfFile(uchar *pConfFile)
/* we now have the complete line, and are positioned at the first non-whitespace
* character. So let's process it
*/
- if(cfline(cbuf, &fCurr) != RS_RET_OK) {
+ if(cfline(cbuf, &pCurrRule) != RS_RET_OK) {
/* we log a message, but otherwise ignore the error. After all, the next
* line can be correct. -- rgerhards, 2007-08-02
*/
@@ -461,28 +470,32 @@ processConfFile(uchar *pConfFile)
dbgprintf("config line NOT successfully processed\n");
snprintf((char*)szErrLoc, sizeof(szErrLoc) / sizeof(uchar),
"%s, line %d", pConfFile, iLnNbr);
- errmsg.LogError(0, NO_ERRCODE, "the last error occured in %s", (char*)szErrLoc);
+ errmsg.LogError(0, NO_ERRCODE, "the last error occured in %s:\"%s\"", (char*)szErrLoc, (char*)pszOrgLine);
bHadAnError = 1;
}
}
/* we probably have one selector left to be added - so let's do that now */
- CHKiRet(selectorAddList(fCurr));
+ if(pCurrRule != NULL) {
+ CHKiRet(ruleset.AddRule(rule.GetAssRuleset(pCurrRule), &pCurrRule));
+ }
/* close the configuration file */
- (void) fclose(cf);
+ fclose(cf);
finalize_it:
if(iRet != RS_RET_OK) {
char errStr[1024];
- if(fCurr != NULL)
- selectorDestruct(fCurr);
+ if(pCurrRule != NULL)
+ rule.Destruct(&pCurrRule);
rs_strerror_r(errno, errStr, sizeof(errStr));
dbgprintf("error %d processing config file '%s'; os error (if any): %s\n",
iRet, pConfFile, errStr);
}
+ free(pszOrgLine);
+
if(bHadAnError && (iRet == RS_RET_OK)) { /* a bit dirty, enhance in future releases */
iRet = RS_RET_NONFATAL_CONFIG_ERR;
}
@@ -526,17 +539,15 @@ rsRetVal cflineParseTemplateName(uchar** pp, omodStringRequest_t *pOMSR, int iEn
tplName = (uchar*) strdup((char*)dfltTplName);
} else {
/* template specified, pick it up */
- if(rsCStrConstruct(&pStrB) != RS_RET_OK) {
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- }
+ CHKiRet(cstrConstruct(&pStrB));
/* now copy the string */
while(*p && *p != '#' && !isspace((int) *p)) {
- CHKiRet(rsCStrAppendChar(pStrB, *p));
+ CHKiRet(cstrAppendChar(pStrB, *p));
++p;
}
- CHKiRet(rsCStrFinish(pStrB));
- CHKiRet(rsCStrConvSzStrAndDestruct(pStrB, &tplName, 0));
+ CHKiRet(cstrFinalize(pStrB));
+ CHKiRet(cstrConvSzStrAndDestruct(pStrB, &tplName, 0));
}
iRet = OMSRsetEntry(pOMSR, iEntry, tplName, iTplOpts);
@@ -588,7 +599,7 @@ cflineParseFileName(uchar* p, uchar *pFileName, omodStringRequest_t *pOMSR, int
* rgerhards 2005-09-15
*/
/* GPLv3 - stems back to sysklogd */
-static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register selector_t *f)
+static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register rule_t *pRule)
{
uchar *p;
register uchar *q;
@@ -603,17 +614,17 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register selector_t *f
ASSERT(pline != NULL);
ASSERT(*pline != NULL);
- ASSERT(f != NULL);
+ ISOBJ_TYPE_assert(pRule, rule);
dbgprintf(" - traditional PRI filter\n");
errno = 0; /* keep strerror_r() stuff out of logerror messages */
- f->f_filter_type = FILTER_PRI;
+ pRule->f_filter_type = FILTER_PRI;
/* Note: file structure is pre-initialized to zero because it was
* created with calloc()!
*/
for (i = 0; i <= LOG_NFACILITIES; i++) {
- f->f_filterData.f_pmask[i] = TABLE_NOPRI;
+ pRule->f_filterData.f_pmask[i] = TABLE_NOPRI;
}
/* scan through the list of selectors */
@@ -668,32 +679,32 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register selector_t *f
for (i = 0; i <= LOG_NFACILITIES; i++) {
if ( pri == INTERNAL_NOPRI ) {
if ( ignorepri )
- f->f_filterData.f_pmask[i] = TABLE_ALLPRI;
+ pRule->f_filterData.f_pmask[i] = TABLE_ALLPRI;
else
- f->f_filterData.f_pmask[i] = TABLE_NOPRI;
+ pRule->f_filterData.f_pmask[i] = TABLE_NOPRI;
}
else if ( singlpri ) {
if ( ignorepri )
- f->f_filterData.f_pmask[i] &= ~(1<<pri);
+ pRule->f_filterData.f_pmask[i] &= ~(1<<pri);
else
- f->f_filterData.f_pmask[i] |= (1<<pri);
+ pRule->f_filterData.f_pmask[i] |= (1<<pri);
}
else
{
if ( pri == TABLE_ALLPRI ) {
if ( ignorepri )
- f->f_filterData.f_pmask[i] = TABLE_NOPRI;
+ pRule->f_filterData.f_pmask[i] = TABLE_NOPRI;
else
- f->f_filterData.f_pmask[i] = TABLE_ALLPRI;
+ pRule->f_filterData.f_pmask[i] = TABLE_ALLPRI;
}
else
{
if ( ignorepri )
for (i2= 0; i2 <= pri; ++i2)
- f->f_filterData.f_pmask[i] &= ~(1<<i2);
+ pRule->f_filterData.f_pmask[i] &= ~(1<<i2);
else
for (i2= 0; i2 <= pri; ++i2)
- f->f_filterData.f_pmask[i] |= (1<<i2);
+ pRule->f_filterData.f_pmask[i] |= (1<<i2);
}
}
}
@@ -708,27 +719,27 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register selector_t *f
if ( pri == INTERNAL_NOPRI ) {
if ( ignorepri )
- f->f_filterData.f_pmask[i >> 3] = TABLE_ALLPRI;
+ pRule->f_filterData.f_pmask[i >> 3] = TABLE_ALLPRI;
else
- f->f_filterData.f_pmask[i >> 3] = TABLE_NOPRI;
+ pRule->f_filterData.f_pmask[i >> 3] = TABLE_NOPRI;
} else if ( singlpri ) {
if ( ignorepri )
- f->f_filterData.f_pmask[i >> 3] &= ~(1<<pri);
+ pRule->f_filterData.f_pmask[i >> 3] &= ~(1<<pri);
else
- f->f_filterData.f_pmask[i >> 3] |= (1<<pri);
+ pRule->f_filterData.f_pmask[i >> 3] |= (1<<pri);
} else {
if ( pri == TABLE_ALLPRI ) {
if ( ignorepri )
- f->f_filterData.f_pmask[i >> 3] = TABLE_NOPRI;
+ pRule->f_filterData.f_pmask[i >> 3] = TABLE_NOPRI;
else
- f->f_filterData.f_pmask[i >> 3] = TABLE_ALLPRI;
+ pRule->f_filterData.f_pmask[i >> 3] = TABLE_ALLPRI;
} else {
if ( ignorepri )
for (i2= 0; i2 <= pri; ++i2)
- f->f_filterData.f_pmask[i >> 3] &= ~(1<<i2);
+ pRule->f_filterData.f_pmask[i >> 3] &= ~(1<<i2);
else
for (i2= 0; i2 <= pri; ++i2)
- f->f_filterData.f_pmask[i >> 3] |= (1<<i2);
+ pRule->f_filterData.f_pmask[i >> 3] |= (1<<i2);
}
}
}
@@ -754,7 +765,7 @@ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register selector_t *f
* A pointer to that beginnig is passed back to the caller.
* rgerhards 2008-01-19
*/
-static rsRetVal cflineProcessIfFilter(uchar **pline, register selector_t *f)
+static rsRetVal cflineProcessIfFilter(uchar **pline, register rule_t *f)
{
DEFiRet;
ctok_t *tok;
@@ -767,7 +778,6 @@ static rsRetVal cflineProcessIfFilter(uchar **pline, register selector_t *f)
dbgprintf(" - general expression-based filter\n");
errno = 0; /* keep strerror_r() stuff out of logerror messages */
-dbgprintf("calling expression parser, pp %p ('%s')\n", *pline, *pline);
f->f_filter_type = FILTER_EXPR;
/* if we come to over here, pline starts with "if ". We just skip that part. */
@@ -823,7 +833,7 @@ finalize_it:
* of the action part. A pointer to that beginnig is passed back to the caller.
* rgerhards 2005-09-15
*/
-static rsRetVal cflineProcessPropFilter(uchar **pline, register selector_t *f)
+static rsRetVal cflineProcessPropFilter(uchar **pline, register rule_t *f)
{
rsParsObj *pPars;
cstr_t *pCSCompOp;
@@ -925,7 +935,7 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register selector_t *f)
*/
static rsRetVal cflineProcessHostSelector(uchar **pline)
{
- rsRetVal iRet;
+ DEFiRet;
ASSERT(pline != NULL);
ASSERT(*pline != NULL);
@@ -951,21 +961,20 @@ static rsRetVal cflineProcessHostSelector(uchar **pline)
dbgprintf("resetting BSD-like hostname filter\n");
eDfltHostnameCmpMode = HN_NO_COMP;
if(pDfltHostnameCmp != NULL) {
- if((iRet = rsCStrSetSzStr(pDfltHostnameCmp, NULL)) != RS_RET_OK)
- return(iRet);
+ CHKiRet(rsCStrSetSzStr(pDfltHostnameCmp, NULL));
}
} else {
dbgprintf("setting BSD-like hostname filter to '%s'\n", *pline);
if(pDfltHostnameCmp == NULL) {
/* create string for parser */
- if((iRet = rsCStrConstructFromszStr(&pDfltHostnameCmp, *pline)) != RS_RET_OK)
- return(iRet);
+ CHKiRet(rsCStrConstructFromszStr(&pDfltHostnameCmp, *pline));
} else { /* string objects exists, just update... */
- if((iRet = rsCStrSetSzStr(pDfltHostnameCmp, *pline)) != RS_RET_OK)
- return(iRet);
+ CHKiRet(rsCStrSetSzStr(pDfltHostnameCmp, *pline));
}
}
- return RS_RET_OK;
+
+finalize_it:
+ RETiRet;
}
@@ -976,7 +985,7 @@ static rsRetVal cflineProcessHostSelector(uchar **pline)
*/
static rsRetVal cflineProcessTagSelector(uchar **pline)
{
- rsRetVal iRet;
+ DEFiRet;
ASSERT(pline != NULL);
ASSERT(*pline != NULL);
@@ -995,29 +1004,28 @@ static rsRetVal cflineProcessTagSelector(uchar **pline)
if(**pline != '\0' && **pline == '*' && *(*pline+1) == '\0') {
dbgprintf("resetting programname filter\n");
if(pDfltProgNameCmp != NULL) {
- if((iRet = rsCStrSetSzStr(pDfltProgNameCmp, NULL)) != RS_RET_OK)
- return(iRet);
+ CHKiRet(rsCStrSetSzStr(pDfltProgNameCmp, NULL));
}
} else {
dbgprintf("setting programname filter to '%s'\n", *pline);
if(pDfltProgNameCmp == NULL) {
/* create string for parser */
- if((iRet = rsCStrConstructFromszStr(&pDfltProgNameCmp, *pline)) != RS_RET_OK)
- return(iRet);
+ CHKiRet(rsCStrConstructFromszStr(&pDfltProgNameCmp, *pline));
} else { /* string objects exists, just update... */
- if((iRet = rsCStrSetSzStr(pDfltProgNameCmp, *pline)) != RS_RET_OK)
- return(iRet);
+ CHKiRet(rsCStrSetSzStr(pDfltProgNameCmp, *pline));
}
}
- return RS_RET_OK;
+
+finalize_it:
+ RETiRet;
}
/* read the filter part of a configuration line and store the filter
- * in the supplied selector_t
+ * in the supplied rule_t
* rgerhards, 2007-08-01
*/
-static rsRetVal cflineDoFilter(uchar **pp, selector_t *f)
+static rsRetVal cflineDoFilter(uchar **pp, rule_t *f)
{
DEFiRet;
@@ -1110,17 +1118,15 @@ static rsRetVal cflineDoAction(uchar **p, action_t **ppAction)
/* Process a configuration file line in traditional "filter selector" format
- * or one that builds upon this format.
+ * or one that builds upon this format. Note that ppRule may be a NULL pointer,
+ * which is valid and happens if there is no previous line (right at the start
+ * of the master config file!).
*/
-static rsRetVal cflineClassic(uchar *p, selector_t **pfCurr)
+static rsRetVal
+cflineClassic(uchar *p, rule_t **ppRule)
{
DEFiRet;
action_t *pAction;
- selector_t *fCurr;
-
- ASSERT(pfCurr != NULL);
-
- fCurr = *pfCurr;
/* lines starting with '&' have no new filters and just add
* new actions to the currently processed selector.
@@ -1138,16 +1144,19 @@ static rsRetVal cflineClassic(uchar *p, selector_t **pfCurr)
* selector is NULL, which means we do not need to care about it at
* all. -- rgerhards, 2007-08-01
*/
- CHKiRet(selectorAddList(fCurr));
- CHKiRet(selectorConstruct(&fCurr)); /* create "fresh" selector */
- CHKiRet(cflineDoFilter(&p, fCurr)); /* pull filters */
+ if(*ppRule != NULL) {
+ CHKiRet(ruleset.AddRule(rule.GetAssRuleset(*ppRule), ppRule));
+ }
+ CHKiRet(rule.Construct(ppRule)); /* create "fresh" selector */
+ CHKiRet(rule.SetAssRuleset(*ppRule, ruleset.GetCurrent())); /* create "fresh" selector */
+ CHKiRet(rule.ConstructFinalize(*ppRule)); /* create "fresh" selector */
+ CHKiRet(cflineDoFilter(&p, *ppRule)); /* pull filters */
}
CHKiRet(cflineDoAction(&p, &pAction));
- CHKiRet(llAppend(&fCurr->llActList, NULL, (void*) pAction));
+ CHKiRet(llAppend(&(*ppRule)->llActList, NULL, (void*) pAction));
finalize_it:
- *pfCurr = fCurr;
RETiRet;
}
@@ -1157,7 +1166,7 @@ finalize_it:
* rgerhards, 2007-08-01
*/
static rsRetVal
-cfline(uchar *line, selector_t **pfCurr)
+cfline(uchar *line, rule_t **pfCurr)
{
DEFiRet;
@@ -1254,6 +1263,8 @@ CODESTARTObjClassExit(conf)
objRelease(module, CORE_COMPONENT);
objRelease(errmsg, CORE_COMPONENT);
objRelease(net, LM_NET_FILENAME);
+ objRelease(rule, CORE_COMPONENT);
+ objRelease(ruleset, CORE_COMPONENT);
ENDObjClassExit(conf)
@@ -1269,6 +1280,8 @@ BEGINAbstractObjClassInit(conf, 1, OBJ_IS_CORE_MODULE) /* class, version - CHANG
CHKiRet(objUse(module, CORE_COMPONENT));
CHKiRet(objUse(errmsg, CORE_COMPONENT));
CHKiRet(objUse(net, LM_NET_FILENAME)); /* TODO: make this dependcy go away! */
+ CHKiRet(objUse(rule, CORE_COMPONENT));
+ CHKiRet(objUse(ruleset, CORE_COMPONENT));
ENDObjClassInit(conf)
/* vi:set ai:
diff --git a/runtime/conf.h b/runtime/conf.h
index 2494d4dc..25b887be 100644
--- a/runtime/conf.h
+++ b/runtime/conf.h
@@ -35,7 +35,7 @@ BEGINinterface(conf) /* name must also be changed in ENDinterface macro! */
rsRetVal (*cfsysline)(uchar *p);
rsRetVal (*doModLoad)(uchar **pp, __attribute__((unused)) void* pVal);
rsRetVal (*doIncludeLine)(uchar **pp, __attribute__((unused)) void* pVal);
- rsRetVal (*cfline)(uchar *line, selector_t **pfCurr);
+ rsRetVal (*cfline)(uchar *line, rule_t **pfCurr);
rsRetVal (*processConfFile)(uchar *pConfFile);
rsRetVal (*ReInitConf)(void);
rsRetVal (*GetNbrActActions)(int *);
@@ -51,5 +51,9 @@ PROTOTYPEObj(conf);
extern EHostnameCmpMode eDfltHostnameCmpMode;
extern cstr_t *pDfltHostnameCmp;
extern cstr_t *pDfltProgNameCmp;
+/* TODO: the following 2 need to go in conf obj interface... */
+rsRetVal cflineParseTemplateName(uchar** pp, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts, uchar *dfltTplName);
+rsRetVal cflineParseFileName(uchar* p, uchar *pFileName, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts, uchar *pszTpl);
+
#endif /* #ifndef INCLUDED_CONF_H */
diff --git a/runtime/ctok.c b/runtime/ctok.c
index d2cd8bbd..263e656c 100644
--- a/runtime/ctok.c
+++ b/runtime/ctok.c
@@ -269,7 +269,7 @@ ctokGetVar(ctok_t *pThis, ctok_token_t *pToken)
}
CHKiRet(ctokUngetCharFromStream(pThis, c)); /* put not processed char back */
- CHKiRet(rsCStrFinish(pstrVal));
+ CHKiRet(cstrFinalize(pstrVal));
CHKiRet(var.SetString(pToken->pVar, pstrVal));
pstrVal = NULL;
@@ -319,7 +319,7 @@ ctokGetSimpStr(ctok_t *pThis, ctok_token_t *pToken)
}
CHKiRet(ctokGetCharFromStream(pThis, &c));
}
- CHKiRet(rsCStrFinish(pStrB));
+ CHKiRet(cstrFinalize(pstrVal));
CHKiRet(var.SetString(pToken->pVar, pstrVal));
pstrVal = NULL;
diff --git a/runtime/datetime.c b/runtime/datetime.c
index 2a0df91a..afd709c3 100644
--- a/runtime/datetime.c
+++ b/runtime/datetime.c
@@ -154,11 +154,10 @@ static void getCurrTime(struct syslogTime *t, time_t *ttSeconds)
static int srSLMGParseInt32(uchar** ppsz)
{
- int i;
+ register int i;
i = 0;
- while(isdigit((int) **ppsz))
- {
+ while(isdigit((int) **ppsz)) {
i = i * 10 + **ppsz - '0';
++(*ppsz);
}
diff --git a/runtime/linkedlist.c b/runtime/linkedlist.c
index 8f842e43..cc095f6e 100644
--- a/runtime/linkedlist.c
+++ b/runtime/linkedlist.c
@@ -398,7 +398,7 @@ rsRetVal llExecFunc(linkedList_t *pThis, rsRetVal (*pFunc)(void*, void*), void*
*/
llCookie = llCookiePrev;
} else if (iRet != RS_RET_OK) {
- goto finalize_it;
+ FINALIZE;
}
llCookiePrev = llCookie;
}
diff --git a/runtime/msg.c b/runtime/msg.c
index dbc3c779..65041a31 100644
--- a/runtime/msg.c
+++ b/runtime/msg.c
@@ -46,6 +46,7 @@
#include "regexp.h"
#include "atomic.h"
#include "unicode-helper.h"
+#include "ruleset.h"
/* static data */
DEFobjStaticHelpers
@@ -104,6 +105,7 @@ static syslogCODE rs_facilitynames[] =
/* some forward declarations */
static int getAPPNAMELen(msg_t *pM);
+static int getProtocolVersion(msg_t *pM);
/* The following functions will support advanced output module
* multithreading, once this is implemented. Currently, we
@@ -342,7 +344,6 @@ CODESTARTobjDestruct(msg)
if(currRefCount == 0)
{
/* DEV Debugging Only! dbgprintf("msgDestruct\t0x%lx, RefCount now 0, doing DESTROY\n", (unsigned long)pThis); */
- free(pThis->pszUxTradMsg);
free(pThis->pszRawMsg);
free(pThis->pszTAG);
free(pThis->pszHOSTNAME);
@@ -364,7 +365,6 @@ CODESTARTobjDestruct(msg)
free(pThis->pszTIMESTAMP_SecFrac);
free(pThis->pszTIMESTAMP_MySQL);
free(pThis->pszTIMESTAMP_PgSQL);
- free(pThis->pszPRI);
if(pThis->pCSProgName != NULL)
rsCStrDestruct(&pThis->pCSProgName);
if(pThis->pCSStrucData != NULL)
@@ -437,14 +437,18 @@ msg_t* MsgDup(msg_t* pOld)
pNew->msgFlags = pOld->msgFlags;
pNew->iProtocolVersion = pOld->iProtocolVersion;
pNew->ttGenTime = pOld->ttGenTime;
+ /* enable this, if someone actually uses UxTradMsg, delete after some time has
+ * passed and nobody complained -- rgerhards, 2009-06-16
+ pNew->offAfterPRI = pOld->offAfterPRI;
+ */
+ memcpy(pNew->bufPRI, pOld->bufPRI, pOld->iLenPRI);
+ pNew->iLenPRI = pOld->iLenPRI;
tmpCOPYSZ(Severity);
tmpCOPYSZ(SeverityStr);
tmpCOPYSZ(Facility);
tmpCOPYSZ(FacilityStr);
- tmpCOPYSZ(PRI);
tmpCOPYSZ(RawMsg);
tmpCOPYSZ(MSG);
- tmpCOPYSZ(UxTradMsg);
tmpCOPYSZ(TAG);
tmpCOPYSZ(HOSTNAME);
tmpCOPYSZ(RcvFrom);
@@ -493,10 +497,13 @@ static rsRetVal MsgSerialize(msg_t *pThis, strm_t *pStrm)
objSerializeSCALAR(pStrm, ttGenTime, INT);
objSerializeSCALAR(pStrm, tRcvdAt, SYSLOGTIME);
objSerializeSCALAR(pStrm, tTIMESTAMP, SYSLOGTIME);
+ /* enable this, if someone actually uses UxTradMsg, delete after some time has
+ * passed and nobody complained -- rgerhards, 2009-06-16
+ objSerializeSCALAR(pStrm, offsAfterPRI, SHORT);
+ */
objSerializePTR(pStrm, pszRawMsg, PSZ);
objSerializePTR(pStrm, pszMSG, PSZ);
- objSerializePTR(pStrm, pszUxTradMsg, PSZ);
objSerializePTR(pStrm, pszTAG, PSZ);
objSerializePTR(pStrm, pszHOSTNAME, PSZ);
objSerializePTR(pStrm, pszInputName, PSZ);
@@ -587,7 +594,7 @@ static rsRetVal aquirePROCIDFromTAG(msg_t *pM)
}
/* OK, finaally we could obtain a PROCID. So let's use it ;) */
- CHKiRet(rsCStrFinish(pM->pCSPROCID));
+ CHKiRet(cstrFinalize(pM->pCSPROCID));
finalize_it:
RETiRet;
@@ -629,7 +636,7 @@ static rsRetVal aquireProgramName(msg_t *pM)
; ++i) {
CHKiRet(rsCStrAppendChar(pM->pCSProgName, pM->pszTAG[i]));
}
- CHKiRet(rsCStrFinish(pM->pCSProgName));
+ CHKiRet(cstrFinalize(pM->pCSProgName));
}
finalize_it:
RETiRet;
@@ -670,7 +677,7 @@ void setProtocolVersion(msg_t *pM, int iNewVersion)
pM->iProtocolVersion = iNewVersion;
}
-int getProtocolVersion(msg_t *pM)
+static int getProtocolVersion(msg_t *pM)
{
assert(pM != NULL);
return(pM->iProtocolVersion);
@@ -689,7 +696,7 @@ int getMSGLen(msg_t *pM)
}
-char *getRawMsg(msg_t *pM)
+static char *getRawMsg(msg_t *pM)
{
if(pM == NULL)
return "";
@@ -700,16 +707,17 @@ char *getRawMsg(msg_t *pM)
return (char*)pM->pszRawMsg;
}
+
+/* enable this, if someone actually uses UxTradMsg, delete after some time has
+ * passed and nobody complained -- rgerhards, 2009-06-16
char *getUxTradMsg(msg_t *pM)
{
if(pM == NULL)
return "";
else
- if(pM->pszUxTradMsg == NULL)
- return "";
- else
- return (char*)pM->pszUxTradMsg;
+ return (char*)pM->pszRawMsg + pM->offAfterPRI;
}
+*/
char *getMSG(msg_t *pM)
{
@@ -723,44 +731,34 @@ char *getMSG(msg_t *pM)
}
-/* Get PRI value in text form */
-char *getPRI(msg_t *pM)
+/* Get PRI value as integer */
+static int getPRIi(msg_t *pM)
{
- int pri;
- BEGINfunc
+ assert(pM != NULL);
+ return (pM->iFacility << 3) + (pM->iSeverity);
+}
+
+/* Get PRI value in text form */
+static inline char *getPRI(msg_t *pM)
+{
if(pM == NULL)
return "";
+ /* there are some cases where bufPRI may not contain a valid string,
+ * and then we need to build it.
+ */
MsgLock(pM);
- if(pM->pszPRI == NULL) {
- /* OK, we need to construct it... we use a 5 byte buffer - as of
- * RFC 3164, it can't be longer. Should it still be, snprintf will truncate...
- * Note that we do not use the LOG_MAKEPRI macro. This macro
- * is a simple add of the two values under FreeBSD 7. So we implement
- * the logic in our own code. This is a change from a bug
- * report. -- rgerhards, 2008-07-14
- */
- pri = pM->iFacility * 8 + pM->iSeverity;
- if((pM->pszPRI = malloc(5)) == NULL) return "";
- pM->iLenPRI = snprintf((char*)pM->pszPRI, 5, "%d", pri);
+ if(pM->bufPRI[0] == '\0') {
+ snprintf((char*)pM->bufPRI, sizeof(pM->bufPRI), "<%d>", getPRIi(pM));
}
MsgUnlock(pM);
- ENDfunc
- return (char*)pM->pszPRI;
+ return (char*)pM->bufPRI;
}
-/* Get PRI value as integer */
-int getPRIi(msg_t *pM)
-{
- assert(pM != NULL);
- return (pM->iFacility << 3) + (pM->iSeverity);
-}
-
-
-char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt)
+static inline char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt)
{
BEGINfunc
if(pM == NULL)
@@ -838,7 +836,7 @@ char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt)
return "INVALID eFmt OPTION!";
}
-char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt)
+static inline char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt)
{
BEGINfunc
if(pM == NULL)
@@ -917,7 +915,7 @@ char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt)
}
-char *getSeverity(msg_t *pM)
+static inline char *getSeverity(msg_t *pM)
{
if(pM == NULL)
return "";
@@ -934,7 +932,7 @@ char *getSeverity(msg_t *pM)
}
-char *getSeverityStr(msg_t *pM)
+static inline char *getSeverityStr(msg_t *pM)
{
syslogCODE *c;
int val;
@@ -964,7 +962,7 @@ char *getSeverityStr(msg_t *pM)
return((char*)pM->pszSeverityStr);
}
-char *getFacility(msg_t *pM)
+static inline char *getFacility(msg_t *pM)
{
if(pM == NULL)
return "";
@@ -983,7 +981,7 @@ char *getFacility(msg_t *pM)
return((char*)pM->pszFacility);
}
-char *getFacilityStr(msg_t *pM)
+static inline char *getFacilityStr(msg_t *pM)
{
syslogCODE *c;
int val;
@@ -1035,6 +1033,17 @@ MsgSetFlowControlType(msg_t *pMsg, flowControl_t eFlowCtl)
RETiRet;
}
+/* set offset after which PRI in raw msg starts
+ * rgerhards, 2009-06-16
+ */
+rsRetVal
+MsgSetAfterPRIOffs(msg_t *pMsg, short offs)
+{
+ assert(pMsg != NULL);
+ pMsg->offAfterPRI = offs;
+ return RS_RET_OK;
+}
+
/* rgerhards 2004-11-24: set APP-NAME in msg object
* TODO: revisit msg locking code!
@@ -1090,7 +1099,7 @@ finalize_it:
/* rgerhards, 2005-11-24
*/
-int getPROCIDLen(msg_t *pM)
+static inline int getPROCIDLen(msg_t *pM)
{
assert(pM != NULL);
MsgLock(pM);
@@ -1135,19 +1144,10 @@ finalize_it:
RETiRet;
}
-/* rgerhards, 2005-11-24
- */
-#if 0 /* This method is currently not called, be we like to preserve it */
-static int getMSGIDLen(msg_t *pM)
-{
- return (pM->pCSMSGID == NULL) ? 1 : rsCStrLen(pM->pCSMSGID);
-}
-#endif
-
/* rgerhards, 2005-11-24
*/
-char *getMSGID(msg_t *pM)
+static inline char *getMSGID(msg_t *pM)
{
return (pM->pCSMSGID == NULL) ? "-" : (char*) rsCStrGetSzStrNoNULL(pM->pCSMSGID);
}
@@ -1166,13 +1166,21 @@ void MsgAssignTAG(msg_t *pMsg, uchar *pBuf)
}
+/* rgerhards 2009-06-12: set associated ruleset
+ */
+void MsgSetRuleset(msg_t *pMsg, ruleset_t *pRuleset)
+{
+ assert(pMsg != NULL);
+ pMsg->pRuleset = pRuleset;
+}
+
+
/* rgerhards 2004-11-16: set TAG in msg object
*/
void MsgSetTAG(msg_t *pMsg, char* pszTAG)
{
assert(pMsg != NULL);
- if(pMsg->pszTAG != NULL)
- free(pMsg->pszTAG);
+ free(pMsg->pszTAG);
pMsg->iLenTAG = strlen(pszTAG);
if((pMsg->pszTAG = malloc(pMsg->iLenTAG + 1)) != NULL)
memcpy(pMsg->pszTAG, pszTAG, pMsg->iLenTAG + 1);
@@ -1229,7 +1237,7 @@ static int getTAGLen(msg_t *pM)
#endif
-char *getTAG(msg_t *pM)
+static inline char *getTAG(msg_t *pM)
{
char *ret;
@@ -1272,7 +1280,7 @@ char *getHOSTNAME(msg_t *pM)
}
-uchar *getInputName(msg_t *pM)
+static uchar *getInputName(msg_t *pM)
{
if(pM == NULL)
return (uchar*) "";
@@ -1339,7 +1347,7 @@ static int getStructuredDataLen(msg_t *pM)
/* get the "STRUCTURED-DATA" as sz string
* rgerhards, 2005-11-24
*/
-char *getStructuredData(msg_t *pM)
+static inline char *getStructuredData(msg_t *pM)
{
return (pM->pCSStrucData == NULL) ? "-" : (char*) rsCStrGetSzStrNoNULL(pM->pCSStrucData);
}
@@ -1465,14 +1473,13 @@ static int getAPPNAMELen(msg_t *pM)
}
/* rgerhards 2008-09-10: set pszInputName in msg object
+ * rgerhards, 2009-06-16
*/
-void MsgSetInputName(msg_t *pMsg, uchar* pszInputName)
+void MsgSetInputName(msg_t *pMsg, uchar* pszInputName, size_t lenInputName)
{
assert(pMsg != NULL);
- if(pMsg->pszInputName != NULL)
- free(pMsg->pszInputName);
-
- pMsg->iLenInputName = ustrlen(pszInputName);
+ free(pMsg->pszInputName);
+ pMsg->iLenInputName = lenInputName;
if((pMsg->pszInputName = malloc(pMsg->iLenInputName + 1)) != NULL) {
memcpy(pMsg->pszInputName, pszInputName, pMsg->iLenInputName + 1);
}
@@ -1549,40 +1556,6 @@ void MsgSetHOSTNAME(msg_t *pMsg, uchar* pszHOSTNAME)
}
-/* Set the UxTradMsg to a caller-provided string. This is thought
- * to be a heap buffer that the caller will no longer use. This
- * function is a performance optimization over MsgSetUxTradMsg().
- * rgerhards 2004-11-19
- */
-#if 0 /* This method is currently not called, be we like to preserve it */
-static void MsgAssignUxTradMsg(msg_t *pMsg, char *pBuf)
-{
- assert(pMsg != NULL);
- assert(pBuf != NULL);
- pMsg->iLenUxTradMsg = strlen(pBuf);
- pMsg->pszUxTradMsg = pBuf;
-}
-#endif
-
-
-/* rgerhards 2004-11-17: set the traditional Unix message in msg object
- */
-int MsgSetUxTradMsg(msg_t *pMsg, char* pszUxTradMsg)
-{
- assert(pMsg != NULL);
- assert(pszUxTradMsg != NULL);
- pMsg->iLenUxTradMsg = strlen(pszUxTradMsg);
- if(pMsg->pszUxTradMsg != NULL)
- free(pMsg->pszUxTradMsg);
- if((pMsg->pszUxTradMsg = malloc(pMsg->iLenUxTradMsg + 1)) != NULL)
- memcpy(pMsg->pszUxTradMsg, pszUxTradMsg, pMsg->iLenUxTradMsg + 1);
- else
- dbgprintf("Could not allocate memory for pszUxTradMsg buffer.");
-
- return(0);
-}
-
-
/* rgerhards 2004-11-09: set MSG in msg object
*/
void MsgSetMSG(msg_t *pMsg, char* pszMSG)
@@ -1761,8 +1734,11 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
pRes = getMSG(pMsg);
} else if(!strcmp((char*) pName, "rawmsg")) {
pRes = getRawMsg(pMsg);
+ /* enable this, if someone actually uses UxTradMsg, delete after some time has
+ * passed and nobody complained -- rgerhards, 2009-06-16
} else if(!strcmp((char*) pName, "uxtradmsg")) {
pRes = getUxTradMsg(pMsg);
+ */
} else if(!strcmp((char*) pName, "inputname")) {
pRes = (char*) getInputName(pMsg);
} else if(!strcmp((char*) pName, "fromhost")) {
@@ -2486,14 +2462,19 @@ rsRetVal MsgSetProperty(msg_t *pThis, var_t *pProp)
pThis->msgFlags = pProp->val.num;
} else if(isProp("pszRawMsg")) {
MsgSetRawMsg(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ /* enable this, if someone actually uses UxTradMsg, delete after some time has
+ * passed and nobody complained -- rgerhards, 2009-06-16
+ } else if(isProp("offAfterPRI")) {
+ pThis->offAfterPRI = pProp->val.num;
+ */
} else if(isProp("pszMSG")) {
MsgSetMSG(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
} else if(isProp("pszUxTradMsg")) {
- MsgSetUxTradMsg(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ /*IGNORE*/; /* this *was* a property, but does no longer exist */
} else if(isProp("pszTAG")) {
MsgSetTAG(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
} else if(isProp("pszInputName")) {
- MsgSetInputName(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ MsgSetInputName(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr), rsCStrLen(pProp->val.pStr));
} else if(isProp("pszRcvFromIP")) {
MsgSetRcvFromIP(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr));
} else if(isProp("pszRcvFrom")) {
diff --git a/runtime/msg.h b/runtime/msg.h
index a30d3fb9..74ff9e60 100644
--- a/runtime/msg.h
+++ b/runtime/msg.h
@@ -51,7 +51,7 @@
struct msg {
BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
pthread_mutexattr_t mutAttr;
- short bDoLock; /* use the mutex? */
+ bool bDoLock; /* use the mutex? */
pthread_mutex_t mut;
flowControl_t flowCtlType; /**< type of flow control we can apply, for enqueueing, needs not to be persisted because
once data has entered the queue, this property is no longer needed. */
@@ -73,12 +73,13 @@ struct msg {
int iLenFacility; /* ... and its length. */
uchar *pszFacilityStr; /* facility name... */
int iLenFacilityStr; /* ... and its length. */
- uchar *pszPRI; /* the PRI as a string */
+ uchar bufPRI[5]; /* PRI as string */
int iLenPRI; /* and its length */
uchar *pszRawMsg; /* message as it was received on the
* wire. This is important in case we
* need to preserve cryptographic verifiers.
*/
+ short offAfterPRI; /* offset, at which raw message WITHOUT PRI part starts in pszRawMsg */
int iLenRawMsg; /* length of raw message */
uchar *pszMSG; /* the MSG part itself */
int iLenMSG; /* Length of the MSG part */
@@ -120,6 +121,8 @@ struct msg {
char *pszTIMESTAMP_PgSQL;/* TIMESTAMP as PgSQL formatted string (always 21 characters) */
char *pszTIMESTAMP_SecFrac;/* TIMESTAMP fractional seconds (always 6 characters) */
int msgFlags; /* flags associated with this message */
+ ruleset_t *pRuleset; /* ruleset to be used for processing this message */
+ /* now follow fixed-size buffers to safe some time otherwise used for allocs */
};
@@ -137,60 +140,53 @@ struct msg {
/* function prototypes
*/
PROTOTYPEObjClassInit(msg);
-char* getProgramName(msg_t*);
rsRetVal msgConstruct(msg_t **ppThis);
rsRetVal msgConstructWithTime(msg_t **ppThis, struct syslogTime *stTime, time_t ttGenTime);
rsRetVal msgDestruct(msg_t **ppM);
msg_t* MsgDup(msg_t* pOld);
msg_t *MsgAddRef(msg_t *pM);
void setProtocolVersion(msg_t *pM, int iNewVersion);
-int getProtocolVersion(msg_t *pM);
-char *getProtocolVersionString(msg_t *pM);
-int getMSGLen(msg_t *pM);
-char *getRawMsg(msg_t *pM);
-char *getUxTradMsg(msg_t *pM);
-char *getMSG(msg_t *pM);
-char *getPRI(msg_t *pM);
-int getPRIi(msg_t *pM);
-char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt);
-char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt);
-char *getSeverity(msg_t *pM);
-char *getSeverityStr(msg_t *pM);
-char *getFacility(msg_t *pM);
-char *getFacilityStr(msg_t *pM);
-void MsgSetInputName(msg_t *pMsg, uchar*);
+void MsgSetInputName(msg_t *pMsg, uchar*, size_t);
rsRetVal MsgSetAPPNAME(msg_t *pMsg, char* pszAPPNAME);
-char *getAPPNAME(msg_t *pM);
rsRetVal MsgSetPROCID(msg_t *pMsg, char* pszPROCID);
-int getPROCIDLen(msg_t *pM);
-char *getPROCID(msg_t *pM);
rsRetVal MsgSetMSGID(msg_t *pMsg, char* pszMSGID);
void MsgAssignTAG(msg_t *pMsg, uchar *pBuf);
void MsgSetTAG(msg_t *pMsg, char* pszTAG);
+void MsgSetRuleset(msg_t *pMsg, ruleset_t*);
rsRetVal MsgSetFlowControlType(msg_t *pMsg, flowControl_t eFlowCtl);
-char *getTAG(msg_t *pM);
-int getHOSTNAMELen(msg_t *pM);
-char *getHOSTNAME(msg_t *pM);
-uchar *getRcvFrom(msg_t *pM);
rsRetVal MsgSetStructuredData(msg_t *pMsg, char* pszStrucData);
-char *getStructuredData(msg_t *pM);
-int getProgramNameLen(msg_t *pM);
-char *getProgramName(msg_t *pM);
void MsgSetRcvFrom(msg_t *pMsg, uchar* pszRcvFrom);
rsRetVal MsgSetRcvFromIP(msg_t *pMsg, uchar* pszRcvFromIP);
void MsgAssignHOSTNAME(msg_t *pMsg, char *pBuf);
void MsgSetHOSTNAME(msg_t *pMsg, uchar* pszHOSTNAME);
-int MsgSetUxTradMsg(msg_t *pMsg, char* pszUxTradMsg);
+rsRetVal MsgSetAfterPRIOffs(msg_t *pMsg, short offs);
void MsgSetMSG(msg_t *pMsg, char* pszMSG);
void MsgSetRawMsg(msg_t *pMsg, char* pszRawMsg);
void moveHOSTNAMEtoTAG(msg_t *pM);
-char *getMSGID(msg_t *pM);
char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
cstr_t *pCSPropName, unsigned short *pbMustBeFreed);
char *textpri(char *pRes, size_t pResLen, int pri);
rsRetVal msgGetMsgVar(msg_t *pThis, cstr_t *pstrPropName, var_t **ppVar);
rsRetVal MsgEnableThreadSafety(void);
+/* TODO: remove these five (so far used in action.c) */
+char *getMSG(msg_t *pM);
+char *getHOSTNAME(msg_t *pM);
+char *getPROCID(msg_t *pM);
+char *getAPPNAME(msg_t *pM);
+int getMSGLen(msg_t *pM);
+
+char *getHOSTNAME(msg_t *pM);
+int getHOSTNAMELen(msg_t *pM);
+char *getProgramName(msg_t *pM);
+int getProgramNameLen(msg_t *pM);
+uchar *getRcvFrom(msg_t *pM);
+
+#if 0
+char *getUxTradMsg(msg_t *pM);
+int MsgSetUxTradMsg(msg_t *pMsg, char* pszUxTradMsg);
+#endif
+
/* The MsgPrepareEnqueue() function is a macro for performance reasons.
* It needs one global variable to work. This is acceptable, as it gains
* us quite some performance and is fully abstracted using this header file.
diff --git a/runtime/nsd_gtls.c b/runtime/nsd_gtls.c
index 1a50e2f8..19dc8678 100644
--- a/runtime/nsd_gtls.c
+++ b/runtime/nsd_gtls.c
@@ -335,7 +335,7 @@ gtlsGetCertInfo(nsd_gtls_t *pThis, cstr_t **ppStr)
gnutls_x509_crt_deinit(cert);
}
- CHKiRet(rsCStrFinish(pStr));
+ CHKiRet(cstrFinalize(pStr));
*ppStr = pStr;
finalize_it:
@@ -455,7 +455,7 @@ GenFingerprintStr(uchar *pFingerprint, size_t sizeFingerprint, cstr_t **ppStr)
snprintf((char*)buf, sizeof(buf), ":%2.2X", pFingerprint[i]);
CHKiRet(rsCStrAppendStrWithLen(pStr, buf, 3));
}
- CHKiRet(rsCStrFinish(pStr));
+ CHKiRet(cstrFinalize(pStr));
*ppStr = pStr;
@@ -723,7 +723,7 @@ gtlsGetCN(nsd_gtls_t *pThis, gnutls_x509_crt *pCert, cstr_t **ppstrCN)
}
++i; /* char processed */
}
- CHKiRet(rsCStrFinish(pstrCN));
+ CHKiRet(cstrFinalize(pstrCN));
/* we got it - we ignore the rest of the DN string (if any). So we may
* not detect if it contains more than one CN
@@ -884,7 +884,7 @@ gtlsChkPeerName(nsd_gtls_t *pThis, gnutls_x509_crt *pCert)
if(!bFoundPositiveMatch) {
dbgprintf("invalid peer name, not permitted to talk to it\n");
if(pThis->bReportAuthErr == 1) {
- CHKiRet(rsCStrFinish(pStr));
+ CHKiRet(cstrFinalize(pStr));
errno = 0;
errmsg.LogError(0, RS_RET_INVALID_FINGERPRINT, "error: peer name not authorized - "
"not permitted to talk to it. Names: %s",
diff --git a/runtime/obj-types.h b/runtime/obj-types.h
index 914c2f2c..6c1381ac 100644
--- a/runtime/obj-types.h
+++ b/runtime/obj-types.h
@@ -105,12 +105,13 @@ struct obj_s { /* the dummy struct that each derived class can be casted to */
# define ISOBJ_TYPE_assert(pObj, objType) \
do { \
ASSERT(pObj != NULL); \
- ASSERT((unsigned) ((obj_t*) (pObj))->iObjCooCKiE == (unsigned) 0xBADEFEE); \
if(strcmp((char*)(((obj_t*)pObj)->pObjInfo->pszID), #objType)) { \
dbgprintf("%s:%d ISOBJ assert failure: invalid object type, expected '%s' " \
- "actual '%s'\n", __FILE__, __LINE__, #objType, (((obj_t*)pObj)->pObjInfo->pszID)); \
+ "actual '%s', cookie: %X\n", __FILE__, __LINE__, #objType, \
+ (((obj_t*)pObj)->pObjInfo->pszID), ((obj_t*)(pObj))->iObjCooCKiE); \
assert(0); /* trigger assertion, messge we already have */ \
} \
+ ASSERT((unsigned) ((obj_t*)(pObj))->iObjCooCKiE == (unsigned) 0xBADEFEE); \
} while(0)
#else /* non-debug mode, no checks but much faster */
# define BEGINobjInstance obj_t objData
@@ -280,7 +281,7 @@ rsRetVal objName##ClassExit(void) \
* rgerhards, 2008-01-30
*/
#define BEGINobjDestruct(OBJ) \
- rsRetVal OBJ##Destruct(OBJ##_t **ppThis) \
+ rsRetVal OBJ##Destruct(OBJ##_t __attribute__((unused)) **ppThis) \
{ \
DEFiRet; \
int iCancelStateSave; \
@@ -314,7 +315,7 @@ rsRetVal objName##ClassExit(void) \
#define PROTOTYPEObjDebugPrint(obj) rsRetVal obj##DebugPrint(obj##_t *pThis)
#define INTERFACEObjDebugPrint(obj) rsRetVal (*DebugPrint)(obj##_t *pThis)
#define BEGINobjDebugPrint(obj) \
- rsRetVal obj##DebugPrint(obj##_t *pThis) \
+ rsRetVal obj##DebugPrint(obj##_t __attribute__((unused)) *pThis) \
{ \
DEFiRet; \
diff --git a/runtime/obj.c b/runtime/obj.c
index 20b918eb..f2cb447e 100644
--- a/runtime/obj.c
+++ b/runtime/obj.c
@@ -87,12 +87,15 @@
#include "modules.h"
#include "errmsg.h"
#include "cfsysline.h"
+#include "unicode-helper.h"
+#include "apc.h"
/* static data */
DEFobjCurrIf(obj) /* we define our own interface, as this is expected by some macros! */
DEFobjCurrIf(var)
DEFobjCurrIf(module)
DEFobjCurrIf(errmsg)
+DEFobjCurrIf(strm)
static objInfo_t *arrObjInfo[OBJ_NUM_IDS]; /* array with object information pointers */
@@ -144,8 +147,8 @@ InfoConstruct(objInfo_t **ppThis, uchar *pszID, int iObjVers,
ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
pThis->pszID = pszID;
- pThis->lenID = strlen((char*)pszID);
- pThis->pszName = (uchar*)strdup((char*)pszID); /* it's OK if we have NULL ptr, GetName() will deal with that! */
+ pThis->lenID = ustrlen(pszID);
+ pThis->pszName = ustrdup(pszID); /* it's OK if we have NULL ptr, GetName() will deal with that! */
pThis->iObjVers = iObjVers;
pThis->QueryIF = pQueryIF;
pThis->pModInfo = pModInfo;
@@ -176,8 +179,7 @@ InfoDestruct(objInfo_t **ppThis)
pThis = *ppThis;
assert(pThis != NULL);
- if(pThis->pszName != NULL)
- free(pThis->pszName);
+ free(pThis->pszName);
free(pThis);
*ppThis = NULL;
@@ -226,20 +228,20 @@ static rsRetVal objSerializeHeader(strm_t *pStrm, obj_t *pObj, uchar *pszRecType
assert(!strcmp((char*) pszRecType, "Obj") || !strcmp((char*) pszRecType, "OPB"));
/* object cookie and serializer version (so far always 1) */
- CHKiRet(strmWriteChar(pStrm, COOKIE_OBJLINE));
- CHKiRet(strmWrite(pStrm, (uchar*) pszRecType, 3)); /* record types are always 3 octets */
- CHKiRet(strmWriteChar(pStrm, ':'));
- CHKiRet(strmWriteChar(pStrm, '1'));
+ CHKiRet(strm.WriteChar(pStrm, COOKIE_OBJLINE));
+ CHKiRet(strm.Write(pStrm, (uchar*) pszRecType, 3)); /* record types are always 3 octets */
+ CHKiRet(strm.WriteChar(pStrm, ':'));
+ CHKiRet(strm.WriteChar(pStrm, '1'));
/* object type, version and string length */
- CHKiRet(strmWriteChar(pStrm, ':'));
- CHKiRet(strmWrite(pStrm, pObj->pObjInfo->pszID, pObj->pObjInfo->lenID));
- CHKiRet(strmWriteChar(pStrm, ':'));
- CHKiRet(strmWriteLong(pStrm, objGetVersion(pObj)));
+ CHKiRet(strm.WriteChar(pStrm, ':'));
+ CHKiRet(strm.Write(pStrm, pObj->pObjInfo->pszID, pObj->pObjInfo->lenID));
+ CHKiRet(strm.WriteChar(pStrm, ':'));
+ CHKiRet(strm.WriteLong(pStrm, objGetVersion(pObj)));
/* record trailer */
- CHKiRet(strmWriteChar(pStrm, ':'));
- CHKiRet(strmWriteChar(pStrm, '\n'));
+ CHKiRet(strm.WriteChar(pStrm, ':'));
+ CHKiRet(strm.WriteChar(pStrm, '\n'));
finalize_it:
RETiRet;
@@ -257,7 +259,7 @@ BeginSerialize(strm_t *pStrm, obj_t *pObj)
ISOBJ_TYPE_assert(pStrm, strm);
ISOBJ_assert(pObj);
- CHKiRet(strmRecordBegin(pStrm));
+ CHKiRet(strm.RecordBegin(pStrm));
CHKiRet(objSerializeHeader(pStrm, pObj, (uchar*) "Obj"));
finalize_it:
@@ -282,7 +284,7 @@ BeginSerializePropBag(strm_t *pStrm, obj_t *pObj)
ISOBJ_TYPE_assert(pStrm, strm);
ISOBJ_assert(pObj);
- CHKiRet(strmRecordBegin(pStrm));
+ CHKiRet(strm.RecordBegin(pStrm));
CHKiRet(objSerializeHeader(pStrm, pObj, (uchar*) "OPB"));
finalize_it:
@@ -318,31 +320,31 @@ SerializeProp(strm_t *pStrm, uchar *pszPropName, propType_t propType, void *pUsr
switch(propType) {
case PROPTYPE_PSZ:
pszBuf = (uchar*) pUsr;
- lenBuf = strlen((char*) pszBuf);
+ lenBuf = ustrlen(pszBuf);
vType = VARTYPE_STR;
break;
case PROPTYPE_SHORT:
CHKiRet(srUtilItoA((char*) szBuf, sizeof(szBuf), (long) *((short*) pUsr)));
pszBuf = szBuf;
- lenBuf = strlen((char*) szBuf);
+ lenBuf = ustrlen(szBuf);
vType = VARTYPE_NUMBER;
break;
case PROPTYPE_INT:
CHKiRet(srUtilItoA((char*) szBuf, sizeof(szBuf), (long) *((int*) pUsr)));
pszBuf = szBuf;
- lenBuf = strlen((char*) szBuf);
+ lenBuf = ustrlen(szBuf);
vType = VARTYPE_NUMBER;
break;
case PROPTYPE_LONG:
CHKiRet(srUtilItoA((char*) szBuf, sizeof(szBuf), *((long*) pUsr)));
pszBuf = szBuf;
- lenBuf = strlen((char*) szBuf);
+ lenBuf = ustrlen(szBuf);
vType = VARTYPE_NUMBER;
break;
case PROPTYPE_INT64:
CHKiRet(srUtilItoA((char*) szBuf, sizeof(szBuf), *((int64*) pUsr)));
pszBuf = szBuf;
- lenBuf = strlen((char*) szBuf);
+ lenBuf = ustrlen(szBuf);
vType = VARTYPE_NUMBER;
break;
case PROPTYPE_CSTR:
@@ -375,23 +377,23 @@ SerializeProp(strm_t *pStrm, uchar *pszPropName, propType_t propType, void *pUsr
}
/* cookie */
- CHKiRet(strmWriteChar(pStrm, COOKIE_PROPLINE));
+ CHKiRet(strm.WriteChar(pStrm, COOKIE_PROPLINE));
/* name */
- CHKiRet(strmWrite(pStrm, pszPropName, strlen((char*)pszPropName)));
- CHKiRet(strmWriteChar(pStrm, ':'));
+ CHKiRet(strm.Write(pStrm, pszPropName, ustrlen(pszPropName)));
+ CHKiRet(strm.WriteChar(pStrm, ':'));
/* type */
- CHKiRet(strmWriteLong(pStrm, (int) vType));
- CHKiRet(strmWriteChar(pStrm, ':'));
+ CHKiRet(strm.WriteLong(pStrm, (int) vType));
+ CHKiRet(strm.WriteChar(pStrm, ':'));
/* length */
- CHKiRet(strmWriteLong(pStrm, lenBuf));
- CHKiRet(strmWriteChar(pStrm, ':'));
+ CHKiRet(strm.WriteLong(pStrm, lenBuf));
+ CHKiRet(strm.WriteChar(pStrm, ':'));
/* data */
- CHKiRet(strmWrite(pStrm, (uchar*) pszBuf, lenBuf));
+ CHKiRet(strm.Write(pStrm, (uchar*) pszBuf, lenBuf));
/* trailer */
- CHKiRet(strmWriteChar(pStrm, ':'));
- CHKiRet(strmWriteChar(pStrm, '\n'));
+ CHKiRet(strm.WriteChar(pStrm, ':'));
+ CHKiRet(strm.WriteChar(pStrm, '\n'));
finalize_it:
RETiRet;
@@ -408,12 +410,12 @@ EndSerialize(strm_t *pStrm)
assert(pStrm != NULL);
- CHKiRet(strmWriteChar(pStrm, COOKIE_ENDLINE));
- CHKiRet(strmWrite(pStrm, (uchar*) "End\n", sizeof("END\n") - 1));
- CHKiRet(strmWriteChar(pStrm, COOKIE_BLANKLINE));
- CHKiRet(strmWriteChar(pStrm, '\n'));
+ CHKiRet(strm.WriteChar(pStrm, COOKIE_ENDLINE));
+ CHKiRet(strm.Write(pStrm, (uchar*) "End\n", sizeof("END\n") - 1));
+ CHKiRet(strm.WriteChar(pStrm, COOKIE_BLANKLINE));
+ CHKiRet(strm.WriteChar(pStrm, '\n'));
- CHKiRet(strmRecordEnd(pStrm));
+ CHKiRet(strm.RecordEnd(pStrm));
finalize_it:
RETiRet;
@@ -421,7 +423,7 @@ finalize_it:
/* define a helper to make code below a bit cleaner (and quicker to write) */
-#define NEXTC CHKiRet(strmReadChar(pStrm, &c))/*;dbgprintf("c: %c\n", c)*/
+#define NEXTC CHKiRet(strm.ReadChar(pStrm, &c))/*;dbgprintf("c: %c\n", c)*/
/* de-serialize an embedded, non-octect-counted string. This is useful
@@ -445,7 +447,7 @@ objDeserializeEmbedStr(cstr_t **ppStr, strm_t *pStrm)
CHKiRet(rsCStrAppendChar(pStr, c));
NEXTC;
}
- CHKiRet(rsCStrFinish(pStr));
+ CHKiRet(cstrFinalize(pStr));
*ppStr = pStr;
@@ -513,7 +515,7 @@ static rsRetVal objDeserializeStr(cstr_t **ppCStr, int iLen, strm_t *pStrm)
CHKiRet(rsCStrAppendChar(pCStr, c));
NEXTC;
}
- CHKiRet(rsCStrFinish(pCStr));
+ CHKiRet(cstrFinalize(pCStr));
/* check terminator */
if(c != ':') ABORT_FINALIZE(RS_RET_INVALID_DELIMITER);
@@ -615,7 +617,7 @@ static rsRetVal objDeserializeProperty(var_t *pProp, strm_t *pStrm)
NEXTC;
if(c != COOKIE_PROPLINE) {
/* oops, we've read one char that does not belong to use - unget it first */
- CHKiRet(strmUnreadChar(pStrm, c));
+ CHKiRet(strm.UnreadChar(pStrm, c));
ABORT_FINALIZE(RS_RET_NO_PROPLINE);
}
@@ -627,7 +629,7 @@ static rsRetVal objDeserializeProperty(var_t *pProp, strm_t *pStrm)
CHKiRet(rsCStrAppendChar(pProp->pcsName, c));
NEXTC;
}
- CHKiRet(rsCStrFinish(pProp->pcsName));
+ CHKiRet(cstrFinalize(pProp->pcsName));
/* property type */
CHKiRet(objDeserializeNumber(&i, pStrm));
@@ -716,7 +718,7 @@ static rsRetVal objDeserializeTryRecover(strm_t *pStrm)
}
}
- CHKiRet(strmUnreadChar(pStrm, c));
+ CHKiRet(strm.UnreadChar(pStrm, c));
finalize_it:
dbgprintf("deserializer has possibly been able to re-sync and recover, state %d\n", iRet);
@@ -801,7 +803,7 @@ Deserialize(void *ppObj, uchar *pszTypeExpected, strm_t *pStrm, rsRetVal (*fFixu
}
} while(iRetLocal != RS_RET_OK);
- if(rsCStrSzStrCmp(pstrID, pszTypeExpected, strlen((char*)pszTypeExpected))) /* TODO: optimize strlen() - caller shall provide */
+ if(rsCStrSzStrCmp(pstrID, pszTypeExpected, ustrlen(pszTypeExpected))) /* TODO: optimize strlen() - caller shall provide */
ABORT_FINALIZE(RS_RET_INVALID_OID);
CHKiRet(FindObjInfo(pstrID, &pObjInfo));
@@ -946,13 +948,8 @@ SetName(obj_t *pThis, uchar *pszName)
{
DEFiRet;
- if(pThis->pszName != NULL)
- free(pThis->pszName);
-
- pThis->pszName = (uchar*) strdup((char*) pszName);
-
- if(pThis->pszName == NULL)
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ free(pThis->pszName);
+ CHKmalloc(pThis->pszName = ustrdup(pszName));
finalize_it:
RETiRet;
@@ -1055,7 +1052,7 @@ RegisterObj(uchar *pszObjName, objInfo_t *pInfo)
i = 0;
while(!bFound && i < OBJ_NUM_IDS && arrObjInfo[i] != NULL) {
if( arrObjInfo[i] != NULL
- && !strcmp((char*)arrObjInfo[i]->pszID, (char*)pszObjName)) {
+ && !ustrcmp(arrObjInfo[i]->pszID, pszObjName)) {
bFound = 1;
break;
}
@@ -1094,7 +1091,7 @@ UnregisterObj(uchar *pszObjName)
i = 0;
while(!bFound && i < OBJ_NUM_IDS) {
if( arrObjInfo[i] != NULL
- && !strcmp((char*)arrObjInfo[i]->pszID, (char*)pszObjName)) {
+ && !ustrcmp(arrObjInfo[i]->pszID, pszObjName)) {
bFound = 1;
break;
}
@@ -1276,14 +1273,15 @@ objClassExit(void)
{
DEFiRet;
/* release objects we no longer need */
+ objRelease(strm, CORE_COMPONENT);
objRelease(var, CORE_COMPONENT);
objRelease(module, CORE_COMPONENT);
objRelease(errmsg, CORE_COMPONENT);
/* TODO: implement the class exits! */
#if 0
- cfsyslineInit(pModInfo);
- varClassInit(pModInfo);
+ cfsyslineExit(pModInfo);
+ varClassExit(pModInfo);
#endif
errmsgClassExit();
moduleClassExit();
@@ -1316,13 +1314,16 @@ objClassInit(modInfo_t *pModInfo)
CHKiRet(objGetObjInterface(&obj)); /* get ourselves ;) */
/* init classes we use (limit to as few as possible!) */
+ CHKiRet(apcClassInit(pModInfo));
CHKiRet(errmsgClassInit(pModInfo));
CHKiRet(cfsyslineInit());
CHKiRet(varClassInit(pModInfo));
CHKiRet(moduleClassInit(pModInfo));
+ CHKiRet(strmClassInit(pModInfo));
CHKiRet(objUse(var, CORE_COMPONENT));
CHKiRet(objUse(module, CORE_COMPONENT));
CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(strm, CORE_COMPONENT));
finalize_it:
RETiRet;
diff --git a/runtime/obj.h b/runtime/obj.h
index 98bd4854..3973a16e 100644
--- a/runtime/obj.h
+++ b/runtime/obj.h
@@ -68,7 +68,7 @@
#define objSerializePTR(strm, propName, propType) \
CHKiRet(obj.SerializeProp(strm, (uchar*) #propName, PROPTYPE_##propType, (void*) pThis->propName));
#define DEFobjStaticHelpers \
- static __attribute__((unused)) objInfo_t *pObjInfoOBJ = NULL; \
+ static objInfo_t __attribute__((unused)) *pObjInfoOBJ = NULL; \
DEFobjCurrIf(obj)
diff --git a/runtime/parser.c b/runtime/parser.c
index 212d40f3..0b45bfd5 100644
--- a/runtime/parser.c
+++ b/runtime/parser.c
@@ -191,6 +191,31 @@ sanitizeMessage(msg_t *pMsg)
lenMsg--;
}
+ /* it is much quicker to sweep over the message and see if it actually
+ * needs sanitation than to do the sanitation in any case. So we first do
+ * this and terminate when it is not needed - which is expectedly the case
+ * for the vast majority of messages. -- rgerhards, 2009-06-15
+ */
+ int bNeedSanitize = 0;
+ for(iSrc = 0 ; iSrc < lenMsg ; iSrc++) {
+ if(pszMsg[iSrc] < 32) {
+ if(pszMsg[iSrc] == '\0' || bEscapeCCOnRcv) {
+ bNeedSanitize = 1;
+ break;
+ }
+ }
+ }
+ if(bNeedSanitize == 0) {
+ /* what a shame - we do not have a \0 byte...
+ * TODO: think about adding it or otherwise be able to use it...
+ */
+ uchar *pRaw;
+ CHKmalloc(pRaw = realloc(pMsg->pszRawMsg, pMsg->iLenRawMsg + 1));
+ pRaw[pMsg->iLenRawMsg] = '\0';
+ pMsg->pszRawMsg = pRaw;
+ FINALIZE;
+ }
+
/* now copy over the message and sanitize it */
/* TODO: can we get cheaper memory alloc? {alloca()?}*/
iMaxLine = glbl.GetMaxLine();
@@ -259,6 +284,7 @@ rsRetVal parseMsg(msg_t *pMsg)
DEFiRet;
uchar *msg;
int pri;
+ int iPriText;
CHKiRet(sanitizeMessage(pMsg));
@@ -268,11 +294,19 @@ rsRetVal parseMsg(msg_t *pMsg)
/* pull PRI */
pri = DEFUPRI;
msg = pMsg->pszRawMsg;
+ iPriText = 0;
if(*msg == '<') {
+ /* while we process the PRI, we also fill the PRI textual representation
+ * inside the msg object. This may not be ideal from an OOP point of view,
+ * but it offers us performance...
+ */
pri = 0;
while(isdigit((int) *++msg)) {
+ pMsg->bufPRI[iPriText++ % 4] = *msg; /* mod 4 to guard against malformed messages! */
pri = 10 * pri + (*msg - '0');
}
+ pMsg->bufPRI[iPriText % 4] = '\0';
+ pMsg->iLenPRI = iPriText % 4;
if(*msg == '>')
++msg;
if(pri & ~(LOG_FACMASK|LOG_PRIMASK))
@@ -280,7 +314,7 @@ rsRetVal parseMsg(msg_t *pMsg)
}
pMsg->iFacility = LOG_FAC(pri);
pMsg->iSeverity = LOG_PRI(pri);
- MsgSetUxTradMsg(pMsg, (char*) msg);
+ MsgSetAfterPRIOffs(pMsg, msg - pMsg->pszRawMsg);
if(pMsg->bParseHOSTNAME == 0)
MsgSetHOSTNAME(pMsg, pMsg->pszRcvFrom);
diff --git a/runtime/queue.c b/runtime/queue.c
index 23d60ddc..9c7e524c 100644
--- a/runtime/queue.c
+++ b/runtime/queue.c
@@ -64,6 +64,7 @@
/* static data */
DEFobjStaticHelpers
DEFobjCurrIf(glbl)
+DEFobjCurrIf(strm)
/* forward-definitions */
static rsRetVal qqueueChkPersist(qqueue_t *pThis, int nUpdates);
@@ -392,6 +393,7 @@ StartDA(qqueue_t *pThis)
CHKiRet(qqueueSetMaxFileSize(pThis->pqDA, pThis->iMaxFileSize));
CHKiRet(qqueueSetFilePrefix(pThis->pqDA, pThis->pszFilePrefix, pThis->lenFilePrefix));
CHKiRet(qqueueSetiPersistUpdCnt(pThis->pqDA, pThis->iPersistUpdCnt));
+ CHKiRet(qqueueSetbSyncQueueFiles(pThis->pqDA, pThis->bSyncQueueFiles));
CHKiRet(qqueueSettoActShutdown(pThis->pqDA, pThis->toActShutdown));
CHKiRet(qqueueSettoEnq(pThis->pqDA, pThis->toEnq));
CHKiRet(SetEnqOnly(pThis->pqDA, pThis->bDAEnqOnly, MUTEX_ALREADY_LOCKED));
@@ -770,7 +772,7 @@ qqueueLoadPersStrmInfoFixup(strm_t *pStrm, qqueue_t __attribute__((unused)) *pTh
DEFiRet;
ISOBJ_TYPE_assert(pStrm, strm);
ISOBJ_TYPE_assert(pThis, qqueue);
- CHKiRet(strmSetDir(pStrm, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir())));
+ CHKiRet(strm.SetDir(pStrm, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir())));
finalize_it:
RETiRet;
}
@@ -845,11 +847,11 @@ qqueueTryLoadPersistedInfo(qqueue_t *pThis)
/* If we reach this point, we have a .qi file */
- CHKiRet(strmConstruct(&psQIF));
- CHKiRet(strmSettOperationsMode(psQIF, STREAMMODE_READ));
- CHKiRet(strmSetsType(psQIF, STREAMTYPE_FILE_SINGLE));
- CHKiRet(strmSetFName(psQIF, pszQIFNam, lenQIFNam));
- CHKiRet(strmConstructFinalize(psQIF));
+ CHKiRet(strm.Construct(&psQIF));
+ CHKiRet(strm.SettOperationsMode(psQIF, STREAMMODE_READ));
+ CHKiRet(strm.SetsType(psQIF, STREAMTYPE_FILE_SINGLE));
+ CHKiRet(strm.SetFName(psQIF, pszQIFNam, lenQIFNam));
+ CHKiRet(strm.ConstructFinalize(psQIF));
/* first, we try to read the property bag for ourselfs */
CHKiRet(obj.DeserializePropBag((obj_t*) pThis, psQIF));
@@ -863,13 +865,13 @@ qqueueTryLoadPersistedInfo(qqueue_t *pThis)
/* create a duplicate for the read "pointer".
*/
- CHKiRet(strmDup(pThis->tVars.disk.pReadDel, &pThis->tVars.disk.pReadDeq));
- CHKiRet(strmSetbDeleteOnClose(pThis->tVars.disk.pReadDeq, 0)); /* deq must NOT delete the files! */
- CHKiRet(strmConstructFinalize(pThis->tVars.disk.pReadDeq));
+ CHKiRet(strm.Dup(pThis->tVars.disk.pReadDel, &pThis->tVars.disk.pReadDeq));
+ CHKiRet(strm.SetbDeleteOnClose(pThis->tVars.disk.pReadDeq, 0)); /* deq must NOT delete the files! */
+ CHKiRet(strm.ConstructFinalize(pThis->tVars.disk.pReadDeq));
- CHKiRet(strmSeekCurrOffs(pThis->tVars.disk.pWrite));
- CHKiRet(strmSeekCurrOffs(pThis->tVars.disk.pReadDel));
- CHKiRet(strmSeekCurrOffs(pThis->tVars.disk.pReadDeq));
+ CHKiRet(strm.SeekCurrOffs(pThis->tVars.disk.pWrite));
+ CHKiRet(strm.SeekCurrOffs(pThis->tVars.disk.pReadDel));
+ CHKiRet(strm.SeekCurrOffs(pThis->tVars.disk.pReadDeq));
/* OK, we could successfully read the file, so we now can request that it be
* deleted when we are done with the persisted information.
@@ -878,7 +880,7 @@ qqueueTryLoadPersistedInfo(qqueue_t *pThis)
finalize_it:
if(psQIF != NULL)
- strmDestruct(&psQIF);
+ strm.Destruct(&psQIF);
if(iRet != RS_RET_OK) {
dbgoprint((obj_t*) pThis, "error %d reading .qi file - can not read persisted info (if any)\n",
@@ -913,32 +915,34 @@ static rsRetVal qConstructDisk(qqueue_t *pThis)
if(bRestarted == 1) {
;
} else {
- CHKiRet(strmConstruct(&pThis->tVars.disk.pWrite));
- CHKiRet(strmSetDir(pThis->tVars.disk.pWrite, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir())));
- CHKiRet(strmSetiMaxFiles(pThis->tVars.disk.pWrite, 10000000));
- CHKiRet(strmSettOperationsMode(pThis->tVars.disk.pWrite, STREAMMODE_WRITE));
- CHKiRet(strmSetsType(pThis->tVars.disk.pWrite, STREAMTYPE_FILE_CIRCULAR));
- CHKiRet(strmConstructFinalize(pThis->tVars.disk.pWrite));
-
- CHKiRet(strmConstruct(&pThis->tVars.disk.pReadDeq));
- CHKiRet(strmSetbDeleteOnClose(pThis->tVars.disk.pReadDeq, 0));
- CHKiRet(strmSetDir(pThis->tVars.disk.pReadDeq, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir())));
- CHKiRet(strmSetiMaxFiles(pThis->tVars.disk.pReadDeq, 10000000));
- CHKiRet(strmSettOperationsMode(pThis->tVars.disk.pReadDeq, STREAMMODE_READ));
- CHKiRet(strmSetsType(pThis->tVars.disk.pReadDeq, STREAMTYPE_FILE_CIRCULAR));
- CHKiRet(strmConstructFinalize(pThis->tVars.disk.pReadDeq));
-
- CHKiRet(strmConstruct(&pThis->tVars.disk.pReadDel));
- CHKiRet(strmSetbDeleteOnClose(pThis->tVars.disk.pReadDel, 1));
- CHKiRet(strmSetDir(pThis->tVars.disk.pReadDel, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir())));
- CHKiRet(strmSetiMaxFiles(pThis->tVars.disk.pReadDel, 10000000));
- CHKiRet(strmSettOperationsMode(pThis->tVars.disk.pReadDel, STREAMMODE_READ));
- CHKiRet(strmSetsType(pThis->tVars.disk.pReadDel, STREAMTYPE_FILE_CIRCULAR));
- CHKiRet(strmConstructFinalize(pThis->tVars.disk.pReadDel));
-
- CHKiRet(strmSetFName(pThis->tVars.disk.pWrite, pThis->pszFilePrefix, pThis->lenFilePrefix));
- CHKiRet(strmSetFName(pThis->tVars.disk.pReadDeq, pThis->pszFilePrefix, pThis->lenFilePrefix));
- CHKiRet(strmSetFName(pThis->tVars.disk.pReadDel, pThis->pszFilePrefix, pThis->lenFilePrefix));
+ CHKiRet(strm.Construct(&pThis->tVars.disk.pWrite));
+ CHKiRet(strm.SetbSync(pThis->tVars.disk.pWrite, pThis->bSyncQueueFiles));
+ CHKiRet(strm.SetDir(pThis->tVars.disk.pWrite, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir())));
+ CHKiRet(strm.SetiMaxFiles(pThis->tVars.disk.pWrite, 10000000));
+ CHKiRet(strm.SettOperationsMode(pThis->tVars.disk.pWrite, STREAMMODE_WRITE));
+ CHKiRet(strm.SetsType(pThis->tVars.disk.pWrite, STREAMTYPE_FILE_CIRCULAR));
+ CHKiRet(strm.ConstructFinalize(pThis->tVars.disk.pWrite));
+
+ CHKiRet(strm.Construct(&pThis->tVars.disk.pReadDeq));
+ CHKiRet(strm.SetbDeleteOnClose(pThis->tVars.disk.pReadDeq, 0));
+ CHKiRet(strm.SetDir(pThis->tVars.disk.pReadDeq, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir())));
+ CHKiRet(strm.SetiMaxFiles(pThis->tVars.disk.pReadDeq, 10000000));
+ CHKiRet(strm.SettOperationsMode(pThis->tVars.disk.pReadDeq, STREAMMODE_READ));
+ CHKiRet(strm.SetsType(pThis->tVars.disk.pReadDeq, STREAMTYPE_FILE_CIRCULAR));
+ CHKiRet(strm.ConstructFinalize(pThis->tVars.disk.pReadDeq));
+
+ CHKiRet(strm.Construct(&pThis->tVars.disk.pReadDel));
+ CHKiRet(strm.SetbSync(pThis->tVars.disk.pReadDel, pThis->bSyncQueueFiles));
+ CHKiRet(strm.SetbDeleteOnClose(pThis->tVars.disk.pReadDel, 1));
+ CHKiRet(strm.SetDir(pThis->tVars.disk.pReadDel, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir())));
+ CHKiRet(strm.SetiMaxFiles(pThis->tVars.disk.pReadDel, 10000000));
+ CHKiRet(strm.SettOperationsMode(pThis->tVars.disk.pReadDel, STREAMMODE_READ));
+ CHKiRet(strm.SetsType(pThis->tVars.disk.pReadDel, STREAMTYPE_FILE_CIRCULAR));
+ CHKiRet(strm.ConstructFinalize(pThis->tVars.disk.pReadDel));
+
+ CHKiRet(strm.SetFName(pThis->tVars.disk.pWrite, pThis->pszFilePrefix, pThis->lenFilePrefix));
+ CHKiRet(strm.SetFName(pThis->tVars.disk.pReadDeq, pThis->pszFilePrefix, pThis->lenFilePrefix));
+ CHKiRet(strm.SetFName(pThis->tVars.disk.pReadDel, pThis->pszFilePrefix, pThis->lenFilePrefix));
}
/* now we set (and overwrite in case of a persisted restart) some parameters which
@@ -946,9 +950,9 @@ static rsRetVal qConstructDisk(qqueue_t *pThis)
* for example file name generation must not be changed as that would break the
* ability to read existing queue files. -- rgerhards, 2008-01-12
*/
- CHKiRet(strmSetiMaxFileSize(pThis->tVars.disk.pWrite, pThis->iMaxFileSize));
- CHKiRet(strmSetiMaxFileSize(pThis->tVars.disk.pReadDeq, pThis->iMaxFileSize));
- CHKiRet(strmSetiMaxFileSize(pThis->tVars.disk.pReadDel, pThis->iMaxFileSize));
+ CHKiRet(strm.SetiMaxFileSize(pThis->tVars.disk.pWrite, pThis->iMaxFileSize));
+ CHKiRet(strm.SetiMaxFileSize(pThis->tVars.disk.pReadDeq, pThis->iMaxFileSize));
+ CHKiRet(strm.SetiMaxFileSize(pThis->tVars.disk.pReadDel, pThis->iMaxFileSize));
finalize_it:
RETiRet;
@@ -961,9 +965,9 @@ static rsRetVal qDestructDisk(qqueue_t *pThis)
ASSERT(pThis != NULL);
- strmDestruct(&pThis->tVars.disk.pWrite);
- strmDestruct(&pThis->tVars.disk.pReadDeq);
- strmDestruct(&pThis->tVars.disk.pReadDel);
+ strm.Destruct(&pThis->tVars.disk.pWrite);
+ strm.Destruct(&pThis->tVars.disk.pReadDeq);
+ strm.Destruct(&pThis->tVars.disk.pReadDel);
RETiRet;
}
@@ -975,10 +979,10 @@ static rsRetVal qAddDisk(qqueue_t *pThis, void* pUsr)
ASSERT(pThis != NULL);
- CHKiRet(strmSetWCntr(pThis->tVars.disk.pWrite, &nWriteCount));
+ CHKiRet(strm.SetWCntr(pThis->tVars.disk.pWrite, &nWriteCount));
CHKiRet((objSerialize(pUsr))(pUsr, pThis->tVars.disk.pWrite));
- CHKiRet(strmFlush(pThis->tVars.disk.pWrite));
- CHKiRet(strmSetWCntr(pThis->tVars.disk.pWrite, NULL)); /* no more counting for now... */
+ CHKiRet(strm.Flush(pThis->tVars.disk.pWrite));
+ CHKiRet(strm.SetWCntr(pThis->tVars.disk.pWrite, NULL)); /* no more counting for now... */
pThis->tVars.disk.sizeOnDisk += nWriteCount;
@@ -1015,10 +1019,10 @@ static rsRetVal qDelDisk(qqueue_t *pThis)
int64 offsIn;
int64 offsOut;
- CHKiRet(strmGetCurrOffset(pThis->tVars.disk.pReadDel, &offsIn));
+ CHKiRet(strm.GetCurrOffset(pThis->tVars.disk.pReadDel, &offsIn));
CHKiRet(obj.Deserialize(&pDummyObj, (uchar*) "msg", pThis->tVars.disk.pReadDel, NULL, NULL));
objDestruct(pDummyObj);
- CHKiRet(strmGetCurrOffset(pThis->tVars.disk.pReadDel, &offsOut));
+ CHKiRet(strm.GetCurrOffset(pThis->tVars.disk.pReadDel, &offsOut));
/* This time it is a bit tricky: we free disk space only upon file deletion. So we need
* to keep track of what we have read until we get an out-offset that is lower than the
@@ -1163,7 +1167,7 @@ qqueueDeq(qqueue_t *pThis, void **ppUsr)
static rsRetVal
tryShutdownWorkersWithinQueueTimeout(qqueue_t *pThis)
{
- DEFVARS_mutexProtection;
+ DEFVARS_mutexProtection_uncond;
struct timespec tTimeout;
rsRetVal iRetLocal;
DEFiRet;
@@ -2213,16 +2217,16 @@ static rsRetVal qqueuePersist(qqueue_t *pThis, int bIsCheckpoint)
pThis->bNeedDelQIF = 0;
}
/* indicate spool file needs to be deleted */
- CHKiRet(strmSetbDeleteOnClose(pThis->tVars.disk.pReadDel, 1));
+ CHKiRet(strm.SetbDeleteOnClose(pThis->tVars.disk.pReadDel, 1));
FINALIZE; /* nothing left to do, so be happy */
}
- CHKiRet(strmConstruct(&psQIF));
- CHKiRet(strmSettOperationsMode(psQIF, STREAMMODE_WRITE));
- CHKiRet(strmSetiAddtlOpenFlags(psQIF, O_TRUNC));
- CHKiRet(strmSetsType(psQIF, STREAMTYPE_FILE_SINGLE));
- CHKiRet(strmSetFName(psQIF, pszQIFNam, lenQIFNam));
- CHKiRet(strmConstructFinalize(psQIF));
+ CHKiRet(strm.Construct(&psQIF));
+ CHKiRet(strm.SettOperationsMode(psQIF, STREAMMODE_WRITE_TRUNC));
+ CHKiRet(strm.SetbSync(psQIF, pThis->bSyncQueueFiles));
+ CHKiRet(strm.SetsType(psQIF, STREAMTYPE_FILE_SINGLE));
+ CHKiRet(strm.SetFName(psQIF, pszQIFNam, lenQIFNam));
+ CHKiRet(strm.ConstructFinalize(psQIF));
/* first, write the property bag for ourselfs
* And, surprisingly enough, we currently need to persist only the size of the
@@ -2237,14 +2241,14 @@ static rsRetVal qqueuePersist(qqueue_t *pThis, int bIsCheckpoint)
CHKiRet(obj.EndSerialize(psQIF));
/* now persist the stream info */
- CHKiRet(strmSerialize(pThis->tVars.disk.pWrite, psQIF));
- CHKiRet(strmSerialize(pThis->tVars.disk.pReadDel, psQIF));
+ CHKiRet(strm.Serialize(pThis->tVars.disk.pWrite, psQIF));
+ CHKiRet(strm.Serialize(pThis->tVars.disk.pReadDel, psQIF));
/* tell the input file object that it must not delete the file on close if the queue
* is non-empty - but only if we are not during a simple checkpoint
*/
if(bIsCheckpoint != QUEUE_CHECKPOINT) {
- CHKiRet(strmSetbDeleteOnClose(pThis->tVars.disk.pReadDel, 0));
+ CHKiRet(strm.SetbDeleteOnClose(pThis->tVars.disk.pReadDel, 0));
}
/* we have persisted the queue object. So whenever it comes to an empty queue,
@@ -2254,7 +2258,7 @@ static rsRetVal qqueuePersist(qqueue_t *pThis, int bIsCheckpoint)
finalize_it:
if(psQIF != NULL)
- strmDestruct(&psQIF);
+ strm.Destruct(&psQIF);
RETiRet;
}
@@ -2613,6 +2617,7 @@ finalize_it:
/* some simple object access methods */
+DEFpropSetMeth(qqueue, bSyncQueueFiles, int)
DEFpropSetMeth(qqueue, iPersistUpdCnt, int)
DEFpropSetMeth(qqueue, iDeqtWinFromHr, int)
DEFpropSetMeth(qqueue, iDeqtWinToHr, int)
@@ -2673,6 +2678,7 @@ rsRetVal qqueueQueryInterface(void) { return RS_RET_NOT_IMPLEMENTED; }
BEGINObjClassInit(qqueue, 1, OBJ_IS_CORE_MODULE)
/* request objects we use */
CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(strm, CORE_COMPONENT));
/* now set our own handlers */
OBJSetMethodHandler(objMethod_SETPROPERTY, qqueueSetProperty);
diff --git a/runtime/queue.h b/runtime/queue.h
index c1fe597d..1a26a9ff 100644
--- a/runtime/queue.h
+++ b/runtime/queue.h
@@ -85,6 +85,7 @@ typedef struct queue_s {
void *pUsr; /* a global, user-supplied pointer. Is passed back to consumer. */
int iUpdsSincePersist;/* nbr of queue updates since the last persist call */
int iPersistUpdCnt; /* persits queue info after this nbr of updates - 0 -> persist only on shutdown */
+ int bSyncQueueFiles;/* if working with files, sync them after each write? */
int iHighWtrMrk; /* high water mark for disk-assisted memory queues */
int iLowWtrMrk; /* low water mark for disk-assisted memory queues */
int iDiscardMrk; /* if the queue is above this mark, low-severity messages are discarded */
@@ -199,6 +200,7 @@ rsRetVal qqueueConstruct(qqueue_t **ppThis, queueType_t qType, int iWorkerThread
int iMaxQueueSize, rsRetVal (*pConsumer)(void*,batch_t*));
PROTOTYPEObjClassInit(qqueue);
PROTOTYPEpropSetMeth(qqueue, iPersistUpdCnt, int);
+PROTOTYPEpropSetMeth(qqueue, bSyncQueueFiles, int);
PROTOTYPEpropSetMeth(qqueue, iDeqtWinFromHr, int);
PROTOTYPEpropSetMeth(qqueue, iDeqtWinToHr, int);
PROTOTYPEpropSetMeth(qqueue, toQShutdown, long);
diff --git a/runtime/rsyslog.c b/runtime/rsyslog.c
index 8df100a1..6f732f0e 100644
--- a/runtime/rsyslog.c
+++ b/runtime/rsyslog.c
@@ -77,6 +77,8 @@
#include "conf.h"
#include "glbl.h"
#include "errmsg.h"
+#include "rule.h"
+#include "ruleset.h"
/* forward definitions */
static rsRetVal dfltErrLogger(int, uchar *errMsg);
@@ -150,14 +152,10 @@ rsrtInit(char **ppErrObj, obj_if_t *pObjIF)
CHKiRet(datetimeClassInit(NULL));
if(ppErrObj != NULL) *ppErrObj = "msg";
CHKiRet(msgClassInit(NULL));
- if(ppErrObj != NULL) *ppErrObj = "str,";
- CHKiRet(strmClassInit(NULL));
- if(ppErrObj != NULL) *ppErrObj = "wti";
- CHKiRet(wtiClassInit(NULL));
- if(ppErrObj != NULL) *ppErrObj = "wtp";
- CHKiRet(wtpClassInit(NULL));
- if(ppErrObj != NULL) *ppErrObj = "queue";
- CHKiRet(qqueueClassInit(NULL));
+ if(ppErrObj != NULL) *ppErrObj = "ctok_token";
+ CHKiRet(ctok_tokenClassInit(NULL));
+ if(ppErrObj != NULL) *ppErrObj = "ctok";
+ CHKiRet(ctokClassInit(NULL));
if(ppErrObj != NULL) *ppErrObj = "vmstk";
CHKiRet(vmstkClassInit(NULL));
if(ppErrObj != NULL) *ppErrObj = "sysvar";
@@ -168,12 +166,18 @@ rsrtInit(char **ppErrObj, obj_if_t *pObjIF)
CHKiRet(vmopClassInit(NULL));
if(ppErrObj != NULL) *ppErrObj = "vmprg";
CHKiRet(vmprgClassInit(NULL));
- if(ppErrObj != NULL) *ppErrObj = "ctok_token";
- CHKiRet(ctok_tokenClassInit(NULL));
- if(ppErrObj != NULL) *ppErrObj = "ctok";
- CHKiRet(ctokClassInit(NULL));
if(ppErrObj != NULL) *ppErrObj = "expr";
CHKiRet(exprClassInit(NULL));
+ if(ppErrObj != NULL) *ppErrObj = "rule";
+ CHKiRet(ruleClassInit(NULL));
+ if(ppErrObj != NULL) *ppErrObj = "ruleset";
+ CHKiRet(rulesetClassInit(NULL));
+ if(ppErrObj != NULL) *ppErrObj = "wti";
+ CHKiRet(wtiClassInit(NULL));
+ if(ppErrObj != NULL) *ppErrObj = "wtp";
+ CHKiRet(wtpClassInit(NULL));
+ if(ppErrObj != NULL) *ppErrObj = "queue";
+ CHKiRet(qqueueClassInit(NULL));
if(ppErrObj != NULL) *ppErrObj = "conf";
CHKiRet(confClassInit(NULL));
@@ -206,6 +210,8 @@ rsrtExit(void)
/* do actual de-init only if we are the last runtime user */
confClassExit();
glblClassExit();
+ rulesetClassExit();
+ ruleClassExit();
objClassExit(); /* *THIS* *MUST/SHOULD?* always be the first class initilizer being called (except debug)! */
}
diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h
index 0ed03203..fa854175 100644
--- a/runtime/rsyslog.h
+++ b/runtime/rsyslog.h
@@ -77,7 +77,9 @@ typedef unsigned char uchar;/* get rid of the unhandy "unsigned char" */
typedef struct aUsrp_s aUsrp_t;
typedef struct thrdInfo thrdInfo_t;
typedef struct obj_s obj_t;
-typedef struct filed selector_t;/* TODO: this so far resides in syslogd.c, think about modularization */
+typedef struct ruleset_s ruleset_t;
+typedef struct rule_s rule_t;
+//typedef struct filed selector_t;/* TODO: this so far resides in syslogd.c, think about modularization */
typedef struct NetAddr netAddr_t;
typedef struct netstrms_s netstrms_t;
typedef struct netstrm_s netstrm_t;
@@ -119,6 +121,8 @@ typedef unsigned int u_int32_t; /* TODO: is this correct? */
typedef int socklen_t;
#endif
+typedef char bool; /* I intentionally use char, to keep it slim so that many fit into the CPU cache! */
+
/* settings for flow control
* TODO: is there a better place for them? -- rgerhards, 2008-03-14
*/
@@ -128,6 +132,20 @@ typedef enum {
eFLOWCTL_FULL_DELAY = 2 /**< delay possible for extended period of time */
} flowControl_t;
+/* filter operations */
+typedef enum {
+ FIOP_NOP = 0, /* do not use - No Operation */
+ FIOP_CONTAINS = 1, /* contains string? */
+ FIOP_ISEQUAL = 2, /* is (exactly) equal? */
+ FIOP_STARTSWITH = 3, /* starts with a string? */
+ FIOP_REGEX = 4, /* matches a (BRE) regular expression? */
+ FIOP_EREREGEX = 5 /* matches a ERE regular expression? */
+} fiop_t;
+
+
+#ifndef _PATH_CONSOLE
+#define _PATH_CONSOLE "/dev/console"
+#endif
/* The error codes below are orginally "borrowed" from
* liblogging. As such, we reserve values up to -2999
@@ -293,7 +311,11 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth
RS_RET_PREVIOUS_COMMITTED = -2122, /**< output plugin status: previous record was committed (an OK state!) */
RS_RET_ACTION_FAILED = -2123, /**< action failed and is now suspended (consider this permanent for the time being) */
RS_RET_NONFATAL_CONFIG_ERR = -2124, /**< non-fatal error during config processing */
+ RS_RET_NON_SIZELIMITCMD = -2125, /**< size limit for file defined, but no size limit command given */
+ RS_RET_SIZELIMITCMD_DIDNT_RESOLVE = -2126, /**< size limit command did not resolve situation */
+ RS_RET_STREAM_DISABLED = -2127, /**< a file has been disabled (e.g. by size limit restriction) */
RS_RET_FILENAME_INVALID = -2140, /**< filename invalid, not found, no access, ... */
+ RS_RET_ZLIB_ERR = -2141, /**< error during zlib call */
/* RainerScript error messages (range 1000.. 1999) */
RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */
@@ -384,6 +406,11 @@ typedef enum rsObjectID rsObjID;
# define O_CLOEXEC 0
#endif
+/* some constants */
+// TODO: do we really need them - if not, delete -- rgerhards, 2009-06-10
+#define IGNORE_ERROR_CODES 1
+#define ABORT_ON_ERROR 0
+
/* The following prototype is convenient, even though it may not be the 100% correct place.. -- rgerhards 2008-01-07 */
void dbgprintf(char *, ...) __attribute__((format(printf, 1, 2)));
diff --git a/runtime/rule.c b/runtime/rule.c
new file mode 100644
index 00000000..f17c524e
--- /dev/null
+++ b/runtime/rule.c
@@ -0,0 +1,450 @@
+/* rule.c - rsyslog's rule object
+ *
+ * See file comment in rule.c for the overall structure of rule processing.
+ *
+ * Module begun 2009-06-10 by Rainer Gerhards
+ *
+ * Copyright 2009 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of the rsyslog runtime library.
+ *
+ * The rsyslog runtime library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The rsyslog runtime library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include "rsyslog.h"
+#include "obj.h"
+#include "action.h"
+#include "rule.h"
+#include "errmsg.h"
+#include "vm.h"
+#include "var.h"
+#include "srUtils.h"
+#include "dirty.h" /* for getFIOPName */
+
+/* static data */
+DEFobjStaticHelpers
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(expr)
+DEFobjCurrIf(var)
+DEFobjCurrIf(vm)
+
+/* iterate over all actions, this is often needed, for example when HUP processing
+ * must be done or a shutdown is pending.
+ */
+static rsRetVal
+iterateAllActions(rule_t *pThis, rsRetVal (*pFunc)(void*, void*), void* pParam)
+{
+ return llExecFunc(&pThis->llActList, pFunc, pParam);
+}
+
+
+
+/* helper to processMsg(), used to call the configured actions. It is
+ * executed from within llExecFunc() of the action list.
+ * rgerhards, 2007-08-02
+ */
+typedef struct processMsgDoActions_s {
+ int bPrevWasSuspended; /* was the previous action suspended? */
+ msg_t *pMsg;
+} processMsgDoActions_t;
+DEFFUNC_llExecFunc(processMsgDoActions)
+{
+ DEFiRet;
+ rsRetVal iRetMod; /* return value of module - we do not always pass that back */
+ action_t *pAction = (action_t*) pData;
+ processMsgDoActions_t *pDoActData = (processMsgDoActions_t*) pParam;
+
+ assert(pAction != NULL);
+
+ if((pAction->bExecWhenPrevSusp == 1) && (pDoActData->bPrevWasSuspended == 0)) {
+ dbgprintf("not calling action because the previous one is not suspended\n");
+ ABORT_FINALIZE(RS_RET_OK);
+ }
+
+ iRetMod = actionCallAction(pAction, pDoActData->pMsg);
+ if(iRetMod == RS_RET_DISCARDMSG) {
+ ABORT_FINALIZE(RS_RET_DISCARDMSG);
+ } else if(iRetMod == RS_RET_SUSPENDED) {
+ /* indicate suspension for next module to be called */
+ pDoActData->bPrevWasSuspended = 1;
+ } else {
+ pDoActData->bPrevWasSuspended = 0;
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* This functions looks at the given message and checks if it matches the
+ * provided filter condition.
+ */
+static rsRetVal
+shouldProcessThisMessage(rule_t *pRule, msg_t *pMsg, int *bProcessMsg)
+{
+ DEFiRet;
+ unsigned short pbMustBeFreed;
+ char *pszPropVal;
+ int bRet = 0;
+ vm_t *pVM = NULL;
+ var_t *pResult = NULL;
+
+ ISOBJ_TYPE_assert(pRule, rule);
+ assert(pMsg != NULL);
+
+ /* we first have a look at the global, BSD-style block filters (for tag
+ * and host). Only if they match, we evaluate the actual filter.
+ * rgerhards, 2005-10-18
+ */
+ if(pRule->eHostnameCmpMode == HN_NO_COMP) {
+ /* EMPTY BY INTENSION - we check this value first, because
+ * it is the one most often used, so this saves us time!
+ */
+ } else if(pRule->eHostnameCmpMode == HN_COMP_MATCH) {
+ if(rsCStrSzStrCmp(pRule->pCSHostnameComp, (uchar*) getHOSTNAME(pMsg), getHOSTNAMELen(pMsg))) {
+ /* not equal, so we are already done... */
+ dbgprintf("hostname filter '+%s' does not match '%s'\n",
+ rsCStrGetSzStrNoNULL(pRule->pCSHostnameComp), getHOSTNAME(pMsg));
+ FINALIZE;
+ }
+ } else { /* must be -hostname */
+ if(!rsCStrSzStrCmp(pRule->pCSHostnameComp, (uchar*) getHOSTNAME(pMsg), getHOSTNAMELen(pMsg))) {
+ /* not equal, so we are already done... */
+ dbgprintf("hostname filter '-%s' does not match '%s'\n",
+ rsCStrGetSzStrNoNULL(pRule->pCSHostnameComp), getHOSTNAME(pMsg));
+ FINALIZE;
+ }
+ }
+
+ if(pRule->pCSProgNameComp != NULL) {
+ int bInv = 0, bEqv = 0, offset = 0;
+ if(*(rsCStrGetSzStrNoNULL(pRule->pCSProgNameComp)) == '-') {
+ if(*(rsCStrGetSzStrNoNULL(pRule->pCSProgNameComp) + 1) == '-')
+ offset = 1;
+ else {
+ bInv = 1;
+ offset = 1;
+ }
+ }
+ if(!rsCStrOffsetSzStrCmp(pRule->pCSProgNameComp, offset, (uchar*) getProgramName(pMsg), getProgramNameLen(pMsg)))
+ bEqv = 1;
+
+ if((!bEqv && !bInv) || (bEqv && bInv)) {
+ /* not equal or inverted selection, so we are already done... */
+ dbgprintf("programname filter '%s' does not match '%s'\n",
+ rsCStrGetSzStrNoNULL(pRule->pCSProgNameComp), getProgramName(pMsg));
+ FINALIZE;
+ }
+ }
+
+ /* done with the BSD-style block filters */
+
+ if(pRule->f_filter_type == FILTER_PRI) {
+ /* skip messages that are incorrect priority */
+ if ( (pRule->f_filterData.f_pmask[pMsg->iFacility] == TABLE_NOPRI) || \
+ ((pRule->f_filterData.f_pmask[pMsg->iFacility] & (1<<pMsg->iSeverity)) == 0) )
+ bRet = 0;
+ else
+ bRet = 1;
+ } else if(pRule->f_filter_type == FILTER_EXPR) {
+ CHKiRet(vm.Construct(&pVM));
+ CHKiRet(vm.ConstructFinalize(pVM));
+ CHKiRet(vm.SetMsg(pVM, pMsg));
+ CHKiRet(vm.ExecProg(pVM, pRule->f_filterData.f_expr->pVmprg));
+ CHKiRet(vm.PopBoolFromStack(pVM, &pResult));
+ dbgprintf("result of expression evaluation: %lld\n", pResult->val.num);
+ /* VM is destructed on function exit */
+ bRet = (pResult->val.num) ? 1 : 0;
+ } else {
+ assert(pRule->f_filter_type == FILTER_PROP); /* assert() just in case... */
+ pszPropVal = MsgGetProp(pMsg, NULL, pRule->f_filterData.prop.pCSPropName, &pbMustBeFreed);
+
+ /* Now do the compares (short list currently ;)) */
+ switch(pRule->f_filterData.prop.operation ) {
+ case FIOP_CONTAINS:
+ if(rsCStrLocateInSzStr(pRule->f_filterData.prop.pCSCompValue, (uchar*) pszPropVal) != -1)
+ bRet = 1;
+ break;
+ case FIOP_ISEQUAL:
+ if(rsCStrSzStrCmp(pRule->f_filterData.prop.pCSCompValue,
+ (uchar*) pszPropVal, strlen(pszPropVal)) == 0)
+ bRet = 1; /* process message! */
+ break;
+ case FIOP_STARTSWITH:
+ if(rsCStrSzStrStartsWithCStr(pRule->f_filterData.prop.pCSCompValue,
+ (uchar*) pszPropVal, strlen(pszPropVal)) == 0)
+ bRet = 1; /* process message! */
+ break;
+ case FIOP_REGEX:
+ if(rsCStrSzStrMatchRegex(pRule->f_filterData.prop.pCSCompValue,
+ (unsigned char*) pszPropVal, 0, &pRule->f_filterData.prop.regex_cache) == RS_RET_OK)
+ bRet = 1;
+ break;
+ case FIOP_EREREGEX:
+ if(rsCStrSzStrMatchRegex(pRule->f_filterData.prop.pCSCompValue,
+ (unsigned char*) pszPropVal, 1, &pRule->f_filterData.prop.regex_cache) == RS_RET_OK)
+ bRet = 1;
+ break;
+ default:
+ /* here, it handles NOP (for performance reasons) */
+ assert(pRule->f_filterData.prop.operation == FIOP_NOP);
+ bRet = 1; /* as good as any other default ;) */
+ break;
+ }
+
+ /* now check if the value must be negated */
+ if(pRule->f_filterData.prop.isNegated)
+ bRet = (bRet == 1) ? 0 : 1;
+
+ if(Debug) {
+ dbgprintf("Filter: check for property '%s' (value '%s') ",
+ rsCStrGetSzStrNoNULL(pRule->f_filterData.prop.pCSPropName),
+ pszPropVal);
+ if(pRule->f_filterData.prop.isNegated)
+ dbgprintf("NOT ");
+ dbgprintf("%s '%s': %s\n",
+ getFIOPName(pRule->f_filterData.prop.operation),
+ rsCStrGetSzStrNoNULL(pRule->f_filterData.prop.pCSCompValue),
+ bRet ? "TRUE" : "FALSE");
+ }
+
+ /* cleanup */
+ if(pbMustBeFreed)
+ free(pszPropVal);
+ }
+
+finalize_it:
+ /* destruct in any case, not just on error, but it makes error handling much easier */
+ if(pVM != NULL)
+ vm.Destruct(&pVM);
+
+ if(pResult != NULL)
+ var.Destruct(&pResult);
+
+ *bProcessMsg = bRet;
+ RETiRet;
+}
+
+
+
+/* Process (consume) a received message. Calls the actions configured.
+ * rgerhards, 2005-10-13
+ */
+static rsRetVal
+processMsg(rule_t *pThis, msg_t *pMsg)
+{
+ int bProcessMsg;
+ processMsgDoActions_t DoActData;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, rule);
+ assert(pMsg != NULL);
+
+ /* first check the filters... */
+ CHKiRet(shouldProcessThisMessage(pThis, pMsg, &bProcessMsg));
+ if(bProcessMsg) {
+ DoActData.pMsg = pMsg;
+ DoActData.bPrevWasSuspended = 0;
+ CHKiRet(llExecFunc(&pThis->llActList, processMsgDoActions, (void*)&DoActData));
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Standard-Constructor
+ */
+BEGINobjConstruct(rule) /* be sure to specify the object type also in END macro! */
+ENDobjConstruct(rule)
+
+
+/* ConstructionFinalizer
+ * rgerhards, 2008-01-09
+ */
+static rsRetVal
+ruleConstructFinalize(rule_t *pThis)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, rule);
+
+ /* note: actionDestruct is from action.c API! */
+ CHKiRet(llInit(&pThis->llActList, actionDestruct, NULL, NULL));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* destructor for the rule object */
+BEGINobjDestruct(rule) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDestruct(rule)
+ if(pThis->pCSHostnameComp != NULL)
+ rsCStrDestruct(&pThis->pCSHostnameComp);
+ if(pThis->pCSProgNameComp != NULL)
+ rsCStrDestruct(&pThis->pCSProgNameComp);
+
+ if(pThis->f_filter_type == FILTER_PROP) {
+ if(pThis->f_filterData.prop.pCSPropName != NULL)
+ rsCStrDestruct(&pThis->f_filterData.prop.pCSPropName);
+ if(pThis->f_filterData.prop.pCSCompValue != NULL)
+ rsCStrDestruct(&pThis->f_filterData.prop.pCSCompValue);
+ if(pThis->f_filterData.prop.regex_cache != NULL)
+ rsCStrRegexDestruct(&pThis->f_filterData.prop.regex_cache);
+ } else if(pThis->f_filter_type == FILTER_EXPR) {
+ if(pThis->f_filterData.f_expr != NULL)
+ expr.Destruct(&pThis->f_filterData.f_expr);
+ }
+
+ llDestroy(&pThis->llActList);
+ENDobjDestruct(rule)
+
+
+/* set the associated ruleset */
+static rsRetVal
+setAssRuleset(rule_t *pThis, ruleset_t *pRuleset)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, rule);
+ ISOBJ_TYPE_assert(pRuleset, ruleset);
+ pThis->pRuleset = pRuleset;
+ RETiRet;
+}
+
+/* get the associated ruleset (may be NULL if not set!) */
+static ruleset_t*
+getAssRuleset(rule_t *pThis)
+{
+ ISOBJ_TYPE_assert(pThis, rule);
+ return pThis->pRuleset;
+}
+
+
+/* helper to DebugPrint, to print out all actions via
+ * the llExecFunc() facility.
+ */
+DEFFUNC_llExecFunc(dbgPrintInitInfoAction)
+{
+ DEFiRet;
+ iRet = actionDbgPrint((action_t*) pData);
+ dbgprintf("\n");
+ RETiRet;
+}
+
+
+/* debugprint for the rule object */
+BEGINobjDebugPrint(rule) /* be sure to specify the object type also in END and CODESTART macros! */
+ int i;
+CODESTARTobjDebugPrint(rule)
+ dbgoprint((obj_t*) pThis, "rsyslog rule:\n");
+ if(pThis->pCSProgNameComp != NULL)
+ dbgprintf("tag: '%s'\n", rsCStrGetSzStrNoNULL(pThis->pCSProgNameComp));
+ if(pThis->eHostnameCmpMode != HN_NO_COMP)
+ dbgprintf("hostname: %s '%s'\n",
+ pThis->eHostnameCmpMode == HN_COMP_MATCH ?
+ "only" : "allbut",
+ rsCStrGetSzStrNoNULL(pThis->pCSHostnameComp));
+ if(pThis->f_filter_type == FILTER_PRI) {
+ for (i = 0; i <= LOG_NFACILITIES; i++)
+ if (pThis->f_filterData.f_pmask[i] == TABLE_NOPRI)
+ dbgprintf(" X ");
+ else
+ dbgprintf("%2X ", pThis->f_filterData.f_pmask[i]);
+ } else if(pThis->f_filter_type == FILTER_EXPR) {
+ dbgprintf("EXPRESSION-BASED Filter: can currently not be displayed");
+ } else {
+ dbgprintf("PROPERTY-BASED Filter:\n");
+ dbgprintf("\tProperty.: '%s'\n",
+ rsCStrGetSzStrNoNULL(pThis->f_filterData.prop.pCSPropName));
+ dbgprintf("\tOperation: ");
+ if(pThis->f_filterData.prop.isNegated)
+ dbgprintf("NOT ");
+ dbgprintf("'%s'\n", getFIOPName(pThis->f_filterData.prop.operation));
+ dbgprintf("\tValue....: '%s'\n",
+ rsCStrGetSzStrNoNULL(pThis->f_filterData.prop.pCSCompValue));
+ dbgprintf("\tAction...: ");
+ }
+
+ dbgprintf("\nActions:\n");
+ llExecFunc(&pThis->llActList, dbgPrintInitInfoAction, NULL); /* actions */
+
+ dbgprintf("\n");
+ENDobjDebugPrint(rule)
+
+
+/* queryInterface function
+ * rgerhards, 2008-02-21
+ */
+BEGINobjQueryInterface(rule)
+CODESTARTobjQueryInterface(rule)
+ if(pIf->ifVersion != ruleCURR_IF_VERSION) { /* check for current version, increment on each change */
+ ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED);
+ }
+
+ /* ok, we have the right interface, so let's fill it
+ * Please note that we may also do some backwards-compatibility
+ * work here (if we can support an older interface version - that,
+ * of course, also affects the "if" above).
+ */
+ pIf->Construct = ruleConstruct;
+ pIf->ConstructFinalize = ruleConstructFinalize;
+ pIf->Destruct = ruleDestruct;
+ pIf->DebugPrint = ruleDebugPrint;
+
+ pIf->IterateAllActions = iterateAllActions;
+ pIf->ProcessMsg = processMsg;
+ pIf->SetAssRuleset = setAssRuleset;
+ pIf->GetAssRuleset = getAssRuleset;
+finalize_it:
+ENDobjQueryInterface(rule)
+
+
+/* Exit the rule class.
+ * rgerhards, 2009-04-06
+ */
+BEGINObjClassExit(rule, OBJ_IS_CORE_MODULE) /* class, version */
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(expr, CORE_COMPONENT);
+ objRelease(var, CORE_COMPONENT);
+ objRelease(vm, CORE_COMPONENT);
+ENDObjClassExit(rule)
+
+
+/* Initialize the rule class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-02-19
+ */
+BEGINObjClassInit(rule, 1, OBJ_IS_CORE_MODULE) /* class, version */
+ /* request objects we use */
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(expr, CORE_COMPONENT));
+ CHKiRet(objUse(var, CORE_COMPONENT));
+ CHKiRet(objUse(vm, CORE_COMPONENT));
+
+ /* set our own handlers */
+ OBJSetMethodHandler(objMethod_DEBUGPRINT, ruleDebugPrint);
+ OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, ruleConstructFinalize);
+ENDObjClassInit(rule)
+
+/* vi:set ai:
+ */
diff --git a/runtime/rule.h b/runtime/rule.h
new file mode 100644
index 00000000..38b11c63
--- /dev/null
+++ b/runtime/rule.h
@@ -0,0 +1,77 @@
+/* The rule object.
+ *
+ * This implements rules within rsyslog.
+ *
+ * Copyright 2009 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of the rsyslog runtime library.
+ *
+ * The rsyslog runtime library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The rsyslog runtime library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
+ */
+#ifndef INCLUDED_RULE_H
+#define INCLUDED_RULE_H
+
+#include "linkedlist.h"
+#include "regexp.h"
+#include "expr.h"
+
+/* the rule object */
+struct rule_s {
+ BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
+ /* filter properties */
+ enum {
+ FILTER_PRI = 0, /* traditional PRI based filer */
+ FILTER_PROP = 1, /* extended filter, property based */
+ FILTER_EXPR = 2 /* extended filter, expression based */
+ } f_filter_type;
+ EHostnameCmpMode eHostnameCmpMode;
+ cstr_t *pCSHostnameComp; /* hostname to check */
+ cstr_t *pCSProgNameComp; /* tag to check or NULL, if not to be checked */
+ union {
+ u_char f_pmask[LOG_NFACILITIES+1]; /* priority mask */
+ struct {
+ cstr_t *pCSPropName;
+ fiop_t operation;
+ regex_t *regex_cache; /* cache for compiled REs, if such are used */
+ cstr_t *pCSCompValue; /* value to "compare" against */
+ char isNegated; /* actually a boolean ;) */
+ } prop;
+ expr_t *f_expr; /* expression object */
+ } f_filterData;
+
+ ruleset_t *pRuleset; /* associated ruleset */
+ linkedList_t llActList; /* list of configured actions */
+};
+
+/* interfaces */
+BEGINinterface(rule) /* name must also be changed in ENDinterface macro! */
+ INTERFACEObjDebugPrint(rule);
+ rsRetVal (*Construct)(rule_t **ppThis);
+ rsRetVal (*ConstructFinalize)(rule_t __attribute__((unused)) *pThis);
+ rsRetVal (*Destruct)(rule_t **ppThis);
+ rsRetVal (*IterateAllActions)(rule_t *pThis, rsRetVal (*pFunc)(void*, void*), void *pParam);
+ rsRetVal (*ProcessMsg)(rule_t *pThis, msg_t *pMsg);
+ rsRetVal (*SetAssRuleset)(rule_t *pThis, ruleset_t*);
+ ruleset_t* (*GetAssRuleset)(rule_t *pThis);
+ENDinterface(rule)
+#define ruleCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+
+
+/* prototypes */
+PROTOTYPEObj(rule);
+
+#endif /* #ifndef INCLUDED_RULE_H */
diff --git a/runtime/ruleset.c b/runtime/ruleset.c
new file mode 100644
index 00000000..93d40e24
--- /dev/null
+++ b/runtime/ruleset.c
@@ -0,0 +1,451 @@
+/* ruleset.c - rsyslog's ruleset object
+ *
+ * We have a two-way structure of linked lists: one global linked list
+ * (llAllRulesets) hold alls rule sets that we know. Included in each
+ * list is a list of rules (which contain a list of actions, but that's
+ * a different story).
+ *
+ * Usually, only a single rule set is executed. However, there exist some
+ * situations where all rules must be iterated over, for example on HUP. Thus,
+ * we also provide interfaces to do that.
+ *
+ * Module begun 2009-06-10 by Rainer Gerhards
+ *
+ * Copyright 2009 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of the rsyslog runtime library.
+ *
+ * The rsyslog runtime library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The rsyslog runtime library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include "rsyslog.h"
+#include "obj.h"
+#include "msg.h"
+#include "ruleset.h"
+#include "rule.h"
+#include "errmsg.h"
+#include "unicode-helper.h"
+
+static rsRetVal debugPrintAll(void); // TODO: remove!
+
+/* static data */
+DEFobjStaticHelpers
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(rule)
+
+linkedList_t llRulesets; /* this is NOT a pointer - no typo here ;) */
+ruleset_t *pCurrRuleset = NULL; /* currently "active" ruleset */
+ruleset_t *pDfltRuleset = NULL; /* currentl default ruleset, e.g. for binding to actions which have no other */
+
+/* ---------- linked-list key handling functions ---------- */
+
+/* destructor for linked list keys.
+ */
+static rsRetVal keyDestruct(void __attribute__((unused)) *pData)
+{
+ free(pData);
+ return RS_RET_OK;
+}
+
+
+/* ---------- END linked-list key handling functions ---------- */
+
+
+/* driver to iterate over all of this ruleset actions */
+typedef struct iterateAllActions_s {
+ rsRetVal (*pFunc)(void*, void*);
+ void *pParam;
+} iterateAllActions_t;
+DEFFUNC_llExecFunc(doIterateRulesetActions)
+{
+ DEFiRet;
+ rule_t* pRule = (rule_t*) pData;
+ iterateAllActions_t *pMyParam = (iterateAllActions_t*) pParam;
+ iRet = rule.IterateAllActions(pRule, pMyParam->pFunc, pMyParam->pParam);
+ RETiRet;
+}
+#if 0
+/* iterate over all actions of THIS rule set.
+ */
+static rsRetVal
+iterateRulesetAllActions(ruleset_t *pThis, rsRetVal (*pFunc)(void*, void*), void* pParam)
+{
+ iterateAllActions_t params;
+ DEFiRet;
+ assert(pFunc != NULL);
+
+ params.pFunc = pFunc;
+ params.pParam = pParam;
+ CHKiRet(llExecFunc(&llRulesets, doIterateRulesetActions, &params));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* driver to iterate over all actions */
+DEFFUNC_llExecFunc(doIterateAllActions)
+{
+ DEFiRet;
+ ruleset_t* pThis = (ruleset_t*) pData;
+ iterateAllActions_t *pMyParam = (iterateAllActions_t*) pParam;
+ iRet = iterateRulesetAllActions(pThis, pMyParam->pFunc, pMyParam->pParam);
+ RETiRet;
+}
+#endif
+/* iterate over ALL actions present in the WHOLE system.
+ * this is often needed, for example when HUP processing
+ * must be done or a shutdown is pending.
+ */
+static rsRetVal
+iterateAllActions(rsRetVal (*pFunc)(void*, void*), void* pParam)
+{
+ iterateAllActions_t params;
+ DEFiRet;
+ assert(pFunc != NULL);
+
+ params.pFunc = pFunc;
+ params.pParam = pParam;
+ //CHKiRet(llExecFunc(&llRulesets, doIterateAllActions, &params));
+ CHKiRet(llExecFunc(&llRulesets, doIterateRulesetActions, &params));
+
+finalize_it:
+ RETiRet;
+}
+
+
+
+/* helper to processMsg(), used to call the configured actions. It is
+ * executed from within llExecFunc() of the action list.
+ * rgerhards, 2007-08-02
+ */
+DEFFUNC_llExecFunc(processMsgDoRules)
+{
+ ISOBJ_TYPE_assert(pData, rule);
+ return rule.ProcessMsg((rule_t*) pData, (msg_t*) pParam);
+}
+
+
+/* Process (consume) a received message. Calls the actions configured.
+ * rgerhards, 2005-10-13
+ */
+static rsRetVal
+processMsg(msg_t *pMsg)
+{
+ ruleset_t *pThis;
+ DEFiRet;
+ assert(pMsg != NULL);
+
+ pThis = (pMsg->pRuleset == NULL) ? pDfltRuleset : pMsg->pRuleset;
+ ISOBJ_TYPE_assert(pThis, ruleset);
+
+ CHKiRet(llExecFunc(&pThis->llRules, processMsgDoRules, pMsg));
+
+finalize_it:
+ if(iRet == RS_RET_DISCARDMSG)
+ iRet = RS_RET_OK;
+
+ RETiRet;
+}
+
+/* Add a new rule to the end of the current rule set. We do a number
+ * of checks and ignore the rule if it does not pass them.
+ */
+static rsRetVal
+addRule(ruleset_t *pThis, rule_t **ppRule)
+{
+ int iActionCnt;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, ruleset);
+ ISOBJ_TYPE_assert(*ppRule, rule);
+
+ CHKiRet(llGetNumElts(&(*ppRule)->llActList, &iActionCnt));
+ if(iActionCnt == 0) {
+ errmsg.LogError(0, NO_ERRCODE, "warning: selector line without actions will be discarded");
+ rule.Destruct(ppRule);
+ } else {
+ CHKiRet(llAppend(&pThis->llRules, NULL, *ppRule));
+ dbgprintf("selector line successfully processed\n");
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* set name for ruleset */
+static rsRetVal setName(ruleset_t *pThis, uchar *pszName)
+{
+ DEFiRet;
+ free(pThis->pszName);
+ CHKmalloc(pThis->pszName = ustrdup(pszName));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* get current ruleset
+ * We use a non-standard calling interface, as nothing can go wrong and it
+ * is really much more natural to return the pointer directly.
+ */
+static ruleset_t*
+GetCurrent(void)
+{
+ return pCurrRuleset;
+}
+
+
+/* Find the ruleset with the given name and return a pointer to its object.
+ */
+static rsRetVal
+GetRuleset(ruleset_t **ppRuleset, uchar *pszName)
+{
+ DEFiRet;
+ assert(ppRuleset != NULL);
+ assert(pszName != NULL);
+
+ CHKiRet(llFind(&llRulesets, pszName, (void*) ppRuleset));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Set a new default rule set. If the default can not be found, no change happens.
+ */
+static rsRetVal
+SetDefaultRuleset(uchar *pszName)
+{
+ ruleset_t *pRuleset;
+ DEFiRet;
+ assert(pszName != NULL);
+
+ CHKiRet(GetRuleset(&pRuleset, pszName));
+ pDfltRuleset = pRuleset;
+ dbgprintf("default rule set changed to %p: '%s'\n", pRuleset, pszName);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Set a new current rule set. If the ruleset can not be found, no change happens.
+ */
+static rsRetVal
+SetCurrRuleset(uchar *pszName)
+{
+ ruleset_t *pRuleset;
+ DEFiRet;
+ assert(pszName != NULL);
+
+ CHKiRet(GetRuleset(&pRuleset, pszName));
+ pCurrRuleset = pRuleset;
+ dbgprintf("current rule set changed to %p: '%s'\n", pRuleset, pszName);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* destructor we need to destruct rules inside our linked list contents.
+ */
+static rsRetVal
+doRuleDestruct(void *pData)
+{
+ rule_t *pRule = (rule_t *) pData;
+ DEFiRet;
+ rule.Destruct(&pRule);
+ RETiRet;
+}
+
+
+/* Standard-Constructor
+ */
+BEGINobjConstruct(ruleset) /* be sure to specify the object type also in END macro! */
+ CHKiRet(llInit(&pThis->llRules, doRuleDestruct, NULL, NULL));
+finalize_it:
+ENDobjConstruct(ruleset)
+
+
+/* ConstructionFinalizer
+ * This also adds the rule set to the list of all known rulesets.
+ */
+static rsRetVal
+rulesetConstructFinalize(ruleset_t *pThis)
+{
+ uchar *keyName;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, ruleset);
+
+ /* we must duplicate our name, as the key destructer would also
+ * free it, resulting in a double-free. It's also cleaner to have
+ * two separate copies.
+ */
+ CHKmalloc(keyName = ustrdup(pThis->pszName));
+ CHKiRet(llAppend(&llRulesets, keyName, pThis));
+
+ /* this now also is the new current ruleset */
+ pCurrRuleset = pThis;
+
+ /* and also the default, if so far none has been set */
+ if(pDfltRuleset == NULL)
+ pDfltRuleset = pThis;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* destructor for the ruleset object */
+BEGINobjDestruct(ruleset) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDestruct(ruleset)
+ dbgprintf("destructing ruleset %p, name %p\n", pThis, pThis->pszName);
+ llDestroy(&pThis->llRules);
+ free(pThis->pszName);
+ENDobjDestruct(ruleset)
+
+/* this is a special destructor for the linkedList class. LinkedList does NOT
+ * provide a pointer to the pointer, but rather the raw pointer itself. So we
+ * must map this, otherwise the destructor will abort.
+ */
+static rsRetVal
+rulesetDestructForLinkedList(void *pData)
+{
+ ruleset_t *pThis = (ruleset_t*) pData;
+ return rulesetDestruct(&pThis);
+}
+
+
+/* destruct ALL rule sets that reside in the system. This must
+ * be callable before unloading this module as the module may
+ * not be unloaded before unload of the actions is required. This is
+ * kind of a left-over from previous logic and may be optimized one
+ * everything runs stable again. -- rgerhards, 2009-06-10
+ */
+static rsRetVal
+destructAllActions(void)
+{
+ DEFiRet;
+
+ CHKiRet(llDestroy(&llRulesets));
+ CHKiRet(llInit(&llRulesets, rulesetDestructForLinkedList, keyDestruct, strcasecmp));
+
+finalize_it:
+ RETiRet;
+}
+
+/* helper for debugPrint(), initiates rule printing */
+DEFFUNC_llExecFunc(doDebugPrintRule)
+{
+ return rule.DebugPrint((rule_t*) pData);
+}
+/* debugprint for the ruleset object */
+BEGINobjDebugPrint(ruleset) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDebugPrint(ruleset)
+ dbgoprint((obj_t*) pThis, "rsyslog ruleset %s:\n", pThis->pszName);
+ llExecFunc(&pThis->llRules, doDebugPrintRule, NULL);
+ENDobjDebugPrint(ruleset)
+
+
+/* helper for debugPrintAll(), prints a single ruleset */
+DEFFUNC_llExecFunc(doDebugPrintAll)
+{
+ return rulesetDebugPrint((ruleset_t*) pData);
+}
+/* debug print all rulesets
+ */
+static rsRetVal
+debugPrintAll(void)
+{
+ DEFiRet;
+ dbgprintf("All Rulesets:\n");
+ llExecFunc(&llRulesets, doDebugPrintAll, NULL);
+ dbgprintf("End of Rulesets.\n");
+ RETiRet;
+}
+
+
+/* queryInterface function
+ * rgerhards, 2008-02-21
+ */
+BEGINobjQueryInterface(ruleset)
+CODESTARTobjQueryInterface(ruleset)
+ if(pIf->ifVersion != rulesetCURR_IF_VERSION) { /* check for current version, increment on each change */
+ ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED);
+ }
+
+ /* ok, we have the right interface, so let's fill it
+ * Please note that we may also do some backwards-compatibility
+ * work here (if we can support an older interface version - that,
+ * of course, also affects the "if" above).
+ */
+ pIf->Construct = rulesetConstruct;
+ pIf->ConstructFinalize = rulesetConstructFinalize;
+ pIf->Destruct = rulesetDestruct;
+ pIf->DebugPrint = rulesetDebugPrint;
+
+ pIf->IterateAllActions = iterateAllActions;
+ pIf->DestructAllActions = destructAllActions;
+ pIf->AddRule = addRule;
+ pIf->ProcessMsg = processMsg;
+ pIf->SetName = setName;
+ pIf->DebugPrintAll = debugPrintAll;
+ pIf->GetCurrent = GetCurrent;
+ pIf->GetRuleset = GetRuleset;
+ pIf->SetDefaultRuleset = SetDefaultRuleset;
+ pIf->SetCurrRuleset = SetCurrRuleset;
+finalize_it:
+ENDobjQueryInterface(ruleset)
+
+
+/* Exit the ruleset class.
+ * rgerhards, 2009-04-06
+ */
+BEGINObjClassExit(ruleset, OBJ_IS_CORE_MODULE) /* class, version */
+ llDestroy(&llRulesets);
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(rule, CORE_COMPONENT);
+ENDObjClassExit(ruleset)
+
+
+/* Initialize the ruleset class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-02-19
+ */
+BEGINObjClassInit(ruleset, 1, OBJ_IS_CORE_MODULE) /* class, version */
+ /* request objects we use */
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(rule, CORE_COMPONENT));
+
+ /* set our own handlers */
+ OBJSetMethodHandler(objMethod_DEBUGPRINT, rulesetDebugPrint);
+ OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, rulesetConstructFinalize);
+
+ /* prepare global data */
+ CHKiRet(llInit(&llRulesets, rulesetDestructForLinkedList, keyDestruct, strcasecmp));
+ENDObjClassInit(ruleset)
+
+/* vi:set ai:
+ */
diff --git a/runtime/ruleset.h b/runtime/ruleset.h
new file mode 100644
index 00000000..32571687
--- /dev/null
+++ b/runtime/ruleset.h
@@ -0,0 +1,60 @@
+/* The ruleset object.
+ *
+ * This implements rulesets within rsyslog.
+ *
+ * Copyright 2009 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of the rsyslog runtime library.
+ *
+ * The rsyslog runtime library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The rsyslog runtime library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
+ */
+#ifndef INCLUDED_RULESET_H
+#define INCLUDED_RULESET_H
+
+#include "linkedlist.h"
+
+/* the ruleset object */
+struct ruleset_s {
+ BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
+ linkedList_t llRules; /* this is NOT a pointer - no typo here ;) */
+ uchar *pszName; /* name of our ruleset */
+};
+
+/* interfaces */
+BEGINinterface(ruleset) /* name must also be changed in ENDinterface macro! */
+ INTERFACEObjDebugPrint(ruleset);
+ rsRetVal (*DebugPrintAll)(void);
+ rsRetVal (*Construct)(ruleset_t **ppThis);
+ rsRetVal (*ConstructFinalize)(ruleset_t __attribute__((unused)) *pThis);
+ rsRetVal (*Destruct)(ruleset_t **ppThis);
+ rsRetVal (*IterateAllActions)(rsRetVal (*pFunc)(void*, void*), void* pParam);
+ rsRetVal (*DestructAllActions)(void);
+ rsRetVal (*AddRule)(ruleset_t *pThis, rule_t **ppRule);
+ rsRetVal (*SetName)(ruleset_t *pThis, uchar *pszName);
+ rsRetVal (*ProcessMsg)(msg_t *pMsg);
+ rsRetVal (*GetRuleset)(ruleset_t **ppThis, uchar*);
+ rsRetVal (*SetDefaultRuleset)(uchar*);
+ rsRetVal (*SetCurrRuleset)(uchar*);
+ ruleset_t* (*GetCurrent)(void);
+ENDinterface(ruleset)
+#define rulesetCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+
+
+/* prototypes */
+PROTOTYPEObj(ruleset);
+
+#endif /* #ifndef INCLUDED_RULESET_H */
diff --git a/runtime/srUtils.h b/runtime/srUtils.h
index 288e9dd7..9d6360e7 100644
--- a/runtime/srUtils.h
+++ b/runtime/srUtils.h
@@ -92,6 +92,7 @@ void srSleep(int iSeconds, int iuSeconds);
char *rs_strerror_r(int errnum, char *buf, size_t buflen);
int decodeSyslogName(uchar *name, syslogName_t *codetab);
int getSubString(uchar **ppSrc, char *pDst, size_t DstSize, char cSep);
+rsRetVal getFileSize(uchar *pszName, off_t *pSize);
/* mutex operations */
/* some macros to cancel-safe lock a mutex (it will automatically be released
@@ -126,9 +127,12 @@ int getSubString(uchar **ppSrc, char *pDst, size_t DstSize, char cSep);
bLockedOpIsLocked = 0; \
pthread_setcancelstate(iCancelStateSave, NULL); \
}
+
/* The unconditional versions of the macro always lock the mutex. They are preferred in
* complex scenarios, where the simple ones might get mixed up by multiple calls.
*/
+#define DEFVARS_mutexProtection_uncond\
+ int iCancelStateSave
#define BEGIN_MTX_PROTECTED_OPERATIONS_UNCOND(mut) \
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave); \
d_pthread_mutex_lock(mut);
diff --git a/runtime/srutils.c b/runtime/srutils.c
index d01ca20d..5407531f 100644
--- a/runtime/srutils.c
+++ b/runtime/srutils.c
@@ -553,6 +553,33 @@ int getSubString(uchar **ppSrc, char *pDst, size_t DstSize, char cSep)
}
+/* get the size of a file or return appropriate error code. If an error is returned,
+ * *pSize content is undefined.
+ * rgerhards, 2009-06-12
+ */
+rsRetVal
+getFileSize(uchar *pszName, off_t *pSize)
+{
+ int ret;
+ struct stat statBuf;
+ DEFiRet;
+
+ ret = stat((char*) pszName, &statBuf);
+ if(ret == -1) {
+ switch(errno) {
+ case EACCES: ABORT_FINALIZE(RS_RET_NO_FILE_ACCESS);
+ case ENOTDIR:
+ case ENOENT: ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND);
+ default: ABORT_FINALIZE(RS_RET_FILE_NO_STAT);
+ }
+ }
+
+ *pSize = statBuf.st_size;
+
+finalize_it:
+ RETiRet;
+}
+
/* vim:set ai:
*/
diff --git a/runtime/stream.c b/runtime/stream.c
index 59e8be3a..372a3a5f 100644
--- a/runtime/stream.c
+++ b/runtime/stream.c
@@ -1,4 +1,3 @@
-//TODO: O_TRUC mode!
/* The serial stream class.
*
* A serial stream provides serial data access. In theory, serial streams
@@ -7,8 +6,9 @@
* "driver").
*
* File begun on 2008-01-09 by RGerhards
+ * Large modifications in 2009-06 to support using it with omfile, including zip writer.
*
- * Copyright 2008 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2008, 2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of the rsyslog runtime library.
*
@@ -39,6 +39,7 @@
#include <unistd.h>
#include <sys/stat.h> /* required for HP UX */
#include <errno.h>
+#include <pthread.h>
#include "rsyslog.h"
#include "stringbuf.h"
@@ -46,18 +47,191 @@
#include "obj.h"
#include "stream.h"
#include "unicode-helper.h"
+#include "module-template.h"
+#include "apc.h"
/* static data */
DEFobjStaticHelpers
+DEFobjCurrIf(zlibw)
+DEFobjCurrIf(apc)
+
+/* forward definitions */
+static rsRetVal strmFlush(strm_t *pThis);
+static rsRetVal strmWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf);
+static rsRetVal strmCloseFile(strm_t *pThis);
+
/* methods */
-/* first, we define type-specific handlers. The provide a generic functionality,
+/* async flush apc handler
+ */
+static void
+flushApc(void *param1, void __attribute__((unused)) *param2)
+{
+ DEFVARS_mutexProtection_uncond;
+ strm_t *pThis = (strm_t*) param1;
+ ISOBJ_TYPE_assert(pThis, strm);
+
+ BEGIN_MTX_PROTECTED_OPERATIONS_UNCOND(&pThis->mut);
+ strmFlush(pThis);
+ END_MTX_PROTECTED_OPERATIONS_UNCOND(&pThis->mut);
+}
+
+
+/* Try to resolve a size limit situation. This is used to support custom-file size handlers
+ * for omfile. It first runs the command, and then checks if we are still above the size
+ * treshold. Note that this works only with single file names, NOT with circular names.
+ * Note that pszCurrFName can NOT be taken from pThis, because the stream is closed when
+ * we are called (and that destroys pszCurrFName, as there is NO CURRENT file name!). So
+ * we need to receive the name as a parameter.
+ * initially wirtten 2005-06-21, moved to this class & updates 2009-06-01, both rgerhards
+ */
+static rsRetVal
+resolveFileSizeLimit(strm_t *pThis, uchar *pszCurrFName)
+{
+ uchar *pParams;
+ uchar *pCmd;
+ uchar *p;
+ off_t actualFileSize;
+ rsRetVal localRet;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strm);
+ assert(pszCurrFName != NULL);
+
+ if(pThis->pszSizeLimitCmd == NULL) {
+ ABORT_FINALIZE(RS_RET_NON_SIZELIMITCMD); /* nothing we can do in this case... */
+ }
+
+ /* we first check if we have command line parameters. We assume this,
+ * when we have a space in the program name. If we find it, everything after
+ * the space is treated as a single argument.
+ */
+ CHKmalloc(pCmd = ustrdup(pThis->pszSizeLimitCmd));
+
+ for(p = pCmd ; *p && *p != ' ' ; ++p) {
+ /* JUST SKIP */
+ }
+
+ if(*p == ' ') {
+ *p = '\0'; /* pretend string-end */
+ pParams = p+1;
+ } else
+ pParams = NULL;
+
+ /* the execProg() below is probably not great, but at least is is
+ * fairly secure now. Once we change the way file size limits are
+ * handled, we should also revisit how this command is run (and
+ * with which parameters). rgerhards, 2007-07-20
+ */
+ execProg(pCmd, 1, pParams);
+
+ free(pCmd);
+
+ localRet = getFileSize(pszCurrFName, &actualFileSize);
+
+ if(localRet == RS_RET_OK && actualFileSize >= pThis->iSizeLimit) {
+ ABORT_FINALIZE(RS_RET_SIZELIMITCMD_DIDNT_RESOLVE); /* OK, it didn't work out... */
+ } else if(localRet != RS_RET_FILE_NOT_FOUND) {
+ /* file not found is OK, the command may have moved away the file */
+ ABORT_FINALIZE(localRet);
+ }
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(iRet == RS_RET_SIZELIMITCMD_DIDNT_RESOLVE)
+ dbgprintf("file size limit cmd for file '%s' did no resolve situation\n", pszCurrFName);
+ else
+ dbgprintf("file size limit cmd for file '%s' failed with code %d.\n", pszCurrFName, iRet);
+ pThis->bDisabled = 1;
+ }
+
+ RETiRet;
+}
+
+
+/* Check if the file has grown beyond the configured omfile iSizeLimit
+ * and, if so, initiate processing.
+ */
+static rsRetVal
+doSizeLimitProcessing(strm_t *pThis)
+{
+ uchar *pszCurrFName = NULL;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, strm);
+ ASSERT(pThis->iSizeLimit != 0);
+ ASSERT(pThis->fd != -1);
+
+ if(pThis->iCurrOffs >= pThis->iSizeLimit) {
+ /* strmClosefile() destroys the current file name, so we
+ * need to preserve it.
+ */
+ CHKmalloc(pszCurrFName = ustrdup(pThis->pszCurrFName));
+ CHKiRet(strmCloseFile(pThis));
+ CHKiRet(resolveFileSizeLimit(pThis, pszCurrFName));
+ }
+
+finalize_it:
+ free(pszCurrFName);
+ RETiRet;
+}
+
+
+/* now, we define type-specific handlers. The provide a generic functionality,
* but for this specific type of strm. The mapping to these handlers happens during
* strm construction. Later on, handlers are called by pointers present in the
* strm instance object.
*/
+/* do the physical open() call on a file.
+ */
+static rsRetVal
+doPhysOpen(strm_t *pThis)
+{
+ int iFlags;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strm);
+
+ /* compute which flags we need to provide to open */
+ switch(pThis->tOperationsMode) {
+ case STREAMMODE_READ:
+ iFlags = O_CLOEXEC | O_NOCTTY | O_RDONLY;
+ break;
+ case STREAMMODE_WRITE: /* legacy mode used inside queue engine */
+ iFlags = O_CLOEXEC | O_NOCTTY | O_WRONLY | O_CREAT;
+ break;
+ case STREAMMODE_WRITE_TRUNC:
+ iFlags = O_CLOEXEC | O_NOCTTY | O_WRONLY | O_CREAT | O_TRUNC;
+ break;
+ case STREAMMODE_WRITE_APPEND:
+ iFlags = O_CLOEXEC | O_NOCTTY | O_WRONLY | O_CREAT | O_APPEND;
+ break;
+ default:assert(0);
+ break;
+ }
+
+ pThis->fd = open((char*)pThis->pszCurrFName, iFlags, pThis->tOpenMode);
+ if(pThis->fd == -1) {
+ int ierrnoSave = errno;
+ dbgoprint((obj_t*) pThis, "open error %d, file '%s'\n", errno, pThis->pszCurrFName);
+ if(ierrnoSave == ENOENT)
+ ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND);
+ else
+ ABORT_FINALIZE(RS_RET_IO_ERROR);
+ } else {
+ if(!ustrcmp(pThis->pszCurrFName, UCHAR_CONSTANT(_PATH_CONSOLE)) || isatty(pThis->fd)) {
+ DBGPRINTF("file %d is a tty-type file\n", pThis->fd);
+ pThis->bIsTTY = 1;
+ } else {
+ pThis->bIsTTY = 0;
+ }
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
/* open a strm file
* It is OK to call this function when the stream is already open. In that
* case, it returns immediately with RS_RET_OK
@@ -65,10 +239,8 @@ DEFobjStaticHelpers
static rsRetVal strmOpenFile(strm_t *pThis)
{
DEFiRet;
- int iFlags;
ASSERT(pThis != NULL);
- ASSERT(pThis->tOperationsMode == STREAMMODE_READ || pThis->tOperationsMode == STREAMMODE_WRITE);
if(pThis->fd != -1)
ABORT_FINALIZE(RS_RET_OK);
@@ -89,31 +261,18 @@ static rsRetVal strmOpenFile(strm_t *pThis)
}
}
- /* compute which flags we need to provide to open */
- if(pThis->tOperationsMode == STREAMMODE_READ)
- iFlags = O_RDONLY;
- else
- iFlags = O_WRONLY | O_CREAT;
-
- iFlags |= pThis->iAddtlOpenFlags;
-
- pThis->fd = open((char*)pThis->pszCurrFName, iFlags, pThis->tOpenMode);
- if(pThis->fd == -1) {
- int ierrnoSave = errno;
- char errStr[1024];
- dbgoprint((obj_t*) pThis, "open error[%d]: '%s'; file '%s'/%s\n", errno,
- rs_strerror_r(errno, errStr, sizeof(errStr)), pThis->pszCurrFName,
- (pThis->tOperationsMode == STREAMMODE_READ) ? "READ" : "WRITE");
- if(ierrnoSave == ENOENT)
- ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND);
- else
- ABORT_FINALIZE(RS_RET_IO_ERROR);
- }
+ CHKiRet(doPhysOpen(pThis));
pThis->iCurrOffs = 0;
+ if(pThis->tOperationsMode == STREAMMODE_WRITE_APPEND) {
+ /* we need to obtain the current offset */
+ off_t offset;
+ CHKiRet(getFileSize(pThis->pszCurrFName, &offset));
+ pThis->iCurrOffs = offset;
+ }
- dbgoprint((obj_t*) pThis, "opened file '%s' for %s (0x%x) as %d\n", pThis->pszCurrFName,
- (pThis->tOperationsMode == STREAMMODE_READ) ? "READ" : "WRITE", iFlags, pThis->fd);
+ dbgoprint((obj_t*) pThis, "opened file '%s' for %s as %d\n", pThis->pszCurrFName,
+ (pThis->tOperationsMode == STREAMMODE_READ) ? "READ" : "WRITE", pThis->fd);
finalize_it:
RETiRet;
@@ -132,14 +291,26 @@ static rsRetVal strmCloseFile(strm_t *pThis)
ASSERT(pThis->fd != -1);
dbgoprint((obj_t*) pThis, "file %d closing\n", pThis->fd);
- if(pThis->tOperationsMode == STREAMMODE_WRITE)
+ if(pThis->tOperationsMode != STREAMMODE_READ)
strmFlush(pThis);
- close(pThis->fd); // TODO: error check
+ close(pThis->fd);
pThis->fd = -1;
+ if(pThis->fdDir != -1) {
+ /* close associated directory handle, if it is open */
+ close(pThis->fdDir);
+ pThis->fdDir = -1;
+ }
+
if(pThis->bDeleteOnClose) {
- unlink((char*) pThis->pszCurrFName); // TODO: check returncode
+ if(unlink((char*) pThis->pszCurrFName) == -1) {
+ char errStr[1024];
+ int err = errno;
+ rs_strerror_r(err, errStr, sizeof(errStr));
+ DBGPRINTF("error %d unlinking '%s' - ignored: %s\n",
+ errno, pThis->pszCurrFName, errStr);
+ }
}
pThis->iCurrOffs = 0; /* we are back at begin of file */
@@ -238,10 +409,6 @@ strmHandleEOF(strm_t *pThis)
case STREAMTYPE_FILE_CIRCULAR:
/* we have multiple files and need to switch to the next one */
/* TODO: think about emulating EOF in this case (not yet needed) */
-#if 0
- if(pThis->iMaxFiles == 0) /* TODO: why do we need this? ;) */
- ABORT_FINALIZE(RS_RET_EOF);
-#endif
dbgoprint((obj_t*) pThis, "file %d EOF\n", pThis->fd);
CHKiRet(strmNextFile(pThis));
break;
@@ -299,7 +466,7 @@ finalize_it:
* NOTE: needs to be enhanced to support sticking with a strm entry (if not
* deleted).
*/
-rsRetVal strmReadChar(strm_t *pThis, uchar *pC)
+static rsRetVal strmReadChar(strm_t *pThis, uchar *pC)
{
DEFiRet;
@@ -333,7 +500,7 @@ finalize_it:
* character buffering capability.
* rgerhards, 2008-01-07
*/
-rsRetVal strmUnreadChar(strm_t *pThis, uchar c)
+static rsRetVal strmUnreadChar(strm_t *pThis, uchar c)
{
ASSERT(pThis != NULL);
ASSERT(pThis->iUngetC == -1);
@@ -355,7 +522,7 @@ rsRetVal strmUnreadChar(strm_t *pThis, uchar c)
* are pthread_killed() upon termination. So if we use their native pointer, they
* can cleanup (but only then).
*/
-rsRetVal
+static rsRetVal
strmReadLine(strm_t *pThis, cstr_t **ppCStr)
{
DEFiRet;
@@ -372,7 +539,7 @@ strmReadLine(strm_t *pThis, cstr_t **ppCStr)
CHKiRet(rsCStrAppendChar(*ppCStr, c));
CHKiRet(strmReadChar(pThis, &c));
}
- CHKiRet(rsCStrFinish(*ppCStr));
+ CHKiRet(cstrFinalize(*ppCStr));
finalize_it:
if(iRet != RS_RET_OK && *ppCStr != NULL)
@@ -387,26 +554,55 @@ finalize_it:
BEGINobjConstruct(strm) /* be sure to specify the object type also in END macro! */
pThis->iCurrFNum = 1;
pThis->fd = -1;
+ pThis->fdDir = -1;
pThis->iUngetC = -1;
pThis->sType = STREAMTYPE_FILE_SINGLE;
pThis->sIOBufSize = glblGetIOBufSize();
- pThis->tOpenMode = 0600; /* TODO: make configurable */
+ pThis->tOpenMode = 0600;
ENDobjConstruct(strm)
/* ConstructionFinalizer
* rgerhards, 2008-01-09
*/
-rsRetVal strmConstructFinalize(strm_t *pThis)
+static rsRetVal strmConstructFinalize(strm_t *pThis)
{
+ rsRetVal localRet;
DEFiRet;
ASSERT(pThis != NULL);
- if(pThis->pIOBuf == NULL) { /* allocate our io buffer in case we have not yet */
- if((pThis->pIOBuf = (uchar*) malloc(sizeof(uchar) * pThis->sIOBufSize)) == NULL)
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- pThis->iBufPtrMax = 0; /* results in immediate read request */
+ CHKmalloc(pThis->pIOBuf = (uchar*) malloc(sizeof(uchar) * pThis->sIOBufSize));
+ pThis->iBufPtrMax = 0; /* results in immediate read request */
+ if(pThis->iZipLevel) { /* do we need a zip buf? */
+ localRet = objUse(zlibw, LM_ZLIBW_FILENAME);
+ if(localRet != RS_RET_OK) {
+ pThis->iZipLevel = 0;
+ DBGPRINTF("stream was requested with zip mode, but zlibw module unavailable (%d) - using "
+ "without zip\n", localRet);
+ } else {
+ /* we use the same size as the original buf, as we would like
+ * to make sure we can write out everyting with a SINGLE api call!
+ */
+ CHKmalloc(pThis->pZipBuf = (Bytef*) malloc(sizeof(uchar) * pThis->sIOBufSize));
+ }
+ }
+
+ /* if we are aset to sync, we must obtain a file handle to the directory for fsync() purposes */
+ if(pThis->bSync && !pThis->bIsTTY) {
+ pThis->fdDir = open((char*)pThis->pszDir, O_RDONLY | O_CLOEXEC | O_NOCTTY);
+ if(pThis->fdDir == -1) {
+ char errStr[1024];
+ int err = errno;
+ rs_strerror_r(err, errStr, sizeof(errStr));
+ DBGPRINTF("error %d opening directory file for fsync() use - fsync for directory disabled: %s\n",
+ errno, errStr);
+ }
+ }
+
+ /* if we should call flush apc's, we need a mutex */
+ if(pThis->iFlushInterval != 0) {
+ pthread_mutex_init(&pThis->mut, 0);
}
finalize_it:
@@ -417,21 +613,27 @@ finalize_it:
/* destructor for the strm object */
BEGINobjDestruct(strm) /* be sure to specify the object type also in END and CODESTART macros! */
CODESTARTobjDestruct(strm)
- if(pThis->tOperationsMode == STREAMMODE_WRITE)
+ if(pThis->tOperationsMode != STREAMMODE_READ)
strmFlush(pThis);
/* ... then free resources */
if(pThis->fd != -1)
strmCloseFile(pThis);
- if(pThis->pszDir != NULL)
- free(pThis->pszDir);
- if(pThis->pIOBuf != NULL)
- free(pThis->pIOBuf);
- if(pThis->pszCurrFName != NULL)
- free(pThis->pszCurrFName);
- if(pThis->pszFName != NULL)
- free(pThis->pszFName);
+ if(pThis->iZipLevel) { /* do we need a zip buf? */
+ objRelease(zlibw, LM_ZLIBW_FILENAME);
+ }
+
+ if(pThis->iFlushInterval != 0) {
+ // TODO: check if there is an apc and remove it!
+ pthread_mutex_destroy(&pThis->mut);
+ }
+
+ free(pThis->pszDir);
+ free(pThis->pIOBuf);
+ free(pThis->pZipBuf);
+ free(pThis->pszCurrFName);
+ free(pThis->pszFName);
ENDobjDestruct(strm)
@@ -457,48 +659,240 @@ finalize_it:
RETiRet;
}
-/* write memory buffer to a stream object.
- * To support direct writes of large objects, this method may be called
- * with a buffer pointing to some region other than the stream buffer itself.
- * However, in that case the stream buffer must be empty (strmFlush() has to
- * be called before), because we would otherwise mess up with the sequence
- * inside the stream. -- rgerhards, 2008-01-10
+
+/* try to recover a tty after a write error. This may have happend
+ * due to vhangup(), and, if so, we can simply re-open it.
*/
-static rsRetVal strmWriteInternal(strm_t *pThis, uchar *pBuf, size_t lenBuf)
+#ifdef linux
+# define ERR_TTYHUP EIO
+#else
+# define ERR_TTYHUP EBADF
+#endif
+static rsRetVal
+tryTTYRecover(strm_t *pThis, int err)
{
DEFiRet;
- int iWritten;
+ ISOBJ_TYPE_assert(pThis, strm);
+ if(err == ERR_TTYHUP) {
+ close(pThis->fd);
+ CHKiRet(doPhysOpen(pThis));
+ }
- ASSERT(pThis != NULL);
- ASSERT(pBuf == pThis->pIOBuf || pThis->iBufPtr == 0);
+finalize_it:
+ RETiRet;
+}
+#undef ER_TTYHUP
+
+
+/* issue write() api calls until either the buffer is completely
+ * written or an error occured (it may happen that multiple writes
+ * are required, what is perfectly legal. On exit, *pLenBuf contains
+ * the number of bytes actually written.
+ * rgerhards, 2009-06-08
+ */
+static rsRetVal
+doWriteCall(strm_t *pThis, uchar *pBuf, size_t *pLenBuf)
+{
+ ssize_t lenBuf;
+ ssize_t iTotalWritten;
+ ssize_t iWritten;
+ char *pWriteBuf;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strm);
+
+ lenBuf = *pLenBuf;
+ pWriteBuf = (char*) pBuf;
+ iTotalWritten = 0;
+ do {
+ iWritten = write(pThis->fd, pWriteBuf, lenBuf);
+ if(iWritten < 0) {
+ char errStr[1024];
+ int err = errno;
+ rs_strerror_r(err, errStr, sizeof(errStr));
+ DBGPRINTF("log file (%d) write error %d: %s\n", pThis->fd, err, errStr);
+ if(err == EINTR) {
+ /*NO ERROR, just continue */;
+ } else {
+ if(pThis->bIsTTY) {
+ CHKiRet(tryTTYRecover(pThis, err));
+ } else {
+ ABORT_FINALIZE(RS_RET_IO_ERROR);
+ /* Would it make sense to cover more error cases? So far, I
+ * do not see good reason to do so.
+ */
+ }
+ }
+ }
+ /* advance buffer to next write position */
+ iTotalWritten += iWritten;
+ lenBuf -= iWritten;
+ pWriteBuf += iWritten;
+ } while(lenBuf > 0); /* Warning: do..while()! */
+
+finalize_it:
+ *pLenBuf = iTotalWritten;
+ RETiRet;
+}
+
+
+/* sync the file to disk, so that any unwritten data is persisted. This
+ * also syncs the directory and thus makes sure that the file survives
+ * fatal failure. Note that we do NOT return an error status if the
+ * sync fails. Doing so would probably cause more trouble than it
+ * is worth (read: data loss may occur where we otherwise might not
+ * have it). -- rgerhards, 2009-06-08
+ */
+static rsRetVal
+syncFile(strm_t *pThis)
+{
+ int ret;
+ DEFiRet;
+
+ if(pThis->bIsTTY)
+ FINALIZE; /* TTYs can not be synced */
+
+ DBGPRINTF("syncing file %d\n", pThis->fd);
+ ret = fdatasync(pThis->fd);
+ if(ret != 0) {
+ char errStr[1024];
+ int err = errno;
+ rs_strerror_r(err, errStr, sizeof(errStr));
+ DBGPRINTF("sync failed for file %d with error (%d): %s - ignoring\n",
+ pThis->fd, err, errStr);
+ }
+
+ if(pThis->fdDir != -1) {
+ ret = fsync(pThis->fdDir);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* physically write to the output file. the provided data is ready for
+ * writing (e.g. zipped if we are requested to do that).
+ * Note that if the write() API fails, we do not reset any pointers, but return
+ * an error code. That means we may redo work in the next iteration.
+ * rgerhards, 2009-06-04
+ */
+static rsRetVal
+strmPhysWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf)
+{
+ size_t iWritten;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strm);
if(pThis->fd == -1)
CHKiRet(strmOpenFile(pThis));
- iWritten = write(pThis->fd, pBuf, lenBuf);
- dbgoprint((obj_t*) pThis, "file %d write wrote %d bytes\n", pThis->fd, iWritten);
- /* TODO: handle error case -- rgerhards, 2008-01-07 */
-
- /* Now indicate buffer empty again. We do this in any case, because there
- * is no way we could react more intelligently to an error during write.
- * This MUST be done BEFORE strCheckNextOutputFile(), otherwise we have an
- * endless loop. We reset the buffer pointer also in finalize_it - this is
- * necessary if we run into problems. Not resetting it would again cause an
- * endless loop. So it is better to loose some data (which also justifies
- * duplicating that code, too...) -- rgerhards, 2008-01-10
- */
+ iWritten = lenBuf;
+ CHKiRet(doWriteCall(pThis, pBuf, &iWritten));
+ dbgoprint((obj_t*) pThis, "file %d write wrote %d bytes\n", pThis->fd, (int) iWritten);
+
pThis->iBufPtr = 0;
pThis->iCurrOffs += iWritten;
/* update user counter, if provided */
if(pThis->pUsrWCntr != NULL)
*pThis->pUsrWCntr += iWritten;
- if(pThis->sType == STREAMTYPE_FILE_CIRCULAR)
+ if(pThis->bSync) {
+ CHKiRet(syncFile(pThis));
+ }
+
+ if(pThis->sType == STREAMTYPE_FILE_CIRCULAR) {
CHKiRet(strmCheckNextOutputFile(pThis));
+ } else if(pThis->iSizeLimit != 0) {
+ CHKiRet(doSizeLimitProcessing(pThis));
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* write the output buffer in zip mode
+ * This means we compress it first and then do a physical write.
+ * Note that we always do a full deflateInit ... deflate ... deflateEnd
+ * sequence. While this is not optimal, we need to do it because we need
+ * to ensure that the file is readable even when we are aborted. Doing the
+ * full sequence brings us as far towards this goal as possible (and not
+ * doing it would be a total failure). It may be worth considering to
+ * add a config switch so that the user can decide the risk he is ready
+ * to take, but so far this is not yet implemented (not even requested ;)).
+ * rgerhards, 2009-06-04
+ */
+static rsRetVal
+doZipWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf)
+{
+ z_stream zstrm;
+ int zRet; /* zlib return state */
+ DEFiRet;
+ assert(pThis != NULL);
+ assert(pBuf != NULL);
+
+ /* allocate deflate state */
+ zstrm.zalloc = Z_NULL;
+ zstrm.zfree = Z_NULL;
+ zstrm.opaque = Z_NULL;
+ /* see note in file header for the params we use with deflateInit2() */
+ zRet = zlibw.DeflateInit2(&zstrm, pThis->iZipLevel, Z_DEFLATED, 31, 9, Z_DEFAULT_STRATEGY);
+ if(zRet != Z_OK) {
+ dbgprintf("error %d returned from zlib/deflateInit2()\n", zRet);
+ ABORT_FINALIZE(RS_RET_ZLIB_ERR);
+ }
+
+ /* now doing the compression */
+ zstrm.avail_in = lenBuf;
+ zstrm.next_in = (Bytef*) pBuf;
+ /* run deflate() on input until output buffer not full, finish
+ compression if all of source has been read in */
+ do {
+ dbgprintf("in deflate() loop, avail_in %d, total_in %ld\n", zstrm.avail_in, zstrm.total_in);
+ zstrm.avail_out = pThis->sIOBufSize;
+ zstrm.next_out = pThis->pZipBuf;
+ zRet = zlibw.Deflate(&zstrm, Z_FINISH); /* no bad return value */
+ dbgprintf("after deflate, ret %d, avail_out %d\n", zRet, zstrm.avail_out);
+ assert(zRet != Z_STREAM_ERROR); /* state not clobbered */
+ CHKiRet(strmPhysWrite(pThis, (uchar*)pThis->pZipBuf, pThis->sIOBufSize - zstrm.avail_out));
+ } while (zstrm.avail_out == 0);
+ assert(zstrm.avail_in == 0); /* all input will be used */
+
+
+ zRet = zlibw.DeflateEnd(&zstrm);
+ if(zRet != Z_OK) {
+ dbgprintf("error %d returned from zlib/deflateEnd()\n", zRet);
+ ABORT_FINALIZE(RS_RET_ZLIB_ERR);
+ }
finalize_it:
- pThis->iBufPtr = 0; /* see comment above */
+ RETiRet;
+}
+
+
+/* write memory buffer to a stream object.
+ * To support direct writes of large objects, this method may be called
+ * with a buffer pointing to some region other than the stream buffer itself.
+ * However, in that case the stream buffer must be empty (strmFlush() has to
+ * be called before), because we would otherwise mess up with the sequence
+ * inside the stream. -- rgerhards, 2008-01-10
+ */
+static rsRetVal
+strmWriteInternal(strm_t *pThis, uchar *pBuf, size_t lenBuf)
+{
+ DEFiRet;
+
+ ASSERT(pThis != NULL);
+ ASSERT(pBuf == pThis->pIOBuf || pThis->iBufPtr == 0);
+ if(pThis->iZipLevel) {
+ CHKiRet(doZipWrite(pThis, pBuf, lenBuf));
+ } else {
+ /* write without zipping */
+ CHKiRet(strmPhysWrite(pThis, pBuf, lenBuf));
+ }
+
+finalize_it:
RETiRet;
}
@@ -507,14 +901,15 @@ finalize_it:
* and is automatically called when the output buffer is full.
* rgerhards, 2008-01-10
*/
-rsRetVal strmFlush(strm_t *pThis)
+static rsRetVal
+strmFlush(strm_t *pThis)
{
DEFiRet;
ASSERT(pThis != NULL);
dbgoprint((obj_t*) pThis, "file %d flush, buflen %ld\n", pThis->fd, (long) pThis->iBufPtr);
- if(pThis->tOperationsMode == STREAMMODE_WRITE && pThis->iBufPtr > 0) {
+ if(pThis->tOperationsMode != STREAMMODE_READ && pThis->iBufPtr > 0) {
iRet = strmWriteInternal(pThis, pThis->pIOBuf, pThis->iBufPtr);
}
@@ -549,7 +944,7 @@ static rsRetVal strmSeek(strm_t *pThis, off_t offs)
/* seek to current offset. This is primarily a helper to readjust the OS file
* pointer after a strm object has been deserialized.
*/
-rsRetVal strmSeekCurrOffs(strm_t *pThis)
+static rsRetVal strmSeekCurrOffs(strm_t *pThis)
{
DEFiRet;
@@ -562,7 +957,7 @@ rsRetVal strmSeekCurrOffs(strm_t *pThis)
/* write a *single* character to a stream object -- rgerhards, 2008-01-10
*/
-rsRetVal strmWriteChar(strm_t *pThis, uchar c)
+static rsRetVal strmWriteChar(strm_t *pThis, uchar c)
{
DEFiRet;
@@ -582,7 +977,7 @@ finalize_it:
/* write an integer value (actually a long) to a stream object */
-rsRetVal strmWriteLong(strm_t *pThis, long i)
+static rsRetVal strmWriteLong(strm_t *pThis, long i)
{
DEFiRet;
uchar szBuf[32];
@@ -597,16 +992,48 @@ finalize_it:
}
+/* schedule an Apc flush request.
+ * rgerhards, 2009-06-15
+ */
+static inline rsRetVal
+scheduleFlushRequest(strm_t *pThis)
+{
+ apc_t *pApc;
+ DEFiRet;
+
+ CHKiRet(apc.CancelApc(pThis->apcID));
+dbgprintf("XXX: requesting to add apc!\n");
+ CHKiRet(apc.Construct(&pApc));
+ CHKiRet(apc.SetProcedure(pApc, (void (*)(void*, void*))flushApc));
+ CHKiRet(apc.SetParam1(pApc, pThis));
+ CHKiRet(apc.ConstructFinalize(pApc, &pThis->apcID));
+
+finalize_it:
+ RETiRet;
+}
+
+
/* write memory buffer to a stream object
*/
-rsRetVal strmWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf)
+static rsRetVal
+strmWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf)
{
+ DEFVARS_mutexProtection_uncond;
DEFiRet;
size_t iPartial;
ASSERT(pThis != NULL);
ASSERT(pBuf != NULL);
+dbgprintf("strmWrite(%p, '%65.65s', %ld);, disabled %d, sizelim %ld, size %lld\n", pThis, pBuf,lenBuf, pThis->bDisabled, pThis->iSizeLimit, pThis->iCurrOffs);
+ if(pThis->bDisabled)
+ ABORT_FINALIZE(RS_RET_STREAM_DISABLED);
+
+RUNLOG_VAR("%d", pThis->iFlushInterval);
+ if(pThis->iFlushInterval != 0) {
+ BEGIN_MTX_PROTECTED_OPERATIONS_UNCOND(&pThis->mut);
+ }
+
/* check if the to-be-written data is larger than our buffer size */
if(lenBuf >= pThis->sIOBufSize) {
/* it is - so we do a direct write, that is most efficient.
@@ -635,7 +1062,17 @@ rsRetVal strmWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf)
}
}
+ /* we ignore the outcome of scheduleFlushRequest(), as we will write the data always at
+ * termination. For Zip mode, it could be fatal if we write after each record.
+ */
+ if(pThis->iFlushInterval != 0)
+ scheduleFlushRequest(pThis);
+
finalize_it:
+ if(pThis->iFlushInterval != 0) {
+ END_MTX_PROTECTED_OPERATIONS_UNCOND(&pThis->mut);
+ }
+
RETiRet;
}
@@ -648,34 +1085,27 @@ DEFpropSetMeth(strm, iFileNumDigits, int)
DEFpropSetMeth(strm, tOperationsMode, int)
DEFpropSetMeth(strm, tOpenMode, mode_t)
DEFpropSetMeth(strm, sType, strmType_t)
-
-rsRetVal strmSetiMaxFiles(strm_t *pThis, int iNewVal)
+DEFpropSetMeth(strm, iZipLevel, int)
+DEFpropSetMeth(strm, bSync, int)
+DEFpropSetMeth(strm, sIOBufSize, size_t)
+DEFpropSetMeth(strm, iSizeLimit, off_t)
+DEFpropSetMeth(strm, iFlushInterval, int)
+DEFpropSetMeth(strm, pszSizeLimitCmd, uchar*)
+
+static rsRetVal strmSetiMaxFiles(strm_t *pThis, int iNewVal)
{
pThis->iMaxFiles = iNewVal;
pThis->iFileNumDigits = getNumberDigits(iNewVal);
return RS_RET_OK;
}
-rsRetVal strmSetiAddtlOpenFlags(strm_t *pThis, int iNewVal)
-{
- DEFiRet;
-
- if(iNewVal & O_APPEND)
- ABORT_FINALIZE(RS_RET_PARAM_ERROR);
-
- pThis->iAddtlOpenFlags = iNewVal;
-
-finalize_it:
- RETiRet;
-}
-
/* set the stream's file prefix
* The passed-in string is duplicated. So if the caller does not need
* it any longer, it must free it.
* rgerhards, 2008-01-09
*/
-rsRetVal
+static rsRetVal
strmSetFName(strm_t *pThis, uchar *pszName, size_t iLenName)
{
DEFiRet;
@@ -689,7 +1119,7 @@ strmSetFName(strm_t *pThis, uchar *pszName, size_t iLenName)
if(pThis->pszFName != NULL)
free(pThis->pszFName);
- if((pThis->pszFName = malloc(sizeof(uchar) * iLenName + 1)) == NULL)
+ if((pThis->pszFName = malloc(sizeof(uchar) * (iLenName + 1))) == NULL)
ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
memcpy(pThis->pszFName, pszName, iLenName + 1); /* always think about the \0! */
@@ -705,7 +1135,7 @@ finalize_it:
* it any longer, it must free it.
* rgerhards, 2008-01-09
*/
-rsRetVal
+static rsRetVal
strmSetDir(strm_t *pThis, uchar *pszDir, size_t iLenDir)
{
DEFiRet;
@@ -749,7 +1179,7 @@ finalize_it:
*
* rgerhards, 2008-01-10
*/
-rsRetVal strmRecordBegin(strm_t *pThis)
+static rsRetVal strmRecordBegin(strm_t *pThis)
{
ASSERT(pThis != NULL);
ASSERT(pThis->bInRecord == 0);
@@ -757,7 +1187,7 @@ rsRetVal strmRecordBegin(strm_t *pThis)
return RS_RET_OK;
}
-rsRetVal strmRecordEnd(strm_t *pThis)
+static rsRetVal strmRecordEnd(strm_t *pThis)
{
DEFiRet;
ASSERT(pThis != NULL);
@@ -779,7 +1209,7 @@ rsRetVal strmRecordEnd(strm_t *pThis)
* We do not serialize the dynamic properties.
* rgerhards, 2008-01-10
*/
-rsRetVal strmSerialize(strm_t *pThis, strm_t *pStrm)
+static rsRetVal strmSerialize(strm_t *pThis, strm_t *pStrm)
{
DEFiRet;
int i;
@@ -840,7 +1270,6 @@ strmDup(strm_t *pThis, strm_t **ppNew)
pNew->lenDir = pThis->lenDir;
pNew->tOperationsMode = pThis->tOperationsMode;
pNew->tOpenMode = pThis->tOpenMode;
- pNew->iAddtlOpenFlags = pThis->iAddtlOpenFlags;
pNew->iMaxFileSize = pThis->iMaxFileSize;
pNew->iMaxFiles = pThis->iMaxFiles;
pNew->iFileNumDigits = pThis->iFileNumDigits;
@@ -866,7 +1295,7 @@ finalize_it:
* any new set overwrites the previous one.
* rgerhards, 2008-02-27
*/
-rsRetVal
+static rsRetVal
strmSetWCntr(strm_t *pThis, number_t *pWCnt)
{
DEFiRet;
@@ -886,8 +1315,8 @@ strmSetWCntr(strm_t *pThis, number_t *pWCnt)
/* This function can be used as a generic way to set properties.
* rgerhards, 2008-01-11
*/
-#define isProp(name) !rsCStrSzStrCmp(pProp->pcsName, (uchar*) name, sizeof(name) - 1)
-rsRetVal strmSetProperty(strm_t *pThis, var_t *pProp)
+#define isProp(name) !rsCStrSzStrCmp(pProp->pcsName, UCHAR_CONSTANT(name), sizeof(name) - 1)
+static rsRetVal strmSetProperty(strm_t *pThis, var_t *pProp)
{
DEFiRet;
@@ -926,7 +1355,7 @@ finalize_it:
* reported on the second call may actually be lower than on the first call. This is due to
* file circulation. A caller must deal with that. -- rgerhards, 2008-01-30
*/
-rsRetVal
+static rsRetVal
strmGetCurrOffset(strm_t *pThis, int64 *pOffs)
{
DEFiRet;
@@ -954,8 +1383,39 @@ CODESTARTobjQueryInterface(strm)
* work here (if we can support an older interface version - that,
* of course, also affects the "if" above).
*/
- /*xxxpIf->oID = OBJvm; SAMPLE */
-
+ pIf->Construct = strmConstruct;
+ pIf->ConstructFinalize = strmConstructFinalize;
+ pIf->Destruct = strmDestruct;
+ pIf->ReadChar = strmReadChar;
+ pIf->UnreadChar = strmUnreadChar;
+ pIf->ReadLine = strmReadLine;
+ pIf->SeekCurrOffs = strmSeekCurrOffs;
+ pIf->Write = strmWrite;
+ pIf->WriteChar = strmWriteChar;
+ pIf->WriteLong = strmWriteLong;
+ pIf->SetFName = strmSetFName;
+ pIf->SetDir = strmSetDir;
+ pIf->Flush = strmFlush;
+ pIf->RecordBegin = strmRecordBegin;
+ pIf->RecordEnd = strmRecordEnd;
+ pIf->Serialize = strmSerialize;
+ pIf->GetCurrOffset = strmGetCurrOffset;
+ pIf->Dup = strmDup;
+ pIf->SetWCntr = strmSetWCntr;
+ /* set methods */
+ pIf->SetbDeleteOnClose = strmSetbDeleteOnClose;
+ pIf->SetiMaxFileSize = strmSetiMaxFileSize;
+ pIf->SetiMaxFiles = strmSetiMaxFiles;
+ pIf->SetiFileNumDigits = strmSetiFileNumDigits;
+ pIf->SettOperationsMode = strmSettOperationsMode;
+ pIf->SettOpenMode = strmSettOpenMode;
+ pIf->SetsType = strmSetsType;
+ pIf->SetiZipLevel = strmSetiZipLevel;
+ pIf->SetbSync = strmSetbSync;
+ pIf->SetsIOBufSize = strmSetsIOBufSize;
+ pIf->SetiSizeLimit = strmSetiSizeLimit;
+ pIf->SetiFlushInterval = strmSetiFlushInterval;
+ pIf->SetpszSizeLimitCmd = strmSetpszSizeLimitCmd;
finalize_it:
ENDobjQueryInterface(strm)
@@ -966,13 +1426,12 @@ ENDobjQueryInterface(strm)
*/
BEGINObjClassInit(strm, 1, OBJ_IS_CORE_MODULE)
/* request objects we use */
+ CHKiRet(objUse(apc, CORE_COMPONENT));
OBJSetMethodHandler(objMethod_SERIALIZE, strmSerialize);
OBJSetMethodHandler(objMethod_SETPROPERTY, strmSetProperty);
OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, strmConstructFinalize);
ENDObjClassInit(strm)
-
-/*
- * vi:set ai:
+/* vi:set ai:
*/
diff --git a/runtime/stream.h b/runtime/stream.h
index e5d05b55..dcf432ca 100644
--- a/runtime/stream.h
+++ b/runtime/stream.h
@@ -19,7 +19,29 @@
* can easily be persistet. The bottom line is that it makes much sense to
* use this class whereever possible as its features may grow in the future.
*
- * Copyright 2008 Rainer Gerhards and Adiscon GmbH.
+ * An important note on writing gzip format via zlib (kept anonymous
+ * by request):
+ *
+ * --------------------------------------------------------------------------
+ * We'd like to make sure the output file is in full gzip format
+ * (compatible with gzip -d/zcat etc). There is a flag in how the output
+ * is initialized within zlib to properly add the gzip wrappers to the
+ * output. (gzip is effectively a small metadata wrapper around raw
+ * zstream output.)
+ *
+ * I had written an old bit of code to do this - the documentation on
+ * deflatInit2() was pretty tricky to nail down on this specific feature:
+ *
+ * int deflateInit2 (z_streamp strm, int level, int method, int windowBits,
+ * int memLevel, int strategy);
+ *
+ * I believe "31" would be the value for the "windowBits" field that you'd
+ * want to try:
+ *
+ * deflateInit2(zstrmptr, 6, Z_DEFLATED, 31, 9, Z_DEFAULT_STRATEGY);
+ * --------------------------------------------------------------------------
+ *
+ * Copyright 2008, 2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of the rsyslog runtime library.
*
@@ -47,6 +69,8 @@
#include "obj-types.h"
#include "glbl.h"
#include "stream.h"
+#include "zlibw.h"
+#include "apc.h"
/* stream types */
typedef enum {
@@ -55,10 +79,12 @@ typedef enum {
STREAMTYPE_FILE_MONITOR = 2 /**< monitor a (third-party) file */
} strmType_t;
-typedef enum {
+typedef enum { /* when extending, do NOT change existing modes! */
STREAMMMODE_INVALID = 0,
STREAMMODE_READ = 1,
- STREAMMODE_WRITE = 2
+ STREAMMODE_WRITE = 2,
+ STREAMMODE_WRITE_TRUNC = 3,
+ STREAMMODE_WRITE_APPEND = 4
} strmMode_t;
/* The strm_t data structure */
@@ -71,62 +97,79 @@ typedef struct strm_s {
int lenFName;
strmMode_t tOperationsMode;
mode_t tOpenMode;
- int iAddtlOpenFlags; /* can be used to specifiy additional (compatible!) open flags */
int64 iMaxFileSize;/* maximum size a file may grow to */
int iMaxFiles; /* maximum number of files if a circular mode is in use */
int iFileNumDigits;/* min number of digits to use in file number (only in circular mode) */
- int bDeleteOnClose; /* set to 1 to auto-delete on close -- be careful with that setting! */
+ bool bDeleteOnClose; /* set to 1 to auto-delete on close -- be careful with that setting! */
int64 iCurrOffs;/* current offset */
int64 *pUsrWCntr; /* NULL or a user-provided counter that receives the nbr of bytes written since the last CntrSet() */
/* dynamic properties, valid only during file open, not to be persistet */
- size_t sIOBufSize;/* size of IO buffer */
+ int bDisabled; /* should file no longer be written to? (currently set only if omfile file size limit fails) */
+ int bSync; /* sync this file after every write? */
+ size_t sIOBufSize;/* size of IO buffer */
uchar *pszDir; /* Directory */
int lenDir;
int fd; /* the file descriptor, -1 if closed */
+ int fdDir; /* the directory's descriptor, in case bSync is requested (-1 if closed) */
uchar *pszCurrFName; /* name of current file (if open) */
uchar *pIOBuf; /* io Buffer */
size_t iBufPtrMax; /* current max Ptr in Buffer (if partial read!) */
size_t iBufPtr; /* pointer into current buffer */
int iUngetC; /* char set via UngetChar() call or -1 if none set */
- int bInRecord; /* if 1, indicates that we are currently writing a not-yet complete record */
+ bool bInRecord; /* if 1, indicates that we are currently writing a not-yet complete record */
+ int iZipLevel; /* zip level (0..9). If 0, zip is completely disabled */
+ Bytef *pZipBuf;
+ /* support for async flush procesing */
+ int iFlushInterval; /* flush in which interval - 0, no flushing */
+ apc_id_t apcID; /* id of current Apc request (used for cancelling) */
+ pthread_mutex_t mut;/* mutex for flush in async mode */
+ /* support for omfile size-limiting commands, special counters, NOT persisted! */
+ off_t iSizeLimit; /* file size limit, 0 = no limit */
+ uchar *pszSizeLimitCmd; /* command to carry out when size limit is reached */
+ bool bIsTTY; /* is this a tty file? */
} strm_t;
/* interfaces */
BEGINinterface(strm) /* name must also be changed in ENDinterface macro! */
+ rsRetVal (*Construct)(strm_t **ppThis);
+ rsRetVal (*ConstructFinalize)(strm_t *pThis);
+ rsRetVal (*Destruct)(strm_t **ppThis);
+ rsRetVal (*SetMaxFileSize)(strm_t *pThis, int64 iMaxFileSize);
+ rsRetVal (*SetFileName)(strm_t *pThis, uchar *pszName, size_t iLenName);
+ rsRetVal (*ReadChar)(strm_t *pThis, uchar *pC);
+ rsRetVal (*UnreadChar)(strm_t *pThis, uchar c);
+ rsRetVal (*ReadLine)(strm_t *pThis, cstr_t **ppCStr);
+ rsRetVal (*SeekCurrOffs)(strm_t *pThis);
+ rsRetVal (*Write)(strm_t *pThis, uchar *pBuf, size_t lenBuf);
+ rsRetVal (*WriteChar)(strm_t *pThis, uchar c);
+ rsRetVal (*WriteLong)(strm_t *pThis, long i);
+ rsRetVal (*SetFName)(strm_t *pThis, uchar *pszPrefix, size_t iLenPrefix);
+ rsRetVal (*SetDir)(strm_t *pThis, uchar *pszDir, size_t iLenDir);
+ rsRetVal (*Flush)(strm_t *pThis);
+ rsRetVal (*RecordBegin)(strm_t *pThis);
+ rsRetVal (*RecordEnd)(strm_t *pThis);
+ rsRetVal (*Serialize)(strm_t *pThis, strm_t *pStrm);
+ rsRetVal (*GetCurrOffset)(strm_t *pThis, int64 *pOffs);
+ rsRetVal (*SetWCntr)(strm_t *pThis, number_t *pWCnt);
+ rsRetVal (*Dup)(strm_t *pThis, strm_t **ppNew);
+ INTERFACEpropSetMeth(strm, bDeleteOnClose, int);
+ INTERFACEpropSetMeth(strm, iMaxFileSize, int);
+ INTERFACEpropSetMeth(strm, iMaxFiles, int);
+ INTERFACEpropSetMeth(strm, iFileNumDigits, int);
+ INTERFACEpropSetMeth(strm, tOperationsMode, int);
+ INTERFACEpropSetMeth(strm, tOpenMode, mode_t);
+ INTERFACEpropSetMeth(strm, sType, strmType_t);
+ INTERFACEpropSetMeth(strm, iZipLevel, int);
+ INTERFACEpropSetMeth(strm, bSync, int);
+ INTERFACEpropSetMeth(strm, sIOBufSize, size_t);
+ INTERFACEpropSetMeth(strm, iSizeLimit, off_t);
+ INTERFACEpropSetMeth(strm, iFlushInterval, int);
+ INTERFACEpropSetMeth(strm, pszSizeLimitCmd, uchar*);
ENDinterface(strm)
-#define strmCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+#define strmCURR_IF_VERSION 5 /* increment whenever you change the interface structure! */
/* prototypes */
-rsRetVal strmConstruct(strm_t **ppThis);
-rsRetVal strmConstructFinalize(strm_t __attribute__((unused)) *pThis);
-rsRetVal strmDestruct(strm_t **ppThis);
-rsRetVal strmSetMaxFileSize(strm_t *pThis, int64 iMaxFileSize);
-rsRetVal strmSetFileName(strm_t *pThis, uchar *pszName, size_t iLenName);
-rsRetVal strmReadChar(strm_t *pThis, uchar *pC);
-rsRetVal strmUnreadChar(strm_t *pThis, uchar c);
-rsRetVal strmReadLine(strm_t *pThis, cstr_t **ppCStr);
-rsRetVal strmSeekCurrOffs(strm_t *pThis);
-rsRetVal strmWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf);
-rsRetVal strmWriteChar(strm_t *pThis, uchar c);
-rsRetVal strmWriteLong(strm_t *pThis, long i);
-rsRetVal strmSetFName(strm_t *pThis, uchar *pszPrefix, size_t iLenPrefix);
-rsRetVal strmSetDir(strm_t *pThis, uchar *pszDir, size_t iLenDir);
-rsRetVal strmFlush(strm_t *pThis);
-rsRetVal strmRecordBegin(strm_t *pThis);
-rsRetVal strmRecordEnd(strm_t *pThis);
-rsRetVal strmSerialize(strm_t *pThis, strm_t *pStrm);
-rsRetVal strmSetiAddtlOpenFlags(strm_t *pThis, int iNewVal);
-rsRetVal strmGetCurrOffset(strm_t *pThis, int64 *pOffs);
-rsRetVal strmSetWCntr(strm_t *pThis, number_t *pWCnt);
-rsRetVal strmDup(strm_t *pThis, strm_t **ppNew);
PROTOTYPEObjClassInit(strm);
-PROTOTYPEpropSetMeth(strm, bDeleteOnClose, int);
-PROTOTYPEpropSetMeth(strm, iMaxFileSize, int);
-PROTOTYPEpropSetMeth(strm, iMaxFiles, int);
-PROTOTYPEpropSetMeth(strm, iFileNumDigits, int);
-PROTOTYPEpropSetMeth(strm, tOperationsMode, int);
-PROTOTYPEpropSetMeth(strm, tOpenMode, mode_t);
-PROTOTYPEpropSetMeth(strm, sType, strmType_t);
#endif /* #ifndef STREAM_H_INCLUDED */
diff --git a/runtime/stringbuf.c b/runtime/stringbuf.c
index 07256fab..a2d9c599 100644
--- a/runtime/stringbuf.c
+++ b/runtime/stringbuf.c
@@ -6,8 +6,9 @@
* Please see syslogd.c for license information.
* All functions in this "class" start with rsCStr (rsyslog Counted String).
* begun 2005-09-07 rgerhards
+ * did some optimization (read: bugs!) rgerhards, 2009-06-16
*
- * Copyright (C) 2007-2008 by Rainer Gerhards and Adiscon GmbH
+ * Copyright (C) 2007-2009 by Rainer Gerhards and Adiscon GmbH
*
* This file is part of the rsyslog runtime library.
*
@@ -40,6 +41,7 @@
#include "regexp.h"
#include "obj.h"
+uchar* rsCStrGetSzStr(cstr_t *pThis);
/* ################################################################# *
* private members *
@@ -54,15 +56,14 @@ DEFobjCurrIf(regexp)
* ################################################################# */
-rsRetVal rsCStrConstruct(cstr_t **ppThis)
+rsRetVal cstrConstruct(cstr_t **ppThis)
{
DEFiRet;
cstr_t *pThis;
ASSERT(ppThis != NULL);
- if((pThis = (cstr_t*) calloc(1, sizeof(cstr_t))) == NULL)
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ CHKmalloc(pThis = (cstr_t*) calloc(1, sizeof(cstr_t)));
rsSETOBJTYPE(pThis, OIDrsCStr);
pThis->pBuf = NULL;
@@ -89,7 +90,7 @@ rsRetVal rsCStrConstructFromszStr(cstr_t **ppThis, uchar *sz)
CHKiRet(rsCStrConstruct(&pThis));
- pThis->iBufSize = pThis->iStrLen = strlen((char*)(char *) sz);
+ pThis->iBufSize = pThis->iStrLen = strlen((char *) sz);
if((pThis->pBuf = (uchar*) malloc(sizeof(uchar) * pThis->iStrLen)) == NULL) {
RSFREEOBJ(pThis);
ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
@@ -137,24 +138,8 @@ void rsCStrDestruct(cstr_t **ppThis)
{
cstr_t *pThis = *ppThis;
- /* rgerhards 2005-10-19: The free of pBuf was contained in conditional compilation.
- * The code was only compiled if STRINGBUF_TRIM_ALLOCSIZE was set to 1. I honestly
- * do not know why it was so, I think it was an artifact. Anyhow, I have changed this
- * now. Should there any issue occur, this comment hopefully will shed some light
- * on what happened. I re-verified, and this function has never before been called
- * by anyone. So changing it can have no impact for obvious reasons...
- *
- * rgerhards, 2008-02-20: I changed the interface to the new calling conventions, where
- * the destructor receives a pointer to the object, so that it can set it to NULL.
- */
- if(pThis->pBuf != NULL) {
- free(pThis->pBuf);
- }
-
- if(pThis->pszBuf != NULL) {
- free(pThis->pszBuf);
- }
-
+ free(pThis->pBuf);
+ free(pThis->pszBuf);
RSFREEOBJ(pThis);
*ppThis = NULL;
}
@@ -166,12 +151,14 @@ void rsCStrDestruct(cstr_t **ppThis)
* allocated. In practice, a bit more is allocated because we envision that
* some more characters may be added after these.
* rgerhards, 2008-01-07
+ * changed to utilized realloc() -- rgerhards, 2009-06-16
*/
-static rsRetVal rsCStrExtendBuf(cstr_t *pThis, size_t iMinNeeded)
+static rsRetVal
+rsCStrExtendBuf(cstr_t *pThis, size_t iMinNeeded)
{
- DEFiRet;
uchar *pNewBuf;
size_t iNewSize;
+ DEFiRet;
/* first compute the new size needed */
if(iMinNeeded > pThis->iAllocIncrement) {
@@ -187,15 +174,9 @@ static rsRetVal rsCStrExtendBuf(cstr_t *pThis, size_t iMinNeeded)
}
iNewSize += pThis->iBufSize; /* add current size */
- /* and then allocate and copy over */
/* DEV debugging only: dbgprintf("extending string buffer, old %d, new %d\n", pThis->iBufSize, iNewSize); */
- if((pNewBuf = (uchar*) malloc(iNewSize * sizeof(uchar))) == NULL)
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- memcpy(pNewBuf, pThis->pBuf, pThis->iBufSize);
+ CHKmalloc(pNewBuf = (uchar*) realloc(pThis->pBuf, iNewSize * sizeof(uchar)));
pThis->iBufSize = iNewSize;
- if(pThis->pBuf != NULL) {
- free(pThis->pBuf);
- }
pThis->pBuf = pNewBuf;
finalize_it:
@@ -288,6 +269,29 @@ finalize_it:
}
+/* NEW VARIANT
+ * Append a character to the current string object. This may only be done until
+ * cstrFinalize() is called.
+ * rgerhards, 2009-06-16
+ */
+rsRetVal cstrAppendChar(cstr_t *pThis, uchar c)
+{
+ DEFiRet;
+
+ rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
+
+ if(pThis->iStrLen >= pThis->iBufSize) {
+ CHKiRet(rsCStrExtendBuf(pThis, 1)); /* need more memory! */
+ }
+
+ /* ok, when we reach this, we have sufficient memory */
+ *(pThis->pBuf + pThis->iStrLen++) = c;
+
+finalize_it:
+ RETiRet;
+}
+
+
/* Sets the string object to the classigal sz-string provided.
* Any previously stored vlaue is discarded. If a NULL pointer
* the the new value (pszNew) is provided, an empty string is
@@ -299,10 +303,8 @@ rsRetVal rsCStrSetSzStr(cstr_t *pThis, uchar *pszNew)
{
rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
- if(pThis->pBuf != NULL)
- free(pThis->pBuf);
- if(pThis->pszBuf != NULL)
- free(pThis->pszBuf);
+ free(pThis->pBuf);
+ free(pThis->pszBuf);
if(pszNew == NULL) {
pThis->iStrLen = 0;
pThis->iBufSize = 0;
@@ -390,35 +392,42 @@ uchar* rsCStrGetSzStr(cstr_t *pThis)
}
+/* NEW VERSION for interface without separate psz buffer! */
+/* Returns the cstr data as a classical C sz string. We use that the
+ * Finalizer did properly terminate our string (but we may stil be NULL).
+ * So it is vital that the finalizer is called BEFORe this function here!
+ * The caller must not free or otherwise manipulate the returned string and must not
+ * destroy the CStr object as long as the ascii string is used.
+ * This function may return NULL, if the string is currently NULL. This
+ * is a feature, not a bug. If you need non-NULL in any case, use
+ * cstrGetSzStrNoNULL() instead.
+ * Note that due to the new single-buffer interface this function almost does nothing!
+ * rgerhards, 2006-09-16
+ */
+uchar* cstrGetSzStr(cstr_t *pThis)
+{
+ rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
+ return(pThis->pBuf);
+}
+
+
/* Converts the CStr object to a classical zero-terminated C string,
* returns that string and destroys the CStr object. The returned string
* MUST be freed by the caller. The function might return NULL if
* no memory can be allocated.
*
- * TODO:
- * This function should at some time become special. The base idea is to
- * add one extra byte to the end of the regular buffer, so that we can
- * convert it to an szString without the need to copy. The extra memory
- * footprint is not hefty, but the performance gain is potentially large.
- * To get it done now, I am not doing the optimiziation right now.
- * rgerhards, 2005-09-07
+ * This is the NEW replacement for rsCStrConvSzStrAndDestruct which does
+ * no longer utilize a special buffer but soley works on pBuf (and also
+ * assumes that cstrFinalize had been called).
*
- * rgerhards, 2007-09-04: I have changed the interface of this function. It now
- * returns an rsRetVal, so that we can communicate back if we have an error.
- * Using the standard method is much better than returning NULL. Secondly, NULL
- * was not actually an error - it was in indication if the string was empty.
- * This was needed in some parts of the code, in others not. I have now added
- * a second parameter to specify what the caller needs. I hope these changes
- * will make it less likely that the function is called incorrectly, what
- * previously happend quite often and was the cause of a number of program
- * aborts. So the parameters are now:
+ * Parameters are as follows:
* pointer to the object, pointer to string-pointer to receive string and
* bRetNULL: 0 - must not return NULL on empty string, return "" in that
* case, 1 - return NULL instead of an empty string.
* PLEASE NOTE: the caller must free the memory returned in ppSz in any case
* (except, of course, if it is NULL).
*/
-rsRetVal rsCStrConvSzStrAndDestruct(cstr_t *pThis, uchar **ppSz, int bRetNULL)
+rsRetVal cstrConvSzStrAndDestruct(cstr_t *pThis, uchar **ppSz, int bRetNULL)
{
DEFiRet;
uchar* pRetBuf;
@@ -429,14 +438,13 @@ rsRetVal rsCStrConvSzStrAndDestruct(cstr_t *pThis, uchar **ppSz, int bRetNULL)
if(pThis->pBuf == NULL) {
if(bRetNULL == 0) {
- if((pRetBuf = malloc(sizeof(uchar))) == NULL)
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ CHKmalloc(pRetBuf = malloc(sizeof(uchar)));
*pRetBuf = '\0';
} else {
pRetBuf = NULL;
}
} else
- pRetBuf = rsCStrGetSzStr(pThis);
+ pRetBuf = pThis->pBuf;
*ppSz = pRetBuf;
@@ -445,60 +453,39 @@ finalize_it:
* that we can NOT use the rsCStrDestruct function as it would
* also free the sz String buffer, which we pass on to the user.
*/
- if(pThis->pBuf != NULL)
- free(pThis->pBuf);
RSFREEOBJ(pThis);
-
RETiRet;
}
-#if STRINGBUF_TRIM_ALLOCSIZE == 1
- /* Only in this mode, we need to trim the string. To do
- * so, we must allocate a new buffer of the exact
- * string size, and then copy the old one over.
- */
- /* WARNING
- * STRINGBUF_TRIM_ALLOCSIZE can, in theory, be used to trim
- * memory buffers. This part of the code was inherited from
- * liblogging (where it is used in a different context) but
- * never put to use in rsyslog. The reason is that it is hardly
- * imaginable where the extra performance cost is worth the save
- * in memory alloc. Then Anders Blomdel rightfully pointed out that
- * the code does not work at all - and nobody even know that it
- * probably shouldn't. Rather than removing, I deciced to somewhat
- * fix the code, so that this feature may be enabled if somebody
- * really has a need for it. Be warned, however, that I NEVER
- * tested the fix. So if you intend to use this feature, you must
- * do full testing before you rely on it. -- rgerhards, 2008-02-12
- */
-rsRetVal rsCStrFinish(cstr_t __attribute__((unused)) *pThis)
+/* Finalize the string object. This must be called after all data is added to it
+ * but before that data is used.
+ * rgerhards, 2009-06-16
+ */
+rsRetVal
+cstrFinalize(cstr_t *pThis)
{
DEFiRet;
- uchar* pBuf;
rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
+
+ assert(pThis->bIsFinalized == 0);
- if((pBuf = malloc((pThis->iStrLen) * sizeof(uchar))) == NULL)
- { /* OK, in this case we use the previous buffer. At least
- * we have it ;)
- */
- }
- else
- { /* got the new buffer, so let's use it */
- memcpy(pBuf, pThis->pBuf, pThis->iStrLen);
- pThis->pBuf = pBuf;
+ if(pThis->iStrLen > 0) {
+ /* terminate string only if one exists */
+ CHKiRet(cstrAppendChar(pThis, '\0'));
+ --pThis->iStrLen; /* do NOT count the \0 byte */
}
+ pThis->bIsFinalized = 1;
+finalize_it:
RETiRet;
}
-#endif /* #if STRINGBUF_TRIM_ALLOCSIZE == 1 */
void rsCStrSetAllocIncrement(cstr_t *pThis, int iNewIncrement)
{
rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
assert(iNewIncrement > 0);
-
pThis->iAllocIncrement = iNewIncrement;
}
@@ -1027,56 +1014,6 @@ int rsCStrCaseInsensitiveLocateInSzStr(cstr_t *pThis, uchar *sz)
}
-#if 0 /* read comment below why this is commented out. In short: for future use! */
-/* locate the first occurence of a standard sz string inside a rsCStr object.
- * Returns the offset (0-bound) of this first occurrence. If not found, -1 is
- * returned.
- * rgerhards 2005-09-19
- * WARNING: I accidently created this function (I later noticed I didn't relly
- * need it... I will not remove the function, as it probably is useful
- * some time later. However, it is not fully tested, so start with testing
- * it before you put it to first use).
- */
-int rsCStrLocateSzStr(cstr_t *pThis, uchar *sz)
-{
- int iLenSz;
- int i;
- int iMax;
- int bFound;
- rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
-
- if(sz == NULL)
- return 0;
-
- iLenSz = strlen((char*)sz);
- if(iLenSz == 0)
- return 0;
-
- /* compute the largest index where a match could occur - after all,
- * the to-be-located string must be able to be present in the
- * searched string (it needs its size ;)).
- */
- iMax = pThis->iStrLen - iLenSz;
-
- bFound = 0;
- i = 0;
- while(i < iMax && !bFound) {
- int iCheck;
- uchar *pComp = pThis->pBuf + i;
- for(iCheck = 0 ; iCheck < iLenSz ; ++iCheck)
- if(*(pComp + iCheck) != *(sz + iCheck))
- break;
- if(iCheck == iLenSz)
- bFound = 1; /* found! - else it wouldn't be equal */
- else
- ++i; /* on to the next try */
- }
-
- return(bFound ? i : -1);
-}
-#endif /* end comment out */
-
-
/* our exit function. TODO: remove once converted to a class
* rgerhards, 2008-03-11
*/
@@ -1100,11 +1037,5 @@ finalize_it:
}
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * tab-width: 8
- * End:
- * vi:set ai:
+/* vi:set ai:
*/
diff --git a/runtime/stringbuf.h b/runtime/stringbuf.h
index 684133bb..d28aee26 100644
--- a/runtime/stringbuf.h
+++ b/runtime/stringbuf.h
@@ -49,13 +49,15 @@ typedef struct cstr_s
size_t iBufSize; /**< current maximum size of the string buffer */
size_t iStrLen; /**< length of the string in characters. */
size_t iAllocIncrement; /**< the amount of bytes the string should be expanded if it needs to */
+ bool bIsFinalized; /**< is this object finished and ready for use? (a debug aid, may be removed later TODO 2009-06-16) */
} cstr_t;
/**
* Construct a rsCStr object.
*/
-rsRetVal rsCStrConstruct(cstr_t **ppThis);
+rsRetVal cstrConstruct(cstr_t **ppThis);
+#define rsCStrConstruct(x) cstrConstruct((x))
rsRetVal rsCStrConstructFromszStr(cstr_t **ppThis, uchar *sz);
rsRetVal rsCStrConstructFromCStr(cstr_t **ppThis, cstr_t *pFrom);
@@ -63,6 +65,7 @@ rsRetVal rsCStrConstructFromCStr(cstr_t **ppThis, cstr_t *pFrom);
* Destruct the string buffer object.
*/
void rsCStrDestruct(cstr_t **ppThis);
+#define cstrDestruct(x) rsCStrDestruct((x))
/**
* Append a character to an existing string. If necessary, the
@@ -71,6 +74,7 @@ void rsCStrDestruct(cstr_t **ppThis);
* \param c Character to append to string.
*/
rsRetVal rsCStrAppendChar(cstr_t *pThis, uchar c);
+rsRetVal cstrAppendChar(cstr_t *pThis, uchar c);
/**
* Truncate "n" number of characters from the end of the
@@ -123,10 +127,9 @@ rsRetVal rsCStrAppendInt(cstr_t *pThis, long i);
rsRetVal strExit(void); /* TODO: remove once we have a real object interface! */
-uchar* rsCStrGetSzStr(cstr_t *pThis);
+uchar* __attribute__((deprecated)) rsCStrGetSzStr(cstr_t *pThis);
uchar* rsCStrGetSzStrNoNULL(cstr_t *pThis);
rsRetVal rsCStrSetSzStr(cstr_t *pThis, uchar *pszNew);
-rsRetVal rsCStrConvSzStrAndDestruct(cstr_t *pThis, uchar **ppSz, int bRetNULL);
int rsCStrCStrCmp(cstr_t *pCS1, cstr_t *pCS2);
int rsCStrSzStrCmp(cstr_t *pCS1, uchar *psz, size_t iLenSz);
int rsCStrOffsetSzStrCmp(cstr_t *pCS1, size_t iOffset, uchar *psz, size_t iLenSz);
@@ -142,6 +145,11 @@ rsRetVal rsCStrConvertToNumber(cstr_t *pStr, number_t *pNumber);
rsRetVal rsCStrConvertToBool(cstr_t *pStr, number_t *pBool);
rsRetVal rsCStrAppendCStr(cstr_t *pThis, cstr_t *pstrAppend);
+/* new calling interface */
+rsRetVal cstrFinalize(cstr_t *pThis);
+rsRetVal cstrConvSzStrAndDestruct(cstr_t *pThis, uchar **ppSz, int bRetNULL);
+uchar* cstrGetSzStr(cstr_t *pThis);
+
/* now come inline-like functions */
#ifdef NDEBUG
# define rsCStrLen(x) ((int)((x)->iStrLen))
@@ -149,19 +157,6 @@ rsRetVal rsCStrAppendCStr(cstr_t *pThis, cstr_t *pstrAppend);
int rsCStrLen(cstr_t *pThis);
#endif
-#if STRINGBUF_TRIM_ALLOCSIZE != 1
-/* This is the normal case (see comment in rsCStrFinish!). In those cases, the function
- * simply needs to do nothing, so that we can save us the function call.
- * rgerhards, 2008-02-12
- */
-# define rsCStrFinish(pThis) RS_RET_OK
-#else
- /**
- * Finish the string buffer dynamic allocation.
- */
- rsRetVal rsCStrFinish(cstr_t *pThis);
-#endif
-
#define rsCStrGetBufBeg(x) ((x)->pBuf)
rsRetVal strInit();
diff --git a/runtime/sysvar.c b/runtime/sysvar.c
index c102d1f5..4a6ace19 100644
--- a/runtime/sysvar.c
+++ b/runtime/sysvar.c
@@ -175,8 +175,6 @@ CODESTARTobjQueryInterface(sysvar)
* work here (if we can support an older interface version - that,
* of course, also affects the "if" above).
*/
- //xxxpIf->oID = "sysvar";//OBJsysvar;
-
pIf->Construct = sysvarConstruct;
pIf->ConstructFinalize = sysvarConstructFinalize;
pIf->Destruct = sysvarDestruct;
diff --git a/runtime/unicode-helper.h b/runtime/unicode-helper.h
index 36d76a78..7a776f68 100644
--- a/runtime/unicode-helper.h
+++ b/runtime/unicode-helper.h
@@ -4,6 +4,9 @@
* The following functions are wrappers which hopefully enable us to move
* from 8-bit chars to unicode with relative ease when we finally attack this
*
+ * Note: while we prefer inline functions, this leads to invalid references in
+ * core dumps. So in a debug build, we use macros where appropriate...
+ *
* Begun 2009-05-21 RGerhards
*
* Copyright (C) 2009 by Rainer Gerhards and Adiscon GmbH
@@ -31,6 +34,22 @@
#include <string.h>
+#ifdef DEBUG
+# define ustrncpy(psz1, psz2, len) strncpy((char*)(psz1), (char*)(psz2), (len))
+# define ustrdup(psz) (uchar*)strdup((char*)(psz))
+#else
+ static inline uchar* ustrncpy(uchar *psz1, uchar *psz2, size_t len)
+ {
+ return (uchar*) strncpy((char*) psz1, (char*) psz2, len);
+ }
+
+ static inline uchar* ustrdup(uchar *psz)
+ {
+ return (uchar*) strdup((char*)psz);
+ }
+
+#endif /* #ifdef DEBUG */
+
static inline int ustrcmp(uchar *psz1, uchar *psz2)
{
return strcmp((char*) psz1, (char*) psz2);
@@ -41,13 +60,9 @@ static inline int ustrlen(uchar *psz)
return strlen((char*) psz);
}
-static inline uchar* ustrdup(uchar *psz)
-{
- return (uchar*) strdup((char*)psz);
-}
-
#define UCHAR_CONSTANT(x) ((uchar*) (x))
+#define CHAR_CONVERT(x) ((char*) (x))
#endif /* multi-include protection */
/* vim:set ai:
diff --git a/runtime/vm.c b/runtime/vm.c
index 125b0d21..8cbf9e12 100644
--- a/runtime/vm.c
+++ b/runtime/vm.c
@@ -563,7 +563,7 @@ rsf_tolower(vmstk_t *pStk, int numOperands)
}
/* Store result and cleanup */
- CHKiRet(rsCStrFinish(pcstr));
+ CHKiRet(cstrFinalize(pcstr));
var.SetString(operand1, pcstr);
vmstk.Push(pStk, operand1);
finalize_it:
diff --git a/runtime/vmop.c b/runtime/vmop.c
index 3e001d27..acacfc9e 100644
--- a/runtime/vmop.c
+++ b/runtime/vmop.c
@@ -85,7 +85,7 @@ CODESTARTobjDebugPrint(vmop)
CHKiRet(var.Obj2Str(pThis->operand.pVar, pStrVar));
}
}
- CHKiRet(rsCStrFinish(&pStrVar));
+ CHKiRet(cstrFinalize(pStrVar));
dbgoprint((obj_t*) pThis, "%.12s\t%s\n", pOpcodeName, rsCStrGetSzStrNoNULL(pStrVar));
if(pThis->opcode != opcode_FUNC_CALL)
rsCStrDestruct(&pStrVar);
diff --git a/runtime/zlibw.c b/runtime/zlibw.c
new file mode 100644
index 00000000..2b386213
--- /dev/null
+++ b/runtime/zlibw.c
@@ -0,0 +1,125 @@
+/* The zlibwrap object.
+ *
+ * This is an rsyslog object wrapper around zlib.
+ *
+ * Copyright 2009 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of the rsyslog runtime library.
+ *
+ * The rsyslog runtime library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The rsyslog runtime library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
+ */
+
+#include "config.h"
+#include <string.h>
+#include <assert.h>
+#include <zlib.h>
+
+#include "rsyslog.h"
+#include "module-template.h"
+#include "obj.h"
+#include "zlibw.h"
+
+MODULE_TYPE_LIB
+
+/* static data */
+DEFobjStaticHelpers
+
+
+/* ------------------------------ methods ------------------------------ */
+
+/* zlib make strong use of macros for its interface functions, so we can not simply
+ * pass function pointers to them. Instead, we create very small wrappers which call
+ * the relevant entry points.
+ */
+
+static int myDeflateInit(z_streamp strm, int level)
+{
+ return deflateInit(strm, level);
+}
+
+static int myDeflateInit2(z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy)
+{
+ return deflateInit2(strm, level, method, windowBits, memLevel, strategy);
+}
+
+static int myDeflateEnd(z_streamp strm)
+{
+ return deflateEnd(strm);
+}
+
+static int myDeflate(z_streamp strm, int flush)
+{
+ return deflate(strm, flush);
+}
+
+
+/* queryInterface function
+ * rgerhards, 2008-03-05
+ */
+BEGINobjQueryInterface(zlibw)
+CODESTARTobjQueryInterface(zlibw)
+ if(pIf->ifVersion != zlibwCURR_IF_VERSION) { /* check for current version, increment on each change */
+ ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED);
+ }
+
+ /* ok, we have the right interface, so let's fill it
+ * Please note that we may also do some backwards-compatibility
+ * work here (if we can support an older interface version - that,
+ * of course, also affects the "if" above).
+ */
+ pIf->DeflateInit = myDeflateInit;
+ pIf->DeflateInit2 = myDeflateInit2;
+ pIf->Deflate = myDeflate;
+ pIf->DeflateEnd = myDeflateEnd;
+finalize_it:
+ENDobjQueryInterface(zlibw)
+
+
+/* Initialize the zlibw class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-02-19
+ */
+BEGINAbstractObjClassInit(zlibw, 1, OBJ_IS_LOADABLE_MODULE) /* class, version */
+ /* request objects we use */
+
+ /* set our own handlers */
+ENDObjClassInit(zlibw)
+
+
+/* --------------- here now comes the plumbing that makes as a library module --------------- */
+
+
+BEGINmodExit
+CODESTARTmodExit
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_LIB_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+
+ CHKiRet(zlibwClassInit(pModInfo)); /* must be done after tcps_sess, as we use it */
+ /* Initialize all classes that are in our module - this includes ourselfs */
+ENDmodInit
+/* vi:set ai:
+ */
diff --git a/runtime/zlibw.h b/runtime/zlibw.h
new file mode 100644
index 00000000..63d8f386
--- /dev/null
+++ b/runtime/zlibw.h
@@ -0,0 +1,46 @@
+/* The zlibw object. It encapsulates the zlib functionality. The primary
+ * purpose of this wrapper class is to enable rsyslogd core to be build without
+ * zlib libraries.
+ *
+ * Copyright 2009 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of the rsyslog runtime library.
+ *
+ * The rsyslog runtime library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The rsyslog runtime library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
+ */
+#ifndef INCLUDED_ZLIBW_H
+#define INCLUDED_ZLIBW_H
+
+#include <zlib.h>
+
+/* interfaces */
+BEGINinterface(zlibw) /* name must also be changed in ENDinterface macro! */
+ int (*DeflateInit)(z_streamp strm, int);
+ int (*DeflateInit2)(z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy);
+ int (*Deflate)(z_streamp strm, int);
+ int (*DeflateEnd)(z_streamp strm);
+ENDinterface(zlibw)
+#define zlibwCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+
+
+/* prototypes */
+PROTOTYPEObj(zlibw);
+
+/* the name of our library binary */
+#define LM_ZLIBW_FILENAME "lmzlibw"
+
+#endif /* #ifndef INCLUDED_ZLIBW_H */
diff --git a/tcps_sess.c b/tcps_sess.c
index 62d51f66..f887e702 100644
--- a/tcps_sess.c
+++ b/tcps_sess.c
@@ -56,6 +56,9 @@ DEFobjCurrIf(datetime)
static int iMaxLine; /* maximum size of a single message */
+static int iNbrTimeUsed = 0; /* how often has previous time been used so far? */
+
+
/* forward definitions */
static rsRetVal Close(tcps_sess_t *pThis);
@@ -97,12 +100,9 @@ CODESTARTobjDestruct(tcps_sess)
pThis->pSrv->pOnSessDestruct(&pThis->pUsr);
}
/* now destruct our own properties */
- if(pThis->fromHost != NULL)
- free(pThis->fromHost);
- if(pThis->fromHostIP != NULL)
- free(pThis->fromHostIP);
- if(pThis->pMsg != NULL)
- free(pThis->pMsg);
+ free(pThis->fromHost);
+ free(pThis->fromHostIP);
+ free(pThis->pMsg);
ENDobjDestruct(tcps_sess)
@@ -124,10 +124,7 @@ SetHost(tcps_sess_t *pThis, uchar *pszHost)
ISOBJ_TYPE_assert(pThis, tcps_sess);
- if(pThis->fromHost != NULL) {
- free(pThis->fromHost);
- }
-
+ free(pThis->fromHost);
pThis->fromHost = pszHost;
RETiRet;
@@ -144,10 +141,7 @@ SetHostIP(tcps_sess_t *pThis, uchar *pszHostIP)
ISOBJ_TYPE_assert(pThis, tcps_sess);
- if(pThis->fromHostIP != NULL) {
- free(pThis->fromHostIP);
- }
-
+ free(pThis->fromHostIP);
pThis->fromHostIP = pszHostIP;
RETiRet;
@@ -226,11 +220,9 @@ SetOnMsgReceive(tcps_sess_t *pThis, rsRetVal (*OnMsgReceive)(tcps_sess_t*, uchar
* rgerhards, 2009-04-23
*/
static rsRetVal
-defaultDoSubmitMessage(tcps_sess_t *pThis)
+defaultDoSubmitMessage(tcps_sess_t *pThis, struct syslogTime *stTime, time_t ttGenTime)
{
msg_t *pMsg;
- struct syslogTime stTime;
- time_t ttGenTime;
DEFiRet;
ISOBJ_TYPE_assert(pThis, tcps_sess);
@@ -240,20 +232,18 @@ defaultDoSubmitMessage(tcps_sess_t *pThis)
FINALIZE;
}
- //TODO: if((iTimeRequery == 0) || (iNbrTimeUsed++ % iTimeRequery) == 0) {
- datetime.getCurrTime(&stTime, &ttGenTime);
- //}
/* we now create our own message object and submit it to the queue */
- CHKiRet(msgConstructWithTime(&pMsg, &stTime, ttGenTime));
+ CHKiRet(msgConstructWithTime(&pMsg, stTime, ttGenTime));
/* first trim the buffer to what we have actually received */
CHKmalloc(pMsg->pszRawMsg = malloc(sizeof(uchar) * pThis->iMsg));
memcpy(pMsg->pszRawMsg, pThis->pMsg, pThis->iMsg);
pMsg->iLenRawMsg = pThis->iMsg;
- MsgSetInputName(pMsg, pThis->pLstnInfo->pszInputName);
+ MsgSetInputName(pMsg, pThis->pLstnInfo->pszInputName, pThis->pLstnInfo->lenInputName);
MsgSetFlowControlType(pMsg, eFLOWCTL_LIGHT_DELAY);
pMsg->msgFlags = NEEDS_PARSING | PARSE_HOSTNAME;
pMsg->bParseHOSTNAME = 1;
MsgSetRcvFrom(pMsg, pThis->fromHost);
+ MsgSetRuleset(pMsg, pThis->pLstnInfo->pRuleset);
CHKiRet(MsgSetRcvFromIP(pMsg, pThis->fromHostIP));
CHKiRet(submitMsg(pMsg));
@@ -281,6 +271,8 @@ finalize_it:
static rsRetVal
PrepareClose(tcps_sess_t *pThis)
{
+ struct syslogTime stTime;
+ time_t ttGenTime;
DEFiRet;
ISOBJ_TYPE_assert(pThis, tcps_sess);
@@ -306,7 +298,8 @@ PrepareClose(tcps_sess_t *pThis)
* this case.
*/
dbgprintf("Extra data at end of stream in legacy syslog/tcp message - processing\n");
- defaultDoSubmitMessage(pThis);
+ datetime.getCurrTime(&stTime, &ttGenTime);
+ defaultDoSubmitMessage(pThis, &stTime, ttGenTime);
}
finalize_it:
@@ -341,7 +334,7 @@ Close(tcps_sess_t *pThis)
* rgerhards, 2008-03-14
*/
static rsRetVal
-processDataRcvd(tcps_sess_t *pThis, char c)
+processDataRcvd(tcps_sess_t *pThis, char c, struct syslogTime *stTime, time_t ttGenTime)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, tcps_sess);
@@ -387,7 +380,7 @@ processDataRcvd(tcps_sess_t *pThis, char c)
if(pThis->iMsg >= iMaxLine) {
/* emergency, we now need to flush, no matter if we are at end of message or not... */
dbgprintf("error: message received is larger than max msg size, we split it\n");
- defaultDoSubmitMessage(pThis);
+ defaultDoSubmitMessage(pThis, stTime, ttGenTime);
/* we might think if it is better to ignore the rest of the
* message than to treat it as a new one. Maybe this is a good
* candidate for a configuration parameter...
@@ -398,7 +391,7 @@ processDataRcvd(tcps_sess_t *pThis, char c)
if(( (c == '\n')
|| ((pThis->pSrv->addtlFrameDelim != TCPSRV_NO_ADDTL_DELIMITER) && (c == pThis->pSrv->addtlFrameDelim))
) && pThis->eFraming == TCP_FRAMING_OCTET_STUFFING) { /* record delimiter? */
- defaultDoSubmitMessage(pThis);
+ defaultDoSubmitMessage(pThis, stTime, ttGenTime);
pThis->inputState = eAtStrtFram;
} else {
/* IMPORTANT: here we copy the actual frame content to the message - for BOTH framing modes!
@@ -415,7 +408,7 @@ processDataRcvd(tcps_sess_t *pThis, char c)
pThis->iOctetsRemain--;
if(pThis->iOctetsRemain < 1) {
/* we have end of frame! */
- defaultDoSubmitMessage(pThis);
+ defaultDoSubmitMessage(pThis, stTime, ttGenTime);
pThis->inputState = eAtStrtFram;
}
}
@@ -436,22 +429,30 @@ processDataRcvd(tcps_sess_t *pThis, char c)
* RS_RET_OK, which means the session should be kept open
* or anything else, which means it must be closed.
* rgerhards, 2008-03-01
+ * As a performance optimization, we pick up the timestamp here. Acutally,
+ * this *is* the *correct* reception step for all the data we received, because
+ * we have just received a bunch of data! -- rgerhards, 2009-06-16
*/
static rsRetVal
DataRcvd(tcps_sess_t *pThis, char *pData, size_t iLen)
{
- DEFiRet;
+ struct syslogTime stTime;
+ time_t ttGenTime;
char *pEnd;
+ DEFiRet;
ISOBJ_TYPE_assert(pThis, tcps_sess);
assert(pData != NULL);
assert(iLen > 0);
+ datetime.getCurrTime(&stTime, &ttGenTime);
+
/* We now copy the message to the session buffer. */
pEnd = pData + iLen; /* this is one off, which is intensional */
+ iNbrTimeUsed = 0; /* full time query */
while(pData < pEnd) {
- CHKiRet(processDataRcvd(pThis, *pData++));
+ CHKiRet(processDataRcvd(pThis, *pData++, &stTime, ttGenTime));
}
finalize_it:
@@ -525,7 +526,5 @@ BEGINObjClassInit(tcps_sess, 1, OBJ_IS_CORE_MODULE) /* class, version - CHANGE c
OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, tcps_sessConstructFinalize);
ENDObjClassInit(tcps_sess)
-
-
/* vim:set ai:
*/
diff --git a/tcpsrv.c b/tcpsrv.c
index 3516b2e3..119aea91 100644
--- a/tcpsrv.c
+++ b/tcpsrv.c
@@ -69,6 +69,7 @@
#include "netstrm.h"
#include "nssel.h"
#include "errmsg.h"
+#include "ruleset.h"
#include "unicode-helper.h"
MODULE_TYPE_LIB
@@ -81,6 +82,7 @@ MODULE_TYPE_LIB
DEFobjStaticHelpers
DEFobjCurrIf(conf)
DEFobjCurrIf(glbl)
+DEFobjCurrIf(ruleset)
DEFobjCurrIf(tcps_sess)
DEFobjCurrIf(errmsg)
DEFobjCurrIf(net)
@@ -104,7 +106,9 @@ addNewLstnPort(tcpsrv_t *pThis, uchar *pszPort)
CHKmalloc(pEntry = malloc(sizeof(tcpLstnPortList_t)));
pEntry->pszPort = pszPort;
pEntry->pSrv = pThis;
+ pEntry->pRuleset = pThis->pRuleset;
CHKmalloc(pEntry->pszInputName = ustrdup(pThis->pszInputName));
+ pEntry->lenInputName = ustrlen(pEntry->pszInputName);
/* and add to list */
pEntry->pNext = pThis->pLstnPorts;
@@ -510,7 +514,7 @@ Run(tcpsrv_t *pThis)
while(nfds && iTCPSess != -1) {
CHKiRet(nssel.IsReady(pSel, pThis->pSessions[iTCPSess]->pStrm, NSDSEL_RD, &bIsReady, &nfds));
if(bIsReady) {
- char buf[8*1024]; /* reception buffer - may hold a partial or multiple messages */
+ char buf[128*1024]; /* reception buffer - may hold a partial or multiple messages */
dbgprintf("netstream %p with new data\n", pThis->pSessions[iTCPSess]->pStrm);
/* Receive message */
@@ -755,6 +759,16 @@ finalize_it:
}
+/* Set the ruleset (ptr) to use */
+static rsRetVal
+SetRuleset(tcpsrv_t *pThis, ruleset_t *pRuleset)
+{
+ DEFiRet;
+ pThis->pRuleset = pRuleset;
+ RETiRet;
+}
+
+
/* here follows a number of methods that shuffle authentication settings down
* to the drivers. Drivers not supporting these settings may return an error
* state.
@@ -855,6 +869,7 @@ CODESTARTobjQueryInterface(tcpsrv)
pIf->SetCBOnRegularClose = SetCBOnRegularClose;
pIf->SetCBOnErrClose = SetCBOnErrClose;
pIf->SetOnMsgReceive = SetOnMsgReceive;
+ pIf->SetRuleset = SetRuleset;
finalize_it:
ENDobjQueryInterface(tcpsrv)
@@ -868,6 +883,7 @@ CODESTARTObjClassExit(tcpsrv)
/* release objects we no longer need */
objRelease(tcps_sess, DONT_LOAD_LIB);
objRelease(conf, CORE_COMPONENT);
+ objRelease(ruleset, CORE_COMPONENT);
objRelease(glbl, CORE_COMPONENT);
objRelease(errmsg, CORE_COMPONENT);
objRelease(netstrms, DONT_LOAD_LIB);
@@ -891,6 +907,7 @@ BEGINObjClassInit(tcpsrv, 1, OBJ_IS_LOADABLE_MODULE) /* class, version - CHANGE
CHKiRet(objUse(tcps_sess, DONT_LOAD_LIB));
CHKiRet(objUse(conf, CORE_COMPONENT));
CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(ruleset, CORE_COMPONENT));
/* set our own handlers */
OBJSetMethodHandler(objMethod_DEBUGPRINT, tcpsrvDebugPrint);
diff --git a/tcpsrv.h b/tcpsrv.h
index 2d174ce0..e2170bef 100644
--- a/tcpsrv.h
+++ b/tcpsrv.h
@@ -37,7 +37,9 @@ typedef enum ETCPsyslogFramingAnomaly {
struct tcpLstnPortList_s {
uchar *pszPort; /**< the ports the listener shall listen on */
uchar *pszInputName; /**< value to be used as input name */
+ size_t lenInputName; /**< length of inputName */
tcpsrv_t *pSrv; /**< pointer to higher-level server instance */
+ ruleset_t *pRuleset; /**< associated ruleset */
tcpLstnPortList_t *pNext; /**< next port or NULL */
};
@@ -50,6 +52,7 @@ struct tcpsrv_s {
int iDrvrMode; /**< mode of the stream driver to use */
uchar *pszDrvrAuthMode; /**< auth mode of the stream driver to use */
uchar *pszInputName; /**< value to be used as input name */
+ ruleset_t *pRuleset; /**< ruleset to bind to */
permittedPeers_t *pPermPeers;/**< driver's permitted peers */
int iLstnMax; /**< max nbr of listeners currently supported */
netstrm_t **ppLstn; /**< our netstream listners */
@@ -107,6 +110,7 @@ BEGINinterface(tcpsrv) /* name must also be changed in ENDinterface macro! */
rsRetVal (*SetSessMax)(tcpsrv_t *pThis, int iMaxSess); /* 2009-04-09 */
/* added v6 */
rsRetVal (*SetOnMsgReceive)(tcpsrv_t *pThis, rsRetVal (*OnMsgReceive)(tcps_sess_t*, uchar*, int)); /* 2009-05-24 */
+ rsRetVal (*SetRuleset)(tcpsrv_t *pThis, ruleset_t*); /* 2009-06-12 */
ENDinterface(tcpsrv)
#define tcpsrvCURR_IF_VERSION 6 /* increment whenever you change the interface structure! */
/* change for v4:
diff --git a/template.c b/template.c
index 4a12aa3b..6e34bcd8 100644
--- a/template.c
+++ b/template.c
@@ -88,7 +88,7 @@ rsRetVal tplToString(struct template *pTpl, msg_t *pMsg, uchar** ppSz)
* free the obtained value (if requested). We continue this
* loop until we got hold of all values.
*/
- CHKiRet(rsCStrConstruct(&pCStr));
+ CHKiRet(cstrConstruct(&pCStr));
pTpe = pTpl->pEntryRoot;
while(pTpe != NULL) {
@@ -99,7 +99,7 @@ rsRetVal tplToString(struct template *pTpl, msg_t *pMsg, uchar** ppSz)
) {
dbgprintf("error %d during tplToString()\n", iRet);
/* it does not make sense to continue now */
- rsCStrDestruct(&pCStr);
+ cstrDestruct(&pCStr);
FINALIZE;
}
} else if(pTpe->eEntryType == FIELD) {
@@ -119,7 +119,7 @@ rsRetVal tplToString(struct template *pTpl, msg_t *pMsg, uchar** ppSz)
CHKiRet_Hdlr(rsCStrAppendStrWithLen(pCStr, (uchar*) pVal, iLenVal)) {
dbgprintf("error %d during tplToString()\n", iRet);
/* it does not make sense to continue now */
- rsCStrDestruct(&pCStr);
+ cstrDestruct(&pCStr);
if(bMustBeFreed)
free(pVal);
FINALIZE;
@@ -133,8 +133,8 @@ rsRetVal tplToString(struct template *pTpl, msg_t *pMsg, uchar** ppSz)
/* we are done with the template, now let's convert the result into a
* "real" (usable) string and discard the helper structures.
*/
- CHKiRet(rsCStrFinish(pCStr));
- CHKiRet(rsCStrConvSzStrAndDestruct(pCStr, &pVal, 0));
+ CHKiRet(cstrFinalize(pCStr));
+ CHKiRet(cstrConvSzStrAndDestruct(pCStr, &pVal, 0));
finalize_it:
*ppSz = (iRet == RS_RET_OK) ? pVal : NULL;
@@ -272,21 +272,21 @@ doSQLEscape(uchar **pp, size_t *pLen, unsigned short *pbMustBeFreed, int escapeM
p = *pp;
iLen = *pLen;
- CHKiRet(rsCStrConstruct(&pStrB));
+ CHKiRet(cstrConstruct(&pStrB));
while(*p) {
if(*p == '\'') {
- CHKiRet(rsCStrAppendChar(pStrB, (escapeMode == 0) ? '\'' : '\\'));
+ CHKiRet(cstrAppendChar(pStrB, (escapeMode == 0) ? '\'' : '\\'));
iLen++; /* reflect the extra character */
} else if((escapeMode == 1) && (*p == '\\')) {
- CHKiRet(rsCStrAppendChar(pStrB, '\\'));
+ CHKiRet(cstrAppendChar(pStrB, '\\'));
iLen++; /* reflect the extra character */
}
- CHKiRet(rsCStrAppendChar(pStrB, *p));
+ CHKiRet(cstrAppendChar(pStrB, *p));
++p;
}
- CHKiRet(rsCStrFinish(pStrB));
- CHKiRet(rsCStrConvSzStrAndDestruct(pStrB, &pszGenerated, 0));
+ CHKiRet(cstrFinalize(pStrB));
+ CHKiRet(cstrConvSzStrAndDestruct(pStrB, &pszGenerated, 0));
if(*pbMustBeFreed)
free(*pp); /* discard previous value */
@@ -299,7 +299,7 @@ finalize_it:
if(iRet != RS_RET_OK) {
doSQLEmergencyEscape(*pp, escapeMode);
if(pStrB != NULL)
- rsCStrDestruct(&pStrB);
+ cstrDestruct(&pStrB);
}
RETiRet;
@@ -376,7 +376,7 @@ static int do_Constant(unsigned char **pp, struct template *pTpl)
p = *pp;
- if(rsCStrConstruct(&pStrB) != RS_RET_OK)
+ if(cstrConstruct(&pStrB) != RS_RET_OK)
return 1;
rsCStrSetAllocIncrement(pStrB, 32);
/* process the message and expand escapes
@@ -387,22 +387,22 @@ static int do_Constant(unsigned char **pp, struct template *pTpl)
switch(*++p) {
case '\0':
/* the best we can do - it's invalid anyhow... */
- rsCStrAppendChar(pStrB, *p);
+ cstrAppendChar(pStrB, *p);
break;
case 'n':
- rsCStrAppendChar(pStrB, '\n');
+ cstrAppendChar(pStrB, '\n');
++p;
break;
case 'r':
- rsCStrAppendChar(pStrB, '\r');
+ cstrAppendChar(pStrB, '\r');
++p;
break;
case '\\':
- rsCStrAppendChar(pStrB, '\\');
+ cstrAppendChar(pStrB, '\\');
++p;
break;
case '%':
- rsCStrAppendChar(pStrB, '%');
+ cstrAppendChar(pStrB, '%');
++p;
break;
case '0': /* numerical escape sequence */
@@ -419,15 +419,15 @@ static int do_Constant(unsigned char **pp, struct template *pTpl)
while(*p && isdigit((int)*p)) {
i = i * 10 + *p++ - '0';
}
- rsCStrAppendChar(pStrB, i);
+ cstrAppendChar(pStrB, i);
break;
default:
- rsCStrAppendChar(pStrB, *p++);
+ cstrAppendChar(pStrB, *p++);
break;
}
}
else
- rsCStrAppendChar(pStrB, *p++);
+ cstrAppendChar(pStrB, *p++);
}
if((pTpe = tpeConstruct(pTpl)) == NULL) {
@@ -438,14 +438,14 @@ static int do_Constant(unsigned char **pp, struct template *pTpl)
return 1;
}
pTpe->eEntryType = CONSTANT;
- rsCStrFinish(pStrB);
+ cstrFinalize(pStrB);
/* We obtain the length from the counted string object
* (before we delete it). Later we might take additional
* benefit from the counted string object.
* 2005-09-09 rgerhards
*/
pTpe->data.constant.iLenConstant = rsCStrLen(pStrB);
- if(rsCStrConvSzStrAndDestruct(pStrB, &pTpe->data.constant.pConstant, 0) != RS_RET_OK)
+ if(cstrConvSzStrAndDestruct(pStrB, &pTpe->data.constant.pConstant, 0) != RS_RET_OK)
return 1;
*pp = p;
@@ -552,7 +552,7 @@ static int do_Parameter(unsigned char **pp, struct template *pTpl)
p = (unsigned char*) *pp;
- if(rsCStrConstruct(&pStrB) != RS_RET_OK)
+ if(cstrConstruct(&pStrB) != RS_RET_OK)
return 1;
if((pTpe = tpeConstruct(pTpl)) == NULL) {
@@ -563,13 +563,13 @@ static int do_Parameter(unsigned char **pp, struct template *pTpl)
pTpe->eEntryType = FIELD;
while(*p && *p != '%' && *p != ':') {
- rsCStrAppendChar(pStrB, tolower(*p));
+ cstrAppendChar(pStrB, tolower(*p));
++p; /* do NOT do this in tolower()! */
}
/* got the name*/
- rsCStrFinish(pStrB);
- if(rsCStrConvSzStrAndDestruct(pStrB, &pTpe->data.field.pPropRepl, 0) != RS_RET_OK)
+ cstrFinalize(pStrB);
+ if(cstrConvSzStrAndDestruct(pStrB, &pTpe->data.field.pPropRepl, 0) != RS_RET_OK)
return 1;
/* Check frompos, if it has an R, then topos should be a regex */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index e22b429c..fe0ec4bb 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -7,9 +7,11 @@ TESTS = $(TESTRUNS) cfg.sh \
da-mainmsg-q.sh \
validation-run.sh \
imtcp-multiport.sh \
- diskqueue.sh manytcp.sh \
- queue-persist.sh \
- daqueue-persist.sh
+ daqueue-persist.sh \
+ diskqueue.sh \
+ diskqueue-fsync.sh \
+ manytcp.sh \
+ queue-persist.sh
if ENABLE_OMSTDOUT
TESTS += omod-if-array.sh parsertest.sh inputname.sh fieldtest.sh
@@ -68,6 +70,8 @@ EXTRA_DIST= 1.rstest 2.rstest 3.rstest err1.rstest \
testsuites/linkedlistqueue.conf \
da-mainmsg-q.sh \
testsuites/da-mainmsg-q.conf \
+ diskqueue-fsync.sh \
+ testsuites/diskqueue-fsync.conf \
imtcp-multiport.sh \
testsuites/imtcp-multiport.conf \
manytcp.sh \
@@ -79,8 +83,6 @@ EXTRA_DIST= 1.rstest 2.rstest 3.rstest err1.rstest \
testsuites/1.inputname_imtcp_12516 \
omod-if-array.sh \
diag.sh \
- daqueue-persist.sh \
- daqueue-persist-drvr.sh \
testsuites/diag-common.conf \
queue-persist.sh \
queue-persist-drvr.sh \
diff --git a/tests/diag.sh b/tests/diag.sh
index 1ceca75b..1fa8f62a 100755
--- a/tests/diag.sh
+++ b/tests/diag.sh
@@ -9,7 +9,7 @@
#valgrind="valgrind --tool=drd --log-fd=1"
#valgrind="valgrind --tool=helgrind --log-fd=1"
#set -o xtrace
-#export RSYSLOG_DEBUG="debug nostdout printmutexaction"
+#export RSYSLOG_DEBUG="debug nostdout noprintmutexaction"
#export RSYSLOG_DEBUGLOG="log"
case $1 in
'init') $srcdir/killrsyslog.sh # kill rsyslogd if it runs for some reason
@@ -17,6 +17,7 @@ case $1 in
rm -f rsyslogd.started work-*.conf
rm -f work rsyslog.out.log rsyslog.out.log.save # common work files
rm -rf test-spool
+ rm -f core.* vgcore.*
mkdir test-spool
;;
'exit') rm -f rsyslogd.started work-*.conf diag-common.conf
diff --git a/tests/diskqueue-fsync.sh b/tests/diskqueue-fsync.sh
new file mode 100755
index 00000000..0282202d
--- /dev/null
+++ b/tests/diskqueue-fsync.sh
@@ -0,0 +1,16 @@
+# Test for disk-only queue mode (with fsync for queue files)
+# This test checks if queue files can be correctly written
+# and read back, but it does not test the transition from
+# memory to disk mode for DA queues.
+# added 2009-06-09 by Rgerhards
+# This file is part of the rsyslog project, released under GPLv3
+# uncomment for debugging support:
+echo testing queue disk-only mode, fsync case
+source $srcdir/diag.sh init
+source $srcdir/diag.sh startup diskqueue-fsync.conf
+# 1000 messages should be enough - the disk fsync test is very slow!
+source $srcdir/diag.sh tcpflood 127.0.0.1 13514 1 1000
+source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown
+source $srcdir/diag.sh seq-check 0 999
+source $srcdir/diag.sh exit
diff --git a/tests/diskqueue.sh b/tests/diskqueue.sh
index 2fe31db9..668a1224 100755
--- a/tests/diskqueue.sh
+++ b/tests/diskqueue.sh
@@ -11,5 +11,6 @@ source $srcdir/diag.sh startup diskqueue.conf
# 20000 messages should be enough - the disk test is slow enough ;)
source $srcdir/diag.sh tcpflood 127.0.0.1 13514 1 20000
source $srcdir/diag.sh shutdown-when-empty # shut down rsyslogd when done processing messages
+source $srcdir/diag.sh wait-shutdown
source $srcdir/diag.sh seq-check 0 19999
source $srcdir/diag.sh exit
diff --git a/tests/ourtail.c b/tests/ourtail.c
index 6781b5fe..4e8a6412 100644
--- a/tests/ourtail.c
+++ b/tests/ourtail.c
@@ -40,4 +40,6 @@ int main(int __attribute__((unused)) argc, char __attribute__((unused)) *argv[])
for( ; c != EOF ; c = getchar())
putchar(c);
+
+ return 0;
}
diff --git a/tests/runtime-dummy.c b/tests/runtime-dummy.c
index 9cddd913..38e6bba1 100644
--- a/tests/runtime-dummy.c
+++ b/tests/runtime-dummy.c
@@ -25,7 +25,9 @@
*
* A copy of the GPL can be found in the file "COPYING" in this distribution.
*/
+#include "config.h"
#include <stdlib.h>
+#include "rsyslog.h"
int bReduceRepeatMsgs = 0;
int repeatinterval = 30;
@@ -37,5 +39,7 @@ void cflineClassic(void) {};
void selectorAddList(void) {};
void selectorConstruct(void) {};
void selectorDestruct(void) {};
+void getFIOPName(void) {};
+ruleset_t *pCurrRuleset;
/* these are required by some dynamically loaded modules */
diff --git a/tests/testsuites/diskqueue-fsync.conf b/tests/testsuites/diskqueue-fsync.conf
new file mode 100644
index 00000000..0a02c6ce
--- /dev/null
+++ b/tests/testsuites/diskqueue-fsync.conf
@@ -0,0 +1,17 @@
+# Test for queue disk mode (see .sh file for details)
+# rgerhards, 2009-04-17
+$IncludeConfig diag-common.conf
+
+$ModLoad ../plugins/imtcp/.libs/imtcp
+$InputTCPServerRun 13514
+
+# set spool locations and switch queue to disk-only mode
+$WorkDirectory test-spool
+$MainMsgQueueSyncQueueFiles on
+$MainMsgQueueTimeoutShutdown 10000
+$MainMsgQueueFilename mainq
+$MainMsgQueueType disk
+
+$template outfmt,"%msg:F,58:2%\n"
+$template dynfile,"rsyslog.out.log" # trick to use relative path names!
+:msg, contains, "msgnum:" ?dynfile;outfmt
diff --git a/tests/validation-run.sh b/tests/validation-run.sh
index 10981290..2e922283 100755
--- a/tests/validation-run.sh
+++ b/tests/validation-run.sh
@@ -22,17 +22,18 @@
# A copy of the GPL can be found in the file "COPYING" in this distribution.
#set -x
echo "testing a failed configuration verification run"
-../tools/rsyslogd -u2 -c3 -N1 -f$srcdir/testsuites/invalid.conf
+../tools/rsyslogd -dn -u2 -c4 -N1 -f$srcdir/testsuites/invalid.conf -M../runtime/.libs:../.libs
if [ $? -ne 1 ]; then
exit 1
fi
echo testing a valid config verification run
-../tools/rsyslogd -u2 -c3 -N1 -f$srcdir/testsuites/valid.conf
+../tools/rsyslogd -u2 -c4 -N1 -f$srcdir/testsuites/valid.conf -M../runtime/.libs:../.libs
if [ $? -ne 0 ]; then
exit 1
fi
echo testing empty config file
-../tools/rsyslogd -u2 -c3 -N1 -f/dev/null
+../tools/rsyslogd -u2 -c4 -N1 -f/dev/null -M../runtime/.libs:../.libs
if [ $? -ne 1 ]; then
exit 1
fi
+echo SUCCESS: validation run tests
diff --git a/tools/Makefile.am b/tools/Makefile.am
index e523b854..f0f9afab 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -26,8 +26,10 @@ rsyslogd_LDADD = $(ZLIB_LIBS) $(PTHREADS_LIBS) $(RSRT_LIBS) $(SOL_LIBS)
rsyslogd_LDFLAGS = -export-dynamic
if ENABLE_DIAGTOOLS
-sbin_PROGRAMS += rsyslog_diag_hostname msggen
+sbin_PROGRAMS += rsyslog_diag_hostname msggen zpipe
rsyslog_diag_hostname_SOURCES = gethostn.c
+zpipe_SOURCES = zpipe.c
+zpipe_LDADD = -lz
msggen_SOURCES = msggen.c
endif
diff --git a/tools/omfile.c b/tools/omfile.c
index 3e845a73..675d313e 100644
--- a/tools/omfile.c
+++ b/tools/omfile.c
@@ -12,7 +12,13 @@
* of the "old" message code without any modifications. However, it
* helps to have things at the right place one we go to the meat of it.
*
- * Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH.
+ * A large re-write of this file was done in June, 2009. The focus was
+ * to introduce many more features (like zipped writing), clean up the code
+ * and make it more reliable. In short, that rewrite tries to provide a new
+ * solid basis for the next three to five years to come. During it, bugs
+ * may have been introduced ;) -- rgerhards, 2009-06-04
+ *
+ * Copyright 2007-2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -41,6 +47,7 @@
#include <assert.h>
#include <errno.h>
#include <ctype.h>
+#include <libgen.h>
#include <unistd.h>
#include <sys/file.h>
@@ -48,7 +55,7 @@
# include <fcntl.h>
#endif
-#include "syslogd.h"
+#include "conf.h"
#include "syslogd-types.h"
#include "srUtils.h"
#include "template.h"
@@ -57,6 +64,9 @@
#include "cfsysline.h"
#include "module-template.h"
#include "errmsg.h"
+#include "stream.h"
+#include "zlibw.h"
+#include "unicode-helper.h"
MODULE_TYPE_OUTPUT
@@ -64,17 +74,22 @@ MODULE_TYPE_OUTPUT
*/
DEF_OMOD_STATIC_DATA
DEFobjCurrIf(errmsg)
+DEFobjCurrIf(zlibw)
+DEFobjCurrIf(strm)
/* The following structure is a dynafile name cache entry.
*/
struct s_dynaFileCacheEntry {
- uchar *pName; /* name currently open, if dynamic name */
- short fd; /* name associated with file name in cache */
- time_t lastUsed; /* for LRU - last access */
+ uchar *pName; /* name currently open, if dynamic name */
+ strm_t *pStrm; /* our output stream */
+ time_t lastUsed; /* for LRU - last access */ // TODO: perforamcne change to counter (see other comment!)
};
typedef struct s_dynaFileCacheEntry dynaFileCacheEntry;
+#define IOBUF_DFLT_SIZE 1024 /* default size for io buffers */
+#define FLUSH_INTRVL_DFLT 1 /* default buffer flush interval (in seconds) */
+
/* globals for default values */
static int iDynaFileCacheSize = 10; /* max cache for dynamic files */
static int fCreateMode = 0644; /* mode to use when creating files */
@@ -86,19 +101,17 @@ static uid_t dirUID; /* UID to be used for newly created directories */
static uid_t dirGID; /* GID to be used for newly created directories */
static int bCreateDirs; /* auto-create directories for dynaFiles: 0 - no, 1 - yes */
static int bEnableSync = 0;/* enable syncing of files (no dash in front of pathname in conf): 0 - no, 1 - yes */
+static int iZipLevel = 0; /* zip compression mode (0..9 as usual) */
+static bool bFlushOnTXEnd = 0;/* flush write buffers when transaction has ended? */
+static int iIOBufSize = IOBUF_DFLT_SIZE; /* size of an io buffer */
+static int iFlushInterval = FLUSH_INTRVL_DFLT; /* how often flush the output buffer on inactivity? */
static uchar *pszTplName = NULL; /* name of the default template to use */
/* end globals for default values */
typedef struct _instanceData {
uchar f_fname[MAXFNAME];/* file or template name (display only) */
- short fd; /* file descriptor for (current) file */
- enum {
- eTypeFILE,
- eTypeTTY,
- eTypeCONSOLE,
- eTypePIPE
- } fileType;
+ strm_t *pStrm; /* our output stream */
char bDynamicName; /* 0 - static name, 1 - dynamic name (with properties) */
int fCreateMode; /* file creation mode for open() */
int fDirCreateMode; /* creation mode for mkdir() */
@@ -117,8 +130,12 @@ typedef struct _instanceData {
* pointer points to the overall structure.
*/
dynaFileCacheEntry **dynCache;
- off_t f_sizeLimit; /* file size limit, 0 = no limit */
- char *f_sizeLimitCmd; /* command to carry out when size limit is reached */
+ off_t iSizeLimit; /* file size limit, 0 = no limit */
+ uchar *pszSizeLimitCmd; /* command to carry out when size limit is reached */
+ int iZipLevel; /* zip mode to use for this selector */
+ int iIOBufSize; /* size of associated io buffer */
+ int iFlushInterval; /* how fast flush buffer on inactivity? */
+ bool bFlushOnTXEnd; /* flush write buffers when transaction has ended? */
} instanceData;
@@ -147,7 +164,7 @@ CODESTARTdbgPrintInstInfo
);
} else { /* regular file */
dbgprintf("%s", pData->f_fname);
- if (pData->fd == -1)
+ if (pData->pStrm == NULL)
dbgprintf(" (unused)");
}
ENDdbgPrintInstInfo
@@ -168,13 +185,13 @@ rsRetVal setDynaFileCacheSize(void __attribute__((unused)) *pVal, int iNewVal)
errmsg.LogError(0, RS_RET_VAL_OUT_OF_RANGE, "%s", errMsg);
iRet = RS_RET_VAL_OUT_OF_RANGE;
iNewVal = 1;
- } else if(iNewVal > 10000) {
+ } else if(iNewVal > 1000) {
snprintf((char*) errMsg, sizeof(errMsg)/sizeof(uchar),
- "DynaFileCacheSize maximum is 10,000 (%d given), changed to 10,000.", iNewVal);
+ "DynaFileCacheSize maximum is 1,000 (%d given), changed to 1,000.", iNewVal);
errno = 0;
errmsg.LogError(0, RS_RET_VAL_OUT_OF_RANGE, "%s", errMsg);
iRet = RS_RET_VAL_OUT_OF_RANGE;
- iNewVal = 10000;
+ iNewVal = 1000;
}
iDynaFileCacheSize = iNewVal;
@@ -199,14 +216,6 @@ static rsRetVal cflineParseOutchannel(instanceData *pData, uchar* p, omodStringR
struct outchannel *pOch;
char szBuf[128]; /* should be more than sufficient */
- /* this must always be a file, because we can not set a size limit
- * on a pipe...
- * rgerhards 2005-06-21: later, this will be a separate type, but let's
- * emulate things for the time being. When everything runs, we can
- * extend it...
- */
- pData->fileType = eTypeFILE;
-
++p; /* skip '$' */
i = 0;
/* get outchannel name */
@@ -241,12 +250,12 @@ static rsRetVal cflineParseOutchannel(instanceData *pData, uchar* p, omodStringR
}
/* OK, we finally got a correct template. So let's use it... */
- strncpy((char*) pData->f_fname, (char*) pOch->pszFileTemplate, MAXFNAME);
- pData->f_sizeLimit = pOch->uSizeLimit;
+ ustrncpy(pData->f_fname, pOch->pszFileTemplate, MAXFNAME);
+ pData->iSizeLimit = pOch->uSizeLimit;
/* WARNING: It is dangerous "just" to pass the pointer. As we
* never rebuild the output channel description, this is acceptable here.
*/
- pData->f_sizeLimitCmd = (char*) pOch->cmdOnSizeLimit;
+ pData->pszSizeLimitCmd = pOch->cmdOnSizeLimit;
iRet = cflineParseTemplateName(&p, pOMSR, iEntry, iTplOpts,
(pszTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszTplName);
@@ -256,64 +265,6 @@ finalize_it:
}
-/* rgerhards 2005-06-21: Try to resolve a size limit
- * situation. This first runs the command, and then
- * checks if we are still above the treshold.
- * returns 0 if ok, 1 otherwise
- * TODO: consider moving the initial check in here, too
- */
-int resolveFileSizeLimit(instanceData *pData)
-{
- uchar *pParams;
- uchar *pCmd;
- uchar *p;
- off_t actualFileSize;
- ASSERT(pData != NULL);
-
- if(pData->f_sizeLimitCmd == NULL)
- return 1; /* nothing we can do in this case... */
-
- /* the execProg() below is probably not great, but at least is is
- * fairly secure now. Once we change the way file size limits are
- * handled, we should also revisit how this command is run (and
- * with which parameters). rgerhards, 2007-07-20
- */
- /* we first check if we have command line parameters. We assume this,
- * when we have a space in the program name. If we find it, everything after
- * the space is treated as a single argument.
- */
- if((pCmd = (uchar*)strdup((char*)pData->f_sizeLimitCmd)) == NULL) {
- /* there is not much we can do - we make syslogd close the file in this case */
- return 1;
- }
-
- for(p = pCmd ; *p && *p != ' ' ; ++p) {
- /* JUST SKIP */
- }
-
- if(*p == ' ') {
- *p = '\0'; /* pretend string-end */
- pParams = p+1;
- } else
- pParams = NULL;
-
- execProg(pCmd, 1, pParams);
-
- free(pCmd);
-
- pData->fd = open((char*) pData->f_fname, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY|O_CLOEXEC,
- pData->fCreateMode);
-
- actualFileSize = lseek(pData->fd, 0, SEEK_END);
- if(actualFileSize >= pData->f_sizeLimit) {
- /* OK, it didn't work out... */
- return 1;
- }
-
- return 0;
-}
-
-
/* This function deletes an entry from the dynamic file name
* cache. A pointer to the cache must be passed in as well
* as the index of the to-be-deleted entry. This index may
@@ -321,23 +272,24 @@ int resolveFileSizeLimit(instanceData *pData)
* function immediately returns. Parameter bFreeEntry is 1
* if the entry should be d_free()ed and 0 if not.
*/
-static void dynaFileDelCacheEntry(dynaFileCacheEntry **pCache, int iEntry, int bFreeEntry)
+static rsRetVal
+dynaFileDelCacheEntry(dynaFileCacheEntry **pCache, int iEntry, int bFreeEntry)
{
+ DEFiRet;
ASSERT(pCache != NULL);
- BEGINfunc;
-
if(pCache[iEntry] == NULL)
FINALIZE;
DBGPRINTF("Removed entry %d for file '%s' from dynaCache.\n", iEntry,
- pCache[iEntry]->pName == NULL ? "[OPEN FAILED]" : (char*)pCache[iEntry]->pName);
+ pCache[iEntry]->pName == NULL ? UCHAR_CONSTANT("[OPEN FAILED]") : pCache[iEntry]->pName);
/* if the name is NULL, this is an improperly initilized entry which
* needs to be discarded. In this case, neither the file is to be closed
* not the name to be freed.
*/
if(pCache[iEntry]->pName != NULL) {
- close(pCache[iEntry]->fd);
+ if(pCache[iEntry]->pStrm != NULL)
+ strm.Destruct(&pCache[iEntry]->pStrm);
d_free(pCache[iEntry]->pName);
pCache[iEntry]->pName = NULL;
}
@@ -348,7 +300,7 @@ static void dynaFileDelCacheEntry(dynaFileCacheEntry **pCache, int iEntry, int b
}
finalize_it:
- ENDfunc;
+ RETiRet;
}
@@ -356,7 +308,8 @@ finalize_it:
* relevant files. Part of Shutdown and HUP processing.
* rgerhards, 2008-10-23
*/
-static inline void dynaFileFreeCacheEntries(instanceData *pData)
+static inline void
+dynaFileFreeCacheEntries(instanceData *pData)
{
register int i;
ASSERT(pData != NULL);
@@ -392,25 +345,18 @@ static void dynaFileFreeCache(instanceData *pData)
static rsRetVal
prepareFile(instanceData *pData, uchar *newFileName)
{
+ int fd;
DEFiRet;
- if(pData->fileType == eTypePIPE) {
- pData->fd = open((char*) pData->f_fname, O_RDWR|O_NONBLOCK|O_CLOEXEC);
- FINALIZE; /* we are done in this case */
- }
- if(access((char*)newFileName, F_OK) == 0) {
- /* file already exists */
- pData->fd = open((char*) newFileName, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY|O_CLOEXEC,
- pData->fCreateMode);
- } else {
- pData->fd = -1;
+ if(access((char*)newFileName, F_OK) != 0) {
/* file does not exist, create it (and eventually parent directories */
+ fd = -1;
if(pData->bCreateDirs) {
/* We first need to create parent dirs if they are missing.
* We do not report any errors here ourselfs but let the code
* fall through to error handler below.
*/
- if(makeFileParentDirs(newFileName, strlen((char*)newFileName),
+ if(makeFileParentDirs(newFileName, ustrlen(newFileName),
pData->fDirCreateMode, pData->dirUID,
pData->dirGID, pData->bFailOnChown) != 0) {
ABORT_FINALIZE(RS_RET_ERR); /* we give up */
@@ -419,17 +365,17 @@ prepareFile(instanceData *pData, uchar *newFileName)
/* no matter if we needed to create directories or not, we now try to create
* the file. -- rgerhards, 2008-12-18 (based on patch from William Tisater)
*/
- pData->fd = open((char*) newFileName, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY|O_CLOEXEC,
+ fd = open((char*) newFileName, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY|O_CLOEXEC,
pData->fCreateMode);
- if(pData->fd != -1) {
+ if(fd != -1) {
/* check and set uid/gid */
if(pData->fileUID != (uid_t)-1 || pData->fileGID != (gid_t) -1) {
/* we need to set owner/group */
- if(fchown(pData->fd, pData->fileUID, pData->fileGID) != 0) {
+ if(fchown(fd, pData->fileUID, pData->fileGID) != 0) {
if(pData->bFailOnChown) {
int eSave = errno;
- close(pData->fd);
- pData->fd = -1;
+ close(fd);
+ fd = -1;
errno = eSave;
}
/* we will silently ignore the chown() failure
@@ -437,19 +383,37 @@ prepareFile(instanceData *pData, uchar *newFileName)
*/
}
}
+ close(fd); /* close again, as we need a stream further on */
}
}
-finalize_it:
- /* this was "pData->fd != 0", which I think was a bug. I guess 0 was intended to mean
- * non-open file descriptor. Anyhow, I leave this comment for the time being to that if
- * problems surface, one at least knows what happened. -- rgerhards, 2009-03-19
- */
- if(pData->fd != -1 && isatty(pData->fd)) {
- DBGPRINTF("file %d is a tty file\n", pData->fd);
- pData->fileType = eTypeTTY;
- untty();
- }
+ /* the copies below are clumpsy, but there is no way around given the
+ * anomalies in dirname() and basename() [they MODIFY the provided buffer...]
+ */
+ uchar szNameBuf[MAXFNAME];
+ uchar szDirName[MAXFNAME];
+ uchar szBaseName[MAXFNAME];
+ ustrncpy(szNameBuf, newFileName, MAXFNAME);
+ ustrncpy(szDirName, (uchar*)dirname((char*)szNameBuf), MAXFNAME);
+ ustrncpy(szNameBuf, newFileName, MAXFNAME);
+ ustrncpy(szBaseName, (uchar*)basename((char*)szNameBuf), MAXFNAME);
+
+ CHKiRet(strm.Construct(&pData->pStrm));
+ CHKiRet(strm.SetFName(pData->pStrm, szBaseName, ustrlen(szBaseName)));
+ CHKiRet(strm.SetDir(pData->pStrm, szDirName, ustrlen(szDirName)));
+ CHKiRet(strm.SetiZipLevel(pData->pStrm, pData->iZipLevel));
+ CHKiRet(strm.SetsIOBufSize(pData->pStrm, (size_t) pData->iIOBufSize));
+ CHKiRet(strm.SetiFlushInterval(pData->pStrm, pData->iFlushInterval));
+ CHKiRet(strm.SettOperationsMode(pData->pStrm, STREAMMODE_WRITE_APPEND));
+ CHKiRet(strm.SettOpenMode(pData->pStrm, 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->pszSizeLimitCmd != NULL)
+ CHKiRet(strm.SetpszSizeLimitCmd(pData->pStrm, ustrdup(pData->pszSizeLimitCmd)));
+ CHKiRet(strm.ConstructFinalize(pData->pStrm));
+
+finalize_it:
RETiRet;
}
@@ -462,15 +426,16 @@ finalize_it:
* be written.
* This is a helper to writeFile(). rgerhards, 2007-07-03
*/
-static int prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts)
+static inline rsRetVal
+prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts)
{
time_t ttOldest; /* timestamp of oldest element */
int iOldest;
int i;
int iFirstFree;
+ rsRetVal localRet;
dynaFileCacheEntry **pCache;
-
- BEGINfunc
+ DEFiRet;
ASSERT(pData != NULL);
ASSERT(newFileName != NULL);
@@ -481,15 +446,17 @@ static int prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsg
* I *hope* this will be a performance enhancement.
*/
if( (pData->iCurrElt != -1)
- && !strcmp((char*) newFileName, (char*) pCache[pData->iCurrElt]->pName)) {
+ && !ustrcmp(newFileName, pCache[pData->iCurrElt]->pName)) {
/* great, we are all set */
- pCache[pData->iCurrElt]->lastUsed = time(NULL); /* update timestamp for LRU */
- return 0;
+ pCache[pData->iCurrElt]->lastUsed = time(NULL); /* update timestamp for LRU */ // TODO: optimize time call!
+ // LRU needs only a strictly monotonically increasing counter, so such a one could do
+ FINALIZE;
}
/* ok, no luck. Now let's search the table if we find a matching spot.
* While doing so, we also prepare for creation of a new one.
*/
+ pData->iCurrElt = -1; /* invalid current element pointer */
iFirstFree = -1; /* not yet found */
iOldest = 0; /* we assume the first element to be the oldest - that will change as we loop */
ttOldest = time(NULL) + 1; /* there must always be an older one */
@@ -498,12 +465,12 @@ static int prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsg
if(iFirstFree == -1)
iFirstFree = i;
} else { /* got an element, let's see if it matches */
- if(!strcmp((char*) newFileName, (char*) pCache[i]->pName)) {
+ if(!ustrcmp(newFileName, pCache[i]->pName)) {
/* we found our element! */
- pData->fd = pCache[i]->fd;
+ pData->pStrm = pCache[i]->pStrm;
pData->iCurrElt = i;
pCache[i]->lastUsed = time(NULL); /* update timestamp for LRU */
- return 0;
+ FINALIZE;
}
/* did not find it - so lets keep track of the counters for LRU */
if(pCache[i]->lastUsed < ttOldest) {
@@ -524,18 +491,14 @@ static int prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsg
iFirstFree = iOldest; /* this one *is* now free ;) */
} else {
/* we need to allocate memory for the cache structure */
- pCache[iFirstFree] = (dynaFileCacheEntry*) calloc(1, sizeof(dynaFileCacheEntry));
- if(pCache[iFirstFree] == NULL) {
- DBGPRINTF("prepareDynfile(): could not alloc mem, discarding this request\n");
- return -1;
- }
+ CHKmalloc(pCache[iFirstFree] = (dynaFileCacheEntry*) calloc(1, sizeof(dynaFileCacheEntry)));
}
/* Ok, we finally can open the file */
- prepareFile(pData, newFileName); /* ignore exact error, we check fd below */
+ localRet = prepareFile(pData, newFileName); /* ignore exact error, we check fd below */
/* file is either open now or an error state set */
- if(pData->fd == -1) {
+ if(pData->pStrm == NULL) {
/* do not report anything if the message is an internally-generated
* message. Otherwise, we could run into a never-ending loop. The bad
* news is that we also lose errors on startup messages, but so it is.
@@ -543,22 +506,43 @@ static int prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsg
if(iMsgOpts & INTERNAL_MSG) {
DBGPRINTF("Could not open dynaFile, discarding message\n");
} else {
- errmsg.LogError(0, NO_ERRCODE, "Could not open dynamic file '%s' - discarding message", (char*)newFileName);
+ errmsg.LogError(0, NO_ERRCODE, "Could not open dynamic file '%s' - discarding message", newFileName);
}
dynaFileDelCacheEntry(pCache, iFirstFree, 1);
- pData->iCurrElt = -1;
- return -1;
+ ABORT_FINALIZE(localRet);
}
- pCache[iFirstFree]->fd = pData->fd;
- pCache[iFirstFree]->pName = (uchar*)strdup((char*)newFileName); /* TODO: check for NULL (very unlikely) */
- pCache[iFirstFree]->lastUsed = time(NULL);
+ CHKmalloc(pCache[iFirstFree]->pName = ustrdup(newFileName));
+ pCache[iFirstFree]->pStrm = pData->pStrm;
+ pCache[iFirstFree]->lastUsed = time(NULL); // monotonically increasing value! TODO: performance
pData->iCurrElt = iFirstFree;
DBGPRINTF("Added new entry %d for file cache, file '%s'.\n", iFirstFree, newFileName);
- ENDfunc
+finalize_it:
+ RETiRet;
+}
+
+
+/* do the actual write process. This function is to be called once we are ready for writing.
+ * It will do buffered writes and persist data only when the buffer is full. Note that we must
+ * be careful to detect when the file handle changed.
+ * rgerhards, 2009-06-03
+ */
+static rsRetVal
+doWrite(instanceData *pData, uchar *pszBuf, int lenBuf)
+{
+ DEFiRet;
+ ASSERT(pData != NULL);
+ ASSERT(pszBuf != NULL);
+
+dbgprintf("doWrite, pData->pStrm %p, lenBuf %d\n", pData->pStrm, lenBuf);
+ if(pData->pStrm != NULL){
+ CHKiRet(strm.Write(pData->pStrm, pszBuf, lenBuf));
+ FINALIZE;
+ }
- return 0;
+finalize_it:
+ RETiRet;
}
@@ -566,10 +550,9 @@ static int prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsg
* will be called for all outputs using file semantics,
* for example also for pipes.
*/
-static rsRetVal writeFile(uchar **ppString, unsigned iMsgOpts, instanceData *pData)
+static rsRetVal
+writeFile(uchar **ppString, unsigned iMsgOpts, instanceData *pData)
{
- off_t actualFileSize;
- int iLenWritten;
DEFiRet;
ASSERT(pData != NULL);
@@ -578,106 +561,27 @@ static rsRetVal writeFile(uchar **ppString, unsigned iMsgOpts, instanceData *pDa
* check if it still is ok or a new file needs to be created
*/
if(pData->bDynamicName) {
- if(prepareDynFile(pData, ppString[1], iMsgOpts) != 0)
- ABORT_FINALIZE(RS_RET_SUSPENDED); /* whatever the failure was, we need to retry */
- }
-
- if(pData->fd == -1) {
- rsRetVal iRetLocal;
- iRetLocal = prepareFile(pData, pData->f_fname);
- if((iRetLocal != RS_RET_OK) || (pData->fd == -1))
- ABORT_FINALIZE(RS_RET_SUSPENDED); /* whatever the failure was, we need to retry */
- }
-
- /* create the message based on format specified */
-again:
- /* check if we have a file size limit and, if so,
- * obey to it.
- */
- if(pData->f_sizeLimit != 0) {
- actualFileSize = lseek(pData->fd, 0, SEEK_END);
- if(actualFileSize >= pData->f_sizeLimit) {
- char errMsg[256];
- /* for now, we simply disable a file once it is
- * beyond the maximum size. This is better than having
- * us aborted by the OS... rgerhards 2005-06-21
- */
- (void) close(pData->fd);
- /* try to resolve the situation */
- if(resolveFileSizeLimit(pData) != 0) {
- /* didn't work out, so disable... */
- snprintf(errMsg, sizeof(errMsg),
- "no longer writing to file %s; grown beyond configured file size of %lld bytes, actual size %lld - configured command did not resolve situation",
- pData->f_fname, (long long) pData->f_sizeLimit, (long long) actualFileSize);
- errno = 0;
- errmsg.LogError(0, RS_RET_DISABLE_ACTION, "%s", errMsg);
- ABORT_FINALIZE(RS_RET_DISABLE_ACTION);
- } else {
- snprintf(errMsg, sizeof(errMsg),
- "file %s had grown beyond configured file size of %lld bytes, actual size was %lld - configured command resolved situation",
- pData->f_fname, (long long) pData->f_sizeLimit, (long long) actualFileSize);
- errno = 0;
- errmsg.LogError(0, NO_ERRCODE, "%s", errMsg);
- }
+ CHKiRet(prepareDynFile(pData, ppString[1], iMsgOpts));
+ } else { /* "regular", non-dynafile */
+ if(pData->pStrm == NULL) {
+ CHKiRet(prepareFile(pData, pData->f_fname));
}
}
- iLenWritten = write(pData->fd, ppString[0], strlen((char*)ppString[0]));
-//dbgprintf("lenwritten: %d\n", iLenWritten);
- if(iLenWritten < 0) {
- int e = errno;
- char errStr[1024];
- rs_strerror_r(errno, errStr, sizeof(errStr));
- DBGPRINTF("log file (%d) write error %d: %s\n", pData->fd, e, errStr);
-
- /* If a named pipe is full, we suspend this action for a while */
- if(pData->fileType == eTypePIPE && e == EAGAIN)
- ABORT_FINALIZE(RS_RET_SUSPENDED);
-
- close(pData->fd);
- pData->fd = -1; /* tell that fd is no longer open! */
- if(pData->bDynamicName && pData->iCurrElt != -1) {
- /* in this case, we need to invalidate the name in the cache, too
- * otherwise, an invalid fd may show up if we had a file name change.
- * rgerhards, 2009-03-19
- */
- pData->dynCache[pData->iCurrElt]->fd = -1;
- }
- /* Check for EBADF on TTY's due to vhangup()
- * Linux uses EIO instead (mrn 12 May 96)
- */
- if((pData->fileType == eTypeTTY || pData->fileType == eTypeCONSOLE)
-#ifdef linux
- && e == EIO
-#else
- && e == EBADF
-#endif
- ) {
- pData->fd = open((char*) pData->f_fname, O_WRONLY|O_APPEND|O_NOCTTY|O_CLOEXEC);
- if (pData->fd < 0) {
- iRet = RS_RET_SUSPENDED;
- errmsg.LogError(0, NO_ERRCODE, "%s", pData->f_fname);
- } else {
- untty();
- goto again;
- }
- } else {
- iRet = RS_RET_SUSPENDED;
- errno = e;
- errmsg.LogError(0, NO_ERRCODE, "%s", pData->f_fname);
- }
- } else if (pData->bSyncFile) {
- fsync(pData->fd);
- }
+ CHKiRet(doWrite(pData, ppString[0], strlen(CHAR_CONVERT(ppString[0]))));
finalize_it:
+ if(iRet != RS_RET_OK) {
+ /* in v5, we shall return different states for message-cause failur (but only there!) */
+ iRet = RS_RET_SUSPENDED;
+ }
RETiRet;
}
BEGINcreateInstance
CODESTARTcreateInstance
- pData->fd = -1;
+ pData->pStrm = NULL;
ENDcreateInstance
@@ -685,8 +589,8 @@ BEGINfreeInstance
CODESTARTfreeInstance
if(pData->bDynamicName) {
dynaFileFreeCache(pData);
- } else if(pData->fd != -1)
- close(pData->fd);
+ } else if(pData->pStrm != NULL)
+ strm.Destruct(&pData->pStrm);
ENDfreeInstance
@@ -696,39 +600,32 @@ ENDtryResume
BEGINdoAction
CODESTARTdoAction
- DBGPRINTF(" (%s)\n", pData->f_fname);
- iRet = writeFile(ppString, iMsgOpts, pData);
+ DBGPRINTF("file to log to: %s\n", pData->f_fname);
+ CHKiRet(writeFile(ppString, iMsgOpts, pData));
+ if(pData->bFlushOnTXEnd) {
+ /* TODO v5: do this in endTransaction only! */
+ CHKiRet(strm.Flush(pData->pStrm));
+ }
+finalize_it:
ENDdoAction
BEGINparseSelectorAct
CODESTARTparseSelectorAct
- /* yes, the if below is redundant, but I need it now. Will go away as
- * the code further changes. -- rgerhards, 2007-07-25
- */
- if(*p == '$' || *p == '?' || *p == '|' || *p == '/' || *p == '-') {
- if((iRet = createInstance(&pData)) != RS_RET_OK) {
- ENDfunc
- return iRet; /* this can not use RET_iRet! */
- }
- } else {
- /* this is not clean, but we need it for the time being
- * TODO: remove when cleaning up modularization
- */
- ENDfunc
- return RS_RET_CONFLINE_UNPROCESSED;
- }
+ if(!(*p == '$' || *p == '?' || *p == '|' || *p == '/' || *p == '-'))
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+
+ CHKiRet(createInstance(&pData));
if(*p == '-') {
pData->bSyncFile = 0;
p++;
} else {
- pData->bSyncFile = bEnableSync ? 1 : 0;
+ pData->bSyncFile = bEnableSync;
}
- pData->f_sizeLimit = 0; /* default value, use outchannels to configure! */
+ pData->iSizeLimit = 0; /* default value, use outchannels to configure! */
- switch (*p)
- {
+ switch(*p) {
case '$':
CODE_STD_STRING_REQUESTparseSelectorAct(1)
/* rgerhards 2005-06-21: this is a special setting for output-channel
@@ -738,13 +635,8 @@ CODESTARTparseSelectorAct
* rgerhards, 2007-07-24: output-channels will go away. We keep them
* for compatibility reasons, but seems to have been a bad idea.
*/
- if((iRet = cflineParseOutchannel(pData, p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS)) == RS_RET_OK) {
- pData->bDynamicName = 0;
- pData->fCreateMode = fCreateMode; /* preserve current setting */
- pData->fDirCreateMode = fDirCreateMode; /* preserve current setting */
- pData->fd = open((char*) pData->f_fname, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY|O_CLOEXEC,
- pData->fCreateMode);
- }
+ CHKiRet(cflineParseOutchannel(pData, p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS));
+ pData->bDynamicName = 0;
break;
case '?': /* This is much like a regular file handle, but we need to obtain
@@ -752,84 +644,56 @@ CODESTARTparseSelectorAct
*/
CODE_STD_STRING_REQUESTparseSelectorAct(2)
++p; /* eat '?' */
- if((iRet = cflineParseFileName(p, (uchar*) pData->f_fname, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
- (pszTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszTplName))
- != RS_RET_OK)
- break;
+ CHKiRet(cflineParseFileName(p, (uchar*) pData->f_fname, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
+ (pszTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszTplName));
/* "filename" is actually a template name, we need this as string 1. So let's add it
* to the pOMSR. -- rgerhards, 2007-07-27
*/
- if((iRet = OMSRsetEntry(*ppOMSR, 1, (uchar*)strdup((char*) pData->f_fname), OMSR_NO_RQD_TPL_OPTS)) != RS_RET_OK)
- break;
+ CHKiRet(OMSRsetEntry(*ppOMSR, 1, ustrdup(pData->f_fname), OMSR_NO_RQD_TPL_OPTS));
pData->bDynamicName = 1;
pData->iCurrElt = -1; /* no current element */
- pData->fCreateMode = fCreateMode; /* freeze current setting */
- pData->fDirCreateMode = fDirCreateMode; /* preserve current setting */
- pData->bCreateDirs = bCreateDirs;
- pData->bFailOnChown = bFailOnChown;
- pData->fileUID = fileUID;
- pData->fileGID = fileGID;
- pData->dirUID = dirUID;
- pData->dirGID = dirGID;
- pData->iDynaFileCacheSize = iDynaFileCacheSize; /* freeze current setting */
- /* we now allocate the cache table. We use calloc() intentionally, as we
- * need all pointers to be initialized to NULL pointers.
- */
- if((pData->dynCache = (dynaFileCacheEntry**)
- calloc(iDynaFileCacheSize, sizeof(dynaFileCacheEntry*))) == NULL) {
- iRet = RS_RET_OUT_OF_MEMORY;
- DBGPRINTF("Could not allocate memory for dynaFileCache - selector disabled.\n");
- }
+ /* we now allocate the cache table */
+ CHKmalloc(pData->dynCache = (dynaFileCacheEntry**)
+ calloc(iDynaFileCacheSize, sizeof(dynaFileCacheEntry*)));
break;
case '|':
case '/':
CODE_STD_STRING_REQUESTparseSelectorAct(1)
- /* rgerhards, 2007-0726: first check if file or pipe */
- if(*p == '|') {
- pData->fileType = eTypePIPE;
- ++p;
- } else {
- pData->fileType = eTypeFILE;
- }
- /* rgerhards 2004-11-17: from now, we need to have different
- * processing, because after the first comma, the template name
- * to use is specified. So we need to scan for the first coma first
- * and then look at the rest of the line.
- */
- if((iRet = cflineParseFileName(p, (uchar*) pData->f_fname, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
- (pszTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszTplName))
- != RS_RET_OK)
- break;
-
+ CHKiRet(cflineParseFileName(p, (uchar*) pData->f_fname, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
+ (pszTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszTplName));
pData->bDynamicName = 0;
- pData->fCreateMode = fCreateMode; /* preserve current setting */
- pData->fDirCreateMode = fDirCreateMode;
- pData->bCreateDirs = bCreateDirs;
- pData->bFailOnChown = bFailOnChown;
- pData->fileUID = fileUID;
- pData->fileGID = fileGID;
- pData->dirUID = dirUID;
- pData->dirGID = dirGID;
-
- /* at this stage, we ignore the return value of prepareFile, this is taken
- * care of in later steps. -- rgerhards, 2009-03-19
+ break;
+ default:
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+ }
+
+ /* freeze current paremeters for this action */
+ pData->iDynaFileCacheSize = iDynaFileCacheSize;
+ pData->fCreateMode = fCreateMode;
+ pData->fDirCreateMode = fDirCreateMode;
+ pData->bCreateDirs = bCreateDirs;
+ pData->bFailOnChown = bFailOnChown;
+ pData->fileUID = fileUID;
+ pData->fileGID = fileGID;
+ pData->dirUID = dirUID;
+ pData->dirGID = dirGID;
+ pData->iZipLevel = iZipLevel;
+ pData->bFlushOnTXEnd = bFlushOnTXEnd;
+ pData->iIOBufSize = iIOBufSize;
+ pData->iFlushInterval = iFlushInterval;
+
+ if(pData->bDynamicName == 0) {
+ /* try open and emit error message if not possible. At this stage, we ignore the
+ * return value of prepareFile, this is taken care of in later steps.
*/
prepareFile(pData, pData->f_fname);
- if(pData->fd < 0 ) {
- pData->fd = -1;
+ if(pData->pStrm == NULL) {
DBGPRINTF("Error opening log file: %s\n", pData->f_fname);
errmsg.LogError(0, RS_RET_NO_FILE_ACCESS, "Could no open output file '%s'", pData->f_fname);
- break;
}
- if(strcmp((char*) p, _PATH_CONSOLE) == 0)
- pData->fileType = eTypeCONSOLE;
- break;
- default:
- iRet = RS_RET_CONFLINE_UNPROCESSED;
- break;
}
CODE_STD_FINALIZERparseSelectorAct
ENDparseSelectorAct
@@ -850,6 +714,10 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
fDirCreateMode = 0700;
bCreateDirs = 1;
bEnableSync = 0;
+ iZipLevel = 0;
+ bFlushOnTXEnd = 0;
+ iIOBufSize = IOBUF_DFLT_SIZE;
+ iFlushInterval = FLUSH_INTRVL_DFLT;
if(pszTplName != NULL) {
free(pszTplName);
pszTplName = NULL;
@@ -865,9 +733,9 @@ CODESTARTdoHUP
dynaFileFreeCacheEntries(pData);
pData->iCurrElt = -1; /* invalidate current element */
} else {
- if(pData->fd != -1) {
- close(pData->fd);
- pData->fd = -1;
+ if(pData->pStrm != NULL) {
+ strm.Destruct(&pData->pStrm);
+ pData->pStrm = NULL;
}
}
ENDdoHUP
@@ -875,8 +743,10 @@ ENDdoHUP
BEGINmodExit
CODESTARTmodExit
- if(pszTplName != NULL)
- free(pszTplName);
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(strm, CORE_COMPONENT);
+ objRelease(zlibw, LM_ZLIBW_FILENAME);
+ free(pszTplName);
ENDmodExit
@@ -892,7 +762,13 @@ CODESTARTmodInit
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(zlibw, LM_ZLIBW_FILENAME));
+ CHKiRet(objUse(strm, CORE_COMPONENT));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"dynafilecachesize", 0, eCmdHdlrInt, (void*) setDynaFileCacheSize, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileziplevel", 0, eCmdHdlrInt, NULL, &iZipLevel, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileflushinterval", 0, eCmdHdlrInt, NULL, &iFlushInterval, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileflushontxend", 0, eCmdHdlrBinary, NULL, &bFlushOnTXEnd, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileiobuffersize", 0, eCmdHdlrSize, NULL, &iIOBufSize, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"dirowner", 0, eCmdHdlrUID, NULL, &dirUID, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"dirgroup", 0, eCmdHdlrGID, NULL, &dirGID, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"fileowner", 0, eCmdHdlrUID, NULL, &fileUID, STD_LOADABLE_MODULE_ID));
diff --git a/tools/omfwd.c b/tools/omfwd.c
index c8fedfc9..2966a5e4 100644
--- a/tools/omfwd.c
+++ b/tools/omfwd.c
@@ -48,6 +48,7 @@
#endif
#include <pthread.h>
#include "syslogd.h"
+#include "conf.h"
#include "syslogd-types.h"
#include "srUtils.h"
#include "net.h"
diff --git a/tools/omshell.c b/tools/omshell.c
index 7b815869..f8a68527 100644
--- a/tools/omshell.c
+++ b/tools/omshell.c
@@ -38,7 +38,7 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
-#include "syslogd.h"
+#include "conf.h"
#include "syslogd-types.h"
#include "srUtils.h"
#include "omshell.h"
diff --git a/tools/omusrmsg.c b/tools/omusrmsg.c
index 830bbc87..499a11dd 100644
--- a/tools/omusrmsg.c
+++ b/tools/omusrmsg.c
@@ -66,7 +66,7 @@
#include "srUtils.h"
#include "stringbuf.h"
#include "syslogd-types.h"
-#include "syslogd.h"
+#include "conf.h"
#include "omusrmsg.h"
#include "module-template.h"
#include "errmsg.h"
diff --git a/tools/syslogd.c b/tools/syslogd.c
index 3d91e22f..6d6bcea1 100644
--- a/tools/syslogd.c
+++ b/tools/syslogd.c
@@ -133,13 +133,16 @@
#include "queue.h"
#include "stream.h"
#include "conf.h"
-#include "vm.h"
#include "errmsg.h"
#include "datetime.h"
#include "parser.h"
-#include "sysvar.h"
+//#include "sysvar.h"
#include "batch.h"
#include "unicode-helper.h"
+#include "ruleset.h"
+#include "rule.h"
+#include "net.h"
+#include "vm.h"
/* definitions for objects we access */
DEFobjCurrIf(obj)
@@ -147,10 +150,10 @@ DEFobjCurrIf(glbl)
DEFobjCurrIf(datetime)
DEFobjCurrIf(conf)
DEFobjCurrIf(expr)
-DEFobjCurrIf(vm)
-DEFobjCurrIf(var)
DEFobjCurrIf(module)
DEFobjCurrIf(errmsg)
+DEFobjCurrIf(rule)
+DEFobjCurrIf(ruleset)
DEFobjCurrIf(net) /* TODO: make go away! */
@@ -222,7 +225,7 @@ static rsRetVal GlobalClassExit(void);
#endif
#ifndef _PATH_TTY
-#define _PATH_TTY "/dev/tty"
+# define _PATH_TTY "/dev/tty"
#endif
static uchar *ConfFile = (uchar*) _PATH_LOGCONF; /* read-only after startup */
@@ -249,8 +252,6 @@ int repeatinterval[2] = { 30, 60 }; /* # of secs before flush */
#define LIST_DELIMITER ':' /* delimiter between two hosts */
-struct filed *Files = NULL; /* read-only after init() (but beware of sigusr1!) */
-
static pid_t ppid; /* This is a quick and dirty hack used for spliting main/startup thread */
typedef struct legacyOptsLL_s {
@@ -300,6 +301,7 @@ static queueType_t MainMsgQueType = QUEUETYPE_FIXED_ARRAY; /* type of the main m
static uchar *pszMainMsgQFName = NULL; /* prefix for the main message queue file */
static int64 iMainMsgQueMaxFileSize = 1024*1024;
static int iMainMsgQPersistUpdCnt = 0; /* persist queue info every n updates */
+static int bMainMsgQSyncQeueFiles = 0; /* sync queue files on every write? */
static int iMainMsgQtoQShutdown = 0; /* queue shutdown */
static int iMainMsgQtoActShutdown = 1000; /* action shutdown (in phase 2) */
static int iMainMsgQtoEnq = 2000; /* timeout for queue enque */
@@ -316,7 +318,8 @@ static int iMainMsgQueueDeqtWinToHr = 25; /* hour begin of time frame when que
/* support for simple textual representation of FIOP names
* rgerhards, 2005-09-27
*/
-static char* getFIOPName(unsigned iFIOP)
+char*
+getFIOPName(unsigned iFIOP)
{
char *pRet;
switch(iFIOP) {
@@ -363,6 +366,7 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
iMainMsgQueMaxFileSize = 1024 * 1024;
iMainMsgQueueNumWorkers = 1;
iMainMsgQPersistUpdCnt = 0;
+ bMainMsgQSyncQeueFiles = 0;
iMainMsgQtoQShutdown = 0;
iMainMsgQtoActShutdown = 1000;
iMainMsgQtoEnq = 2000;
@@ -399,7 +403,6 @@ static char **crunch_list(char *list);
static void reapchild();
static void debug_switch();
static void sighup_handler();
-static void freeSelectors(void);
static void processImInternal(void);
@@ -434,67 +437,6 @@ diagGetMainMsgQSize(int *piSize)
/* ------------------------------ end support functions for imdiag ------------------------------ */
-/* function to destruct a selector_t object
- * rgerhards, 2007-08-01
- */
-rsRetVal
-selectorDestruct(void *pVal)
-{
- selector_t *pThis = (selector_t *) pVal;
-
- assert(pThis != NULL);
-
- if(pThis->pCSHostnameComp != NULL)
- rsCStrDestruct(&pThis->pCSHostnameComp);
- if(pThis->pCSProgNameComp != NULL)
- rsCStrDestruct(&pThis->pCSProgNameComp);
-
- if(pThis->f_filter_type == FILTER_PROP) {
- if(pThis->f_filterData.prop.pCSPropName != NULL)
- rsCStrDestruct(&pThis->f_filterData.prop.pCSPropName);
- if(pThis->f_filterData.prop.pCSCompValue != NULL)
- rsCStrDestruct(&pThis->f_filterData.prop.pCSCompValue);
- if(pThis->f_filterData.prop.regex_cache != NULL)
- rsCStrRegexDestruct(&pThis->f_filterData.prop.regex_cache);
- } else if(pThis->f_filter_type == FILTER_EXPR) {
- if(pThis->f_filterData.f_expr != NULL)
- expr.Destruct(&pThis->f_filterData.f_expr);
- }
-
- llDestroy(&pThis->llActList);
- free(pThis);
-
- return RS_RET_OK;
-}
-
-
-/* function to construct a selector_t object
- * rgerhards, 2007-08-01
- */
-rsRetVal
-selectorConstruct(selector_t **ppThis)
-{
- DEFiRet;
- selector_t *pThis;
-
- assert(ppThis != NULL);
-
- if((pThis = (selector_t*) calloc(1, sizeof(selector_t))) == NULL) {
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- }
- CHKiRet(llInit(&pThis->llActList, actionDestruct, NULL, NULL));
-
-finalize_it:
- if(iRet != RS_RET_OK) {
- if(pThis != NULL) {
- selectorDestruct(pThis);
- }
- }
- *ppThis = pThis;
- RETiRet;
-}
-
-
/* rgerhards, 2005-10-24: crunch_list is called only during option processing. So
* it is never called once rsyslogd is running (not even when HUPed). This code
* contains some exits, but they are considered safe because they only happen
@@ -557,7 +499,7 @@ static char **crunch_list(char *list)
#if 0
count=0;
while (result[count])
- dbgprintf("#%d: %s\n", count, StripDomains[count++]);
+ DBGPRINTF("#%d: %s\n", count, StripDomains[count++]);
#endif
return result;
}
@@ -566,7 +508,7 @@ static char **crunch_list(char *list)
void untty(void)
#ifdef HAVE_SETSID
{
- if ( !Debug ) {
+ if(!Debug) {
setsid();
}
return;
@@ -575,18 +517,18 @@ void untty(void)
{
int i;
- if ( !Debug ) {
+ if(!Debug) {
i = open(_PATH_TTY, O_RDWR|O_CLOEXEC);
if (i >= 0) {
# if !defined(__hpux)
- (void) ioctl(i, (int) TIOCNOTTY, (char *)0);
+ (void) ioctl(i, (int) TIOCNOTTY, NULL);
# else
/* TODO: we need to implement something for HP UX! -- rgerhards, 2008-03-04 */
/* actually, HP UX should have setsid, so the code directly above should
* trigger. So the actual question is why it doesn't do that...
*/
# endif
- (void) close(i);
+ close(i);
}
}
}
@@ -643,7 +585,7 @@ static inline rsRetVal printline(uchar *hname, uchar *hnameIP, uchar *msg, int f
CHKiRet(msgConstructWithTime(&pMsg, stTime, ttGenTime));
}
if(pszInputName != NULL)
- MsgSetInputName(pMsg, pszInputName);
+ MsgSetInputName(pMsg, pszInputName, ustrlen(pszInputName));
MsgSetFlowControlType(pMsg, flowCtlType);
MsgSetRawMsg(pMsg, (char*)msg);
@@ -674,16 +616,9 @@ static inline rsRetVal printline(uchar *hname, uchar *hnameIP, uchar *msg, int f
if((pMsg->msgFlags & PARSE_HOSTNAME) == 0)
MsgSetHOSTNAME(pMsg, hname);
MsgSetRcvFrom(pMsg, hname);
+ MsgSetAfterPRIOffs(pMsg, p - msg);
CHKiRet(MsgSetRcvFromIP(pMsg, hnameIP));
- /* rgerhards 2004-11-19: well, well... we've now seen that we
- * have the "hostname problem" also with the traditional Unix
- * message. As we like to emulate it, we need to add the hostname
- * to it.
- */
- if(MsgSetUxTradMsg(pMsg, (char*)p) != 0)
- ABORT_FINALIZE(RS_RET_ERR);
-
logmsg(pMsg, flags);
finalize_it:
@@ -784,7 +719,7 @@ parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int fla
* rgerhards, 2007-09-14
*/
if(*(msg + len - 1) == '\0') {
- dbgprintf("dropped NUL at very end of message\n");
+ DBGPRINTF("dropped NUL at very end of message\n");
len--;
}
@@ -794,7 +729,7 @@ parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int fla
* turn on/off this handling. rgerhards, 2007-07-23
*/
if(bDropTrailingLF && *(msg + len - 1) == '\n') {
- dbgprintf("dropped LF at very end of message (DropTrailingLF is set)\n");
+ DBGPRINTF("dropped LF at very end of message (DropTrailingLF is set)\n");
len--;
}
@@ -819,7 +754,7 @@ parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int fla
iLenDefBuf = iMaxLine;
CHKmalloc(deflateBuf = malloc(sizeof(uchar) * (iMaxLine + 1)));
ret = uncompress((uchar *) deflateBuf, &iLenDefBuf, (uchar *) msg+1, len-1);
- dbgprintf("Compressed message uncompressed with status %d, length: new %ld, old %d.\n",
+ DBGPRINTF("Compressed message uncompressed with status %d, length: new %ld, old %d.\n",
ret, (long) iLenDefBuf, len-1);
/* Now check if the uncompression worked. If not, there is not much we can do. In
* that case, we log an error message but ignore the message itself. Storing the
@@ -865,7 +800,7 @@ parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int fla
* (I couldn't do any more smart things anyway...).
* rgerhards, 2007-9-20
*/
- dbgprintf("internal error: iMsg > max msg size in printchopped()\n");
+ DBGPRINTF("internal error: iMsg > max msg size in printchopped()\n");
}
FINALIZE; /* in this case, we are done... nothing left we can do */
}
@@ -951,8 +886,7 @@ logmsgInternal(int iErr, int pri, uchar *msg, int flags)
DEFiRet;
CHKiRet(msgConstruct(&pMsg));
- MsgSetInputName(pMsg, UCHAR_CONSTANT("rsyslogd"));
- MsgSetUxTradMsg(pMsg, (char*)msg);
+ MsgSetInputName(pMsg, UCHAR_CONSTANT("rsyslogd"), sizeof("rsyslogd")-1);
MsgSetRawMsg(pMsg, (char*)msg);
MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName());
MsgSetRcvFrom(pMsg, glbl.GetLocalHostName());
@@ -996,6 +930,8 @@ finalize_it:
RETiRet;
}
+#if 0 // Code obsoleted by merge imfile, but check for changes if there are problems
+//thus I leave it in for the time being TODO: remove
/* This functions looks at the given message and checks if it matches the
* provided filter condition. If so, it returns true, else it returns
* false. This is a helper to logmsg() and meant to drive the decision
@@ -1226,6 +1162,7 @@ processMsg(msg_t *pMsg)
ENDfunc
}
+#endif // if 0 from merge omfile
/* The consumer of dequeued messages. This function is called by the
* queue engine on dequeueing of a message. It runs on a SEPARATE
@@ -1248,7 +1185,7 @@ msgConsumer(void __attribute__((unused)) *notNeeded, batch_t *pBatch)
if((pMsg->msgFlags & NEEDS_PARSING) != 0) {
parseMsg(pMsg);
}
- processMsg(pMsg);
+ ruleset.ProcessMsg(pMsg);
}
RETiRet;
@@ -1382,8 +1319,8 @@ int parseRFCSyslogMsg(msg_t *pMsg, int flags)
BEGINfunc
assert(pMsg != NULL);
- assert(pMsg->pszUxTradMsg != NULL);
- p2parse = pMsg->pszUxTradMsg;
+ assert(pMsg->pszRawMsg != NULL);
+ p2parse = pMsg->pszRawMsg + pMsg->offAfterPRI; /* point to start of text, after PRI */
/* do a sanity check on the version and eat it */
assert(p2parse[0] == '1' && p2parse[1] == ' ');
@@ -1411,7 +1348,7 @@ int parseRFCSyslogMsg(msg_t *pMsg, int flags)
memcpy(&pMsg->tTIMESTAMP, &pMsg->tRcvdAt, sizeof(struct syslogTime));
}
} else {
- dbgprintf("no TIMESTAMP detected!\n");
+ DBGPRINTF("no TIMESTAMP detected!\n");
bContParse = 0;
}
@@ -1483,8 +1420,8 @@ int parseLegacySyslogMsg(msg_t *pMsg, int flags)
BEGINfunc
assert(pMsg != NULL);
- assert(pMsg->pszUxTradMsg != NULL);
- p2parse = pMsg->pszUxTradMsg;
+ assert(pMsg->pszRawMsg != NULL);
+ p2parse = pMsg->pszRawMsg + pMsg->offAfterPRI; /* point to start of text, after PRI */
/* Check to see if msg contains a timestamp. We start by assuming
* that the message timestamp is the time of reciption (which we
@@ -1573,7 +1510,7 @@ int parseLegacySyslogMsg(msg_t *pMsg, int flags)
/* indeed, this smells like a TAG, so lets use it for this. We take
* the HOSTNAME from the sender system instead.
*/
- dbgprintf("HOSTNAME contains invalid characters, assuming it to be a TAG.\n");
+ DBGPRINTF("HOSTNAME contains invalid characters, assuming it to be a TAG.\n");
moveHOSTNAMEtoTAG(pMsg);
MsgSetHOSTNAME(pMsg, getRcvFrom(pMsg));
}
@@ -1598,22 +1535,21 @@ int parseLegacySyslogMsg(msg_t *pMsg, int flags)
* the records: the code is currently clean, but we could optimize it! */
if(!bTAGCharDetected) {
uchar *pszTAG;
- if(rsCStrConstruct(&pStrB) != RS_RET_OK)
+ if(cstrConstruct(&pStrB) != RS_RET_OK)
return 1;
rsCStrSetAllocIncrement(pStrB, 33);
pWork = pBuf;
iCnt = 0;
while(*p2parse && *p2parse != ':' && *p2parse != ' ') {
- rsCStrAppendChar(pStrB, *p2parse++);
+ cstrAppendChar(pStrB, *p2parse++);
++iCnt;
}
if(*p2parse == ':') {
++p2parse;
- rsCStrAppendChar(pStrB, ':');
+ cstrAppendChar(pStrB, ':');
}
- rsCStrFinish(pStrB);
-
- rsCStrConvSzStrAndDestruct(pStrB, &pszTAG, 1);
+ cstrFinalize(pStrB);
+ cstrConvSzStrAndDestruct(pStrB, &pszTAG, 1);
if(pszTAG == NULL)
{ /* rger, 2005-11-10: no TAG found - this implies that what
* we have considered to be the HOSTNAME is most probably the
@@ -1623,7 +1559,7 @@ int parseLegacySyslogMsg(msg_t *pMsg, int flags)
* the hostname. This situation is the standard case with
* stock BSD syslogd.
*/
- dbgprintf("No TAG in message, assuming that HOSTNAME is missing.\n");
+ DBGPRINTF("No TAG in message, assuming that HOSTNAME is missing.\n");
moveHOSTNAMEtoTAG(pMsg);
MsgSetHOSTNAME(pMsg, getRcvFrom(pMsg));
} else { /* we have a TAG, so we can happily set it ;) */
@@ -1639,7 +1575,7 @@ int parseLegacySyslogMsg(msg_t *pMsg, int flags)
*/
if(!(flags & INTERNAL_MSG))
{
- dbgprintf("HOSTNAME and TAG not parsed by user configuraton.\n");
+ DBGPRINTF("HOSTNAME and TAG not parsed by user configuraton.\n");
MsgSetHOSTNAME(pMsg, getRcvFrom(pMsg));
}
}
@@ -1698,9 +1634,10 @@ logmsg(msg_t *pMsg, int flags)
BEGINfunc
assert(pMsg != NULL);
- assert(pMsg->pszUxTradMsg != NULL);
- msg = (char*) pMsg->pszUxTradMsg;
- dbgprintf("logmsg: flags %x, from '%s', msg %s\n", flags, getRcvFrom(pMsg), msg);
+ assert(pMsg->pszRawMsg != NULL);
+
+ msg = (char*) pMsg->pszRawMsg + pMsg->offAfterPRI; /* point to start of text, after PRI */
+ DBGPRINTF("logmsg: flags %x, from '%s', msg %s\n", flags, getRcvFrom(pMsg), msg);
/* rger 2005-11-24 (happy thanksgiving!): we now need to check if we have
* a traditional syslog message or one formatted according to syslog-protocol.
@@ -1708,14 +1645,14 @@ logmsg(msg_t *pMsg, int flags)
* -protocol VERSION field for the detection.
*/
if(msg[0] == '1' && msg[1] == ' ') {
- dbgprintf("Message has syslog-protocol format.\n");
+ DBGPRINTF("Message has syslog-protocol format.\n");
setProtocolVersion(pMsg, 1);
if(parseRFCSyslogMsg(pMsg, flags) == 1) {
msgDestruct(&pMsg);
return;
}
} else { /* we have legacy syslog */
- dbgprintf("Message has legacy syslog format.\n");
+ DBGPRINTF("Message has legacy syslog format.\n");
setProtocolVersion(pMsg, 0);
if(parseLegacySyslogMsg(pMsg, flags) == 1) {
msgDestruct(&pMsg);
@@ -1755,7 +1692,6 @@ reapchild()
DEFFUNC_llExecFunc(flushRptdMsgsActions)
{
action_t *pAction = (action_t*) pData;
-
assert(pAction != NULL);
BEGINfunc
@@ -1765,7 +1701,7 @@ DEFFUNC_llExecFunc(flushRptdMsgsActions)
* in an acceptable way. -- rgerhards, 2008-09-16
*/
if (pAction->f_prevcount && time(NULL) >= REPEATTIME(pAction)) {
- dbgprintf("flush %s: repeated %d times, %d sec.\n",
+ DBGPRINTF("flush %s: repeated %d times, %d sec.\n",
module.GetStateName(pAction->pMod), pAction->f_prevcount,
repeatinterval[pAction->f_repeatcount]);
actionWriteToAction(pAction);
@@ -1778,20 +1714,12 @@ DEFFUNC_llExecFunc(flushRptdMsgsActions)
}
-/* This method flushes reapeat messages.
+/* This method flushes repeat messages.
*/
static void
doFlushRptdMsgs(void)
{
- register selector_t *f;
-
- /* see if we need to flush any "message repeated n times"...
- * Note that this interferes with objects running on other threads.
- * We are using appropriate locking inside the function to handle that.
- */
- for (f = Files; f != NULL ; f = f->f_next) {
- llExecFunc(&f->llActList, flushRptdMsgsActions, NULL);
- }
+ ruleset.IterateAllActions(flushRptdMsgsActions, NULL);
}
@@ -1801,9 +1729,9 @@ static void debug_switch()
if(debugging_on == 0) {
debugging_on = 1;
- dbgprintf("Switching debugging_on to true\n");
+ DBGPRINTF("Switching debugging_on to true\n");
} else {
- dbgprintf("Switching debugging_on to false\n");
+ DBGPRINTF("Switching debugging_on to false\n");
debugging_on = 0;
}
@@ -1947,7 +1875,7 @@ void legacyOptsParseTCP(char ch, char *arg)
* 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
+ * Note: we do not call DBGPRINTF() as this may cause us to block in case something
* with the threading is wrong.
*/
static void doDie(int sig)
@@ -1983,6 +1911,16 @@ freeAllDynMemForTermination(void)
}
+/* Finalize and destruct all actions.
+ */
+static inline void
+destructAllActions(void)
+{
+ ruleset.DestructAllActions();
+ bHaveMainQueue = 0; // flag that internal messages need to be temporarily stored
+}
+
+
/* die() is called when the program shall end. This typically only occurs
* during sigterm or during the initialization.
* As die() is intended to shutdown rsyslogd, it is
@@ -1996,7 +1934,7 @@ die(int sig)
{
char buf[256];
- dbgprintf("exiting on signal %d\n", sig);
+ DBGPRINTF("exiting on signal %d\n", sig);
/* IMPORTANT: we should close the inputs first, and THEN send our termination
* message. If we do it the other way around, logmsgInternal() may block on
@@ -2011,7 +1949,7 @@ die(int sig)
*/
/* close the inputs */
- dbgprintf("Terminating input threads...\n");
+ DBGPRINTF("Terminating input threads...\n");
thrdTerminateAll();
/* and THEN send the termination log message (see long comment above) */
@@ -2025,17 +1963,17 @@ die(int sig)
}
/* drain queue (if configured so) and stop main queue worker thread pool */
- dbgprintf("Terminating main queue...\n");
+ DBGPRINTF("Terminating main queue...\n");
qqueueDestruct(&pMsgQueue);
pMsgQueue = NULL;
/* Free ressources and close connections. This includes flushing any remaining
* repeated msgs.
*/
- dbgprintf("Terminating outputs...\n");
- freeSelectors();
+ DBGPRINTF("Terminating outputs...\n");
+ destructAllActions();
- dbgprintf("all primary multi-thread sources have been terminated - now doing aux cleanup...\n");
+ DBGPRINTF("all primary multi-thread sources have been terminated - now doing aux cleanup...\n");
/* rger 2005-02-22
* now clean up the in-memory structures. OK, the OS
* would also take care of that, but if we do it
@@ -2069,11 +2007,11 @@ die(int sig)
* rgerhards, 2007-08-03
* I have added some code now, but all that mod init/de-init should be moved to
* init, so that modules are unloaded and reloaded on HUP to. Eventually it should go
- * into freeSelectors() - but that needs to be seen. -- rgerhards, 2007-08-09
+ * into destructAllActions() - but that needs to be seen. -- rgerhards, 2007-08-09
*/
module.UnloadAndDestructAll(eMOD_LINK_ALL);
- dbgprintf("Clean shutdown completed, bye\n");
+ DBGPRINTF("Clean shutdown completed, bye\n");
/* dbgClassExit MUST be the last one, because it de-inits the debug system */
dbgClassExit();
@@ -2130,7 +2068,7 @@ static rsRetVal setMaxFiles(void __attribute__((unused)) *pVal, int iFiles)
iFiles, errStr, (long) maxFiles.rlim_max);
ABORT_FINALIZE(RS_RET_ERR_RLIM_NOFILE);
}
- dbgprintf("Max number of files set to %d [kernel max %ld].\n", iFiles, (long) maxFiles.rlim_max);
+ DBGPRINTF("Max number of files set to %d [kernel max %ld].\n", iFiles, (long) maxFiles.rlim_max);
finalize_it:
RETiRet;
@@ -2141,7 +2079,7 @@ finalize_it:
static rsRetVal setUmask(void __attribute__((unused)) *pVal, int iUmask)
{
umask(iUmask);
- dbgprintf("umask set to 0%3.3o.\n", iUmask);
+ DBGPRINTF("umask set to 0%3.3o.\n", iUmask);
return RS_RET_OK;
}
@@ -2197,56 +2135,6 @@ static void doDropPrivUid(int iUid)
}
-/* helper to freeSelectors(), used with llExecFunc() to flush
- * pending output. -- rgerhards, 2007-08-02
- * We do not need to lock the action object here as the processing
- * queue is already empty and no other threads are running when
- * we call this function. -- rgerhards, 2007-12-12
- */
-DEFFUNC_llExecFunc(freeSelectorsActions)
-{
- action_t *pAction = (action_t*) pData;
-
- assert(pAction != NULL);
-
- /* flush any pending output */
- if(pAction->f_prevcount) {
- actionWriteToAction(pAction);
- }
-
- return RS_RET_OK; /* never fails ;) */
-}
-
-
-/* Close all open log files and free selector descriptor array.
- */
-static void freeSelectors(void)
-{
- selector_t *f;
- selector_t *fPrev;
-
- if(Files != NULL) {
- dbgprintf("Freeing log structures.\n");
-
- for(f = Files ; f != NULL ; f = f->f_next) {
- llExecFunc(&f->llActList, freeSelectorsActions, NULL);
- }
-
- /* actions flushed and ready for destruction - so do that... */
- f = Files;
- while (f != NULL) {
- fPrev = f;
- f = f->f_next;
- selectorDestruct(fPrev);
- }
-
- /* Reflect the deletion of the selectors linked list. */
- Files = NULL;
- bHaveMainQueue = 0;
- }
-}
-
-
/* helper to generateConfigDAG, to print out all actions via
* the llExecFunc() facility.
* rgerhards, 2007-08-02
@@ -2323,14 +2211,14 @@ DEFFUNC_llExecFunc(generateConfigDAGAction)
static rsRetVal
generateConfigDAG(uchar *pszDAGFile)
{
- selector_t *f;
+ //rule_t *f;
FILE *fp;
int iActUnit = 1;
- int bHasFilter = 0; /* filter associated with this action unit? */
- int bHadFilter;
- int i;
+ //int bHasFilter = 0; /* filter associated with this action unit? */
+ //int bHadFilter;
+ //int i;
struct dag_info dagInfo;
- char *pszFilterName;
+ //char *pszFilterName;
char szConnectingNode[64];
DEFiRet;
@@ -2359,6 +2247,8 @@ generateConfigDAG(uchar *pszDAGFile)
strcpy(szConnectingNode, "act0_0");
dagInfo.bDiscarded = 0;
+/* TODO: re-enable! */
+#if 0
for(f = Files; f != NULL ; f = f->f_next) {
/* BSD-Style filters are currently ignored */
bHadFilter = bHasFilter;
@@ -2414,6 +2304,7 @@ generateConfigDAG(uchar *pszDAGFile)
++iActUnit;
}
+#endif
fprintf(fp, "\t%s -> act%d_0\n", szConnectingNode, iActUnit);
fprintf(fp, "\tact%d_0\t\t[label=discard shape=box]\n"
@@ -2425,20 +2316,6 @@ finalize_it:
}
-/* helper to dbPrintInitInfo, to print out all actions via
- * the llExecFunc() facility.
- * rgerhards, 2007-08-02
- */
-DEFFUNC_llExecFunc(dbgPrintInitInfoAction)
-{
- DEFiRet;
- iRet = actionDbgPrint((action_t*) pData);
- dbgprintf("\n");
-
- RETiRet;
-}
-
-
/* print debug information as part of init(). This pretty much
* outputs the whole config of rsyslogd. I've moved this code
* out of init() to clean it somewhat up.
@@ -2446,47 +2323,8 @@ DEFFUNC_llExecFunc(dbgPrintInitInfoAction)
*/
static void dbgPrintInitInfo(void)
{
- selector_t *f;
- int iSelNbr = 1;
- int i;
-
- dbgprintf("\nActive selectors:\n");
- for (f = Files; f != NULL ; f = f->f_next) {
- dbgprintf("Selector %d:\n", iSelNbr++);
- if(f->pCSProgNameComp != NULL)
- dbgprintf("tag: '%s'\n", rsCStrGetSzStrNoNULL(f->pCSProgNameComp));
- if(f->eHostnameCmpMode != HN_NO_COMP)
- dbgprintf("hostname: %s '%s'\n",
- f->eHostnameCmpMode == HN_COMP_MATCH ?
- "only" : "allbut",
- rsCStrGetSzStrNoNULL(f->pCSHostnameComp));
- if(f->f_filter_type == FILTER_PRI) {
- for (i = 0; i <= LOG_NFACILITIES; i++)
- if (f->f_filterData.f_pmask[i] == TABLE_NOPRI)
- dbgprintf(" X ");
- else
- dbgprintf("%2X ", f->f_filterData.f_pmask[i]);
- } else if(f->f_filter_type == FILTER_EXPR) {
- dbgprintf("EXPRESSION-BASED Filter: can currently not be displayed");
- } else {
- dbgprintf("PROPERTY-BASED Filter:\n");
- dbgprintf("\tProperty.: '%s'\n",
- rsCStrGetSzStrNoNULL(f->f_filterData.prop.pCSPropName));
- dbgprintf("\tOperation: ");
- if(f->f_filterData.prop.isNegated)
- dbgprintf("NOT ");
- dbgprintf("'%s'\n", getFIOPName(f->f_filterData.prop.operation));
- dbgprintf("\tValue....: '%s'\n",
- rsCStrGetSzStrNoNULL(f->f_filterData.prop.pCSCompValue));
- dbgprintf("\tAction...: ");
- }
-
- dbgprintf("\nActions:\n");
- llExecFunc(&f->llActList, dbgPrintInitInfoAction, NULL); /* actions */
-
- dbgprintf("\n");
- }
- dbgprintf("\n");
+ ruleset.DebugPrintAll();
+ DBGPRINTF("\n");
if(bDebugPrintTemplateList)
tplPrintList();
if(bDebugPrintModuleList)
@@ -2496,24 +2334,24 @@ static void dbgPrintInitInfo(void)
if(bDebugPrintCfSysLineHandlerList)
dbgPrintCfSysLineHandlers();
- dbgprintf("Messages with malicious PTR DNS Records are %sdropped.\n",
+ DBGPRINTF("Messages with malicious PTR DNS Records are %sdropped.\n",
glbl.GetDropMalPTRMsgs() ? "" : "not ");
- dbgprintf("Control characters are %sreplaced upon reception.\n",
+ DBGPRINTF("Control characters are %sreplaced upon reception.\n",
bEscapeCCOnRcv? "" : "not ");
if(bEscapeCCOnRcv)
- dbgprintf("Control character escape sequence prefix is '%c'.\n",
+ DBGPRINTF("Control character escape sequence prefix is '%c'.\n",
cCCEscapeChar);
- dbgprintf("Main queue size %d messages.\n", iMainMsgQueueSize);
- dbgprintf("Main queue worker threads: %d, wThread shutdown: %d, Perists every %d updates.\n",
+ DBGPRINTF("Main queue size %d messages.\n", iMainMsgQueueSize);
+ DBGPRINTF("Main queue worker threads: %d, wThread shutdown: %d, Perists every %d updates.\n",
iMainMsgQueueNumWorkers, iMainMsgQtoWrkShutdown, iMainMsgQPersistUpdCnt);
- dbgprintf("Main queue timeouts: shutdown: %d, action completion shutdown: %d, enq: %d\n",
+ DBGPRINTF("Main queue timeouts: shutdown: %d, action completion shutdown: %d, enq: %d\n",
iMainMsgQtoQShutdown, iMainMsgQtoActShutdown, iMainMsgQtoEnq);
- dbgprintf("Main queue watermarks: high: %d, low: %d, discard: %d, discard-severity: %d\n",
+ DBGPRINTF("Main queue watermarks: high: %d, low: %d, discard: %d, discard-severity: %d\n",
iMainMsgQHighWtrMark, iMainMsgQLowWtrMark, iMainMsgQDiscardMark, iMainMsgQDiscardSeverity);
- dbgprintf("Main queue save on shutdown %d, max disk space allowed %lld\n",
+ DBGPRINTF("Main queue save on shutdown %d, max disk space allowed %lld\n",
bMainMsgQSaveOnShutdown, iMainMsgQueMaxDiskSpace);
/* TODO: add
iActionRetryCount = 0;
@@ -2524,7 +2362,7 @@ static void dbgPrintInitInfo(void)
setQPROP(qqueueSetiMinMsgsPerWrkr, "$MainMsgQueueWorkerThreadMinimumMessages", 100);
setQPROP(qqueueSetbSaveOnShutdown, "$MainMsgQueueSaveOnShutdown", 1);
*/
- dbgprintf("Work Directory: '%s'.\n", glbl.GetWorkDir());
+ DBGPRINTF("Work Directory: '%s'.\n", glbl.GetWorkDir());
}
@@ -2547,7 +2385,7 @@ startInputModules(void)
/* activate here */
thrdCreate(pMod->mod.im.runInput, pMod->mod.im.afterRun);
} else {
- dbgprintf("module %lx will not run, iRet %d\n", (unsigned long) pMod, iRet);
+ DBGPRINTF("module %lx will not run, iRet %d\n", (unsigned long) pMod, iRet);
}
pMod = module.GetNxtType(pMod, eMOD_IN);
}
@@ -2565,13 +2403,14 @@ startInputModules(void)
static rsRetVal
init(void)
{
- DEFiRet;
rsRetVal localRet;
int iNbrActions;
int bHadConfigErr = 0;
+ ruleset_t *pRuleset;
char cbuf[BUFSIZ];
char bufStartUpMsg[512];
struct sigaction sigAct;
+ DEFiRet;
thrdTerminateAll(); /* stop all running input threads - TODO: reconsider location! */
@@ -2580,11 +2419,11 @@ init(void)
pDfltProgNameCmp = NULL;
eDfltHostnameCmpMode = HN_NO_COMP;
- dbgprintf("rsyslog %s - called init()\n", VERSION);
+ DBGPRINTF("rsyslog %s - called init()\n", VERSION);
/* delete the message queue, which also flushes all messages left over */
if(pMsgQueue != NULL) {
- dbgprintf("deleting main message queue\n");
+ DBGPRINTF("deleting main message queue\n");
qqueueDestruct(&pMsgQueue); /* delete pThis here! */
pMsgQueue = NULL;
}
@@ -2592,13 +2431,13 @@ init(void)
/* Close all open log files and free log descriptor array. This also frees
* all output-modules instance data.
*/
- freeSelectors();
+ destructAllActions();
/* Unload all non-static modules */
- dbgprintf("Unloading non-static modules.\n");
+ DBGPRINTF("Unloading non-static modules.\n");
module.UnloadAndDestructAll(eMOD_LINK_DYNAMIC_LOADED);
- dbgprintf("Clearing templates.\n");
+ DBGPRINTF("Clearing templates.\n");
tplDeleteNew();
/* re-setting values to defaults (where applicable) */
@@ -2613,6 +2452,11 @@ init(void)
conf.ReInitConf();
+ /* construct the default ruleset */
+ ruleset.Construct(&pRuleset);
+ ruleset.SetName(pRuleset, UCHAR_CONSTANT("RSYSLOG_DefaultRuleset"));
+ ruleset.ConstructFinalize(pRuleset);
+
/* open the configuration file */
localRet = conf.processConfFile(ConfFile);
CHKiRet(conf.GetNbrActActions(&iNbrActions));
@@ -2634,23 +2478,23 @@ init(void)
* We ignore any errors while doing this - we would be lost anyhow...
*/
errmsg.LogError(0, NO_ERRCODE, "EMERGENCY CONFIGURATION ACTIVATED - fix rsyslog config file!");
- selector_t *f = NULL;
/* note: we previously used _POSIY_TTY_NAME_MAX+1, but this turned out to be
* too low on linux... :-S -- rgerhards, 2008-07-28
*/
char szTTYNameBuf[128];
- conf.cfline((uchar*)"*.ERR\t" _PATH_CONSOLE, &f);
- conf.cfline((uchar*)"syslog.*\t" _PATH_CONSOLE, &f);
- conf.cfline((uchar*)"*.PANIC\t*", &f);
- conf.cfline((uchar*)"syslog.*\troot", &f);
+ rule_t *pRule = NULL; /* initialization to NULL is *vitally* important! */
+ conf.cfline(UCHAR_CONSTANT("*.ERR\t" _PATH_CONSOLE), &pRule);
+ conf.cfline(UCHAR_CONSTANT("syslog.*\t" _PATH_CONSOLE), &pRule);
+ conf.cfline(UCHAR_CONSTANT("*.PANIC\t*"), &pRule);
+ conf.cfline(UCHAR_CONSTANT("syslog.*\troot"), &pRule);
if(ttyname_r(0, szTTYNameBuf, sizeof(szTTYNameBuf)) == 0) {
snprintf(cbuf,sizeof(cbuf), "*.*\t%s", szTTYNameBuf);
- conf.cfline((uchar*)cbuf, &f);
+ conf.cfline((uchar*)cbuf, &pRule);
} else {
- dbgprintf("error %d obtaining controlling terminal, not using that emergency rule\n", errno);
+ DBGPRINTF("error %d obtaining controlling terminal, not using that emergency rule\n", errno);
}
- selectorAddList(f);
+ ruleset.AddRule(ruleset.GetCurrent(), &pRule);
}
legacyOptsHook();
@@ -2730,6 +2574,7 @@ init(void)
setQPROP(qqueueSetiDeqBatchSize, "$MainMsgQueueDequeueBatchSize", iMainMsgQueDeqBatchSize);
setQPROPstr(qqueueSetFilePrefix, "$MainMsgQueueFileName", pszMainMsgQFName);
setQPROP(qqueueSetiPersistUpdCnt, "$MainMsgQueueCheckpointInterval", iMainMsgQPersistUpdCnt);
+ setQPROP(qqueueSetbSyncQueueFiles, "$MainMsgQueueSyncQueueFiles", bMainMsgQSyncQeueFiles);
setQPROP(qqueueSettoQShutdown, "$MainMsgQueueTimeoutShutdown", iMainMsgQtoQShutdown );
setQPROP(qqueueSettoActShutdown, "$MainMsgQueueTimeoutActionCompletion", iMainMsgQtoActShutdown);
setQPROP(qqueueSettoWrkShutdown, "$MainMsgQueueWorkerTimeoutThreadShutdown", iMainMsgQtoWrkShutdown);
@@ -2755,7 +2600,7 @@ init(void)
}
bHaveMainQueue = (MainMsgQueType == QUEUETYPE_DIRECT) ? 0 : 1;
- dbgprintf("Main processing queue is initialized and running\n");
+ DBGPRINTF("Main processing queue is initialized and running\n");
/* the output part and the queue is now ready to run. So it is a good time
* to start the inputs. Please note that the net code above should be
@@ -2782,56 +2627,55 @@ init(void)
sigAct.sa_handler = sighup_handler;
sigaction(SIGHUP, &sigAct, NULL);
- dbgprintf(" (re)started.\n");
+ DBGPRINTF(" (re)started.\n");
finalize_it:
RETiRet;
}
-/* add a completely-processed selector (after config line parsing) to
- * the linked list of selectors. We now need to check
- * if it has any actions associated and, if so, link it to the linked
- * list. If it has nothing associated with it, we can simply discard
- * it.
- * We have one special case during initialization: then, the current
- * selector is NULL, which means we do not need to care about it at
- * all. -- rgerhards, 2007-08-01
+/* Switch the default ruleset (that, what servcies bind to if nothing specific
+ * is specified).
+ * rgerhards, 2009-06-12
*/
-rsRetVal
-selectorAddList(selector_t *f)
+static rsRetVal
+setDefaultRuleset(void __attribute__((unused)) *pVal, uchar *pszName)
{
DEFiRet;
- int iActionCnt;
- static selector_t *nextp = NULL; /* TODO: make this go away (see comment below) */
+ CHKiRet(ruleset.SetDefaultRuleset(pszName));
- if(f != NULL) {
- CHKiRet(llGetNumElts(&f->llActList, &iActionCnt));
- if(iActionCnt == 0) {
- errmsg.LogError(0, NO_ERRCODE, "warning: selector line without actions will be discarded");
- selectorDestruct(f);
- } else {
- /* successfully created an entry */
- dbgprintf("selector line successfully processed\n");
- /* TODO: we should use the linked list class for the selector list, else we need to add globals
- * ... well nextp could be added temporarily...
- * Thanks to varmojfekoj for having the idea to just use "Files" to make this
- * code work. I had actually forgotten to fix the code here before moving to 1.18.0.
- * And, of course, I also did not migrate the selector_t structure to the linked list class.
- * However, that should still be one of the very next things to happen.
- * rgerhards, 2007-08-06
- */
- if(Files == NULL) {
- Files = f;
- } else {
- nextp->f_next = f;
- }
- nextp = f;
- }
+finalize_it:
+ free(pszName); /* no longer needed */
+ RETiRet;
+}
+
+
+/* Switch to either an already existing rule set or start a new one. The
+ * named rule set becomes the new "current" rule set (what means that new
+ * actions are added to it).
+ * rgerhards, 2009-06-12
+ */
+static rsRetVal
+setCurrRuleset(void __attribute__((unused)) *pVal, uchar *pszName)
+{
+ ruleset_t *pRuleset;
+ rsRetVal localRet;
+ DEFiRet;
+
+ localRet = ruleset.SetCurrRuleset(pszName);
+
+ if(localRet == RS_RET_NOT_FOUND) {
+ DBGPRINTF("begin new current rule set '%s'\n", pszName);
+ CHKiRet(ruleset.Construct(&pRuleset));
+ CHKiRet(ruleset.SetName(pRuleset, pszName));
+ CHKiRet(ruleset.ConstructFinalize(pRuleset));
+ } else {
+ ABORT_FINALIZE(localRet);
}
finalize_it:
+ free(pszName); /* no longer needed */
RETiRet;
}
@@ -2845,16 +2689,16 @@ static rsRetVal setMainMsgQueType(void __attribute__((unused)) *pVal, uchar *psz
if (!strcasecmp((char *) pszType, "fixedarray")) {
MainMsgQueType = QUEUETYPE_FIXED_ARRAY;
- dbgprintf("main message queue type set to FIXED_ARRAY\n");
+ DBGPRINTF("main message queue type set to FIXED_ARRAY\n");
} else if (!strcasecmp((char *) pszType, "linkedlist")) {
MainMsgQueType = QUEUETYPE_LINKEDLIST;
- dbgprintf("main message queue type set to LINKEDLIST\n");
+ DBGPRINTF("main message queue type set to LINKEDLIST\n");
} else if (!strcasecmp((char *) pszType, "disk")) {
MainMsgQueType = QUEUETYPE_DISK;
- dbgprintf("main message queue type set to DISK\n");
+ DBGPRINTF("main message queue type set to DISK\n");
} else if (!strcasecmp((char *) pszType, "direct")) {
MainMsgQueType = QUEUETYPE_DIRECT;
- dbgprintf("main message queue type set to DIRECT (no queueing at all)\n");
+ DBGPRINTF("main message queue type set to DIRECT (no queueing at all)\n");
} else {
errmsg.LogError(0, RS_RET_INVALID_PARAMS, "unknown mainmessagequeuetype parameter: %s", (char *) pszType);
iRet = RS_RET_INVALID_PARAMS;
@@ -2923,7 +2767,6 @@ DEFFUNC_llExecFunc(doHUPActions)
static inline void
doHUP(void)
{
- selector_t *f;
char buf[512];
snprintf(buf, sizeof(buf) / sizeof(char),
@@ -2938,9 +2781,7 @@ doHUP(void)
init(); /* main queue is stopped as part of init() */
} else {
DBGPRINTF("Received SIGHUP, configured to be a non-restart type of HUP - notifying actions.\n");
- for(f = Files; f != NULL ; f = f->f_next) {
- llExecFunc(&f->llActList, doHUPActions, NULL);
- }
+ ruleset.IterateAllActions(doHUPActions, NULL);
}
}
@@ -2971,7 +2812,8 @@ mainloop(void)
* 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 = (bReduceRepeatMsgs == 1) ? TIMERINTVL : 86400 /*1 day*/;
+ //tvSelectTimeout.tv_sec = (bReduceRepeatMsgs == 1) ? TIMERINTVL : 86400 /*1 day*/;
+tvSelectTimeout.tv_sec = 5; // TESTING ONLY!!! TODO: change back!!!
tvSelectTimeout.tv_usec = 0;
select(1, NULL, NULL, NULL, &tvSelectTimeout);
if(bFinished)
@@ -3006,49 +2848,11 @@ mainloop(void)
bHadHUP = 0;
continue;
}
+ execScheduled(); /* handle Apc calls (if any) */
}
ENDfunc
}
-/* If user is not root, prints warnings or even exits
- * TODO: check all dynafiles for write permission
- * ... but it is probably better to wait here until we have
- * a module interface - rgerhards, 2007-07-23
- */
-static void checkPermissions()
-{
-#if 0
- /* TODO: this function must either be redone or removed - now with the input modules,
- * there is no such simple check we can do. What we can check, however, is if there is
- * any input module active and terminate, if not. -- rgerhards, 2007-12-26
- */
- /* we are not root */
- if (geteuid() != 0)
- {
- fputs("WARNING: Local messages will not be logged! If you want to log them, run rsyslog as root.\n",stderr);
-#ifdef SYSLOG_INET
- /* udp enabled and port number less than or equal to 1024 */
- if ( AcceptRemote && (atoi(LogPort) <= 1024) )
- fprintf(stderr, "WARNING: Will not listen on UDP port %s. Use port number higher than 1024 or run rsyslog as root!\n", LogPort);
-
- /* tcp enabled and port number less or equal to 1024 */
- if( bEnableTCP && (atoi(TCPLstnPort) <= 1024) )
- fprintf(stderr, "WARNING: Will not listen on TCP port %s. Use port number higher than 1024 or run rsyslog as root!\n", TCPLstnPort);
-
- /* Neither explicit high UDP port nor explicit high TCP port.
- * It is useless to run anymore */
- if( !(AcceptRemote && (atoi(LogPort) > 1024)) && !( bEnableTCP && (atoi(TCPLstnPort) > 1024)) )
- {
-#endif
- fprintf(stderr, "ERROR: Nothing to log, no reason to run. Please run rsyslog as root.\n");
- exit(EXIT_FAILURE);
-#ifdef SYSLOG_INET
- }
-#endif
- }
-#endif
-}
-
/* load build-in modules
* very first version begun on 2007-07-23 by rgerhards
@@ -3057,23 +2861,23 @@ static rsRetVal loadBuildInModules(void)
{
DEFiRet;
- if((iRet = module.doModInit(modInitFile, (uchar*) "builtin-file", NULL)) != RS_RET_OK) {
+ if((iRet = module.doModInit(modInitFile, UCHAR_CONSTANT("builtin-file"), NULL)) != RS_RET_OK) {
RETiRet;
}
#ifdef SYSLOG_INET
- if((iRet = module.doModInit(modInitFwd, (uchar*) "builtin-fwd", NULL)) != RS_RET_OK) {
+ if((iRet = module.doModInit(modInitFwd, UCHAR_CONSTANT("builtin-fwd"), NULL)) != RS_RET_OK) {
RETiRet;
}
#endif
- if((iRet = module.doModInit(modInitShell, (uchar*) "builtin-shell", NULL)) != RS_RET_OK) {
+ if((iRet = module.doModInit(modInitShell, UCHAR_CONSTANT("builtin-shell"), NULL)) != RS_RET_OK) {
RETiRet;
}
- if((iRet = module.doModInit(modInitDiscard, (uchar*) "builtin-discard", NULL)) != RS_RET_OK) {
+ if((iRet = module.doModInit(modInitDiscard, UCHAR_CONSTANT("builtin-discard"), NULL)) != RS_RET_OK) {
RETiRet;
}
/* dirty, but this must be for the time being: the usrmsg module must always be
- * loaded as last module. This is because it processes any time of action selector.
+ * loaded as last module. This is because it processes any type of action selector.
* If we load it before other modules, these others will never have a chance of
* working with the config file. We may change that implementation so that a user name
* must start with an alnum, that would definitely help (but would it break backwards
@@ -3081,8 +2885,7 @@ static rsRetVal loadBuildInModules(void)
* User names now must begin with:
* [a-zA-Z0-9_.]
*/
- if((iRet = module.doModInit(modInitUsrMsg, (uchar*) "builtin-usrmsg", NULL)) != RS_RET_OK)
- RETiRet;
+ CHKiRet(module.doModInit(modInitUsrMsg, (uchar*) "builtin-usrmsg", NULL));
/* ok, initialization of the command handler probably does not 100% belong right in
* this space here. However, with the current design, this is actually quite a good
@@ -3092,6 +2895,8 @@ static rsRetVal loadBuildInModules(void)
* This, I think, is the right thing to do. -- rgerhards, 2007-07-31
*/
CHKiRet(regCfSysLineHdlr((uchar *)"actionresumeretrycount", 0, eCmdHdlrInt, NULL, &glbliActionResumeRetryCount, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"defaultruleset", 0, eCmdHdlrGetWord, setDefaultRuleset, NULL, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"ruleset", 0, eCmdHdlrGetWord, setCurrRuleset, NULL, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuefilename", 0, eCmdHdlrGetWord, NULL, &pszMainMsgQFName, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuesize", 0, eCmdHdlrInt, NULL, &iMainMsgQueueSize, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuehighwatermark", 0, eCmdHdlrInt, NULL, &iMainMsgQHighWtrMark, NULL));
@@ -3099,6 +2904,7 @@ static rsRetVal loadBuildInModules(void)
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuediscardmark", 0, eCmdHdlrInt, NULL, &iMainMsgQDiscardMark, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuediscardseverity", 0, eCmdHdlrSeverity, NULL, &iMainMsgQDiscardSeverity, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuecheckpointinterval", 0, eCmdHdlrInt, NULL, &iMainMsgQPersistUpdCnt, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuesyncqueuefiles", 0, eCmdHdlrBinary, NULL, &bMainMsgQSyncQeueFiles, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuetype", 0, eCmdHdlrGetWord, setMainMsgQueType, NULL, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueueworkerthreads", 0, eCmdHdlrInt, NULL, &iMainMsgQueueNumWorkers, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuetimeoutshutdown", 0, eCmdHdlrInt, NULL, &iMainMsgQtoQShutdown, NULL));
@@ -3234,7 +3040,7 @@ static rsRetVal mainThread()
CHKiRet(init());
if(Debug && debugging_on) {
- dbgprintf("Debugging enabled, SIGUSR1 to turn off debugging.\n");
+ DBGPRINTF("Debugging enabled, SIGUSR1 to turn off debugging.\n");
}
/* Send a signal to the parent so it can terminate.
*/
@@ -3266,7 +3072,7 @@ static rsRetVal mainThread()
* do the init() and then restart things.
* rgerhards, 2005-10-24
*/
- dbgprintf("initialization completed, transitioning to regular run mode\n");
+ DBGPRINTF("initialization completed, transitioning to regular run mode\n");
mainloop();
@@ -3297,14 +3103,14 @@ InitGlobalClasses(void)
CHKiRet(objUse(errmsg, CORE_COMPONENT));
pErrObj = "module";
CHKiRet(objUse(module, CORE_COMPONENT));
- pErrObj = "var";
- CHKiRet(objUse(var, CORE_COMPONENT));
pErrObj = "datetime";
CHKiRet(objUse(datetime, CORE_COMPONENT));
- pErrObj = "vm";
- CHKiRet(objUse(vm, CORE_COMPONENT));
pErrObj = "expr";
CHKiRet(objUse(expr, CORE_COMPONENT));
+ pErrObj = "rule";
+ CHKiRet(objUse(rule, CORE_COMPONENT));
+ pErrObj = "ruleset";
+ CHKiRet(objUse(ruleset, CORE_COMPONENT));
pErrObj = "conf";
CHKiRet(objUse(conf, CORE_COMPONENT));
@@ -3348,33 +3154,13 @@ GlobalClassExit(void)
/* first, release everything we used ourself */
objRelease(net, LM_NET_FILENAME);/* TODO: the dependency on net shall go away! -- rgerhards, 2008-03-07 */
objRelease(conf, CORE_COMPONENT);
+ objRelease(ruleset, CORE_COMPONENT);
+ objRelease(rule, CORE_COMPONENT);
objRelease(expr, CORE_COMPONENT);
vmClassExit(); /* this is hack, currently core_modules do not get this automatically called */
- objRelease(vm, CORE_COMPONENT);
- objRelease(var, CORE_COMPONENT);
objRelease(datetime, CORE_COMPONENT);
/* TODO: implement the rest of the deinit */
-#if 0
- CHKiRet(datetimeClassInit(NULL));
- CHKiRet(msgClassInit(NULL));
- CHKiRet(strmClassInit(NULL));
- CHKiRet(wtiClassInit(NULL));
- CHKiRet(wtpClassInit(NULL));
- CHKiRet(qqueueClassInit(NULL));
- CHKiRet(vmstkClassInit(NULL));
- CHKiRet(sysvarClassInit(NULL));
- CHKiRet(vmClassInit(NULL));
- CHKiRet(vmopClassInit(NULL));
- CHKiRet(vmprgClassInit(NULL));
- CHKiRet(ctok_tokenClassInit(NULL));
- CHKiRet(ctokClassInit(NULL));
- CHKiRet(exprClassInit(NULL));
-
- /* dummy "classes" */
- CHKiRet(actionClassInit());
- CHKiRet(templateInit());
-#endif
/* dummy "classes */
strExit();
@@ -3468,12 +3254,11 @@ doGlblProcessInit(void)
int i;
DEFiRet;
- checkPermissions();
thrdInit();
if( !(Debug || NoFork) )
{
- dbgprintf("Checking pidfile.\n");
+ DBGPRINTF("Checking pidfile.\n");
if (!check_pid(PidFile))
{
memset(&sigAct, 0, sizeof (sigAct));
@@ -3509,7 +3294,7 @@ doGlblProcessInit(void)
}
/* tuck my process id away */
- dbgprintf("Writing pidfile %s.\n", PidFile);
+ DBGPRINTF("Writing pidfile %s.\n", PidFile);
if (!check_pid(PidFile))
{
if (!write_pid(PidFile))
@@ -3655,7 +3440,7 @@ int realMain(int argc, char **argv)
if ((argc -= optind))
usage();
- dbgprintf("rsyslogd %s startup, compatibility mode %d, module path '%s'\n",
+ DBGPRINTF("rsyslogd %s startup, compatibility mode %d, module path '%s'\n",
VERSION, iCompatibilityMode, glblModPath == NULL ? "" : (char*)glblModPath);
/* we are done with the initial option parsing and processing. Now we init the system. */
@@ -3737,7 +3522,7 @@ int realMain(int argc, char **argv)
/* END core initializations - we now come back to carrying out command line options*/
while((iRet = bufOptRemove(&ch, &arg)) == RS_RET_OK) {
- dbgprintf("deque option %c, optarg '%s'\n", ch, (arg == NULL) ? "" : arg);
+ DBGPRINTF("deque option %c, optarg '%s'\n", ch, (arg == NULL) ? "" : arg);
switch((char)ch) {
case '4':
glbl.SetDefPFFamily(PF_INET);
diff --git a/tools/syslogd.h b/tools/syslogd.h
index 8b9bd131..3dfdbe2b 100644
--- a/tools/syslogd.h
+++ b/tools/syslogd.h
@@ -29,66 +29,6 @@
#include "linkedlist.h"
#include "expr.h"
-
-#ifndef _PATH_CONSOLE
-#define _PATH_CONSOLE "/dev/console"
-#endif
-
-
-/* This structure represents the files that will have log
- * copies printed.
- * RGerhards 2004-11-08: Each instance of the filed structure
- * describes what I call an "output channel". This is important
- * to mention as we now allow database connections to be
- * present in the filed structure. If helps immensely, if we
- * think of it as the abstraction of an output channel.
- * rgerhards, 2005-10-26: The structure below provides ample
- * opportunity for non-thread-safety. Each of the variable
- * accesses must be carefully evaluated, many of them probably
- * be guarded by mutexes. But beware of deadlocks...
- * rgerhards, 2007-08-01: as you can see, the structure has shrunk pretty much. I will
- * remove some of the comments some time. It's still the structure that controls much
- * of the processing that goes on in syslogd, but it now has lots of helpers.
- */
-struct filed {
- struct filed *f_next; /* next in linked list */
- /* filter properties */
- enum {
- FILTER_PRI = 0, /* traditional PRI based filer */
- FILTER_PROP = 1, /* extended filter, property based */
- FILTER_EXPR = 2 /* extended filter, expression based */
- } f_filter_type;
- EHostnameCmpMode eHostnameCmpMode;
- cstr_t *pCSHostnameComp; /* hostname to check */
- cstr_t *pCSProgNameComp; /* tag to check or NULL, if not to be checked */
- union {
- u_char f_pmask[LOG_NFACILITIES+1]; /* priority mask */
- struct {
- cstr_t *pCSPropName;
- enum {
- FIOP_NOP = 0, /* do not use - No Operation */
- FIOP_CONTAINS = 1, /* contains string? */
- FIOP_ISEQUAL = 2, /* is (exactly) equal? */
- FIOP_STARTSWITH = 3, /* starts with a string? */
- FIOP_REGEX = 4, /* matches a (BRE) regular expression? */
- FIOP_EREREGEX = 5 /* matches a ERE regular expression? */
- } operation;
- regex_t *regex_cache; /* cache for compiled REs, if such are used */
- cstr_t *pCSCompValue; /* value to "compare" against */
- char isNegated; /* actually a boolean ;) */
- } prop;
- expr_t *f_expr; /* expression object */
- } f_filterData;
-
- linkedList_t llActList; /* list of configured actions */
-};
-
-
-#include "net.h" /* TODO: remove when you remoe isAllowedSender from here! */
-void untty(void);
-rsRetVal selectorConstruct(selector_t **ppThis);
-rsRetVal selectorDestruct(void *pVal);
-rsRetVal selectorAddList(selector_t *f);
/* the following prototypes should go away once we have an input
* module interface -- rgerhards, 2007-12-12
*/
diff --git a/tools/zpipe.c b/tools/zpipe.c
new file mode 100644
index 00000000..bde6c5c1
--- /dev/null
+++ b/tools/zpipe.c
@@ -0,0 +1,254 @@
+/* zpipe.c: example of proper use of zlib's inflate() and deflate()
+ Not copyrighted -- provided to the public domain
+ Version 1.5 11 December 2005 Mark Adler
+ Version 2.0 03 June 2009 Rainer Gerhards */
+
+/* RSYSLOG NOTE:
+ * This file is beeing distributed as part of rsyslog, but is just an
+ * add-on. Most importantly, rsyslog's copyright does not apply but
+ * rather the (non-) copyright stated above.
+ */
+
+/* Version history:
+ 1.0 30 Oct 2004 First version
+ 1.1 8 Nov 2004 Add void casting for unused return values
+ Use switch statement for inflate() return values
+ 1.2 9 Nov 2004 Add assertions to document zlib guarantees
+ 1.3 6 Apr 2005 Remove incorrect assertion in inf()
+ 1.4 11 Dec 2005 Add hack to avoid MSDOS end-of-line conversions
+ Avoid some compiler warnings for input and output buffers
+ 2.0 03 Jun 2009 Add hack to support multiple deflate records inside a single
+ file on inflate. This is needed in order to support reading
+ files created by rsyslog's zip output writer.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "zlib.h"
+
+#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
+# include <fcntl.h>
+# include <io.h>
+# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
+#else
+# define SET_BINARY_MODE(file)
+#endif
+
+#define CHUNK 16384
+
+/* Compress from file source to file dest until EOF on source.
+ def() returns Z_OK on success, Z_MEM_ERROR if memory could not be
+ allocated for processing, Z_STREAM_ERROR if an invalid compression
+ level is supplied, Z_VERSION_ERROR if the version of zlib.h and the
+ version of the library linked do not match, or Z_ERRNO if there is
+ an error reading or writing the files. */
+int def(FILE *source, FILE *dest, int level)
+{
+ int ret, flush;
+ unsigned have;
+ z_stream strm;
+ unsigned char in[CHUNK];
+ unsigned char out[CHUNK];
+
+ /* allocate deflate state */
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ ret = deflateInit(&strm, level);
+ if (ret != Z_OK)
+ return ret;
+
+ /* compress until end of file */
+ do {
+ strm.avail_in = fread(in, 1, CHUNK, source);
+ if (ferror(source)) {
+ (void)deflateEnd(&strm);
+ return Z_ERRNO;
+ }
+ flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
+ strm.next_in = in;
+
+ /* run deflate() on input until output buffer not full, finish
+ compression if all of source has been read in */
+ do {
+ strm.avail_out = CHUNK;
+ strm.next_out = out;
+ ret = deflate(&strm, flush); /* no bad return value */
+ assert(ret != Z_STREAM_ERROR); /* state not clobbered */
+ have = CHUNK - strm.avail_out;
+ if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
+ (void)deflateEnd(&strm);
+ return Z_ERRNO;
+ }
+ } while (strm.avail_out == 0);
+ assert(strm.avail_in == 0); /* all input will be used */
+
+ /* done when last data in file processed */
+ } while (flush != Z_FINISH);
+ assert(ret == Z_STREAM_END); /* stream will be complete */
+
+ /* clean up and return */
+ (void)deflateEnd(&strm);
+ return Z_OK;
+}
+
+
+/* initialize stream for deflating (we need this in case of
+ * multiple records.
+ * rgerhards, 2009-06-03
+ */
+int doInflateInit(z_stream *strm)
+{
+ int ret;
+
+ /* allocate inflate state */
+ strm->zalloc = Z_NULL;
+ strm->zfree = Z_NULL;
+ strm->opaque = Z_NULL;
+ strm->avail_in = 0;
+ strm->next_in = Z_NULL;
+ ret = inflateInit(strm);
+ return ret;
+}
+
+
+/* Decompress from file source to file dest until stream ends or EOF.
+ inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be
+ allocated for processing, Z_DATA_ERROR if the deflate data is
+ invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and
+ the version of the library linked do not match, or Z_ERRNO if there
+ is an error reading or writing the files. */
+int inf(FILE *source, FILE *dest)
+{
+ int ret;
+ unsigned have;
+ z_stream strm;
+ unsigned char in[CHUNK];
+ int len;
+ unsigned char *next_in_save;
+ unsigned char out[CHUNK];
+
+ ret = doInflateInit(&strm);
+ if (ret != Z_OK)
+ return ret;
+
+ /* decompress until deflate stream ends or end of file */
+ do {
+ len = fread(in, 1, CHUNK, source);
+ if (ferror(source)) {
+ (void)inflateEnd(&strm);
+ return Z_ERRNO;
+ }
+ if (len == 0) {
+ break;
+ }
+ strm.avail_in = len;
+ strm.next_in = in;
+
+ /* run inflate() on input until output buffer not full */
+ strm.avail_out = CHUNK;
+ strm.next_out = out;
+ do {
+ /* fprintf(stderr, "---inner LOOP---, avail_in %d, avail_out %d Byte 0: %x, 1: %x\n", strm.avail_in, strm.avail_out, *strm.next_in, *(strm.next_in+1));*/
+ do {
+ ret = inflate(&strm, Z_NO_FLUSH);
+ assert(ret != Z_STREAM_ERROR); /* state not clobbered */
+ switch (ret) {
+ case Z_NEED_DICT:
+ ret = Z_DATA_ERROR; /* and fall through */
+ case Z_DATA_ERROR:
+ case Z_MEM_ERROR:
+ (void)inflateEnd(&strm);
+ return ret;
+ }
+ have = CHUNK - strm.avail_out;
+ if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
+ (void)inflateEnd(&strm);
+ return Z_ERRNO;
+ }
+ } while (strm.avail_out == 0);
+ /* handle the case that more than one deflate record is contained
+ * in a single file. -- rgerhards, 2009-06-03
+ */
+ if(ret == Z_STREAM_END) {
+ len -= strm.total_in;
+ if(len > 0) {
+ next_in_save = strm.next_in;
+ (void)inflateEnd(&strm);
+ ret = doInflateInit(&strm);
+ if (ret != Z_OK)
+ return ret;
+ strm.avail_in = len;
+ strm.next_in = next_in_save;
+ strm.avail_out = CHUNK;
+ strm.next_out = out;
+ ret = Z_OK; /* continue outer loop */
+ }
+ }
+ } while (strm.avail_in > 0);
+
+ /* done when inflate() says it's done */
+ } while (ret != Z_STREAM_END);
+
+ /* clean up and return */
+ (void)inflateEnd(&strm);
+ return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
+}
+
+/* report a zlib or i/o error */
+void zerr(int ret)
+{
+ fputs("zpipe: ", stdout);
+ switch (ret) {
+ case Z_ERRNO:
+ if (ferror(stdin))
+ fputs("error reading stdin\n", stdout);
+ if (ferror(stdout))
+ fputs("error writing stdout\n", stdout);
+ break;
+ case Z_STREAM_ERROR:
+ fputs("invalid compression level\n", stdout);
+ break;
+ case Z_DATA_ERROR:
+ fputs("invalid or incomplete deflate data\n", stdout);
+ break;
+ case Z_MEM_ERROR:
+ fputs("out of memory\n", stdout);
+ break;
+ case Z_VERSION_ERROR:
+ fputs("zlib version mismatch!\n", stdout);
+ }
+}
+
+/* compress or decompress from stdin to stdout */
+int main(int argc, char **argv)
+{
+ int ret;
+
+ /* avoid end-of-line conversions */
+ SET_BINARY_MODE(stdin);
+ SET_BINARY_MODE(stdout);
+
+ /* do compression if no arguments */
+ if (argc == 1) {
+ ret = def(stdin, stdout, Z_DEFAULT_COMPRESSION);
+ if (ret != Z_OK)
+ zerr(ret);
+ return ret;
+ }
+
+ /* do decompression if -d specified */
+ else if (argc == 2 && strcmp(argv[1], "-d") == 0) {
+ ret = inf(stdin, stdout);
+ if (ret != Z_OK)
+ zerr(ret);
+ return ret;
+ }
+
+ /* otherwise, report usage */
+ else {
+ fputs("zpipe usage: zpipe [-d] < source > dest\n", stdout);
+ return 1;
+ }
+}