summaryrefslogtreecommitdiffstats
path: root/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'runtime')
-rw-r--r--runtime/Makefile.am20
-rw-r--r--runtime/apc.c400
-rw-r--r--runtime/apc.h56
-rw-r--r--runtime/atomic.h7
-rw-r--r--runtime/batch.h72
-rw-r--r--runtime/cfsysline.c6
-rw-r--r--runtime/conf.c147
-rw-r--r--runtime/conf.h12
-rw-r--r--runtime/ctok.c17
-rw-r--r--runtime/datetime.c303
-rw-r--r--runtime/datetime.h16
-rw-r--r--runtime/debug.c71
-rw-r--r--runtime/debug.h3
-rw-r--r--runtime/glbl.c73
-rw-r--r--runtime/glbl.h13
-rw-r--r--runtime/linkedlist.c2
-rw-r--r--runtime/module-template.h11
-rw-r--r--runtime/modules.c67
-rw-r--r--runtime/modules.h3
-rw-r--r--runtime/msg.c2149
-rw-r--r--runtime/msg.h144
-rw-r--r--runtime/nsd_gtls.c34
-rw-r--r--runtime/obj-types.h16
-rw-r--r--runtime/obj.c145
-rw-r--r--runtime/obj.h8
-rw-r--r--runtime/parser.c95
-rw-r--r--runtime/prop.c247
-rw-r--r--runtime/prop.h58
-rw-r--r--runtime/queue.c1726
-rw-r--r--runtime/queue.h72
-rw-r--r--runtime/rsyslog.c33
-rw-r--r--runtime/rsyslog.h127
-rw-r--r--runtime/rule.c449
-rw-r--r--runtime/rule.h77
-rw-r--r--runtime/ruleset.c451
-rw-r--r--runtime/ruleset.h60
-rw-r--r--runtime/srUtils.h9
-rw-r--r--runtime/srutils.c31
-rw-r--r--runtime/stream.c990
-rw-r--r--runtime/stream.h136
-rw-r--r--runtime/stringbuf.c140
-rw-r--r--runtime/stringbuf.h125
-rw-r--r--runtime/syslogd-types.h37
-rw-r--r--runtime/sysvar.c2
-rw-r--r--runtime/unicode-helper.h25
-rw-r--r--runtime/vm.c12
-rw-r--r--runtime/vmop.c2
-rw-r--r--runtime/wti.c369
-rw-r--r--runtime/wti.h28
-rw-r--r--runtime/wtp.c377
-rw-r--r--runtime/wtp.h46
-rw-r--r--runtime/zlibw.c125
-rw-r--r--runtime/zlibw.h46
53 files changed, 6739 insertions, 2951 deletions
diff --git a/runtime/Makefile.am b/runtime/Makefile.am
index eeb656d6..caf7c5ca 100644
--- a/runtime/Makefile.am
+++ b/runtime/Makefile.am
@@ -9,6 +9,7 @@ librsyslog_la_SOURCES = \
rsyslog.h \
unicode-helper.h \
atomic.h \
+ batch.h \
syslogd-types.h \
module-template.h \
obj-types.h \
@@ -39,6 +40,8 @@ librsyslog_la_SOURCES = \
obj.h \
modules.c \
modules.h \
+ apc.c \
+ apc.h \
sync.c \
sync.h \
expr.c \
@@ -67,6 +70,12 @@ librsyslog_la_SOURCES = \
vmop.h \
queue.c \
queue.h \
+ ruleset.c \
+ ruleset.h \
+ rule.c \
+ rule.h \
+ prop.c \
+ prop.h \
cfsysline.c \
cfsysline.h \
\
@@ -105,6 +114,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..c2f61266
--- /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;
+ DEFiRet;
+
+ d_pthread_mutex_lock(&listMutex);
+ iRet = unlistCurrent(&pExecList);
+ d_pthread_mutex_unlock(&listMutex);
+ CHKiRet(iRet);
+
+ if(pExecList != NULL) {
+ 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)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, apc);
+ assert(pID != NULL);
+ d_pthread_mutex_lock(&listMutex);
+ insertApc(pThis, pID);
+ d_pthread_mutex_unlock(&listMutex);
+ 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)
+{
+ BEGINfunc
+ d_pthread_mutex_lock(&listMutex);
+ deleteApc(id);
+ d_pthread_mutex_unlock(&listMutex);
+ ENDfunc
+ 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/atomic.h b/runtime/atomic.h
index fdf64214..b507b769 100644
--- a/runtime/atomic.h
+++ b/runtime/atomic.h
@@ -41,11 +41,18 @@
* They simply came in too late. -- rgerhards, 2008-04-02
*/
#ifdef HAVE_ATOMIC_BUILTINS
+# define ATOMIC_SUB(data, val) __sync_fetch_and_sub(&(data), val)
+# define ATOMIC_ADD(data, val) __sync_fetch_and_add(&(data), val)
# define ATOMIC_INC(data) ((void) __sync_fetch_and_add(&(data), 1))
+# define ATOMIC_INC_AND_FETCH(data) __sync_fetch_and_add(&(data), 1)
# define ATOMIC_DEC(data) ((void) __sync_sub_and_fetch(&(data), 1))
# define ATOMIC_DEC_AND_FETCH(data) __sync_sub_and_fetch(&(data), 1)
# define ATOMIC_FETCH_32BIT(data) ((unsigned) __sync_fetch_and_and(&(data), 0xffffffff))
# define ATOMIC_STORE_1_TO_32BIT(data) __sync_lock_test_and_set(&(data), 1)
+# define ATOMIC_STORE_0_TO_INT(data) __sync_fetch_and_and(&(data), 0)
+# define ATOMIC_STORE_1_TO_INT(data) __sync_fetch_and_or(&(data), 1)
+# define ATOMIC_CAS(data, oldVal, newVal) __sync_bool_compare_and_swap(&(data), (oldVal), (newVal));
+# define ATOMIC_CAS_VAL(data, oldVal, newVal) __sync_val_compare_and_swap(&(data), (oldVal), (newVal));
#else
/* note that we gained parctical proof that theoretical problems DO occur
* if we do not properly address them. See this blog post for details:
diff --git a/runtime/batch.h b/runtime/batch.h
new file mode 100644
index 00000000..031718a7
--- /dev/null
+++ b/runtime/batch.h
@@ -0,0 +1,72 @@
+/* Definition of the batch_t data structure.
+ * I am not sure yet if this will become a full-blown object. For now, this header just
+ * includes the object definition and is not accompanied by code.
+ *
+ * Copyright 2009 by Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of the rsyslog runtime library.
+ *
+ * The rsyslog runtime library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The rsyslog runtime library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
+ */
+
+#ifndef BATCH_H_INCLUDED
+#define BATCH_H_INCLUDED
+
+/* enum for batch states. Actually, we violate a layer here, in that we assume that a batch is used
+ * for action processing. So far, this seems acceptable, the status is simply ignored inside the
+ * main message queue. But over time, it could potentially be useful to split the two.
+ * rgerhad, 2009-05-12
+ */
+typedef enum {
+ BATCH_STATE_RDY = 0, /* object ready for processing */
+ BATCH_STATE_BAD = 1, /* unrecoverable failure while processing, do NOT resubmit to same action */
+ BATCH_STATE_SUB = 2, /* message submitted for processing, outcome yet unkonwn */
+ BATCH_STATE_COMM = 3, /* message successfully commited */
+ BATCH_STATE_DISC = 4, /* discarded - processed OK, but do not submit to any other action */
+} batch_state_t;
+
+
+/* an object inside a batch, including any information (state!) needed for it to "life".
+ */
+struct batch_obj_s {
+ obj_t *pUsrp; /* pointer to user object (most often message) */
+ batch_state_t state; /* associated state */
+};
+
+/* the batch
+ * This object is used to dequeue multiple user pointers which are than handed over
+ * to processing. The size of elements is fixed after queue creation, but may be
+ * modified by config variables (better said: queue properties).
+ * Note that a "user pointer" in rsyslog context so far always is a message
+ * object. We stick to the more generic term because queues may potentially hold
+ * other types of objects, too.
+ * rgerhards, 2009-05-12
+ * Note that nElem is not necessarily equal to nElemDeq. This is the case when we
+ * discard some elements (because of configuration) during dequeue processing. As
+ * all Elements are only deleted when the batch is processed, we can not immediately
+ * delete them. So we need to keep their number that we can delete them when the batch
+ * is completed (else, the whole process does not work correctly).
+ */
+struct batch_s {
+ int nElem; /* actual number of element in this entry */
+ int nElemDeq; /* actual number of elements dequeued (and thus to be deleted) - see comment above! */
+ int iDoneUpTo; /* all messages below this index have state other than RDY */
+ qDeqID deqID; /* ID of dequeue operation that generated this batch */
+ batch_obj_t *pElem; /* batch elements */
+};
+
+#endif /* #ifndef BATCH_H_INCLUDED */
diff --git a/runtime/cfsysline.c b/runtime/cfsysline.c
index 0fb4247d..184c0d87 100644
--- a/runtime/cfsysline.c
+++ b/runtime/cfsysline.c
@@ -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 a1d74b93..2e37edf2 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,8 +90,10 @@ 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() */
+static int iNbrActions = 0; /* number of currently defined actions */
/* The following global variables are used for building
* tag and host selector lines during startup and config reload.
@@ -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;
}
@@ -586,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;
@@ -601,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 */
@@ -666,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);
}
}
}
@@ -706,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);
}
}
}
@@ -752,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;
@@ -765,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. */
@@ -821,10 +833,11 @@ 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;
+ cstr_t *pCSPropName;
rsRetVal iRet;
int iOffset; /* for compare operations */
@@ -844,12 +857,19 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register selector_t *f)
}
/* read property */
- iRet = parsDelimCStr(pPars, &f->f_filterData.prop.pCSPropName, ',', 1, 1, 1);
+ iRet = parsDelimCStr(pPars, &pCSPropName, ',', 1, 1, 1);
+ if(iRet != RS_RET_OK) {
+ errmsg.LogError(0, iRet, "error %d parsing filter property - ignoring selector", iRet);
+ rsParsDestruct(pPars);
+ return(iRet);
+ }
+ iRet = propNameToID(pCSPropName, &f->f_filterData.prop.propID);
if(iRet != RS_RET_OK) {
errmsg.LogError(0, iRet, "error %d parsing filter property - ignoring selector", iRet);
rsParsDestruct(pPars);
return(iRet);
}
+ cstrDestruct(&pCSPropName);
/* read operation */
iRet = parsDelimCStr(pPars, &pCSCompOp, ',', 1, 1, 1);
@@ -1010,10 +1030,10 @@ finalize_it:
/* 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;
@@ -1082,7 +1102,7 @@ static rsRetVal cflineDoAction(uchar **p, action_t **ppAction)
dbgprintf("module is incompatible with RepeatedMsgReduction - turned off\n");
pAction->f_ReduceRepeated = 0;
}
- pAction->bEnabled = 1; /* action is enabled */
+ pAction->eState = ACT_STATE_RDY; /* action is enabled */
iNbrActions++; /* one more active action! */
}
break;
@@ -1106,17 +1126,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.
@@ -1134,16 +1152,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;
}
@@ -1153,7 +1174,7 @@ finalize_it:
* rgerhards, 2007-08-01
*/
static rsRetVal
-cfline(uchar *line, selector_t **pfCurr)
+cfline(uchar *line, rule_t **pfCurr)
{
DEFiRet;
@@ -1183,21 +1204,6 @@ cfline(uchar *line, selector_t **pfCurr)
}
-/* Reinitialize the configuration subsystem. This is a "work-around" to the fact
- * that we do not yet have actual config objects. This method is to be called
- * whenever a totally new config is started (which means on startup and HUP).
- * Note that it MUST NOT be called for an included config file.
- * rgerhards, 2008-07-28
- */
-static rsRetVal
-ReInitConf(void)
-{
- DEFiRet;
- iNbrActions = 0; /* this is what we created the function for ;) - action count is reset */
- RETiRet;
-}
-
-
/* return the current number of active actions
* rgerhards, 2008-07-28
*/
@@ -1231,7 +1237,6 @@ CODESTARTobjQueryInterface(conf)
pIf->doIncludeLine = doIncludeLine;
pIf->cfline = cfline;
pIf->processConfFile = processConfFile;
- pIf->ReInitConf = ReInitConf;
pIf->GetNbrActActions = GetNbrActActions;
finalize_it:
@@ -1250,6 +1255,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)
@@ -1265,6 +1272,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..6db1623e 100644
--- a/runtime/conf.h
+++ b/runtime/conf.h
@@ -35,12 +35,14 @@ 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 *);
ENDinterface(conf)
-#define confCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */
+#define confCURR_IF_VERSION 3 /* increment whenever you change the interface structure! */
+/* in Version 3, entry point "ReInitConf()" was removed, as we do not longer need
+ * to support restart-type HUP -- rgerhards, 2009-07-15
+ */
/* prototypes */
@@ -51,5 +53,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 263e656c..6f5f0273 100644
--- a/runtime/ctok.c
+++ b/runtime/ctok.c
@@ -258,13 +258,13 @@ ctokGetVar(ctok_t *pThis, ctok_token_t *pToken)
pToken->tok = ctok_MSGVAR;
}
- CHKiRet(rsCStrConstruct(&pstrVal));
+ CHKiRet(cstrConstruct(&pstrVal));
/* this loop is quite simple, a variable name is terminated when a non-supported
* character is detected. Note that we currently permit a numerical digit as the
* first char, which is not permitted by ABNF. -- rgerhards, 2009-03-10
*/
while(isalpha(c) || isdigit(c) || (c == '_') || (c == '-')) {
- CHKiRet(rsCStrAppendChar(pstrVal, tolower(c)));
+ CHKiRet(cstrAppendChar(pstrVal, tolower(c)));
CHKiRet(ctokGetCharFromStream(pThis, &c));
}
CHKiRet(ctokUngetCharFromStream(pThis, c)); /* put not processed char back */
@@ -277,7 +277,7 @@ ctokGetVar(ctok_t *pThis, ctok_token_t *pToken)
finalize_it:
if(iRet != RS_RET_OK) {
if(pstrVal != NULL) {
- rsCStrDestruct(&pstrVal);
+ cstrDestruct(&pstrVal);
}
}
@@ -301,20 +301,20 @@ ctokGetSimpStr(ctok_t *pThis, ctok_token_t *pToken)
pToken->tok = ctok_SIMPSTR;
- CHKiRet(rsCStrConstruct(&pstrVal));
+ CHKiRet(cstrConstruct(&pstrVal));
CHKiRet(ctokGetCharFromStream(pThis, &c));
/* while we are in escape mode (had a backslash), no sequence
* terminates the loop. If outside, it is terminated by a single quote.
*/
while(bInEsc || c != '\'') {
if(bInEsc) {
- CHKiRet(rsCStrAppendChar(pstrVal, c));
+ CHKiRet(cstrAppendChar(pstrVal, c));
bInEsc = 0;
} else {
if(c == '\\') {
bInEsc = 1;
} else {
- CHKiRet(rsCStrAppendChar(pstrVal, c));
+ CHKiRet(cstrAppendChar(pstrVal, c));
}
}
CHKiRet(ctokGetCharFromStream(pThis, &c));
@@ -327,7 +327,7 @@ ctokGetSimpStr(ctok_t *pThis, ctok_token_t *pToken)
finalize_it:
if(iRet != RS_RET_OK) {
if(pstrVal != NULL) {
- rsCStrDestruct(&pstrVal);
+ cstrDestruct(&pstrVal);
}
}
@@ -519,8 +519,9 @@ ctokGetToken(ctok_t *pThis, ctok_token_t **ppToken)
CHKiRet(ctokUngetCharFromStream(pThis, c));
pToken->tok = ctok_FUNCTION;
/* fill function name */
- CHKiRet(rsCStrConstruct(&pstrVal));
+ CHKiRet(cstrConstruct(&pstrVal));
CHKiRet(rsCStrSetSzStr(pstrVal, szWord));
+ CHKiRet(cstrFinalize(pstrVal));
CHKiRet(var.SetString(pToken->pVar, pstrVal));
} else { /* give up... */
dbgprintf("parser has an invalid word (token) '%s'\n", szWord);
diff --git a/runtime/datetime.c b/runtime/datetime.c
index 99caaf97..6160bd7c 100644
--- a/runtime/datetime.c
+++ b/runtime/datetime.c
@@ -49,6 +49,8 @@
DEFobjStaticHelpers
DEFobjCurrIf(errmsg)
+/* the following table of ten powers saves us some computation */
+static const int tenPowers[6] = { 1, 10, 100, 1000, 10000, 100000 };
/* ------------------------------ methods ------------------------------ */
@@ -121,6 +123,7 @@ static void getCurrTime(struct syslogTime *t, time_t *ttSeconds)
t->OffsetMode = '+';
t->OffsetHour = lBias / 3600;
t->OffsetMinute = lBias % 3600;
+ t->timeType = TIME_TYPE_RFC5424; /* we have a high precision timestamp */
}
@@ -363,6 +366,10 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS, int *pLenStr)
* We will use this for parsing, as it probably is the
* fastest way to parse it.
*
+ * 2009-08-17: we now do case-insensitive comparisons, as some devices obviously do not
+ * obey to the RFC-specified case. As we need to guess in any case, we can ignore case
+ * in the first place -- rgerhards
+ *
* 2005-07-18, well sometimes it pays to be a bit more verbose, even in C...
* Fixed a bug that lead to invalid detection of the data. The issue was that
* we had an if(++pszTS == 'x') inside of some of the consturcts below. However,
@@ -377,20 +384,21 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS, int *pLenStr)
switch(*pszTS++)
{
+ case 'j':
case 'J':
- if(*pszTS == 'a') {
+ if(*pszTS == 'a' || *pszTS == 'A') {
++pszTS;
- if(*pszTS == 'n') {
+ if(*pszTS == 'n' || *pszTS == 'N') {
++pszTS;
month = 1;
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
- } else if(*pszTS == 'u') {
+ } else if(*pszTS == 'u' || *pszTS == 'U') {
++pszTS;
- if(*pszTS == 'n') {
+ if(*pszTS == 'n' || *pszTS == 'N') {
++pszTS;
month = 6;
- } else if(*pszTS == 'l') {
+ } else if(*pszTS == 'l' || *pszTS == 'L') {
++pszTS;
month = 7;
} else
@@ -398,10 +406,11 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS, int *pLenStr)
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
break;
+ case 'f':
case 'F':
- if(*pszTS == 'e') {
+ if(*pszTS == 'e' || *pszTS == 'E') {
++pszTS;
- if(*pszTS == 'b') {
+ if(*pszTS == 'b' || *pszTS == 'B') {
++pszTS;
month = 2;
} else
@@ -409,13 +418,14 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS, int *pLenStr)
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
break;
+ case 'm':
case 'M':
- if(*pszTS == 'a') {
+ if(*pszTS == 'a' || *pszTS == 'A') {
++pszTS;
- if(*pszTS == 'r') {
+ if(*pszTS == 'r' || *pszTS == 'R') {
++pszTS;
month = 3;
- } else if(*pszTS == 'y') {
+ } else if(*pszTS == 'y' || *pszTS == 'Y') {
++pszTS;
month = 5;
} else
@@ -423,17 +433,18 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS, int *pLenStr)
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
break;
+ case 'a':
case 'A':
- if(*pszTS == 'p') {
+ if(*pszTS == 'p' || *pszTS == 'P') {
++pszTS;
- if(*pszTS == 'r') {
+ if(*pszTS == 'r' || *pszTS == 'R') {
++pszTS;
month = 4;
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
- } else if(*pszTS == 'u') {
+ } else if(*pszTS == 'u' || *pszTS == 'U') {
++pszTS;
- if(*pszTS == 'g') {
+ if(*pszTS == 'g' || *pszTS == 'G') {
++pszTS;
month = 8;
} else
@@ -441,10 +452,11 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS, int *pLenStr)
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
break;
+ case 's':
case 'S':
- if(*pszTS == 'e') {
+ if(*pszTS == 'e' || *pszTS == 'E') {
++pszTS;
- if(*pszTS == 'p') {
+ if(*pszTS == 'p' || *pszTS == 'P') {
++pszTS;
month = 9;
} else
@@ -452,10 +464,11 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS, int *pLenStr)
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
break;
+ case 'o':
case 'O':
- if(*pszTS == 'c') {
+ if(*pszTS == 'c' || *pszTS == 'C') {
++pszTS;
- if(*pszTS == 't') {
+ if(*pszTS == 't' || *pszTS == 'T') {
++pszTS;
month = 10;
} else
@@ -463,10 +476,11 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS, int *pLenStr)
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
break;
+ case 'n':
case 'N':
- if(*pszTS == 'o') {
+ if(*pszTS == 'o' || *pszTS == 'O') {
++pszTS;
- if(*pszTS == 'v') {
+ if(*pszTS == 'v' || *pszTS == 'V') {
++pszTS;
month = 11;
} else
@@ -474,10 +488,11 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS, int *pLenStr)
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
break;
+ case 'd':
case 'D':
- if(*pszTS == 'e') {
+ if(*pszTS == 'e' || *pszTS == 'E') {
++pszTS;
- if(*pszTS == 'c') {
+ if(*pszTS == 'c' || *pszTS == 'C') {
++pszTS;
month = 12;
} else
@@ -589,7 +604,7 @@ finalize_it:
* returns the size of the timestamp written in bytes (without
* the string terminator). If 0 is returend, an error occured.
*/
-int formatTimestampToMySQL(struct syslogTime *ts, char* pDst, size_t iLenDst)
+int formatTimestampToMySQL(struct syslogTime *ts, char* pBuf)
{
/* currently we do not consider localtime/utc. This may later be
* added. If so, I recommend using a property replacer option
@@ -598,28 +613,54 @@ int formatTimestampToMySQL(struct syslogTime *ts, char* pDst, size_t iLenDst)
* rgerhards, 2007-06-26
*/
assert(ts != NULL);
- assert(pDst != NULL);
-
- if (iLenDst < 15) /* we need at least 14 bytes
- 14 digits for timestamp + '\n' */
- return(0);
+ assert(pBuf != NULL);
- return(snprintf(pDst, iLenDst, "%4.4d%2.2d%2.2d%2.2d%2.2d%2.2d",
- ts->year, ts->month, ts->day, ts->hour, ts->minute, ts->second));
+ pBuf[0] = (ts->year / 1000) % 10 + '0';
+ pBuf[1] = (ts->year / 100) % 10 + '0';
+ pBuf[2] = (ts->year / 10) % 10 + '0';
+ pBuf[3] = ts->year % 10 + '0';
+ pBuf[4] = (ts->month / 10) % 10 + '0';
+ pBuf[5] = ts->month % 10 + '0';
+ pBuf[6] = (ts->day / 10) % 10 + '0';
+ pBuf[7] = ts->day % 10 + '0';
+ pBuf[8] = (ts->hour / 10) % 10 + '0';
+ pBuf[9] = ts->hour % 10 + '0';
+ pBuf[10] = (ts->minute / 10) % 10 + '0';
+ pBuf[11] = ts->minute % 10 + '0';
+ pBuf[12] = (ts->second / 10) % 10 + '0';
+ pBuf[13] = ts->second % 10 + '0';
+ pBuf[14] = '\0';
+ return 15;
}
-int formatTimestampToPgSQL(struct syslogTime *ts, char *pDst, size_t iLenDst)
+int formatTimestampToPgSQL(struct syslogTime *ts, char *pBuf)
{
- /* see note in formatTimestampToMySQL, applies here as well */
- assert(ts != NULL);
- assert(pDst != NULL);
-
- if (iLenDst < 21) /* we need 20 bytes + '\n' */
- return(0);
+ /* see note in formatTimestampToMySQL, applies here as well */
+ assert(ts != NULL);
+ assert(pBuf != NULL);
- return(snprintf(pDst, iLenDst, "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d",
- ts->year, ts->month, ts->day, ts->hour, ts->minute, ts->second));
+ pBuf[0] = (ts->year / 1000) % 10 + '0';
+ pBuf[1] = (ts->year / 100) % 10 + '0';
+ pBuf[2] = (ts->year / 10) % 10 + '0';
+ pBuf[3] = ts->year % 10 + '0';
+ pBuf[4] = '-';
+ pBuf[5] = (ts->month / 10) % 10 + '0';
+ pBuf[6] = ts->month % 10 + '0';
+ pBuf[7] = '-';
+ pBuf[8] = (ts->day / 10) % 10 + '0';
+ pBuf[9] = ts->day % 10 + '0';
+ pBuf[10] = ' ';
+ pBuf[11] = (ts->hour / 10) % 10 + '0';
+ pBuf[12] = ts->hour % 10 + '0';
+ pBuf[13] = ':';
+ pBuf[14] = (ts->minute / 10) % 10 + '0';
+ pBuf[15] = ts->minute % 10 + '0';
+ pBuf[16] = ':';
+ pBuf[17] = (ts->second / 10) % 10 + '0';
+ pBuf[18] = ts->second % 10 + '0';
+ pBuf[19] = '\0';
+ return 19;
}
@@ -629,35 +670,36 @@ int formatTimestampToPgSQL(struct syslogTime *ts, char *pDst, size_t iLenDst)
* buffer that will receive the resulting string. The function
* returns the size of the timestamp written in bytes (without
* the string terminator). If 0 is returend, an error occured.
- * The buffer must be at least 10 bytes large.
+ * The buffer must be at least 7 bytes large.
* rgerhards, 2008-06-06
*/
-int formatTimestampSecFrac(struct syslogTime *ts, char* pBuf, size_t iLenBuf)
+int formatTimestampSecFrac(struct syslogTime *ts, char* pBuf)
{
- int lenRet;
- char szFmtStr[64];
+ int iBuf;
+ int power;
+ int secfrac;
+ short digit;
assert(ts != NULL);
assert(pBuf != NULL);
- assert(iLenBuf >= 10);
+ iBuf = 0;
if(ts->secfracPrecision > 0)
- { /* We must look at
- * the precision specified. For example, if we have millisec precision (3 digits), a
- * secFrac value of 12 is not equivalent to ".12" but ".012". Obviously, this
- * is a huge difference ;). To avoid this, we first create a format string with
- * the specific precision and *then* use that format string to do the actual formating.
- */
- /* be careful: there is ONE actual %d in the format string below ;) */
- snprintf(szFmtStr, sizeof(szFmtStr), "%%0%dd", ts->secfracPrecision);
- lenRet = snprintf(pBuf, iLenBuf, szFmtStr, ts->secfrac);
+ {
+ power = tenPowers[(ts->secfracPrecision - 1) % 6];
+ secfrac = ts->secfrac;
+ while(power > 0) {
+ digit = secfrac / power;
+ secfrac -= digit * power;
+ power /= 10;
+ pBuf[iBuf++] = digit + '0';
+ }
} else {
- pBuf[0] = '0';
- pBuf[1] = '\0';
- lenRet = 1;
+ pBuf[iBuf++] = '0';
}
+ pBuf[iBuf] = '\0';
- return(lenRet);
+ return iBuf;
}
@@ -669,48 +711,73 @@ int formatTimestampSecFrac(struct syslogTime *ts, char* pBuf, size_t iLenBuf)
* returns the size of the timestamp written in bytes (without
* the string terminator). If 0 is returend, an error occured.
*/
-int formatTimestamp3339(struct syslogTime *ts, char* pBuf, size_t iLenBuf)
+int formatTimestamp3339(struct syslogTime *ts, char* pBuf)
{
- int iRet;
- char szTZ[7]; /* buffer for TZ information */
+ int iBuf;
+ int power;
+ int secfrac;
+ short digit;
+ BEGINfunc
assert(ts != NULL);
assert(pBuf != NULL);
-
- if(iLenBuf < 20)
- return(0); /* we NEED at least 20 bytes */
- /* do TZ information first, this is easier to take care of "Z" zone in rfc3339 */
+ /* start with fixed parts */
+ /* year yyyy */
+ pBuf[0] = (ts->year / 1000) % 10 + '0';
+ pBuf[1] = (ts->year / 100) % 10 + '0';
+ pBuf[2] = (ts->year / 10) % 10 + '0';
+ pBuf[3] = ts->year % 10 + '0';
+ pBuf[4] = '-';
+ /* month */
+ pBuf[5] = (ts->month / 10) % 10 + '0';
+ pBuf[6] = ts->month % 10 + '0';
+ pBuf[7] = '-';
+ /* day */
+ pBuf[8] = (ts->day / 10) % 10 + '0';
+ pBuf[9] = ts->day % 10 + '0';
+ pBuf[10] = 'T';
+ /* hour */
+ pBuf[11] = (ts->hour / 10) % 10 + '0';
+ pBuf[12] = ts->hour % 10 + '0';
+ pBuf[13] = ':';
+ /* minute */
+ pBuf[14] = (ts->minute / 10) % 10 + '0';
+ pBuf[15] = ts->minute % 10 + '0';
+ pBuf[16] = ':';
+ /* second */
+ pBuf[17] = (ts->second / 10) % 10 + '0';
+ pBuf[18] = ts->second % 10 + '0';
+
+ iBuf = 19; /* points to next free entry, now it becomes dynamic! */
+
+ if(ts->secfracPrecision > 0) {
+ pBuf[iBuf++] = '.';
+ power = tenPowers[(ts->secfracPrecision - 1) % 6];
+ secfrac = ts->secfrac;
+ while(power > 0) {
+ digit = secfrac / power;
+ secfrac -= digit * power;
+ power /= 10;
+ pBuf[iBuf++] = digit + '0';
+ }
+ }
+
if(ts->OffsetMode == 'Z') {
- szTZ[0] = 'Z';
- szTZ[1] = '\0';
+ pBuf[iBuf++] = 'Z';
} else {
- snprintf(szTZ, sizeof(szTZ) / sizeof(char), "%c%2.2d:%2.2d",
- ts->OffsetMode, ts->OffsetHour, ts->OffsetMinute);
+ pBuf[iBuf++] = ts->OffsetMode;
+ pBuf[iBuf++] = (ts->OffsetHour / 10) % 10 + '0';
+ pBuf[iBuf++] = ts->OffsetHour % 10 + '0';
+ pBuf[iBuf++] = ':';
+ pBuf[iBuf++] = (ts->OffsetMinute / 10) % 10 + '0';
+ pBuf[iBuf++] = ts->OffsetMinute % 10 + '0';
}
- if(ts->secfracPrecision > 0)
- { /* we now need to include fractional seconds. While doing so, we must look at
- * the precision specified. For example, if we have millisec precision (3 digits), a
- * secFrac value of 12 is not equivalent to ".12" but ".012". Obviously, this
- * is a huge difference ;). To avoid this, we first create a format string with
- * the specific precision and *then* use that format string to do the actual
- * formating (mmmmhhh... kind of self-modifying code... ;)).
- */
- char szFmtStr[64];
- /* be careful: there is ONE actual %d in the format string below ;) */
- snprintf(szFmtStr, sizeof(szFmtStr),
- "%%04d-%%02d-%%02dT%%02d:%%02d:%%02d.%%0%dd%%s",
- ts->secfracPrecision);
- iRet = snprintf(pBuf, iLenBuf, szFmtStr, ts->year, ts->month, ts->day,
- ts->hour, ts->minute, ts->second, ts->secfrac, szTZ);
- }
- else
- iRet = snprintf(pBuf, iLenBuf,
- "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d%s",
- ts->year, ts->month, ts->day,
- ts->hour, ts->minute, ts->second, szTZ);
- return(iRet);
+ pBuf[iBuf] = '\0';
+
+ ENDfunc
+ return iBuf;
}
/**
@@ -720,46 +787,35 @@ int formatTimestamp3339(struct syslogTime *ts, char* pBuf, size_t iLenBuf)
* returns the size of the timestamp written in bytes (without
* the string termnator). If 0 is returend, an error occured.
*/
-int formatTimestamp3164(struct syslogTime *ts, char* pBuf, size_t iLenBuf)
+int formatTimestamp3164(struct syslogTime *ts, char* pBuf)
{
- static char* monthNames[13] = {"ERR", "Jan", "Feb", "Mar",
- "Apr", "May", "Jun", "Jul",
- "Aug", "Sep", "Oct", "Nov", "Dec"};
+ static char* monthNames[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+ int iDay;
assert(ts != NULL);
assert(pBuf != NULL);
- if(iLenBuf < 16)
- return(0); /* we NEED 16 bytes */
- return(snprintf(pBuf, iLenBuf, "%s %2d %2.2d:%2.2d:%2.2d",
- monthNames[ts->month], ts->day, ts->hour,
- ts->minute, ts->second
- ));
+ pBuf[0] = monthNames[(ts->month - 1)% 12][0];
+ pBuf[1] = monthNames[(ts->month - 1) % 12][1];
+ pBuf[2] = monthNames[(ts->month - 1) % 12][2];
+ pBuf[3] = ' ';
+ iDay = (ts->day / 10) % 10; /* we need to write a space if the first digit is 0 */
+ pBuf[4] = iDay ? iDay + '0' : ' ';
+ pBuf[5] = ts->day % 10 + '0';
+ pBuf[6] = ' ';
+ pBuf[7] = (ts->hour / 10) % 10 + '0';
+ pBuf[8] = ts->hour % 10 + '0';
+ pBuf[9] = ':';
+ pBuf[10] = (ts->minute / 10) % 10 + '0';
+ pBuf[11] = ts->minute % 10 + '0';
+ pBuf[12] = ':';
+ pBuf[13] = (ts->second / 10) % 10 + '0';
+ pBuf[14] = ts->second % 10 + '0';
+ pBuf[15] = '\0';
+ return 16; /* traditional: number of bytes written */
}
-/**
- * Format a syslogTimestamp to a text format.
- * The caller must provide the timestamp as well as a character
- * buffer that will receive the resulting string. The function
- * returns the size of the timestamp written in bytes (without
- * the string termnator). If 0 is returend, an error occured.
- */
-#if 0 /* This method is currently not called, be we like to preserve it */
-static int formatTimestamp(struct syslogTime *ts, char* pBuf, size_t iLenBuf)
-{
- assert(ts != NULL);
- assert(pBuf != NULL);
-
- if(ts->timeType == 1) {
- return(formatTimestamp3164(ts, pBuf, iLenBuf));
- }
-
- if(ts->timeType == 2) {
- return(formatTimestamp3339(ts, pBuf, iLenBuf));
- }
- return(0);
-}
-#endif
/* queryInterface function
* rgerhards, 2008-03-05
*/
@@ -793,7 +849,6 @@ ENDobjQueryInterface(datetime)
BEGINAbstractObjClassInit(datetime, 1, OBJ_IS_CORE_MODULE) /* class, version */
/* request objects we use */
CHKiRet(objUse(errmsg, CORE_COMPONENT));
-
ENDObjClassInit(datetime)
/* vi:set ai:
diff --git a/runtime/datetime.h b/runtime/datetime.h
index 6377a4a4..8140eb71 100644
--- a/runtime/datetime.h
+++ b/runtime/datetime.h
@@ -23,8 +23,6 @@
#ifndef INCLUDED_DATETIME_H
#define INCLUDED_DATETIME_H
-#include "datetime.h"
-
/* TODO: define error codes */
#define NO_ERRCODE -1
@@ -36,13 +34,13 @@ typedef struct datetime_s {
/* interfaces */
BEGINinterface(datetime) /* name must also be changed in ENDinterface macro! */
void (*getCurrTime)(struct syslogTime *t, time_t *ttSeconds);
- rsRetVal (*ParseTIMESTAMP3339)(struct syslogTime *pTime, uchar** ppszTS, int *);
- rsRetVal (*ParseTIMESTAMP3164)(struct syslogTime *pTime, uchar** pszTS, int *);
- int (*formatTimestampToMySQL)(struct syslogTime *ts, char* pDst, size_t iLenDst);
- int (*formatTimestampToPgSQL)(struct syslogTime *ts, char *pDst, size_t iLenDst);
- int (*formatTimestamp3339)(struct syslogTime *ts, char* pBuf, size_t iLenBuf);
- int (*formatTimestamp3164)(struct syslogTime *ts, char* pBuf, size_t iLenBuf);
- int (*formatTimestampSecFrac)(struct syslogTime *ts, char* pBuf, size_t iLenBuf);
+ rsRetVal (*ParseTIMESTAMP3339)(struct syslogTime *pTime, uchar** ppszTS, int*);
+ rsRetVal (*ParseTIMESTAMP3164)(struct syslogTime *pTime, uchar** pszTS, int*);
+ int (*formatTimestampToMySQL)(struct syslogTime *ts, char* pDst);
+ int (*formatTimestampToPgSQL)(struct syslogTime *ts, char *pDst);
+ int (*formatTimestamp3339)(struct syslogTime *ts, char* pBuf);
+ int (*formatTimestamp3164)(struct syslogTime *ts, char* pBuf);
+ int (*formatTimestampSecFrac)(struct syslogTime *ts, char* pBuf);
ENDinterface(datetime)
#define datetimeCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */
/* interface changes:
diff --git a/runtime/debug.c b/runtime/debug.c
index 4ee90226..959d56a3 100644
--- a/runtime/debug.c
+++ b/runtime/debug.c
@@ -732,6 +732,8 @@ static void dbgGetThrdName(char *pszBuf, size_t lenBuf, pthread_t thrd, int bInc
*/
void dbgSetThrdName(uchar *pszName)
{
+return;
+
dbgThrdInfo_t *pThrd = dbgGetThrdInfo();
if(pThrd->pszThrdName != NULL)
free(pThrd->pszThrdName);
@@ -776,7 +778,7 @@ static void dbgCallStackPrint(dbgThrdInfo_t *pThrd)
/* print all threads call stacks
*/
-static void dbgCallStackPrintAll(void)
+void dbgCallStackPrintAll(void)
{
dbgThrdInfo_t *pThrd;
/* stack info */
@@ -828,34 +830,19 @@ sigsegvHdlr(int signum)
abort();
}
-#if 1
-#pragma GCC diagnostic ignored "-Wempty-body"
-/* write the debug message. This is a helper to dbgprintf and dbgoprint which
- * contains common code. added 2008-09-26 rgerhards
+/* actually write the debug message. This is a separate fuction because the cleanup_push/_pop
+ * interface otherwise is unsafe to use (generates compiler warnings at least).
+ * 2009-05-20 rgerhards
*/
-static void
-dbgprint(obj_t *pObj, char *pszMsg, size_t lenMsg)
+static inline void
+do_dbgprint(uchar *pszObjName, char *pszMsg, size_t lenMsg)
{
static pthread_t ptLastThrdID = 0;
static int bWasNL = 0;
char pszThrdName[64]; /* 64 is to be on the safe side, anything over 20 is bad... */
- char pszWriteBuf[1024];
+ char pszWriteBuf[32*1024];
size_t lenWriteBuf;
struct timespec t;
- uchar *pszObjName = NULL;
-
- /* we must get the object name before we lock the mutex, because the object
- * potentially calls back into us. If we locked the mutex, we would deadlock
- * ourselfs. On the other hand, the GetName call needs not to be protected, as
- * this thread has a valid reference. If such an object is deleted by another
- * thread, we are in much more trouble than just for dbgprint(). -- rgerhards, 2008-09-26
- */
- if(pObj != NULL) {
- pszObjName = obj.GetName(pObj);
- }
-
- pthread_mutex_lock(&mutdbgprint);
- pthread_cleanup_push(dbgMutexCancelCleanupHdlr, &mutdbgprint);
/* The bWasNL handler does not really work. It works if no thread
* switching occurs during non-NL messages. Else, things are messed
@@ -903,11 +890,35 @@ dbgprint(obj_t *pObj, char *pszMsg, size_t lenMsg)
if(altdbg != -1) write(altdbg, pszMsg, lenMsg);
bWasNL = (pszMsg[lenMsg - 1] == '\n') ? 1 : 0;
+}
+
+#pragma GCC diagnostic ignored "-Wempty-body"
+/* write the debug message. This is a helper to dbgprintf and dbgoprint which
+ * contains common code. added 2008-09-26 rgerhards
+ */
+static void
+dbgprint(obj_t *pObj, char *pszMsg, size_t lenMsg)
+{
+ uchar *pszObjName = NULL;
+
+ /* we must get the object name before we lock the mutex, because the object
+ * potentially calls back into us. If we locked the mutex, we would deadlock
+ * ourselfs. On the other hand, the GetName call needs not to be protected, as
+ * this thread has a valid reference. If such an object is deleted by another
+ * thread, we are in much more trouble than just for dbgprint(). -- rgerhards, 2008-09-26
+ */
+ if(pObj != NULL) {
+ pszObjName = obj.GetName(pObj);
+ }
+
+ pthread_mutex_lock(&mutdbgprint);
+ pthread_cleanup_push(dbgMutexCancelCleanupHdlr, &mutdbgprint);
+
+ do_dbgprint(pszObjName, pszMsg, lenMsg);
pthread_cleanup_pop(1);
}
#pragma GCC diagnostic warning "-Wempty-body"
-#endif
/* print some debug output when an object is given
* This is mostly a copy of dbgprintf, but I do not know how to combine it
@@ -1050,7 +1061,9 @@ int dbgEntrFunc(dbgFuncDB_t **ppFuncDB, const char *file, const char *func, int
/* when we reach this point, we have a fully-initialized FuncDB! */
ATOMIC_INC(pFuncDB->nTimesCalled);
if(bLogFuncFlow && dbgPrintNameIsInList((const uchar*)pFuncDB->file, printNameFileRoot))
- dbgprintf("%s:%d: %s: enter\n", pFuncDB->file, pFuncDB->line, pFuncDB->func);
+ if(strcmp(pFuncDB->file, "stringbuf.c")) { /* TODO: make configurable */
+ dbgprintf("%s:%d: %s: enter\n", pFuncDB->file, pFuncDB->line, pFuncDB->func);
+ }
if(pThrd->stackPtr >= (int) (sizeof(pThrd->callStack) / sizeof(dbgFuncDB_t*))) {
dbgprintf("%s:%d: %s: debug module: call stack for this thread full, suspending call tracking\n",
pFuncDB->file, pFuncDB->line, pFuncDB->func);
@@ -1080,10 +1093,12 @@ void dbgExitFunc(dbgFuncDB_t *pFuncDB, int iStackPtrRestore, int iRet)
dbgFuncDBPrintActiveMutexes(pFuncDB, "WARNING: mutex still owned by us as we exit function, mutex: ", pthread_self());
if(bLogFuncFlow && dbgPrintNameIsInList((const uchar*)pFuncDB->file, printNameFileRoot)) {
- if(iRet == RS_RET_NO_IRET)
- dbgprintf("%s:%d: %s: exit: (no iRet)\n", pFuncDB->file, pFuncDB->line, pFuncDB->func);
- else
- dbgprintf("%s:%d: %s: exit: %d\n", pFuncDB->file, pFuncDB->line, pFuncDB->func, iRet);
+ if(strcmp(pFuncDB->file, "stringbuf.c")) { /* TODO: make configurable */
+ if(iRet == RS_RET_NO_IRET)
+ dbgprintf("%s:%d: %s: exit: (no iRet)\n", pFuncDB->file, pFuncDB->line, pFuncDB->func);
+ else
+ dbgprintf("%s:%d: %s: exit: %d\n", pFuncDB->file, pFuncDB->line, pFuncDB->func, iRet);
+ }
}
pThrd->stackPtr = iStackPtrRestore;
if(pThrd->stackPtr < 0) {
diff --git a/runtime/debug.h b/runtime/debug.h
index 1375493d..dcbfb930 100644
--- a/runtime/debug.h
+++ b/runtime/debug.h
@@ -134,8 +134,7 @@ void dbgPrintAllDebugInfo(void);
/* debug aides */
-//#ifdef RTINST
-#if 0 // temporarily removed for helgrind
+#ifdef RTINST
#define d_pthread_mutex_lock(x) dbgMutexLock(x, pdbgFuncDB, __LINE__, dbgCALLStaCK_POP_POINT )
#define d_pthread_mutex_trylock(x) dbgMutexTryLock(x, pdbgFuncDB, __LINE__, dbgCALLStaCK_POP_POINT )
#define d_pthread_mutex_unlock(x) dbgMutexUnlock(x, pdbgFuncDB, __LINE__, dbgCALLStaCK_POP_POINT )
diff --git a/runtime/glbl.c b/runtime/glbl.c
index 28f14320..f27b8e73 100644
--- a/runtime/glbl.c
+++ b/runtime/glbl.c
@@ -35,8 +35,11 @@
#include "rsyslog.h"
#include "obj.h"
+#include "unicode-helper.h"
#include "cfsysline.h"
#include "glbl.h"
+#include "prop.h"
+#include "atomic.h"
/* some defaults */
#ifndef DFLT_NETSTRM_DRVR
@@ -45,6 +48,7 @@
/* static data */
DEFobjStaticHelpers
+DEFobjCurrIf(prop)
/* static data
* For this object, these variables are obviously what makes the "meat" of the
@@ -52,13 +56,13 @@ DEFobjStaticHelpers
*/
static uchar *pszWorkDir = NULL;
static int bOptimizeUniProc = 1; /* enable uniprocessor optimizations */
-static int bHUPisRestart = 1; /* should SIGHUP cause a full system restart? */
static int bPreserveFQDN = 0; /* should FQDNs always be preserved? */
static int iMaxLine = 2048; /* maximum length of a syslog message */
static int iDefPFFamily = PF_UNSPEC; /* protocol family (IPv4, IPv6 or both) */
static int bDropMalPTRMsgs = 0;/* Drop messages which have malicious PTR records during DNS lookup */
static int option_DisallowWarning = 1; /* complain if message from disallowed sender is received */
static int bDisableDNS = 0; /* don't look up IP addresses of remote messages */
+static prop_t *propLocalHostName = NULL;/* our hostname as FQDN - read-only after startup */
static uchar *LocalHostName = NULL;/* our hostname - read-only after startup */
static uchar *LocalFQDNName = NULL;/* our hostname as FQDN - read-only after startup */
static uchar *LocalDomain; /* our local domain name - read-only after startup */
@@ -68,6 +72,7 @@ static uchar *pszDfltNetstrmDrvr = NULL; /* module name of default netstream dri
static uchar *pszDfltNetstrmDrvrCAF = NULL; /* default CA file for the netstrm driver */
static uchar *pszDfltNetstrmDrvrKeyFile = NULL; /* default key file for the netstrm driver (server) */
static uchar *pszDfltNetstrmDrvrCertFile = NULL; /* default cert file for the netstrm driver (server) */
+static int bTerminateInputs = 0; /* global switch that inputs shall terminate ASAP (1=> terminate) */
/* define a macro for the simple properties' set and get functions
@@ -91,7 +96,6 @@ static dataType Get##nameFunc(void) \
SIMP_PROP(OptimizeUniProc, bOptimizeUniProc, int)
SIMP_PROP(PreserveFQDN, bPreserveFQDN, int)
-SIMP_PROP(HUPisRestart, bHUPisRestart, int)
SIMP_PROP(MaxLine, iMaxLine, int)
SIMP_PROP(DefPFFamily, iDefPFFamily, int) /* note that in the future we may check the family argument */
SIMP_PROP(DropMalPTRMsgs, bDropMalPTRMsgs, int)
@@ -113,6 +117,24 @@ SIMP_PROP_SET(DfltNetstrmDrvrCertFile, pszDfltNetstrmDrvrCertFile, uchar*) /* TO
#undef SIMP_PROP_GET
+/* return global input termination status
+ * rgerhards, 2009-07-20
+ */
+static int GetGlobalInputTermState(void)
+{
+ return ATOMIC_FETCH_32BIT(bTerminateInputs);
+}
+
+
+/* set global termiantion state to "terminate". Note that this is a
+ * "once in a lifetime" action which can not be undone. -- gerhards, 2009-07-20
+ */
+static void SetGlobalInputTermination(void)
+{
+ ATOMIC_STORE_1_TO_INT(bTerminateInputs);
+}
+
+
/* return our local hostname. if it is not set, "[localhost]" is returned
*/
static uchar*
@@ -132,6 +154,44 @@ GetLocalHostName(void)
}
+/* generate the local hostname property. This must be done after the hostname info
+ * has been set as well as PreserveFQDN.
+ * rgerhards, 2009-06-30
+ */
+static rsRetVal
+GenerateLocalHostNameProperty(void)
+{
+ DEFiRet;
+ uchar *pszName;
+
+ if(propLocalHostName != NULL)
+ prop.Destruct(&propLocalHostName);
+
+ CHKiRet(prop.Construct(&propLocalHostName));
+ if(LocalHostName == NULL)
+ pszName = (uchar*) "[localhost]";
+ else {
+ if(GetPreserveFQDN() == 1)
+ pszName = LocalFQDNName;
+ else
+ pszName = LocalHostName;
+ }
+ CHKiRet(prop.SetString(propLocalHostName, pszName, ustrlen(pszName)));
+ CHKiRet(prop.ConstructFinalize(propLocalHostName));
+
+finalize_it:
+ RETiRet;
+}
+
+/* return our local hostname as a string property
+ */
+static prop_t*
+GetLocalHostNameProp(void)
+{
+ return(propLocalHostName);
+}
+
+
/* return the current localhost name as FQDN (requires FQDN to be set)
* TODO: we should set the FQDN ourselfs in here!
*/
@@ -197,13 +257,16 @@ CODESTARTobjQueryInterface(glbl)
* of course, also affects the "if" above).
*/
pIf->GetWorkDir = GetWorkDir;
+ pIf->GenerateLocalHostNameProperty = GenerateLocalHostNameProperty;
+ pIf->GetLocalHostNameProp = GetLocalHostNameProp;
+ pIf->SetGlobalInputTermination = SetGlobalInputTermination;
+ pIf->GetGlobalInputTermState = GetGlobalInputTermState;
#define SIMP_PROP(name) \
pIf->Get##name = Get##name; \
pIf->Set##name = Set##name;
SIMP_PROP(MaxLine);
SIMP_PROP(OptimizeUniProc);
SIMP_PROP(PreserveFQDN);
- SIMP_PROP(HUPisRestart);
SIMP_PROP(DefPFFamily);
SIMP_PROP(DropMalPTRMsgs);
SIMP_PROP(Option_DisallowWarning);
@@ -249,7 +312,6 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
}
bDropMalPTRMsgs = 0;
bOptimizeUniProc = 1;
- bHUPisRestart = 1;
bPreserveFQDN = 0;
return RS_RET_OK;
}
@@ -262,6 +324,7 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
*/
BEGINAbstractObjClassInit(glbl, 1, OBJ_IS_CORE_MODULE) /* class, version */
/* request objects we use */
+ CHKiRet(objUse(prop, CORE_COMPONENT));
/* register config handlers (TODO: we need to implement a way to unregister them) */
CHKiRet(regCfSysLineHdlr((uchar *)"workdirectory", 0, eCmdHdlrGetWord, NULL, &pszWorkDir, NULL));
@@ -271,7 +334,6 @@ BEGINAbstractObjClassInit(glbl, 1, OBJ_IS_CORE_MODULE) /* class, version */
CHKiRet(regCfSysLineHdlr((uchar *)"defaultnetstreamdriverkeyfile", 0, eCmdHdlrGetWord, NULL, &pszDfltNetstrmDrvrKeyFile, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"defaultnetstreamdrivercertfile", 0, eCmdHdlrGetWord, NULL, &pszDfltNetstrmDrvrCertFile, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"optimizeforuniprocessor", 0, eCmdHdlrBinary, NULL, &bOptimizeUniProc, NULL));
- CHKiRet(regCfSysLineHdlr((uchar *)"hupisrestart", 0, eCmdHdlrBinary, NULL, &bHUPisRestart, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"preservefqdn", 0, eCmdHdlrBinary, NULL, &bPreserveFQDN, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, NULL));
ENDObjClassInit(glbl)
@@ -295,6 +357,7 @@ BEGINObjClassExit(glbl, OBJ_IS_CORE_MODULE) /* class, version */
free(LocalHostName);
if(LocalFQDNName != NULL)
free(LocalFQDNName);
+ objRelease(prop, CORE_COMPONENT);
ENDObjClassExit(glbl)
/* vi:set ai:
diff --git a/runtime/glbl.h b/runtime/glbl.h
index 5bdf4f57..0d0c8210 100644
--- a/runtime/glbl.h
+++ b/runtime/glbl.h
@@ -8,7 +8,7 @@
* Please note that there currently is no glbl.c file as we do not yet
* have any implementations.
*
- * Copyright 2008 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2008, 2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of the rsyslog runtime library.
*
@@ -32,6 +32,8 @@
#ifndef GLBL_H_INCLUDED
#define GLBL_H_INCLUDED
+#include "prop.h"
+
#define glblGetIOBufSize() 4096 /* size of the IO buffer, e.g. for strm class */
/* interfaces */
@@ -42,7 +44,6 @@ BEGINinterface(glbl) /* name must also be changed in ENDinterface macro! */
rsRetVal (*Set##name)(dataType);
SIMP_PROP(MaxLine, int)
SIMP_PROP(OptimizeUniProc, int)
- SIMP_PROP(HUPisRestart, int)
SIMP_PROP(PreserveFQDN, int)
SIMP_PROP(DefPFFamily, int)
SIMP_PROP(DropMalPTRMsgs, int)
@@ -57,9 +58,15 @@ BEGINinterface(glbl) /* name must also be changed in ENDinterface macro! */
SIMP_PROP(DfltNetstrmDrvrCAF, uchar*)
SIMP_PROP(DfltNetstrmDrvrKeyFile, uchar*)
SIMP_PROP(DfltNetstrmDrvrCertFile, uchar*)
+ /* added v3, 2009-06-30 */
+ rsRetVal (*GenerateLocalHostNameProperty)(void);
+ prop_t* (*GetLocalHostNameProp)(void);
+ /* added v4, 2009-07-20 */
+ int (*GetGlobalInputTermState)(void);
+ void (*SetGlobalInputTermination)(void);
#undef SIMP_PROP
ENDinterface(glbl)
-#define glblCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */
+#define glblCURR_IF_VERSION 4 /* increment whenever you change the interface structure! */
/* version 2 had PreserveFQDN added - rgerhards, 2008-12-08 */
/* the remaining prototypes */
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/module-template.h b/runtime/module-template.h
index 3e963199..d49da2c9 100644
--- a/runtime/module-template.h
+++ b/runtime/module-template.h
@@ -368,6 +368,17 @@ static rsRetVal queryEtryPt(uchar *name, rsRetVal (**pEtryPoint)())\
*pEtryPoint = endTransaction;\
}
+
+/* the following definition is a queryEtryPt block that must be added
+ * if a non-output module supports "isCompatibleWithFeature".
+ * rgerhards, 2009-07-20
+ */
+#define CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES \
+ else if(!strcmp((char*) name, "isCompatibleWithFeature")) {\
+ *pEtryPoint = isCompatibleWithFeature;\
+ }
+
+
/* the following definition is the standard block for queryEtryPt for INPUT
* modules. This can be used if no specific handling (e.g. to cover version
* differences) is needed.
diff --git a/runtime/modules.c b/runtime/modules.c
index 32ae659f..bdb15e7f 100644
--- a/runtime/modules.c
+++ b/runtime/modules.c
@@ -77,6 +77,27 @@ static modInfo_t *pLoadedModulesLast = NULL; /* tail-pointer */
uchar *pModDir = NULL; /* read-only after startup */
+/* we provide a set of dummy functions for modules that do not support the
+ * some interfaces.
+ * On the commit feature: As the modules do not support it, they commit each message they
+ * receive, and as such the dummies can always return RS_RET_OK without causing
+ * harm. This simplifies things as in action processing we do not need to check
+ * if the transactional entry points exist.
+ */
+static rsRetVal dummyBeginTransaction()
+{
+ return RS_RET_OK;
+}
+static rsRetVal dummyEndTransaction()
+{
+ return RS_RET_OK;
+}
+static rsRetVal dummyIsCompatibleWithFeature()
+{
+dbgprintf("XXX: dummy isCompatibleWithFeature called!\n");
+ return RS_RET_INCOMPATIBLE;
+}
+
#ifdef DEBUG
/* we add some home-grown support to track our users (and detect who does not free us). In
* the long term, this should probably be migrated into debug.c (TODO). -- rgerhards, 2008-03-11
@@ -216,19 +237,38 @@ static void moduleDestruct(modInfo_t *pThis)
}
+/* This enables a module to query the core for specific features.
+ * rgerhards, 2009-04-22
+ */
+static rsRetVal queryCoreFeatureSupport(int *pBool, unsigned uFeat)
+{
+ DEFiRet;
+
+ if((pBool == NULL))
+ ABORT_FINALIZE(RS_RET_PARAM_ERROR);
+
+ *pBool = (uFeat & CORE_FEATURE_BATCHING) ? 1 : 0;
+
+finalize_it:
+ RETiRet;
+}
+
+
/* The following function is the queryEntryPoint for host-based entry points.
* Modules may call it to get access to core interface functions. Please note
* that utility functions can be accessed via shared libraries - at least this
* is my current shool of thinking.
* Please note that the implementation as a query interface allows to take
* care of plug-in interface version differences. -- rgerhards, 2007-07-31
+ * ... but often it better not to use a new interface. So we now add core
+ * functions here that a plugin may request. -- rgerhards, 2009-04-22
*/
static rsRetVal queryHostEtryPt(uchar *name, rsRetVal (**pEtryPoint)())
{
DEFiRet;
if((name == NULL) || (pEtryPoint == NULL))
- return RS_RET_PARAM_ERROR;
+ ABORT_FINALIZE(RS_RET_PARAM_ERROR);
if(!strcmp((char*) name, "regCfSysLineHdlr")) {
*pEtryPoint = regCfSysLineHdlr;
@@ -236,6 +276,8 @@ static rsRetVal queryHostEtryPt(uchar *name, rsRetVal (**pEtryPoint)())
*pEtryPoint = objGetObjInterface;
} else if(!strcmp((char*) name, "OMSRgetSupportedTplOpts")) {
*pEtryPoint = OMSRgetSupportedTplOpts;
+ } else if(!strcmp((char*) name, "queryCoreFeatureSupport")) {
+ *pEtryPoint = queryCoreFeatureSupport;
} else {
*pEtryPoint = NULL; /* to be on the safe side */
ABORT_FINALIZE(RS_RET_ENTRY_POINT_NOT_FOUND);
@@ -383,7 +425,7 @@ doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)(), modInfo_
* can never change in the lifetime of an module. -- rgerhards, 2007-12-14
*/
CHKiRet((*pNew->modQueryEtryPt)((uchar*)"getType", &modGetType));
- CHKiRet((iRet = (*modGetType)(&pNew->eType)) != RS_RET_OK);
+ CHKiRet((*modGetType)(&pNew->eType));
dbgprintf("module of type %d being loaded.\n", pNew->eType);
/* OK, we know we can successfully work with the module. So we now fill the
@@ -392,6 +434,11 @@ doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)(), modInfo_
*/
CHKiRet((*pNew->modQueryEtryPt)((uchar*)"modGetID", &pNew->modGetID));
CHKiRet((*pNew->modQueryEtryPt)((uchar*)"modExit", &pNew->modExit));
+ localRet = (*pNew->modQueryEtryPt)((uchar*)"isCompatibleWithFeature", &pNew->isCompatibleWithFeature);
+ if(localRet == RS_RET_MODULE_ENTRY_POINT_NOT_FOUND)
+ pNew->isCompatibleWithFeature = dummyIsCompatibleWithFeature;
+ else if(localRet != RS_RET_OK)
+ ABORT_FINALIZE(localRet);
/* ... and now the module-specific interfaces */
switch(pNew->eType) {
@@ -399,18 +446,32 @@ doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)(), modInfo_
CHKiRet((*pNew->modQueryEtryPt)((uchar*)"runInput", &pNew->mod.im.runInput));
CHKiRet((*pNew->modQueryEtryPt)((uchar*)"willRun", &pNew->mod.im.willRun));
CHKiRet((*pNew->modQueryEtryPt)((uchar*)"afterRun", &pNew->mod.im.afterRun));
+ pNew->mod.im.bCanRun = 0;
break;
case eMOD_OUT:
CHKiRet((*pNew->modQueryEtryPt)((uchar*)"freeInstance", &pNew->freeInstance));
CHKiRet((*pNew->modQueryEtryPt)((uchar*)"dbgPrintInstInfo", &pNew->dbgPrintInstInfo));
CHKiRet((*pNew->modQueryEtryPt)((uchar*)"doAction", &pNew->mod.om.doAction));
CHKiRet((*pNew->modQueryEtryPt)((uchar*)"parseSelectorAct", &pNew->mod.om.parseSelectorAct));
- CHKiRet((*pNew->modQueryEtryPt)((uchar*)"isCompatibleWithFeature", &pNew->isCompatibleWithFeature));
CHKiRet((*pNew->modQueryEtryPt)((uchar*)"tryResume", &pNew->tryResume));
/* try load optional interfaces */
localRet = (*pNew->modQueryEtryPt)((uchar*)"doHUP", &pNew->doHUP);
if(localRet != RS_RET_OK && localRet != RS_RET_MODULE_ENTRY_POINT_NOT_FOUND)
ABORT_FINALIZE(localRet);
+
+ localRet = (*pNew->modQueryEtryPt)((uchar*)"beginTransaction", &pNew->mod.om.beginTransaction);
+ if(localRet == RS_RET_MODULE_ENTRY_POINT_NOT_FOUND)
+ pNew->mod.om.beginTransaction = dummyBeginTransaction;
+ else if(localRet != RS_RET_OK)
+ ABORT_FINALIZE(localRet);
+
+ localRet = (*pNew->modQueryEtryPt)((uchar*)"endTransaction", &pNew->mod.om.endTransaction);
+ if(localRet == RS_RET_MODULE_ENTRY_POINT_NOT_FOUND) {
+ pNew->mod.om.endTransaction = dummyEndTransaction;
+ //pNew->mod.om.beginTransaction = dummyEndTransaction;
+ } else if(localRet != RS_RET_OK) {
+ ABORT_FINALIZE(localRet);
+ }
break;
case eMOD_LIB:
break;
diff --git a/runtime/modules.h b/runtime/modules.h
index 372529ee..71e3199c 100644
--- a/runtime/modules.h
+++ b/runtime/modules.h
@@ -106,11 +106,14 @@ typedef struct modInfo_s {
rsRetVal (*runInput)(thrdInfo_t*); /* function to gather input and submit to queue */
rsRetVal (*willRun)(void); /* function to gather input and submit to queue */
rsRetVal (*afterRun)(thrdInfo_t*); /* function to gather input and submit to queue */
+ int bCanRun; /* cached value of whether willRun() succeeded */
} im;
struct {/* data for output modules */
/* below: perform the configured action
*/
+ rsRetVal (*beginTransaction)(void*);
rsRetVal (*doAction)(uchar**, unsigned, void*);
+ rsRetVal (*endTransaction)(void*);
rsRetVal (*parseSelectorAct)(uchar**, void**,omodStringRequest_t**);
} om;
struct { /* data for library modules */
diff --git a/runtime/msg.c b/runtime/msg.c
index e26fe5ee..d28ee350 100644
--- a/runtime/msg.c
+++ b/runtime/msg.c
@@ -35,6 +35,9 @@
#include <string.h>
#include <assert.h>
#include <ctype.h>
+#if HAVE_MALLOC_H
+# include <malloc.h>
+#endif
#include "rsyslog.h"
#include "srUtils.h"
#include "stringbuf.h"
@@ -46,6 +49,8 @@
#include "regexp.h"
#include "atomic.h"
#include "unicode-helper.h"
+#include "ruleset.h"
+#include "prop.h"
/* static data */
DEFobjStaticHelpers
@@ -53,58 +58,433 @@ DEFobjCurrIf(var)
DEFobjCurrIf(datetime)
DEFobjCurrIf(glbl)
DEFobjCurrIf(regexp)
+DEFobjCurrIf(prop)
+
+static struct {
+ uchar *pszName;
+ short lenName;
+} syslog_pri_names[192] = {
+ { UCHAR_CONSTANT("0"), 3},
+ { UCHAR_CONSTANT("1"), 3},
+ { UCHAR_CONSTANT("2"), 3},
+ { UCHAR_CONSTANT("3"), 3},
+ { UCHAR_CONSTANT("4"), 3},
+ { UCHAR_CONSTANT("5"), 3},
+ { UCHAR_CONSTANT("6"), 3},
+ { UCHAR_CONSTANT("7"), 3},
+ { UCHAR_CONSTANT("8"), 3},
+ { UCHAR_CONSTANT("9"), 3},
+ { UCHAR_CONSTANT("10"), 4},
+ { UCHAR_CONSTANT("11"), 4},
+ { UCHAR_CONSTANT("12"), 4},
+ { UCHAR_CONSTANT("13"), 4},
+ { UCHAR_CONSTANT("14"), 4},
+ { UCHAR_CONSTANT("15"), 4},
+ { UCHAR_CONSTANT("16"), 4},
+ { UCHAR_CONSTANT("17"), 4},
+ { UCHAR_CONSTANT("18"), 4},
+ { UCHAR_CONSTANT("19"), 4},
+ { UCHAR_CONSTANT("20"), 4},
+ { UCHAR_CONSTANT("21"), 4},
+ { UCHAR_CONSTANT("22"), 4},
+ { UCHAR_CONSTANT("23"), 4},
+ { UCHAR_CONSTANT("24"), 4},
+ { UCHAR_CONSTANT("25"), 4},
+ { UCHAR_CONSTANT("26"), 4},
+ { UCHAR_CONSTANT("27"), 4},
+ { UCHAR_CONSTANT("28"), 4},
+ { UCHAR_CONSTANT("29"), 4},
+ { UCHAR_CONSTANT("30"), 4},
+ { UCHAR_CONSTANT("31"), 4},
+ { UCHAR_CONSTANT("32"), 4},
+ { UCHAR_CONSTANT("33"), 4},
+ { UCHAR_CONSTANT("34"), 4},
+ { UCHAR_CONSTANT("35"), 4},
+ { UCHAR_CONSTANT("36"), 4},
+ { UCHAR_CONSTANT("37"), 4},
+ { UCHAR_CONSTANT("38"), 4},
+ { UCHAR_CONSTANT("39"), 4},
+ { UCHAR_CONSTANT("40"), 4},
+ { UCHAR_CONSTANT("41"), 4},
+ { UCHAR_CONSTANT("42"), 4},
+ { UCHAR_CONSTANT("43"), 4},
+ { UCHAR_CONSTANT("44"), 4},
+ { UCHAR_CONSTANT("45"), 4},
+ { UCHAR_CONSTANT("46"), 4},
+ { UCHAR_CONSTANT("47"), 4},
+ { UCHAR_CONSTANT("48"), 4},
+ { UCHAR_CONSTANT("49"), 4},
+ { UCHAR_CONSTANT("50"), 4},
+ { UCHAR_CONSTANT("51"), 4},
+ { UCHAR_CONSTANT("52"), 4},
+ { UCHAR_CONSTANT("53"), 4},
+ { UCHAR_CONSTANT("54"), 4},
+ { UCHAR_CONSTANT("55"), 4},
+ { UCHAR_CONSTANT("56"), 4},
+ { UCHAR_CONSTANT("57"), 4},
+ { UCHAR_CONSTANT("58"), 4},
+ { UCHAR_CONSTANT("59"), 4},
+ { UCHAR_CONSTANT("60"), 4},
+ { UCHAR_CONSTANT("61"), 4},
+ { UCHAR_CONSTANT("62"), 4},
+ { UCHAR_CONSTANT("63"), 4},
+ { UCHAR_CONSTANT("64"), 4},
+ { UCHAR_CONSTANT("65"), 4},
+ { UCHAR_CONSTANT("66"), 4},
+ { UCHAR_CONSTANT("67"), 4},
+ { UCHAR_CONSTANT("68"), 4},
+ { UCHAR_CONSTANT("69"), 4},
+ { UCHAR_CONSTANT("70"), 4},
+ { UCHAR_CONSTANT("71"), 4},
+ { UCHAR_CONSTANT("72"), 4},
+ { UCHAR_CONSTANT("73"), 4},
+ { UCHAR_CONSTANT("74"), 4},
+ { UCHAR_CONSTANT("75"), 4},
+ { UCHAR_CONSTANT("76"), 4},
+ { UCHAR_CONSTANT("77"), 4},
+ { UCHAR_CONSTANT("78"), 4},
+ { UCHAR_CONSTANT("79"), 4},
+ { UCHAR_CONSTANT("80"), 4},
+ { UCHAR_CONSTANT("81"), 4},
+ { UCHAR_CONSTANT("82"), 4},
+ { UCHAR_CONSTANT("83"), 4},
+ { UCHAR_CONSTANT("84"), 4},
+ { UCHAR_CONSTANT("85"), 4},
+ { UCHAR_CONSTANT("86"), 4},
+ { UCHAR_CONSTANT("87"), 4},
+ { UCHAR_CONSTANT("88"), 4},
+ { UCHAR_CONSTANT("89"), 4},
+ { UCHAR_CONSTANT("90"), 4},
+ { UCHAR_CONSTANT("91"), 4},
+ { UCHAR_CONSTANT("92"), 4},
+ { UCHAR_CONSTANT("93"), 4},
+ { UCHAR_CONSTANT("94"), 4},
+ { UCHAR_CONSTANT("95"), 4},
+ { UCHAR_CONSTANT("96"), 4},
+ { UCHAR_CONSTANT("97"), 4},
+ { UCHAR_CONSTANT("98"), 4},
+ { UCHAR_CONSTANT("99"), 4},
+ { UCHAR_CONSTANT("100"), 5},
+ { UCHAR_CONSTANT("101"), 5},
+ { UCHAR_CONSTANT("102"), 5},
+ { UCHAR_CONSTANT("103"), 5},
+ { UCHAR_CONSTANT("104"), 5},
+ { UCHAR_CONSTANT("105"), 5},
+ { UCHAR_CONSTANT("106"), 5},
+ { UCHAR_CONSTANT("107"), 5},
+ { UCHAR_CONSTANT("108"), 5},
+ { UCHAR_CONSTANT("109"), 5},
+ { UCHAR_CONSTANT("110"), 5},
+ { UCHAR_CONSTANT("111"), 5},
+ { UCHAR_CONSTANT("112"), 5},
+ { UCHAR_CONSTANT("113"), 5},
+ { UCHAR_CONSTANT("114"), 5},
+ { UCHAR_CONSTANT("115"), 5},
+ { UCHAR_CONSTANT("116"), 5},
+ { UCHAR_CONSTANT("117"), 5},
+ { UCHAR_CONSTANT("118"), 5},
+ { UCHAR_CONSTANT("119"), 5},
+ { UCHAR_CONSTANT("120"), 5},
+ { UCHAR_CONSTANT("121"), 5},
+ { UCHAR_CONSTANT("122"), 5},
+ { UCHAR_CONSTANT("123"), 5},
+ { UCHAR_CONSTANT("124"), 5},
+ { UCHAR_CONSTANT("125"), 5},
+ { UCHAR_CONSTANT("126"), 5},
+ { UCHAR_CONSTANT("127"), 5},
+ { UCHAR_CONSTANT("128"), 5},
+ { UCHAR_CONSTANT("129"), 5},
+ { UCHAR_CONSTANT("130"), 5},
+ { UCHAR_CONSTANT("131"), 5},
+ { UCHAR_CONSTANT("132"), 5},
+ { UCHAR_CONSTANT("133"), 5},
+ { UCHAR_CONSTANT("134"), 5},
+ { UCHAR_CONSTANT("135"), 5},
+ { UCHAR_CONSTANT("136"), 5},
+ { UCHAR_CONSTANT("137"), 5},
+ { UCHAR_CONSTANT("138"), 5},
+ { UCHAR_CONSTANT("139"), 5},
+ { UCHAR_CONSTANT("140"), 5},
+ { UCHAR_CONSTANT("141"), 5},
+ { UCHAR_CONSTANT("142"), 5},
+ { UCHAR_CONSTANT("143"), 5},
+ { UCHAR_CONSTANT("144"), 5},
+ { UCHAR_CONSTANT("145"), 5},
+ { UCHAR_CONSTANT("146"), 5},
+ { UCHAR_CONSTANT("147"), 5},
+ { UCHAR_CONSTANT("148"), 5},
+ { UCHAR_CONSTANT("149"), 5},
+ { UCHAR_CONSTANT("150"), 5},
+ { UCHAR_CONSTANT("151"), 5},
+ { UCHAR_CONSTANT("152"), 5},
+ { UCHAR_CONSTANT("153"), 5},
+ { UCHAR_CONSTANT("154"), 5},
+ { UCHAR_CONSTANT("155"), 5},
+ { UCHAR_CONSTANT("156"), 5},
+ { UCHAR_CONSTANT("157"), 5},
+ { UCHAR_CONSTANT("158"), 5},
+ { UCHAR_CONSTANT("159"), 5},
+ { UCHAR_CONSTANT("160"), 5},
+ { UCHAR_CONSTANT("161"), 5},
+ { UCHAR_CONSTANT("162"), 5},
+ { UCHAR_CONSTANT("163"), 5},
+ { UCHAR_CONSTANT("164"), 5},
+ { UCHAR_CONSTANT("165"), 5},
+ { UCHAR_CONSTANT("166"), 5},
+ { UCHAR_CONSTANT("167"), 5},
+ { UCHAR_CONSTANT("168"), 5},
+ { UCHAR_CONSTANT("169"), 5},
+ { UCHAR_CONSTANT("170"), 5},
+ { UCHAR_CONSTANT("171"), 5},
+ { UCHAR_CONSTANT("172"), 5},
+ { UCHAR_CONSTANT("173"), 5},
+ { UCHAR_CONSTANT("174"), 5},
+ { UCHAR_CONSTANT("175"), 5},
+ { UCHAR_CONSTANT("176"), 5},
+ { UCHAR_CONSTANT("177"), 5},
+ { UCHAR_CONSTANT("178"), 5},
+ { UCHAR_CONSTANT("179"), 5},
+ { UCHAR_CONSTANT("180"), 5},
+ { UCHAR_CONSTANT("181"), 5},
+ { UCHAR_CONSTANT("182"), 5},
+ { UCHAR_CONSTANT("183"), 5},
+ { UCHAR_CONSTANT("184"), 5},
+ { UCHAR_CONSTANT("185"), 5},
+ { UCHAR_CONSTANT("186"), 5},
+ { UCHAR_CONSTANT("187"), 5},
+ { UCHAR_CONSTANT("188"), 5},
+ { UCHAR_CONSTANT("189"), 5},
+ { UCHAR_CONSTANT("190"), 5},
+ { UCHAR_CONSTANT("191"), 5}
+ };
+
+/*syslog facility names (as of RFC5424) */
+static char *syslog_fac_names[24] = { "kern", "user", "mail", "daemon", "auth", "syslog", "lpr",
+ "news", "uucp", "cron", "authpriv", "ftp", "ntp", "audit",
+ "alert", "clock", "local0", "local1", "local2", "local3",
+ "local4", "local5", "local6", "local7" };
+
+/* table of severity names (in numerical order)*/
+static char *syslog_severity_names[8] = { "emerg", "alert", "crit", "err", "warning", "notice", "info", "debug" };
+
+/* numerical values as string - this is the most efficient approach to convert severity
+ * and facility values to a numerical string... -- rgerhars, 2009-06-17
+ */
-static syslogCODE rs_prioritynames[] =
- {
- { "alert", LOG_ALERT },
- { "crit", LOG_CRIT },
- { "debug", LOG_DEBUG },
- { "emerg", LOG_EMERG },
- { "err", LOG_ERR },
- { "error", LOG_ERR }, /* DEPRECATED */
- { "info", LOG_INFO },
- { "none", INTERNAL_NOPRI }, /* INTERNAL */
- { "notice", LOG_NOTICE },
- { "panic", LOG_EMERG }, /* DEPRECATED */
- { "warn", LOG_WARNING }, /* DEPRECATED */
- { "warning", LOG_WARNING },
- { NULL, -1 }
- };
-
-#ifndef LOG_AUTHPRIV
-# define LOG_AUTHPRIV LOG_AUTH
-#endif
-static syslogCODE rs_facilitynames[] =
- {
- { "auth", LOG_AUTH },
- { "authpriv", LOG_AUTHPRIV },
- { "cron", LOG_CRON },
- { "daemon", LOG_DAEMON },
-#if defined(LOG_FTP)
- {"ftp", LOG_FTP},
-#endif
- { "kern", LOG_KERN },
- { "lpr", LOG_LPR },
- { "mail", LOG_MAIL },
- { "news", LOG_NEWS },
- { "security", LOG_AUTH }, /* DEPRECATED */
- { "syslog", LOG_SYSLOG },
- { "user", LOG_USER },
- { "uucp", LOG_UUCP },
- { "local0", LOG_LOCAL0 },
- { "local1", LOG_LOCAL1 },
- { "local2", LOG_LOCAL2 },
- { "local3", LOG_LOCAL3 },
- { "local4", LOG_LOCAL4 },
- { "local5", LOG_LOCAL5 },
- { "local6", LOG_LOCAL6 },
- { "local7", LOG_LOCAL7 },
- { NULL, -1 }
- };
+static char *syslog_number_names[24] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14",
+ "15", "16", "17", "18", "19", "20", "21", "22", "23" };
/* some forward declarations */
-static int getAPPNAMELen(msg_t *pM);
-static int getProtocolVersion(msg_t *pM);
+static int getAPPNAMELen(msg_t *pM, bool bLockMutex);
+
+
+static inline int getProtocolVersion(msg_t *pM)
+{
+ return(pM->iProtocolVersion);
+}
+
+
+static inline void
+getInputName(msg_t *pM, uchar **ppsz, int *plen)
+{
+ BEGINfunc
+ if(pM == NULL) {
+ *ppsz = UCHAR_CONSTANT("");
+ *plen = 0;
+ } else {
+ prop.GetString(pM->pInputName, ppsz, plen);
+ }
+ ENDfunc
+}
+
+
+static inline uchar*
+getRcvFromIP(msg_t *pM)
+{
+ uchar *psz;
+ int len;
+ BEGINfunc
+ if(pM == NULL) {
+ psz = UCHAR_CONSTANT("");
+ } else {
+ if(pM->pRcvFromIP == NULL)
+ psz = UCHAR_CONSTANT("");
+ else
+ prop.GetString(pM->pRcvFromIP, &psz, &len);
+ }
+ ENDfunc
+ return psz;
+}
+
+
+
+/* map a property name (string) to a property ID */
+rsRetVal propNameToID(cstr_t *pCSPropName, propid_t *pPropID)
+{
+ uchar *pName;
+ DEFiRet;
+
+ assert(pCSPropName != NULL);
+ assert(pPropID != NULL);
+ pName = rsCStrGetSzStrNoNULL(pCSPropName);
+
+ /* sometimes there are aliases to the original MonitoWare
+ * property names. These come after || in the ifs below. */
+ if(!strcmp((char*) pName, "msg")) {
+ *pPropID = PROP_MSG;
+ } else if(!strcmp((char*) pName, "timestamp")
+ || !strcmp((char*) pName, "timereported")) {
+ *pPropID = PROP_TIMESTAMP;
+ } else if(!strcmp((char*) pName, "hostname") || !strcmp((char*) pName, "source")) {
+ *pPropID = PROP_HOSTNAME;
+ } else if(!strcmp((char*) pName, "syslogtag")) {
+ *pPropID = PROP_SYSLOGTAG;
+ } else if(!strcmp((char*) pName, "rawmsg")) {
+ *pPropID = PROP_RAWMSG;
+ /* 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")) {
+ *pPropID = PROP_INPUTNAME;
+ } else if(!strcmp((char*) pName, "fromhost")) {
+ *pPropID = PROP_FROMHOST;
+ } else if(!strcmp((char*) pName, "fromhost-ip")) {
+ *pPropID = PROP_FROMHOST_IP;
+ } else if(!strcmp((char*) pName, "pri")) {
+ *pPropID = PROP_PRI;
+ } else if(!strcmp((char*) pName, "pri-text")) {
+ *pPropID = PROP_PRI_TEXT;
+ } else if(!strcmp((char*) pName, "iut")) {
+ *pPropID = PROP_IUT;
+ } else if(!strcmp((char*) pName, "syslogfacility")) {
+ *pPropID = PROP_SYSLOGFACILITY;
+ } else if(!strcmp((char*) pName, "syslogfacility-text")) {
+ *pPropID = PROP_SYSLOGFACILITY_TEXT;
+ } else if(!strcmp((char*) pName, "syslogseverity") || !strcmp((char*) pName, "syslogpriority")) {
+ *pPropID = PROP_SYSLOGSEVERITY;
+ } else if(!strcmp((char*) pName, "syslogseverity-text") || !strcmp((char*) pName, "syslogpriority-text")) {
+ *pPropID = PROP_SYSLOGSEVERITY_TEXT;
+ } else if(!strcmp((char*) pName, "timegenerated")) {
+ *pPropID = PROP_TIMEGENERATED;
+ } else if(!strcmp((char*) pName, "programname")) {
+ *pPropID = PROP_PROGRAMNAME;
+ } else if(!strcmp((char*) pName, "protocol-version")) {
+ *pPropID = PROP_PROTOCOL_VERSION;
+ } else if(!strcmp((char*) pName, "structured-data")) {
+ *pPropID = PROP_STRUCTURED_DATA;
+ } else if(!strcmp((char*) pName, "app-name")) {
+ *pPropID = PROP_APP_NAME;
+ } else if(!strcmp((char*) pName, "procid")) {
+ *pPropID = PROP_PROCID;
+ } else if(!strcmp((char*) pName, "msgid")) {
+ *pPropID = PROP_MSGID;
+ /* here start system properties (those, that do not relate to the message itself */
+ } else if(!strcmp((char*) pName, "$now")) {
+ *pPropID = PROP_SYS_NOW;
+ } else if(!strcmp((char*) pName, "$year")) {
+ *pPropID = PROP_SYS_YEAR;
+ } else if(!strcmp((char*) pName, "$month")) {
+ *pPropID = PROP_SYS_MONTH;
+ } else if(!strcmp((char*) pName, "$day")) {
+ *pPropID = PROP_SYS_DAY;
+ } else if(!strcmp((char*) pName, "$hour")) {
+ *pPropID = PROP_SYS_HOUR;
+ } else if(!strcmp((char*) pName, "$hhour")) {
+ *pPropID = PROP_SYS_HHOUR;
+ } else if(!strcmp((char*) pName, "$qhour")) {
+ *pPropID = PROP_SYS_QHOUR;
+ } else if(!strcmp((char*) pName, "$minute")) {
+ *pPropID = PROP_SYS_MINUTE;
+ } else if(!strcmp((char*) pName, "$myhostname")) {
+ *pPropID = PROP_SYS_MYHOSTNAME;
+ } else {
+ *pPropID = PROP_INVALID;
+ iRet = RS_RET_VAR_NOT_FOUND;
+ }
+
+ RETiRet;
+}
+
+
+/* map a property ID to a name string (useful for displaying) */
+uchar *propIDToName(propid_t propID)
+{
+ switch(propID) {
+ case PROP_MSG:
+ return UCHAR_CONSTANT("msg");
+ case PROP_TIMESTAMP:
+ return UCHAR_CONSTANT("timestamp");
+ case PROP_HOSTNAME:
+ return UCHAR_CONSTANT("hostname");
+ case PROP_SYSLOGTAG:
+ return UCHAR_CONSTANT("syslogtag");
+ case PROP_RAWMSG:
+ return UCHAR_CONSTANT("rawmsg");
+ /* enable this, if someone actually uses UxTradMsg, delete after some time has
+ * passed and nobody complained -- rgerhards, 2009-06-16
+ case PROP_UXTRADMSG:
+ pRes = getUxTradMsg(pMsg);
+ break;
+ */
+ case PROP_INPUTNAME:
+ return UCHAR_CONSTANT("inputname");
+ case PROP_FROMHOST:
+ return UCHAR_CONSTANT("fromhost");
+ case PROP_FROMHOST_IP:
+ return UCHAR_CONSTANT("fromhost-ip");
+ case PROP_PRI:
+ return UCHAR_CONSTANT("pri");
+ case PROP_PRI_TEXT:
+ return UCHAR_CONSTANT("pri-text");
+ case PROP_IUT:
+ return UCHAR_CONSTANT("iut");
+ case PROP_SYSLOGFACILITY:
+ return UCHAR_CONSTANT("syslogfacility");
+ case PROP_SYSLOGFACILITY_TEXT:
+ return UCHAR_CONSTANT("syslogfacility-text");
+ case PROP_SYSLOGSEVERITY:
+ return UCHAR_CONSTANT("syslogseverity");
+ case PROP_SYSLOGSEVERITY_TEXT:
+ return UCHAR_CONSTANT("syslogseverity-text");
+ case PROP_TIMEGENERATED:
+ return UCHAR_CONSTANT("timegenerated");
+ case PROP_PROGRAMNAME:
+ return UCHAR_CONSTANT("programname");
+ case PROP_PROTOCOL_VERSION:
+ return UCHAR_CONSTANT("protocol-version");
+ case PROP_STRUCTURED_DATA:
+ return UCHAR_CONSTANT("structured-data");
+ case PROP_APP_NAME:
+ return UCHAR_CONSTANT("app-name");
+ case PROP_PROCID:
+ return UCHAR_CONSTANT("procid");
+ case PROP_MSGID:
+ return UCHAR_CONSTANT("msgid");
+ case PROP_SYS_NOW:
+ return UCHAR_CONSTANT("$NOW");
+ case PROP_SYS_YEAR:
+ return UCHAR_CONSTANT("$YEAR");
+ case PROP_SYS_MONTH:
+ return UCHAR_CONSTANT("$MONTH");
+ case PROP_SYS_DAY:
+ return UCHAR_CONSTANT("$DAY");
+ case PROP_SYS_HOUR:
+ return UCHAR_CONSTANT("$HOUR");
+ case PROP_SYS_HHOUR:
+ return UCHAR_CONSTANT("$HHOUR");
+ case PROP_SYS_QHOUR:
+ return UCHAR_CONSTANT("$QHOUR");
+ case PROP_SYS_MINUTE:
+ return UCHAR_CONSTANT("$MINUTE");
+ case PROP_SYS_MYHOSTNAME:
+ return UCHAR_CONSTANT("$MYHOSTNAME");
+ default:
+ return UCHAR_CONSTANT("*invalid property id*");
+ }
+}
+
/* The following functions will support advanced output module
* multithreading, once this is implemented. Currently, we
@@ -166,32 +546,9 @@ static void MsgLockingDummy(msg_t __attribute__((unused)) *pMsg)
*/
static void MsgPrepareEnqueueLockingCase(msg_t *pThis)
{
- int iErr;
BEGINfunc
assert(pThis != NULL);
- iErr = pthread_mutexattr_init(&pThis->mutAttr);
- if(iErr != 0) {
- dbgprintf("error initializing mutex attribute in %s:%d, trying to continue\n",
- __FILE__, __LINE__);
- }
- iErr = pthread_mutexattr_settype(&pThis->mutAttr, PTHREAD_MUTEX_RECURSIVE);
- if(iErr != 0) {
- dbgprintf("ERROR setting mutex attribute to recursive in %s:%d, trying to continue "
- "but we will probably either abort or hang soon\n",
- __FILE__, __LINE__);
- /* TODO: it makes very little sense to continue here,
- * but it requires an iRet interface to gracefully shut
- * down. We should do that over time. -- rgerhards, 2008-07-14
- */
- }
- pthread_mutex_init(&pThis->mut, &pThis->mutAttr);
-
- /* we do no longer need the attribute. According to the
- * POSIX spec, we can destroy it without affecting the
- * initialized mutex (that used the attribute).
- * rgerhards, 2008-07-14
- */
- pthread_mutexattr_destroy(&pThis->mutAttr);
+ pthread_mutex_init(&pThis->mut, NULL);
pThis->bDoLock = 1;
ENDfunc
}
@@ -256,6 +613,11 @@ rsRetVal MsgEnableThreadSafety(void)
* itself but rather uses a user-supplied value. This enables the caller
* to do some tricks to save processing time (done, for example, in the
* udp input).
+ * NOTE: this constructor does NOT call calloc(), as we have many bytes
+ * inside the structure which do not need to be cleared. bzero() will
+ * heavily thrash the cache, so we do the init manually (which also
+ * is the right thing to do with pointers, as they are not neccessarily
+ * a binary 0 on all machines [but today almost always...]).
* rgerhards, 2008-10-06
*/
static inline rsRetVal msgBaseConstruct(msg_t **ppThis)
@@ -264,14 +626,50 @@ static inline rsRetVal msgBaseConstruct(msg_t **ppThis)
msg_t *pM;
assert(ppThis != NULL);
- if((pM = calloc(1, sizeof(msg_t))) == NULL)
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ CHKmalloc(pM = malloc(sizeof(msg_t)));
+ objConstructSetObjInfo(pM); /* intialize object helper entities */
- /* initialize members that are non-zero */
+ /* initialize members in ORDER they appear in structure (think "cache line"!) */
+ pM->flowCtlType = 0;
+ pM->bDoLock = 0;
+ pM->bParseHOSTNAME = 0;
pM->iRefCount = 1;
pM->iSeverity = -1;
pM->iFacility = -1;
- objConstructSetObjInfo(pM);
+ pM->offAfterPRI = 0;
+ pM->offMSG = -1;
+ pM->iProtocolVersion = 0;
+ pM->msgFlags = 0;
+ pM->iLenRawMsg = 0;
+ pM->iLenMSG = 0;
+ pM->iLenTAG = 0;
+ pM->iLenHOSTNAME = 0;
+ pM->pszRawMsg = NULL;
+ pM->pszHOSTNAME = NULL;
+ pM->pszRcvdAt3164 = NULL;
+ pM->pszRcvdAt3339 = NULL;
+ pM->pszRcvdAt_MySQL = NULL;
+ pM->pszRcvdAt_PgSQL = NULL;
+ pM->pszTIMESTAMP3164 = NULL;
+ pM->pszTIMESTAMP3339 = NULL;
+ pM->pszTIMESTAMP_MySQL = NULL;
+ pM->pszTIMESTAMP_PgSQL = NULL;
+ pM->pCSProgName = NULL;
+ pM->pCSStrucData = NULL;
+ pM->pCSAPPNAME = NULL;
+ pM->pCSPROCID = NULL;
+ pM->pCSMSGID = NULL;
+ pM->pInputName = NULL;
+ pM->pRcvFromIP = NULL;
+ pM->pRcvFrom = NULL;
+ pM->pRuleset = NULL;
+ memset(&pM->tRcvdAt, 0, sizeof(pM->tRcvdAt));
+ memset(&pM->tTIMESTAMP, 0, sizeof(pM->tTIMESTAMP));
+ pM->TAG.pszTAG = NULL;
+ pM->pszTimestamp3164[0] = '\0';
+ pM->pszTimestamp3339[0] = '\0';
+ pM->pszTIMESTAMP_SecFrac[0] = '\0';
+ pM->pszRcvdAt_SecFrac[0] = '\0';
/* DEV debugging only! dbgprintf("msgConstruct\t0x%x, ref 1\n", (int)pM);*/
@@ -330,6 +728,21 @@ finalize_it:
}
+/* some free handlers for (slightly) complicated cases... All of them may be called
+ * with an empty element.
+ */
+static inline void freeTAG(msg_t *pThis)
+{
+ if(pThis->iLenTAG >= CONF_TAG_BUFSIZE)
+ free(pThis->TAG.pszTAG);
+}
+static inline void freeHOSTNAME(msg_t *pThis)
+{
+ if(pThis->iLenHOSTNAME >= CONF_HOSTNAME_BUFSIZE)
+ free(pThis->pszHOSTNAME);
+}
+
+
BEGINobjDestruct(msg) /* be sure to specify the object type also in END and CODESTART macros! */
int currRefCount;
CODESTARTobjDestruct(msg)
@@ -343,25 +756,20 @@ CODESTARTobjDestruct(msg)
if(currRefCount == 0)
{
/* DEV Debugging Only! dbgprintf("msgDestruct\t0x%lx, RefCount now 0, doing DESTROY\n", (unsigned long)pThis); */
- free(pThis->pszRawMsg);
- free(pThis->pszTAG);
- free(pThis->pszHOSTNAME);
- free(pThis->pszInputName);
- free(pThis->pszRcvFrom);
- free(pThis->pszRcvFromIP);
- free(pThis->pszMSG);
- free(pThis->pszFacility);
- free(pThis->pszFacilityStr);
- free(pThis->pszSeverity);
- free(pThis->pszSeverityStr);
+ if(pThis->pszRawMsg != pThis->szRawMsg)
+ free(pThis->pszRawMsg);
+ freeTAG(pThis);
+ freeHOSTNAME(pThis);
+ if(pThis->pInputName != NULL)
+ prop.Destruct(&pThis->pInputName);
+ if(pThis->pRcvFrom != NULL)
+ prop.Destruct(&pThis->pRcvFrom);
+ if(pThis->pRcvFromIP != NULL)
+ prop.Destruct(&pThis->pRcvFromIP);
free(pThis->pszRcvdAt3164);
free(pThis->pszRcvdAt3339);
- free(pThis->pszRcvdAt_SecFrac);
free(pThis->pszRcvdAt_MySQL);
free(pThis->pszRcvdAt_PgSQL);
- free(pThis->pszTIMESTAMP3164);
- free(pThis->pszTIMESTAMP3339);
- free(pThis->pszTIMESTAMP_SecFrac);
free(pThis->pszTIMESTAMP_MySQL);
free(pThis->pszTIMESTAMP_PgSQL);
if(pThis->pCSProgName != NULL)
@@ -378,6 +786,25 @@ CODESTARTobjDestruct(msg)
MsgUnlock(pThis);
# endif
funcDeleteMutex(pThis);
+ /* now we need to do our own optimization. Testing has shown that at least the glibc
+ * malloc() subsystem returns memory to the OS far too late in our case. So we need
+ * to help it a bit, by calling malloc_trim(), which will tell the alloc subsystem
+ * to consolidate and return to the OS. We keep 128K for our use, as a safeguard
+ * to too-frequent reallocs. But more importantly, we call this hook only every
+ * 100,000 messages (which is an approximation, as we do not work with atomic
+ * operations on the counter. --- rgerhards, 2009-06-22.
+ */
+# if HAVE_MALLOC_TRIM
+ { /* standard C requires a new block for a new variable definition!
+ * To simplify matters, we use modulo arithmetic and live with the fact
+ * that we trim too often when the counter wraps.
+ */
+ static unsigned iTrimCtr = 1;
+ if(ATOMIC_INC_AND_FETCH(iTrimCtr) % 100000 == 0) {
+ malloc_trim(128*1024);
+ }
+ }
+# endif
} else {
# ifndef HAVE_ATOMIC_BUILTINS
MsgUnlock(pThis);
@@ -438,21 +865,50 @@ 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
+ pNew->offMSG = pOld->offMSG;
+ pNew->iLenRawMsg = pOld->iLenRawMsg;
+ pNew->iLenMSG = pOld->iLenMSG;
+ pNew->iLenTAG = pOld->iLenTAG;
+ pNew->iLenHOSTNAME = pOld->iLenHOSTNAME;
+ if(pOld->pRcvFrom != NULL) {
+ pNew->pRcvFrom = pOld->pRcvFrom;
+ prop.AddRef(pNew->pRcvFrom);
+ }
+ if(pOld->pRcvFromIP != NULL) {
+ pNew->pRcvFromIP = pOld->pRcvFromIP;
+ prop.AddRef(pNew->pRcvFromIP);
+ }
+ if(pOld->pInputName != NULL) {
+ pNew->pInputName = pOld->pInputName;
+ prop.AddRef(pNew->pInputName);
+ }
+ /* 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(RawMsg);
- tmpCOPYSZ(MSG);
- tmpCOPYSZ(TAG);
- tmpCOPYSZ(HOSTNAME);
- tmpCOPYSZ(RcvFrom);
+ if(pOld->iLenTAG > 0) {
+ if(pOld->iLenTAG < CONF_TAG_BUFSIZE) {
+ memcpy(pNew->TAG.szBuf, pOld->TAG.szBuf, pOld->iLenTAG);
+ } else {
+ if((pNew->TAG.pszTAG = srUtilStrDup(pOld->TAG.pszTAG, pOld->iLenTAG)) == NULL) {
+ msgDestruct(&pNew);
+ return NULL;
+ }
+ pNew->iLenTAG = pOld->iLenTAG;
+ }
+ }
+ if(pOld->iLenRawMsg < CONF_RAWMSG_BUFSIZE) {
+ memcpy(pNew->szRawMsg, pOld->szRawMsg, pOld->iLenRawMsg + 1);
+ pNew->pszRawMsg = pNew->szRawMsg;
+ } else {
+ tmpCOPYSZ(RawMsg);
+ }
+ if(pOld->iLenHOSTNAME < CONF_HOSTNAME_BUFSIZE) {
+ memcpy(pNew->szHOSTNAME, pOld->szHOSTNAME, pOld->iLenHOSTNAME + 1);
+ pNew->pszHOSTNAME = pNew->szHOSTNAME;
+ } else {
+ tmpCOPYSZ(HOSTNAME);
+ }
tmpCOPYCSTR(ProgName);
tmpCOPYCSTR(StrucData);
@@ -485,11 +941,14 @@ msg_t* MsgDup(msg_t* pOld)
*/
static rsRetVal MsgSerialize(msg_t *pThis, strm_t *pStrm)
{
+ uchar *psz;
+ int len;
DEFiRet;
assert(pThis != NULL);
assert(pStrm != NULL);
+ /* then serialize elements */
CHKiRet(obj.BeginSerialize(pStrm, (obj_t*) pThis));
objSerializeSCALAR(pStrm, iProtocolVersion, SHORT);
objSerializeSCALAR(pStrm, iSeverity, SHORT);
@@ -503,19 +962,28 @@ static rsRetVal MsgSerialize(msg_t *pThis, strm_t *pStrm)
objSerializeSCALAR(pStrm, offsAfterPRI, SHORT);
*/
+ CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("pszTAG"), PROPTYPE_PSZ, (void*)
+ ((pThis->iLenTAG < CONF_TAG_BUFSIZE) ? pThis->TAG.szBuf : pThis->TAG.pszTAG)));
+
objSerializePTR(pStrm, pszRawMsg, PSZ);
- objSerializePTR(pStrm, pszMSG, PSZ);
- objSerializePTR(pStrm, pszTAG, PSZ);
objSerializePTR(pStrm, pszHOSTNAME, PSZ);
- objSerializePTR(pStrm, pszInputName, PSZ);
- objSerializePTR(pStrm, pszRcvFrom, PSZ);
- objSerializePTR(pStrm, pszRcvFromIP, PSZ);
+ getInputName(pThis, &psz, &len);
+ CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("pszInputName"), PROPTYPE_PSZ, (void*) psz));
+ psz = getRcvFrom(pThis);
+ CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("pszRcvFrom"), PROPTYPE_PSZ, (void*) psz));
+ psz = getRcvFromIP(pThis);
+ CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("pszRcvFromIP"), PROPTYPE_PSZ, (void*) psz));
objSerializePTR(pStrm, pCSStrucData, CSTR);
objSerializePTR(pStrm, pCSAPPNAME, CSTR);
objSerializePTR(pStrm, pCSPROCID, CSTR);
objSerializePTR(pStrm, pCSMSGID, CSTR);
+ /* offset must be serialized after pszRawMsg, because we need that to obtain the correct
+ * MSG size.
+ */
+ objSerializeSCALAR(pStrm, offMSG, SHORT);
+
CHKiRet(obj.EndSerialize(pStrm));
finalize_it:
@@ -554,22 +1022,27 @@ msg_t *MsgAddRef(msg_t *pM)
* can obtain a PROCID. Take in mind that not every legacy syslog message
* actually has a PROCID.
* rgerhards, 2005-11-24
+ * THIS MUST be called with the message lock locked.
*/
static rsRetVal aquirePROCIDFromTAG(msg_t *pM)
{
register int i;
+ uchar *pszTag;
DEFiRet;
assert(pM != NULL);
+
if(pM->pCSPROCID != NULL)
return RS_RET_OK; /* we are already done ;) */
if(getProtocolVersion(pM) != 0)
return RS_RET_OK; /* we can only emulate if we have legacy format */
+ pszTag = (uchar*) ((pM->iLenTAG < CONF_TAG_BUFSIZE) ? pM->TAG.szBuf : pM->TAG.pszTAG);
+
/* find first '['... */
i = 0;
- while((i < pM->iLenTAG) && (pM->pszTAG[i] != '['))
+ while((i < pM->iLenTAG) && (pszTag[i] != '['))
++i;
if(!(i < pM->iLenTAG))
return RS_RET_OK; /* no [, so can not emulate... */
@@ -577,10 +1050,9 @@ static rsRetVal aquirePROCIDFromTAG(msg_t *pM)
++i; /* skip '[' */
/* now obtain the PROCID string... */
- CHKiRet(rsCStrConstruct(&pM->pCSPROCID));
- rsCStrSetAllocIncrement(pM->pCSPROCID, 16);
- while((i < pM->iLenTAG) && (pM->pszTAG[i] != ']')) {
- CHKiRet(rsCStrAppendChar(pM->pCSPROCID, pM->pszTAG[i]));
+ CHKiRet(cstrConstruct(&pM->pCSPROCID));
+ while((i < pM->iLenTAG) && (pszTag[i] != ']')) {
+ CHKiRet(cstrAppendChar(pM->pCSPROCID, pszTag[i]));
++i;
}
@@ -590,7 +1062,7 @@ static rsRetVal aquirePROCIDFromTAG(msg_t *pM)
* the buffer and simply return. Note that this is NOT an error
* case!
*/
- rsCStrDestruct(&pM->pCSPROCID);
+ cstrDestruct(&pM->pCSPROCID);
FINALIZE;
}
@@ -615,27 +1087,27 @@ finalize_it:
* The program name is not parsed by default, because it is infrequently-used.
* If it is needed, this function should be called first. It checks if it is
* already set and extracts it, if not.
- * A message object must be provided, else a crash will occur.
+ *
+ * IMPORTANT: A locked message object must be provided, else a crash will occur.
* rgerhards, 2005-10-19
*/
static rsRetVal aquireProgramName(msg_t *pM)
{
- DEFiRet;
register int i;
+ uchar *pszTag;
+ DEFiRet;
assert(pM != NULL);
if(pM->pCSProgName == NULL) {
- /* ok, we do not yet have it. So let's parse the TAG
- * to obtain it.
- */
- CHKiRet(rsCStrConstruct(&pM->pCSProgName));
- rsCStrSetAllocIncrement(pM->pCSProgName, 33);
+ /* ok, we do not yet have it. So let's parse the TAG to obtain it. */
+ pszTag = (uchar*) ((pM->iLenTAG < CONF_TAG_BUFSIZE) ? pM->TAG.szBuf : pM->TAG.pszTAG);
+ CHKiRet(cstrConstruct(&pM->pCSProgName));
for( i = 0
- ; (i < pM->iLenTAG) && isprint((int) pM->pszTAG[i])
- && (pM->pszTAG[i] != '\0') && (pM->pszTAG[i] != ':')
- && (pM->pszTAG[i] != '[') && (pM->pszTAG[i] != '/')
+ ; (i < pM->iLenTAG) && isprint((int) pszTag[i])
+ && (pszTag[i] != '\0') && (pszTag[i] != ':')
+ && (pszTag[i] != '[') && (pszTag[i] != '/')
; ++i) {
- CHKiRet(rsCStrAppendChar(pM->pCSProgName, pM->pszTAG[i]));
+ CHKiRet(cstrAppendChar(pM->pCSProgName, pszTag[i]));
}
CHKiRet(cstrFinalize(pM->pCSProgName));
}
@@ -644,28 +1116,6 @@ finalize_it:
}
-/* This function moves the HOSTNAME inside the message object to the
- * TAG. It is a specialised function used to handle the condition when
- * a message without HOSTNAME is being processed. The missing HOSTNAME
- * is only detected at a later stage, during TAG processing, so that
- * we already had set the HOSTNAME property and now need to move it to
- * the TAG. Of course, we could do this via a couple of get/set methods,
- * but it is far more efficient to do it via this specialised method.
- * This is especially important as this can be a very common case, e.g.
- * when BSD syslog is acting as a sender.
- * rgerhards, 2005-11-10.
- */
-void moveHOSTNAMEtoTAG(msg_t *pM)
-{
- assert(pM != NULL);
- if(pM->pszTAG != NULL)
- free(pM->pszTAG);
- pM->pszTAG = pM->pszHOSTNAME;
- pM->iLenTAG = pM->iLenHOSTNAME;
- pM->pszHOSTNAME = NULL;
- pM->iLenHOSTNAME = 0;
-}
-
/* Access methods - dumb & easy, not a comment for each ;)
*/
void setProtocolVersion(msg_t *pM, int iNewVersion)
@@ -678,12 +1128,6 @@ void setProtocolVersion(msg_t *pM, int iNewVersion)
pM->iProtocolVersion = iNewVersion;
}
-static int getProtocolVersion(msg_t *pM)
-{
- assert(pM != NULL);
- return(pM->iProtocolVersion);
-}
-
/* note: string is taken from constant pool, do NOT free */
char *getProtocolVersionString(msg_t *pM)
{
@@ -691,21 +1135,22 @@ char *getProtocolVersionString(msg_t *pM)
return(pM->iProtocolVersion ? "1" : "0");
}
-int getMSGLen(msg_t *pM)
-{
- return((pM == NULL) ? 0 : pM->iLenMSG);
-}
-
-static char *getRawMsg(msg_t *pM)
+static inline void
+getRawMsg(msg_t *pM, uchar **pBuf, int *piLen)
{
- if(pM == NULL)
- return "";
- else
- if(pM->pszRawMsg == NULL)
- return "";
- else
- return (char*)pM->pszRawMsg;
+ if(pM == NULL) {
+ *pBuf= UCHAR_CONSTANT("");
+ *piLen = 0;
+ } else {
+ if(pM->pszRawMsg == NULL) {
+ *pBuf= UCHAR_CONSTANT("");
+ *piLen = 0;
+ } else {
+ *pBuf = pM->pszRawMsg;
+ *piLen = pM->iLenRawMsg;
+ }
+ }
}
@@ -720,42 +1165,48 @@ char *getUxTradMsg(msg_t *pM)
}
*/
-char *getMSG(msg_t *pM)
+
+int getMSGLen(msg_t *pM)
+{
+ return((pM == NULL) ? 0 : pM->iLenMSG);
+}
+
+uchar *getMSG(msg_t *pM)
{
+ uchar *ret;
if(pM == NULL)
- return "";
- else
- if(pM->pszMSG == NULL)
- return "";
+ ret = UCHAR_CONSTANT("");
+ else {
+ if(pM->iLenMSG == 0)
+ ret = UCHAR_CONSTANT("");
else
- return (char*)pM->pszMSG;
+ ret = pM->pszRawMsg + pM->offMSG;
+ }
+ return ret;
}
/* Get PRI value as integer */
static int getPRIi(msg_t *pM)
{
- assert(pM != NULL);
return (pM->iFacility << 3) + (pM->iSeverity);
}
-/* Get PRI value in text form */
+/* Get PRI value in text form
+ */
static inline char *getPRI(msg_t *pM)
{
+ /* PRI is a number in the range 0..191. Thus, we use a simple lookup table to obtain the
+ * string value. It looks a bit clumpsy here in code ;)
+ */
+ int iPRI;
+
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->bufPRI[0] == '\0') {
- snprintf((char*)pM->bufPRI, sizeof(pM->bufPRI), "%d", getPRIi(pM));
- }
- MsgUnlock(pM);
-
- return (char*)pM->bufPRI;
+ iPRI = getPRIi(pM);
+ return (iPRI > 191) ? "invld" : (char*)syslog_pri_names[iPRI].pszName;
}
@@ -767,13 +1218,11 @@ static inline char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt)
switch(eFmt) {
case tplFmtDefault:
+ case tplFmtRFC3164Date:
MsgLock(pM);
if(pM->pszTIMESTAMP3164 == NULL) {
- if((pM->pszTIMESTAMP3164 = malloc(16)) == NULL) {
- MsgUnlock(pM);
- return "";
- }
- datetime.formatTimestamp3164(&pM->tTIMESTAMP, pM->pszTIMESTAMP3164, 16);
+ pM->pszTIMESTAMP3164 = pM->pszTimestamp3164;
+ datetime.formatTimestamp3164(&pM->tTIMESTAMP, pM->pszTIMESTAMP3164);
}
MsgUnlock(pM);
return(pM->pszTIMESTAMP3164);
@@ -784,7 +1233,7 @@ static inline char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt)
MsgUnlock(pM);
return "";
}
- datetime.formatTimestampToMySQL(&pM->tTIMESTAMP, pM->pszTIMESTAMP_MySQL, 15);
+ datetime.formatTimestampToMySQL(&pM->tTIMESTAMP, pM->pszTIMESTAMP_MySQL);
}
MsgUnlock(pM);
return(pM->pszTIMESTAMP_MySQL);
@@ -795,42 +1244,27 @@ static inline char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt)
MsgUnlock(pM);
return "";
}
- datetime.formatTimestampToPgSQL(&pM->tTIMESTAMP, pM->pszTIMESTAMP_PgSQL, 21);
+ datetime.formatTimestampToPgSQL(&pM->tTIMESTAMP, pM->pszTIMESTAMP_PgSQL);
}
MsgUnlock(pM);
return(pM->pszTIMESTAMP_PgSQL);
- case tplFmtRFC3164Date:
- MsgLock(pM);
- if(pM->pszTIMESTAMP3164 == NULL) {
- if((pM->pszTIMESTAMP3164 = malloc(16)) == NULL) {
- MsgUnlock(pM);
- return "";
- }
- datetime.formatTimestamp3164(&pM->tTIMESTAMP, pM->pszTIMESTAMP3164, 16);
- }
- MsgUnlock(pM);
- return(pM->pszTIMESTAMP3164);
case tplFmtRFC3339Date:
MsgLock(pM);
if(pM->pszTIMESTAMP3339 == NULL) {
- if((pM->pszTIMESTAMP3339 = malloc(33)) == NULL) {
- MsgUnlock(pM);
- return ""; /* TODO: check this: can it cause a free() of constant memory?) */
- }
- datetime.formatTimestamp3339(&pM->tTIMESTAMP, pM->pszTIMESTAMP3339, 33);
+ pM->pszTIMESTAMP3339 = pM->pszTimestamp3339;
+ datetime.formatTimestamp3339(&pM->tTIMESTAMP, pM->pszTIMESTAMP3339);
}
MsgUnlock(pM);
return(pM->pszTIMESTAMP3339);
case tplFmtSecFrac:
- MsgLock(pM);
- if(pM->pszTIMESTAMP_SecFrac == NULL) {
- if((pM->pszTIMESTAMP_SecFrac = malloc(10)) == NULL) {
- MsgUnlock(pM);
- return ""; /* TODO: check this: can it cause a free() of constant memory?) */
+ if(pM->pszTIMESTAMP_SecFrac[0] == '\0') {
+ MsgLock(pM);
+ /* re-check, may have changed while we did not hold lock */
+ if(pM->pszTIMESTAMP_SecFrac[0] == '\0') {
+ datetime.formatTimestampSecFrac(&pM->tTIMESTAMP, pM->pszTIMESTAMP_SecFrac);
}
- datetime.formatTimestampSecFrac(&pM->tTIMESTAMP, pM->pszTIMESTAMP_SecFrac, 10);
+ MsgUnlock(pM);
}
- MsgUnlock(pM);
return(pM->pszTIMESTAMP_SecFrac);
}
ENDfunc
@@ -851,7 +1285,7 @@ static inline char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt)
MsgUnlock(pM);
return "";
}
- datetime.formatTimestamp3164(&pM->tRcvdAt, pM->pszRcvdAt3164, 16);
+ datetime.formatTimestamp3164(&pM->tRcvdAt, pM->pszRcvdAt3164);
}
MsgUnlock(pM);
return(pM->pszRcvdAt3164);
@@ -862,7 +1296,7 @@ static inline char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt)
MsgUnlock(pM);
return "";
}
- datetime.formatTimestampToMySQL(&pM->tRcvdAt, pM->pszRcvdAt_MySQL, 15);
+ datetime.formatTimestampToMySQL(&pM->tRcvdAt, pM->pszRcvdAt_MySQL);
}
MsgUnlock(pM);
return(pM->pszRcvdAt_MySQL);
@@ -873,7 +1307,7 @@ static inline char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt)
MsgUnlock(pM);
return "";
}
- datetime.formatTimestampToPgSQL(&pM->tRcvdAt, pM->pszRcvdAt_PgSQL, 21);
+ datetime.formatTimestampToPgSQL(&pM->tRcvdAt, pM->pszRcvdAt_PgSQL);
}
MsgUnlock(pM);
return(pM->pszRcvdAt_PgSQL);
@@ -884,7 +1318,7 @@ static inline char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt)
MsgUnlock(pM);
return "";
}
- datetime.formatTimestamp3164(&pM->tRcvdAt, pM->pszRcvdAt3164, 16);
+ datetime.formatTimestamp3164(&pM->tRcvdAt, pM->pszRcvdAt3164);
}
MsgUnlock(pM);
return(pM->pszRcvdAt3164);
@@ -895,20 +1329,19 @@ static inline char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt)
MsgUnlock(pM);
return "";
}
- datetime.formatTimestamp3339(&pM->tRcvdAt, pM->pszRcvdAt3339, 33);
+ datetime.formatTimestamp3339(&pM->tRcvdAt, pM->pszRcvdAt3339);
}
MsgUnlock(pM);
return(pM->pszRcvdAt3339);
case tplFmtSecFrac:
- MsgLock(pM);
- if(pM->pszRcvdAt_SecFrac == NULL) {
- if((pM->pszRcvdAt_SecFrac = malloc(10)) == NULL) {
- MsgUnlock(pM);
- return ""; /* TODO: check this: can it cause a free() of constant memory?) */
+ if(pM->pszRcvdAt_SecFrac[0] == '\0') {
+ MsgLock(pM);
+ /* re-check, may have changed while we did not hold lock */
+ if(pM->pszRcvdAt_SecFrac[0] == '\0') {
+ datetime.formatTimestampSecFrac(&pM->tRcvdAt, pM->pszRcvdAt_SecFrac);
}
- datetime.formatTimestampSecFrac(&pM->tRcvdAt, pM->pszRcvdAt_SecFrac, 10);
+ MsgUnlock(pM);
}
- MsgUnlock(pM);
return(pM->pszRcvdAt_SecFrac);
}
ENDfunc
@@ -918,101 +1351,67 @@ static inline char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt)
static inline char *getSeverity(msg_t *pM)
{
+ char *name = NULL;
+
if(pM == NULL)
return "";
- MsgLock(pM);
- if(pM->pszSeverity == NULL) {
- /* we use a 2 byte buffer - can only be one digit */
- if((pM->pszSeverity = malloc(2)) == NULL) { MsgUnlock(pM) ; return ""; }
- pM->iLenSeverity =
- snprintf((char*)pM->pszSeverity, 2, "%d", pM->iSeverity);
+ if(pM->iSeverity < 0 || pM->iSeverity > 7) {
+ name = "invld";
+ } else {
+ name = syslog_number_names[pM->iSeverity];
}
- MsgUnlock(pM);
- return((char*)pM->pszSeverity);
+
+ return name;
}
static inline char *getSeverityStr(msg_t *pM)
{
- syslogCODE *c;
- int val;
char *name = NULL;
if(pM == NULL)
return "";
- MsgLock(pM);
- if(pM->pszSeverityStr == NULL) {
- for(c = rs_prioritynames, val = pM->iSeverity; c->c_name; c++)
- if(c->c_val == val) {
- name = c->c_name;
- break;
- }
- if(name == NULL) {
- /* we use a 2 byte buffer - can only be one digit */
- if((pM->pszSeverityStr = malloc(2)) == NULL) { MsgUnlock(pM) ; return ""; }
- pM->iLenSeverityStr =
- snprintf((char*)pM->pszSeverityStr, 2, "%d", pM->iSeverity);
- } else {
- if((pM->pszSeverityStr = (uchar*) strdup(name)) == NULL) { MsgUnlock(pM) ; return ""; }
- pM->iLenSeverityStr = strlen((char*)name);
- }
+ if(pM->iSeverity < 0 || pM->iSeverity > 7) {
+ name = "invld";
+ } else {
+ name = syslog_severity_names[pM->iSeverity];
}
- MsgUnlock(pM);
- return((char*)pM->pszSeverityStr);
+
+ return name;
}
static inline char *getFacility(msg_t *pM)
{
+ char *name = NULL;
+
if(pM == NULL)
return "";
- MsgLock(pM);
- if(pM->pszFacility == NULL) {
- /* we use a 12 byte buffer - as of
- * syslog-protocol, facility can go
- * up to 2^32 -1
- */
- if((pM->pszFacility = malloc(12)) == NULL) { MsgUnlock(pM) ; return ""; }
- pM->iLenFacility =
- snprintf((char*)pM->pszFacility, 12, "%d", pM->iFacility);
+ if(pM->iFacility < 0 || pM->iFacility > 23) {
+ name = "invld";
+ } else {
+ name = syslog_number_names[pM->iFacility];
}
- MsgUnlock(pM);
- return((char*)pM->pszFacility);
+
+ return name;
}
static inline char *getFacilityStr(msg_t *pM)
{
- syslogCODE *c;
- int val;
char *name = NULL;
if(pM == NULL)
return "";
- MsgLock(pM);
- if(pM->pszFacilityStr == NULL) {
- for(c = rs_facilitynames, val = pM->iFacility << 3; c->c_name; c++)
- if(c->c_val == val) {
- name = c->c_name;
- break;
- }
- if(name == NULL) {
- /* we use a 12 byte buffer - as of
- * syslog-protocol, facility can go
- * up to 2^32 -1
- */
- if((pM->pszFacilityStr = malloc(12)) == NULL) { MsgUnlock(pM) ; return ""; }
- pM->iLenFacilityStr =
- snprintf((char*)pM->pszFacilityStr, 12, "%d", val >> 3);
- } else {
- if((pM->pszFacilityStr = (uchar*)strdup(name)) == NULL) { MsgUnlock(pM) ; return ""; }
- pM->iLenFacilityStr = strlen((char*)name);
- }
- }
- MsgUnlock(pM);
- return((char*)pM->pszFacilityStr);
+ if(pM->iFacility < 0 || pM->iFacility > 23) {
+ name = "invld";
+ } else {
+ name = syslog_fac_names[pM->iFacility];
+ }
+
+ return name;
}
@@ -1047,7 +1446,10 @@ MsgSetAfterPRIOffs(msg_t *pMsg, short offs)
/* rgerhards 2004-11-24: set APP-NAME in msg object
- * TODO: revisit msg locking code!
+ * This is not locked, because it either is called during message
+ * construction (where we need no locking) or later as part of a function
+ * which already obtained the lock. So in general, this function here must
+ * only be called when it it safe to do so without it aquiring a lock.
*/
rsRetVal MsgSetAPPNAME(msg_t *pMsg, char* pszAPPNAME)
{
@@ -1056,7 +1458,6 @@ rsRetVal MsgSetAPPNAME(msg_t *pMsg, char* pszAPPNAME)
if(pMsg->pCSAPPNAME == NULL) {
/* we need to obtain the object first */
CHKiRet(rsCStrConstruct(&pMsg->pCSAPPNAME));
- rsCStrSetAllocIncrement(pMsg->pCSAPPNAME, 128);
}
/* if we reach this point, we have the object */
iRet = rsCStrSetSzStr(pMsg->pCSAPPNAME, (uchar*) pszAPPNAME);
@@ -1066,20 +1467,6 @@ finalize_it:
}
-static void tryEmulateAPPNAME(msg_t *pM); /* forward reference */
-/* rgerhards, 2005-11-24
- */
-char *getAPPNAME(msg_t *pM)
-{
- assert(pM != NULL);
- MsgLock(pM);
- if(pM->pCSAPPNAME == NULL)
- tryEmulateAPPNAME(pM);
- MsgUnlock(pM);
- return (pM->pCSAPPNAME == NULL) ? "" : (char*) rsCStrGetSzStrNoNULL(pM->pCSAPPNAME);
-}
-
-
/* rgerhards 2004-11-24: set PROCID in msg object
*/
rsRetVal MsgSetPROCID(msg_t *pMsg, char* pszPROCID)
@@ -1088,42 +1475,54 @@ rsRetVal MsgSetPROCID(msg_t *pMsg, char* pszPROCID)
ISOBJ_TYPE_assert(pMsg, msg);
if(pMsg->pCSPROCID == NULL) {
/* we need to obtain the object first */
- CHKiRet(rsCStrConstruct(&pMsg->pCSPROCID));
- rsCStrSetAllocIncrement(pMsg->pCSPROCID, 128);
+ CHKiRet(cstrConstruct(&pMsg->pCSPROCID));
}
/* if we reach this point, we have the object */
iRet = rsCStrSetSzStr(pMsg->pCSPROCID, (uchar*) pszPROCID);
+ CHKiRet(cstrFinalize(pMsg->pCSPROCID));
finalize_it:
RETiRet;
}
+
+/* check if we have a procid, and, if not, try to aquire/emulate it.
+ * This must be called WITHOUT the message lock being held.
+ * rgerhards, 2009-06-26
+ */
+static inline void preparePROCID(msg_t *pM, bool bLockMutex)
+{
+ if(pM->pCSPROCID == NULL) {
+ if(bLockMutex == LOCK_MUTEX)
+ MsgLock(pM);
+ /* re-query, things may have changed in the mean time... */
+ if(pM->pCSPROCID == NULL)
+ aquirePROCIDFromTAG(pM);
+ if(bLockMutex == LOCK_MUTEX)
+ MsgUnlock(pM);
+ }
+}
+
+
+#if 0
/* rgerhards, 2005-11-24
*/
-static inline int getPROCIDLen(msg_t *pM)
+static inline int getPROCIDLen(msg_t *pM, bool bLockMutex)
{
assert(pM != NULL);
- MsgLock(pM);
- if(pM->pCSPROCID == NULL)
- aquirePROCIDFromTAG(pM);
- MsgUnlock(pM);
+ preparePROCID(pM, bLockMutex);
return (pM->pCSPROCID == NULL) ? 1 : rsCStrLen(pM->pCSPROCID);
}
+#endif
/* rgerhards, 2005-11-24
*/
-char *getPROCID(msg_t *pM)
+char *getPROCID(msg_t *pM, bool bLockMutex)
{
- char* pszRet;
-
ISOBJ_TYPE_assert(pM, msg);
- MsgLock(pM);
- if(pM->pCSPROCID == NULL)
- aquirePROCIDFromTAG(pM);
- pszRet = (pM->pCSPROCID == NULL) ? "-" : (char*) rsCStrGetSzStrNoNULL(pM->pCSPROCID);
- MsgUnlock(pM);
- return pszRet;
+ preparePROCID(pM, bLockMutex);
+ return (pM->pCSPROCID == NULL) ? "-" : (char*) cstrGetSzStrNoNULL(pM->pCSPROCID);
}
@@ -1136,7 +1535,6 @@ rsRetVal MsgSetMSGID(msg_t *pMsg, char* pszMSGID)
if(pMsg->pCSMSGID == NULL) {
/* we need to obtain the object first */
CHKiRet(rsCStrConstruct(&pMsg->pCSMSGID));
- rsCStrSetAllocIncrement(pMsg->pCSMSGID, 128);
}
/* if we reach this point, we have the object */
iRet = rsCStrSetSzStr(pMsg->pCSMSGID, (uchar*) pszMSGID);
@@ -1154,30 +1552,41 @@ static inline char *getMSGID(msg_t *pM)
}
-/* Set the TAG 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 MsgSetTAG().
- * rgerhards 2004-11-19
+/* rgerhards 2009-06-12: set associated ruleset
*/
-void MsgAssignTAG(msg_t *pMsg, uchar *pBuf)
+void MsgSetRuleset(msg_t *pMsg, ruleset_t *pRuleset)
{
assert(pMsg != NULL);
- pMsg->iLenTAG = (pBuf == NULL) ? 0 : strlen((char*)pBuf);
- pMsg->pszTAG = (uchar*) pBuf;
+ pMsg->pRuleset = pRuleset;
}
-/* rgerhards 2004-11-16: set TAG in msg object
+/* set TAG in msg object
+ * (rewritten 2009-06-18 rgerhards)
*/
-void MsgSetTAG(msg_t *pMsg, char* pszTAG)
+void MsgSetTAG(msg_t *pMsg, uchar* pszBuf, size_t lenBuf)
{
+ uchar *pBuf;
assert(pMsg != NULL);
- free(pMsg->pszTAG);
- pMsg->iLenTAG = strlen(pszTAG);
- if((pMsg->pszTAG = malloc(pMsg->iLenTAG + 1)) != NULL)
- memcpy(pMsg->pszTAG, pszTAG, pMsg->iLenTAG + 1);
- else
- dbgprintf("Could not allocate memory in MsgSetTAG()\n");
+
+ freeTAG(pMsg);
+
+ pMsg->iLenTAG = lenBuf;
+ if(pMsg->iLenTAG < CONF_TAG_BUFSIZE) {
+ /* small enough: use fixed buffer (faster!) */
+ pBuf = pMsg->TAG.szBuf;
+ } else {
+ if((pBuf = (uchar*) malloc(pMsg->iLenTAG + 1)) == NULL) {
+ /* truncate message, better than completely loosing it... */
+ pBuf = pMsg->TAG.szBuf;
+ pMsg->iLenTAG = CONF_TAG_BUFSIZE - 1;
+ } else {
+ pMsg->TAG.pszTAG = pBuf;
+ }
+ }
+
+ memcpy(pBuf, pszBuf, pMsg->iLenTAG);
+ pBuf[pMsg->iLenTAG] = '\0'; /* this also works with truncation! */
}
@@ -1188,63 +1597,51 @@ void MsgSetTAG(msg_t *pMsg, char* pszTAG)
* if there is a TAG and, if not, if it can emulate it.
* rgerhards, 2005-11-24
*/
-static void tryEmulateTAG(msg_t *pM)
+static inline void tryEmulateTAG(msg_t *pM, bool bLockMutex)
{
- int iTAGLen;
- uchar *pBuf;
+ size_t lenTAG;
+ uchar bufTAG[CONF_TAG_MAXSIZE];
assert(pM != NULL);
- if(pM->pszTAG != NULL)
+ if(bLockMutex == LOCK_MUTEX)
+ MsgLock(pM);
+ if(pM->iLenTAG > 0)
return; /* done, no need to emulate */
if(getProtocolVersion(pM) == 1) {
- if(!strcmp(getPROCID(pM), "-")) {
+ if(!strcmp(getPROCID(pM, MUTEX_ALREADY_LOCKED), "-")) {
/* no process ID, use APP-NAME only */
- MsgSetTAG(pM, getAPPNAME(pM));
+ MsgSetTAG(pM, (uchar*) getAPPNAME(pM, MUTEX_ALREADY_LOCKED), getAPPNAMELen(pM, MUTEX_ALREADY_LOCKED));
} else {
/* now we can try to emulate */
- iTAGLen = getAPPNAMELen(pM) + getPROCIDLen(pM) + 3;
- if((pBuf = malloc(iTAGLen * sizeof(char))) == NULL)
- return; /* nothing we can do */
- snprintf((char*)pBuf, iTAGLen, "%s[%s]", getAPPNAME(pM), getPROCID(pM));
- MsgAssignTAG(pM, pBuf);
+ lenTAG = snprintf((char*)bufTAG, CONF_TAG_MAXSIZE, "%s[%s]",
+ getAPPNAME(pM, MUTEX_ALREADY_LOCKED), getPROCID(pM, MUTEX_ALREADY_LOCKED));
+ bufTAG[32] = '\0'; /* just to make sure... */
+ MsgSetTAG(pM, bufTAG, lenTAG);
}
}
+ if(bLockMutex == LOCK_MUTEX)
+ MsgUnlock(pM);
}
-#if 0 /* This method is currently not called, be we like to preserve it */
-static int getTAGLen(msg_t *pM)
-{
- if(pM == NULL)
- return 0;
- else {
- tryEmulateTAG(pM);
- if(pM->pszTAG == NULL)
- return 0;
- else
- return pM->iLenTAG;
- }
-}
-#endif
-
-
-static inline char *getTAG(msg_t *pM)
+static inline void
+getTAG(msg_t *pM, uchar **ppBuf, int *piLen)
{
- char *ret;
-
- if(pM == NULL)
- ret = "";
- else {
- MsgLock(pM);
- tryEmulateTAG(pM);
- if(pM->pszTAG == NULL)
- ret = "";
- else
- ret = (char*) pM->pszTAG;
- MsgUnlock(pM);
+ if(pM == NULL) {
+ *ppBuf = UCHAR_CONSTANT("");
+ *piLen = 0;
+ } else {
+ if(pM->iLenTAG == 0)
+ tryEmulateTAG(pM, LOCK_MUTEX);
+ if(pM->iLenTAG == 0) {
+ *ppBuf = UCHAR_CONSTANT("");
+ *piLen = 0;
+ } else {
+ *ppBuf = (pM->iLenTAG < CONF_TAG_BUFSIZE) ? pM->TAG.szBuf : pM->TAG.pszTAG;
+ *piLen = pM->iLenTAG;
+ }
}
- return(ret);
}
@@ -1254,7 +1651,10 @@ int getHOSTNAMELen(msg_t *pM)
return 0;
else
if(pM->pszHOSTNAME == NULL)
- return 0;
+ if(pM->pRcvFrom == NULL)
+ return 0;
+ else
+ return prop.GetStringLen(pM->pRcvFrom);
else
return pM->iLenHOSTNAME;
}
@@ -1265,48 +1665,39 @@ char *getHOSTNAME(msg_t *pM)
if(pM == NULL)
return "";
else
- if(pM->pszHOSTNAME == NULL)
- return "";
- else
+ if(pM->pszHOSTNAME == NULL) {
+ if(pM->pRcvFrom == NULL) {
+ return "";
+ } else {
+ uchar *psz;
+ int len;
+ prop.GetString(pM->pRcvFrom, &psz, &len);
+ return (char*) psz;
+ }
+ } else {
return (char*) pM->pszHOSTNAME;
-}
-
-
-static uchar *getInputName(msg_t *pM)
-{
- if(pM == NULL)
- return (uchar*) "";
- else
- if(pM->pszInputName == NULL)
- return (uchar*) "";
- else
- return pM->pszInputName;
+ }
}
uchar *getRcvFrom(msg_t *pM)
{
- if(pM == NULL)
- return UCHAR_CONSTANT("");
- else
- if(pM->pszRcvFrom == NULL)
- return UCHAR_CONSTANT("");
+ uchar *psz;
+ int len;
+ BEGINfunc
+ if(pM == NULL) {
+ psz = UCHAR_CONSTANT("");
+ } else {
+ if(pM->pRcvFrom == NULL)
+ psz = UCHAR_CONSTANT("");
else
- return pM->pszRcvFrom;
+ prop.GetString(pM->pRcvFrom, &psz, &len);
+ }
+ ENDfunc
+ return psz;
}
-uchar *getRcvFromIP(msg_t *pM)
-{
- if(pM == NULL)
- return (uchar*) "";
- else
- if(pM->pszRcvFromIP == NULL)
- return (uchar*) "";
- else
- return pM->pszRcvFromIP;
-}
-
/* rgerhards 2004-11-24: set STRUCTURED DATA in msg object
*/
rsRetVal MsgSetStructuredData(msg_t *pMsg, char* pszStrucData)
@@ -1316,7 +1707,6 @@ rsRetVal MsgSetStructuredData(msg_t *pMsg, char* pszStrucData)
if(pMsg->pCSStrucData == NULL) {
/* we need to obtain the object first */
CHKiRet(rsCStrConstruct(&pMsg->pCSStrucData));
- rsCStrSetAllocIncrement(pMsg->pCSStrucData, 128);
}
/* if we reach this point, we have the object */
iRet = rsCStrSetSzStr(pMsg->pCSStrucData, (uchar*) pszStrucData);
@@ -1345,101 +1735,50 @@ static inline char *getStructuredData(msg_t *pM)
}
-
-/* get the length of the "programname" sz string
- * rgerhards, 2005-10-19
+/* check if we have a ProgramName, and, if not, try to aquire/emulate it.
+ * rgerhards, 2009-06-26
*/
-int getProgramNameLen(msg_t *pM)
+static inline void prepareProgramName(msg_t *pM, bool bLockMutex)
{
- int iRet;
+ if(pM->pCSProgName == NULL) {
+ if(bLockMutex == LOCK_MUTEX)
+ MsgLock(pM);
- assert(pM != NULL);
- MsgLock(pM);
- if((iRet = aquireProgramName(pM)) != RS_RET_OK) {
- dbgprintf("error %d returned by aquireProgramName() in getProgramNameLen()\n", iRet);
- MsgUnlock(pM);
- return 0; /* best we can do (consistent wiht what getProgramName() returns) */
- }
- MsgUnlock(pM);
+ /* re-query as things might have changed during locking */
+ if(pM->pCSProgName == NULL)
+ aquireProgramName(pM);
- return (pM->pCSProgName == NULL) ? 0 : rsCStrLen(pM->pCSProgName);
+ if(bLockMutex == LOCK_MUTEX)
+ MsgUnlock(pM);
+ }
}
-/* get the "programname" as sz string
+/* get the length of the "programname" sz string
* rgerhards, 2005-10-19
*/
-char *getProgramName(msg_t *pM) /* this is the non-locking version for internal use */
+int getProgramNameLen(msg_t *pM, bool bLockMutex)
{
- int iRet;
- char *pszRet;
-
assert(pM != NULL);
- MsgLock(pM);
- if((iRet = aquireProgramName(pM)) != RS_RET_OK) {
- dbgprintf("error %d returned by aquireProgramName() in getProgramName()\n", iRet);
- pszRet = ""; /* best we can do */
- } else {
- pszRet = (pM->pCSProgName == NULL) ? "" : (char*) rsCStrGetSzStrNoNULL(pM->pCSProgName);
- }
-
- MsgUnlock(pM);
- return pszRet;
+ prepareProgramName(pM, bLockMutex);
+ return (pM->pCSProgName == NULL) ? 0 : rsCStrLen(pM->pCSProgName);
}
-/* The code below was an approach without PTHREAD_MUTEX_RECURSIVE
- * However, it turned out to be quite complex. So far, we use recursive
- * locking, which is OK from a performance point of view, especially as
- * we do not anticipate that multithreading msg objects is used often.
- * However, we may re-think about using non-recursive locking and I leave this
- * code in here to conserve the idea. -- rgerhards, 2008-01-05
- */
-#if 0
-static char *getProgramNameNoLock(msg_t *pM) /* this is the non-locking version for internal use */
-{
- int iRet;
- assert(pM != NULL);
- if((iRet = aquireProgramName(pM)) != RS_RET_OK) {
- dbgprintf("error %d returned by aquireProgramName() in getProgramName()\n", iRet);
- return ""; /* best we can do */
- }
- return (pM->pCSProgName == NULL) ? "" : (char*) rsCStrGetSzStrNoNULL(pM->pCSProgName);
-}
-char *getProgramName(msg_t *pM) /* this is the external callable version */
+/* get the "programname" as sz string
+ * rgerhards, 2005-10-19
+ */
+uchar *getProgramName(msg_t *pM, bool bLockMutex)
{
- char *pszRet;
-
- MsgLock(pM);
- pszRet = getProgramNameNoLock(pM);
- MsgUnlock(pM);
- return pszRet;
+ prepareProgramName(pM, bLockMutex);
+ return (pM->pCSProgName == NULL) ? UCHAR_CONSTANT("") : rsCStrGetSzStrNoNULL(pM->pCSProgName);
}
-/* an alternative approach has been: */
-/* The macro below is used to generate external function definitions
- * for such functions that may also be called internally (and thus have
- * both a locking and non-locking implementation. Over time, we could
- * reconsider how we handle that. -- rgerhards, 2008-01-05
- */
-#define EXT_LOCKED_FUNC(fName, ret) \
-ret fName(msg_t *pM) \
-{ \
- ret valRet; \
- MsgLock(pM); \
- valRet = fName##NoLock(pM); \
- MsgUnlock(pM); \
- return(valRet); \
-}
-EXT_LOCKED_FUNC(getProgramName, char*)
-/* in this approach, the external function is provided by the macro and
- * needs not to be writen.
- */
-#endif /* #if 0 -- saved code */
/* This function tries to emulate APPNAME if it is not present. Its
* main use is when we have received a log record via legacy syslog and
* now would like to send out the same one via syslog-protocol.
+ * MUST be called with the Msg Lock locked!
*/
static void tryEmulateAPPNAME(msg_t *pM)
{
@@ -1449,79 +1788,136 @@ static void tryEmulateAPPNAME(msg_t *pM)
if(getProtocolVersion(pM) == 0) {
/* only then it makes sense to emulate */
- MsgSetAPPNAME(pM, getProgramName(pM));
+ MsgSetAPPNAME(pM, (char*)getProgramName(pM, MUTEX_ALREADY_LOCKED));
}
}
+
+/* check if we have a APPNAME, and, if not, try to aquire/emulate it.
+ * This must be called WITHOUT the message lock being held.
+ * rgerhards, 2009-06-26
+ */
+static inline void prepareAPPNAME(msg_t *pM, bool bLockMutex)
+{
+ if(pM->pCSAPPNAME == NULL) {
+ if(bLockMutex == LOCK_MUTEX)
+ MsgLock(pM);
+
+ /* re-query as things might have changed during locking */
+ if(pM->pCSAPPNAME == NULL)
+ tryEmulateAPPNAME(pM);
+
+ if(bLockMutex == LOCK_MUTEX)
+ MsgUnlock(pM);
+ }
+}
+
+/* rgerhards, 2005-11-24
+ */
+char *getAPPNAME(msg_t *pM, bool bLockMutex)
+{
+ assert(pM != NULL);
+ prepareAPPNAME(pM, bLockMutex);
+ return (pM->pCSAPPNAME == NULL) ? "" : (char*) rsCStrGetSzStrNoNULL(pM->pCSAPPNAME);
+}
+
/* rgerhards, 2005-11-24
*/
-static int getAPPNAMELen(msg_t *pM)
+static int getAPPNAMELen(msg_t *pM, bool bLockMutex)
{
assert(pM != NULL);
- if(pM->pCSAPPNAME == NULL)
- tryEmulateAPPNAME(pM);
+ prepareAPPNAME(pM, bLockMutex);
return (pM->pCSAPPNAME == NULL) ? 0 : rsCStrLen(pM->pCSAPPNAME);
}
-/* rgerhards 2008-09-10: set pszInputName in msg object
+/* rgerhards 2008-09-10: set pszInputName in msg object. This calls AddRef()
+ * on the property, because this must be done in all current cases and there
+ * is no case expected where this may not be necessary.
* rgerhards, 2009-06-16
*/
-void MsgSetInputName(msg_t *pMsg, uchar* pszInputName, size_t lenInputName)
+void MsgSetInputName(msg_t *pThis, prop_t *inputName)
{
- assert(pMsg != NULL);
- free(pMsg->pszInputName);
- pMsg->iLenInputName = lenInputName;
- if((pMsg->pszInputName = malloc(pMsg->iLenInputName + 1)) != NULL) {
- memcpy(pMsg->pszInputName, pszInputName, pMsg->iLenInputName + 1);
- }
+ assert(pThis != NULL);
+
+ prop.AddRef(inputName);
+ if(pThis->pInputName != NULL)
+ prop.Destruct(&pThis->pInputName);
+ pThis->pInputName = inputName;
}
-/* rgerhards 2004-11-16: set pszRcvFrom in msg object
+
+/* rgerhards 2008-09-10: set RcvFrom name in msg object. This calls AddRef()
+ * on the property, because this must be done in all current cases and there
+ * is no case expected where this may not be necessary.
+ * rgerhards, 2009-06-30
*/
-void MsgSetRcvFrom(msg_t *pMsg, uchar* pszRcvFrom)
+void MsgSetRcvFrom(msg_t *pThis, prop_t *new)
{
- assert(pMsg != NULL);
- free(pMsg->pszRcvFrom);
+ assert(pThis != NULL);
- pMsg->iLenRcvFrom = ustrlen(pszRcvFrom);
- if((pMsg->pszRcvFrom = malloc(pMsg->iLenRcvFrom + 1)) != NULL) {
- memcpy(pMsg->pszRcvFrom, pszRcvFrom, pMsg->iLenRcvFrom + 1);
- }
+ prop.AddRef(new);
+ if(pThis->pRcvFrom != NULL)
+ prop.Destruct(&pThis->pRcvFrom);
+ pThis->pRcvFrom = new;
}
-/* rgerhards 2005-05-16: set pszRcvFromIP in msg object */
-rsRetVal
-MsgSetRcvFromIP(msg_t *pMsg, uchar* pszRcvFromIP)
+/* This is used to set the property via a string. This function should not be
+ * called if there is a reliable way for a caller to make sure that the
+ * same name can be used across multiple messages. However, if it can not
+ * ensure that, calling this function is the second best thing, because it
+ * will re-use the previously created property if it contained the same
+ * name (but it works only for the immediate previous).
+ * rgerhards, 2009-06-31
+ */
+void MsgSetRcvFromStr(msg_t *pThis, uchar *psz, int len, prop_t **ppProp)
{
- DEFiRet;
- assert(pMsg != NULL);
- if(pMsg->pszRcvFromIP != NULL) {
- free(pMsg->pszRcvFromIP);
- pMsg->iLenRcvFromIP = 0;
- }
+ assert(pThis != NULL);
+ assert(ppProp != NULL);
- CHKmalloc(pMsg->pszRcvFromIP = (uchar*)strdup((char*)pszRcvFromIP));
- pMsg->iLenRcvFromIP = strlen((char*)pszRcvFromIP);
-finalize_it:
- RETiRet;
+ prop.CreateOrReuseStringProp(ppProp, psz, len);
+ MsgSetRcvFrom(pThis, *ppProp);
}
-/* Set the HOSTNAME 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 MsgSetHOSTNAME().
- * rgerhards 2004-11-19
+/* set RcvFromIP name in msg object. This calls AddRef()
+ * on the property, because this must be done in all current cases and there
+ * is no case expected where this may not be necessary.
+ * rgerhards, 2009-06-30
*/
-void MsgAssignHOSTNAME(msg_t *pMsg, char *pBuf)
+rsRetVal MsgSetRcvFromIP(msg_t *pThis, prop_t *new)
{
- assert(pMsg != NULL);
- assert(pBuf != NULL);
- if(pMsg->pszHOSTNAME != NULL)
- free(pMsg->pszHOSTNAME);
- pMsg->iLenHOSTNAME = strlen(pBuf);
- pMsg->pszHOSTNAME = (uchar*) pBuf;
+ assert(pThis != NULL);
+
+ BEGINfunc
+ prop.AddRef(new);
+ if(pThis->pRcvFromIP != NULL)
+ prop.Destruct(&pThis->pRcvFromIP);
+ pThis->pRcvFromIP = new;
+ ENDfunc
+ return RS_RET_OK;
+}
+
+
+/* This is used to set the property via a string. This function should not be
+ * called if there is a reliable way for a caller to make sure that the
+ * same name can be used across multiple messages. However, if it can not
+ * ensure that, calling this function is the second best thing, because it
+ * will re-use the previously created property if it contained the same
+ * name (but it works only for the immediate previous).
+ * rgerhards, 2009-06-31
+ */
+rsRetVal MsgSetRcvFromIPStr(msg_t *pThis, uchar *psz, int len, prop_t **ppProp)
+{
+ DEFiRet;
+ assert(pThis != NULL);
+
+ CHKiRet(prop.CreateOrReuseStringProp(ppProp, psz, len));
+ MsgSetRcvFrom(pThis, *ppProp);
+
+finalize_it:
+ RETiRet;
}
@@ -1535,49 +1931,118 @@ void MsgAssignHOSTNAME(msg_t *pMsg, char *pBuf)
* we need it. The rest of the code already knows how to handle an
* unset HOSTNAME.
*/
-void MsgSetHOSTNAME(msg_t *pMsg, uchar* pszHOSTNAME)
+void MsgSetHOSTNAME(msg_t *pThis, uchar* pszHOSTNAME, int lenHOSTNAME)
{
- assert(pMsg != NULL);
- free(pMsg->pszHOSTNAME);
+ assert(pThis != NULL);
- pMsg->iLenHOSTNAME = ustrlen(pszHOSTNAME);
- if((pMsg->pszHOSTNAME = malloc(pMsg->iLenHOSTNAME + 1)) != NULL)
- memcpy(pMsg->pszHOSTNAME, pszHOSTNAME, pMsg->iLenHOSTNAME + 1);
- else
- DBGPRINTF("Could not allocate memory in MsgSetHOSTNAME()\n");
+ freeHOSTNAME(pThis);
+
+ pThis->iLenHOSTNAME = lenHOSTNAME;
+ if(pThis->iLenHOSTNAME < CONF_HOSTNAME_BUFSIZE) {
+ /* small enough: use fixed buffer (faster!) */
+ pThis->pszHOSTNAME = pThis->szHOSTNAME;
+ } else if((pThis->pszHOSTNAME = (uchar*) malloc(pThis->iLenHOSTNAME + 1)) == NULL) {
+ /* truncate message, better than completely loosing it... */
+ pThis->pszHOSTNAME = pThis->szHOSTNAME;
+ pThis->iLenHOSTNAME = CONF_HOSTNAME_BUFSIZE - 1;
+ }
+
+ memcpy(pThis->pszHOSTNAME, pszHOSTNAME, pThis->iLenHOSTNAME);
+ pThis->pszHOSTNAME[pThis->iLenHOSTNAME] = '\0'; /* this also works with truncation! */
}
-/* rgerhards 2004-11-09: set MSG in msg object
+/* set the offset of the MSG part into the raw msg buffer
+ * Note that the offset may be higher than the length of the raw message
+ * (exactly by one). This can happen if we have a message that does not
+ * contain any MSG part.
*/
-void MsgSetMSG(msg_t *pMsg, char* pszMSG)
+void MsgSetMSGoffs(msg_t *pMsg, short offs)
{
- assert(pMsg != NULL);
+ ISOBJ_TYPE_assert(pMsg, msg);
+ pMsg->offMSG = offs;
+ if(offs > pMsg->iLenRawMsg) {
+ assert(offs - 1 == pMsg->iLenRawMsg);
+ pMsg->iLenMSG = 0;
+ } else {
+ pMsg->iLenMSG = pMsg->iLenRawMsg - offs;
+ }
+}
+
+
+/* replace the MSG part of a message. The update actually takes place inside
+ * rawmsg.
+ * There are two cases: either the new message will be larger than the new msg
+ * or it will be less than or equal. If it is less than or equal, we can utilize
+ * the previous message buffer. If it is larger, we can utilize the msg_t-included
+ * message buffer if it fits in there. If this is not the case, we need to alloc
+ * a new, larger, chunk and copy over the data to it. Note that this function is
+ * (hopefully) relatively seldom being called, so some performance impact is
+ * uncritical. In any case, pszMSG is copied, so if it was dynamically allocated,
+ * the caller is responsible for freeing it.
+ * rgerhards, 2009-06-23
+ */
+rsRetVal MsgReplaceMSG(msg_t *pThis, uchar* pszMSG, int lenMSG)
+{
+ int lenNew;
+ uchar *bufNew;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, msg);
assert(pszMSG != NULL);
- if(pMsg->pszMSG != NULL)
- free(pMsg->pszMSG);
+ lenNew = pThis->iLenRawMsg + lenMSG - pThis->iLenMSG;
+ if(lenMSG > pThis->iLenMSG && lenNew >= CONF_RAWMSG_BUFSIZE) {
+ /* we have lost our "bet" and need to alloc a new buffer ;) */
+ CHKmalloc(bufNew = malloc(lenNew + 1));
+ memcpy(bufNew, pThis->pszRawMsg, pThis->offMSG);
+ if(pThis->pszRawMsg != pThis->szRawMsg)
+ free(pThis->pszRawMsg);
+ pThis->pszRawMsg = bufNew;
+ }
- pMsg->iLenMSG = strlen(pszMSG);
- if((pMsg->pszMSG = (uchar*) malloc(pMsg->iLenMSG + 1)) != NULL)
- memcpy(pMsg->pszMSG, pszMSG, pMsg->iLenMSG + 1);
- else
- dbgprintf("MsgSetMSG could not allocate memory for pszMSG buffer.");
+ if(lenMSG > 0)
+ memcpy(pThis->pszRawMsg + pThis->offMSG, pszMSG, lenMSG);
+ pThis->pszRawMsg[lenNew] = '\0'; /* this also works with truncation! */
+ pThis->iLenRawMsg = lenNew;
+ pThis->iLenMSG = lenMSG;
+
+finalize_it:
+ RETiRet;
}
-/* rgerhards 2004-11-11: set RawMsg in msg object
+
+/* set raw message in message object. Size of message is provided.
+ * rgerhards, 2009-06-16
*/
-void MsgSetRawMsg(msg_t *pMsg, char* pszRawMsg)
+void MsgSetRawMsg(msg_t *pThis, char* pszRawMsg, size_t lenMsg)
{
- assert(pMsg != NULL);
- if(pMsg->pszRawMsg != NULL)
- free(pMsg->pszRawMsg);
+ assert(pThis != NULL);
+ if(pThis->pszRawMsg != pThis->szRawMsg)
+ free(pThis->pszRawMsg);
- pMsg->iLenRawMsg = strlen(pszRawMsg);
- if((pMsg->pszRawMsg = (uchar*) malloc(pMsg->iLenRawMsg + 1)) != NULL)
- memcpy(pMsg->pszRawMsg, pszRawMsg, pMsg->iLenRawMsg + 1);
- else
- dbgprintf("Could not allocate memory for pszRawMsg buffer.");
+ pThis->iLenRawMsg = lenMsg;
+ if(pThis->iLenRawMsg < CONF_RAWMSG_BUFSIZE) {
+ /* small enough: use fixed buffer (faster!) */
+ pThis->pszRawMsg = pThis->szRawMsg;
+ } else if((pThis->pszRawMsg = (uchar*) malloc(pThis->iLenRawMsg + 1)) == NULL) {
+ /* truncate message, better than completely loosing it... */
+ pThis->pszRawMsg = pThis->szRawMsg;
+ pThis->iLenRawMsg = CONF_RAWMSG_BUFSIZE - 1;
+ }
+
+ memcpy(pThis->pszRawMsg, pszRawMsg, pThis->iLenRawMsg);
+ pThis->pszRawMsg[pThis->iLenRawMsg] = '\0'; /* this also works with truncation! */
+}
+
+
+/* set raw message in message object. Size of message is not provided. This
+ * function should only be used when it is unavoidable (and over time we should
+ * try to remove it altogether).
+ * rgerhards, 2009-06-16
+ */
+void MsgSetRawMsgWOSize(msg_t *pMsg, char* pszRawMsg)
+{
+ MsgSetRawMsg(pMsg, pszRawMsg, strlen(pszRawMsg));
}
@@ -1591,15 +2056,11 @@ void MsgSetRawMsg(msg_t *pMsg, char* pszRawMsg)
*/
char *textpri(char *pRes, size_t pResLen, int pri)
{
- syslogCODE *c_pri, *c_fac;
-
assert(pRes != NULL);
assert(pResLen > 0);
- for (c_fac = rs_facilitynames; c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri)<<3); c_fac++);
- for (c_pri = rs_prioritynames; c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
-
- snprintf (pRes, pResLen, "%s.%s<%d>", c_fac->c_name, c_pri->c_name, pri);
+ snprintf(pRes, pResLen, "%s.%s<%d>", syslog_fac_names[LOG_FAC(pri)],
+ syslog_severity_names[LOG_PRI(pri)], pri);
return pRes;
}
@@ -1693,143 +2154,172 @@ static uchar *getNOW(eNOWType eNow)
* be used in selector line processing.
* rgerhards 2005-09-15
*/
-char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
- cstr_t *pCSPropName, unsigned short *pbMustBeFreed)
+uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
+ propid_t propID, size_t *pPropLen,
+ unsigned short *pbMustBeFreed)
{
- uchar *pName;
- char *pRes; /* result pointer */
- char *pBufStart;
- char *pBuf;
+ uchar *pRes; /* result pointer */
+ int bufLen = -1; /* length of string or -1, if not known */
+ uchar *pBufStart;
+ uchar *pBuf;
int iLen;
short iOffs;
+ BEGINfunc
+ assert(pMsg != NULL);
+ assert(pbMustBeFreed != NULL);
+
#ifdef FEATURE_REGEXP
/* Variables necessary for regular expression matching */
size_t nmatch = 10;
regmatch_t pmatch[10];
#endif
- assert(pMsg != NULL);
- assert(pbMustBeFreed != NULL);
-
- if(pCSPropName == NULL) {
- assert(pTpe != NULL);
- pName = pTpe->data.field.pPropRepl;
- } else {
- pName = rsCStrGetSzStrNoNULL(pCSPropName);
- }
*pbMustBeFreed = 0;
- /* sometimes there are aliases to the original MonitoWare
- * property names. These come after || in the ifs below. */
- if(!strcmp((char*) pName, "msg")) {
- 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")) {
- pRes = (char*) getRcvFrom(pMsg);
- } else if(!strcmp((char*) pName, "fromhost-ip")) {
- pRes = (char*) getRcvFromIP(pMsg);
- } else if(!strcmp((char*) pName, "source") || !strcmp((char*) pName, "hostname")) {
- pRes = getHOSTNAME(pMsg);
- } else if(!strcmp((char*) pName, "syslogtag")) {
- pRes = getTAG(pMsg);
- } else if(!strcmp((char*) pName, "pri")) {
- pRes = getPRI(pMsg);
- } else if(!strcmp((char*) pName, "pri-text")) {
- pBuf = malloc(20 * sizeof(char));
- if(pBuf == NULL) {
- *pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
- } else {
- *pbMustBeFreed = 1;
- pRes = textpri(pBuf, 20, getPRIi(pMsg));
- }
- } else if(!strcmp((char*) pName, "iut")) {
- pRes = "1"; /* always 1 for syslog messages (a MonitorWare thing;)) */
- } else if(!strcmp((char*) pName, "syslogfacility")) {
- pRes = getFacility(pMsg);
- } else if(!strcmp((char*) pName, "syslogfacility-text")) {
- pRes = getFacilityStr(pMsg);
- } else if(!strcmp((char*) pName, "syslogseverity") || !strcmp((char*) pName, "syslogpriority")) {
- pRes = getSeverity(pMsg);
- } else if(!strcmp((char*) pName, "syslogseverity-text") || !strcmp((char*) pName, "syslogpriority-text")) {
- pRes = getSeverityStr(pMsg);
- } else if(!strcmp((char*) pName, "timegenerated")) {
- pRes = getTimeGenerated(pMsg, pTpe->data.field.eDateFormat);
- } else if(!strcmp((char*) pName, "timereported")
- || !strcmp((char*) pName, "timestamp")) {
- pRes = getTimeReported(pMsg, pTpe->data.field.eDateFormat);
- } else if(!strcmp((char*) pName, "programname")) {
- pRes = getProgramName(pMsg);
- } else if(!strcmp((char*) pName, "protocol-version")) {
- pRes = getProtocolVersionString(pMsg);
- } else if(!strcmp((char*) pName, "structured-data")) {
- pRes = getStructuredData(pMsg);
- } else if(!strcmp((char*) pName, "app-name")) {
- pRes = getAPPNAME(pMsg);
- } else if(!strcmp((char*) pName, "procid")) {
- pRes = getPROCID(pMsg);
- } else if(!strcmp((char*) pName, "msgid")) {
- pRes = getMSGID(pMsg);
- /* here start system properties (those, that do not relate to the message itself */
- } else if(!strcmp((char*) pName, "$now")) {
- if((pRes = (char*) getNOW(NOW_NOW)) == NULL) {
- return "***OUT OF MEMORY***";
- } else
- *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
- } else if(!strcmp((char*) pName, "$year")) {
- if((pRes = (char*) getNOW(NOW_YEAR)) == NULL) {
- return "***OUT OF MEMORY***";
- } else
- *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
- } else if(!strcmp((char*) pName, "$month")) {
- if((pRes = (char*) getNOW(NOW_MONTH)) == NULL) {
- return "***OUT OF MEMORY***";
- } else
- *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
- } else if(!strcmp((char*) pName, "$day")) {
- if((pRes = (char*) getNOW(NOW_DAY)) == NULL) {
- return "***OUT OF MEMORY***";
- } else
- *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
- } else if(!strcmp((char*) pName, "$hour")) {
- if((pRes = (char*) getNOW(NOW_HOUR)) == NULL) {
- return "***OUT OF MEMORY***";
- } else
- *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
- } else if(!strcmp((char*) pName, "$hhour")) {
- if((pRes = (char*) getNOW(NOW_HHOUR)) == NULL) {
- return "***OUT OF MEMORY***";
- } else
- *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
- } else if(!strcmp((char*) pName, "$qhour")) {
- if((pRes = (char*) getNOW(NOW_QHOUR)) == NULL) {
- return "***OUT OF MEMORY***";
- } else
- *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
- } else if(!strcmp((char*) pName, "$minute")) {
- if((pRes = (char*) getNOW(NOW_MINUTE)) == NULL) {
- return "***OUT OF MEMORY***";
- } else
- *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
- } else if(!strcmp((char*) pName, "$myhostname")) {
- pRes = (char*) glbl.GetLocalHostName();
- } else {
- /* there is no point in continuing, we may even otherwise render the
- * error message unreadable. rgerhards, 2007-07-10
- */
- dbgprintf("invalid property name: '%s'\n", pName);
- return "**INVALID PROPERTY NAME**";
+ switch(propID) {
+ case PROP_MSG:
+ pRes = getMSG(pMsg);
+ bufLen = getMSGLen(pMsg);
+ break;
+ case PROP_TIMESTAMP:
+ pRes = (uchar*)getTimeReported(pMsg, pTpe->data.field.eDateFormat);
+ break;
+ case PROP_HOSTNAME:
+ pRes = (uchar*)getHOSTNAME(pMsg);
+ bufLen = getHOSTNAMELen(pMsg);
+ break;
+ case PROP_SYSLOGTAG:
+ getTAG(pMsg, &pRes, &bufLen);
+ break;
+ case PROP_RAWMSG:
+ getRawMsg(pMsg, &pRes, &bufLen);
+ break;
+ /* enable this, if someone actually uses UxTradMsg, delete after some time has
+ * passed and nobody complained -- rgerhards, 2009-06-16
+ case PROP_UXTRADMSG:
+ pRes = getUxTradMsg(pMsg);
+ break;
+ */
+ case PROP_INPUTNAME:
+ getInputName(pMsg, &pRes, &bufLen);
+ break;
+ case PROP_FROMHOST:
+ pRes = getRcvFrom(pMsg);
+ break;
+ case PROP_FROMHOST_IP:
+ pRes = getRcvFromIP(pMsg);
+ break;
+ case PROP_PRI:
+ pRes = (uchar*)getPRI(pMsg);
+ break;
+ case PROP_PRI_TEXT:
+ pBuf = malloc(20 * sizeof(uchar));
+ if(pBuf == NULL) {
+ *pbMustBeFreed = 0;
+ return UCHAR_CONSTANT("**OUT OF MEMORY**");
+ } else {
+ *pbMustBeFreed = 1;
+ pRes = (uchar*)textpri((char*)pBuf, 20, getPRIi(pMsg));
+ }
+ break;
+ case PROP_IUT:
+ pRes = UCHAR_CONSTANT("1"); /* always 1 for syslog messages (a MonitorWare thing;)) */
+ bufLen = 1;
+ break;
+ case PROP_SYSLOGFACILITY:
+ pRes = (uchar*)getFacility(pMsg);
+ break;
+ case PROP_SYSLOGFACILITY_TEXT:
+ pRes = (uchar*)getFacilityStr(pMsg);
+ break;
+ case PROP_SYSLOGSEVERITY:
+ pRes = (uchar*)getSeverity(pMsg);
+ break;
+ case PROP_SYSLOGSEVERITY_TEXT:
+ pRes = (uchar*)getSeverityStr(pMsg);
+ break;
+ case PROP_TIMEGENERATED:
+ pRes = (uchar*)getTimeGenerated(pMsg, pTpe->data.field.eDateFormat);
+ break;
+ case PROP_PROGRAMNAME:
+ pRes = getProgramName(pMsg, LOCK_MUTEX);
+ break;
+ case PROP_PROTOCOL_VERSION:
+ pRes = (uchar*)getProtocolVersionString(pMsg);
+ break;
+ case PROP_STRUCTURED_DATA:
+ pRes = (uchar*)getStructuredData(pMsg);
+ break;
+ case PROP_APP_NAME:
+ pRes = (uchar*)getAPPNAME(pMsg, LOCK_MUTEX);
+ break;
+ case PROP_PROCID:
+ pRes = (uchar*)getPROCID(pMsg, LOCK_MUTEX);
+ break;
+ case PROP_MSGID:
+ pRes = (uchar*)getMSGID(pMsg);
+ break;
+ case PROP_SYS_NOW:
+ if((pRes = getNOW(NOW_NOW)) == NULL) {
+ return UCHAR_CONSTANT("**OUT OF MEMORY**");
+ } else
+ *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
+ break;
+ case PROP_SYS_YEAR:
+ if((pRes = getNOW(NOW_YEAR)) == NULL) {
+ return UCHAR_CONSTANT("**OUT OF MEMORY**");
+ } else
+ *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
+ break;
+ case PROP_SYS_MONTH:
+ if((pRes = getNOW(NOW_MONTH)) == NULL) {
+ return UCHAR_CONSTANT("**OUT OF MEMORY**");
+ } else
+ *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
+ break;
+ case PROP_SYS_DAY:
+ if((pRes = getNOW(NOW_DAY)) == NULL) {
+ return UCHAR_CONSTANT("**OUT OF MEMORY**");
+ } else
+ *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
+ break;
+ case PROP_SYS_HOUR:
+ if((pRes = getNOW(NOW_HOUR)) == NULL) {
+ return UCHAR_CONSTANT("**OUT OF MEMORY**");
+ } else
+ *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
+ break;
+ case PROP_SYS_HHOUR:
+ if((pRes = getNOW(NOW_HHOUR)) == NULL) {
+ return UCHAR_CONSTANT("**OUT OF MEMORY**");
+ } else
+ *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
+ break;
+ case PROP_SYS_QHOUR:
+ if((pRes = getNOW(NOW_QHOUR)) == NULL) {
+ return UCHAR_CONSTANT("**OUT OF MEMORY**");
+ } else
+ *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
+ break;
+ case PROP_SYS_MINUTE:
+ if((pRes = getNOW(NOW_MINUTE)) == NULL) {
+ return UCHAR_CONSTANT("**OUT OF MEMORY**");
+ } else
+ *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
+ break;
+ case PROP_SYS_MYHOSTNAME:
+ pRes = glbl.GetLocalHostName();
+ break;
+ default:
+ /* there is no point in continuing, we may even otherwise render the
+ * error message unreadable. rgerhards, 2007-07-10
+ */
+ dbgprintf("invalid property id: '%d'\n", propID);
+ return UCHAR_CONSTANT("**INVALID PROPERTY NAME**");
}
+
/* If we did not receive a template pointer, we are already done... */
if(pTpe == NULL) {
return pRes;
@@ -1846,8 +2336,8 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
*/
if(pTpe->data.field.has_fields == 1) {
size_t iCurrFld;
- char *pFld;
- char *pFldEnd;
+ uchar *pFld;
+ uchar *pFldEnd;
/* first, skip to the field in question. The field separator
* is always one character and is stored in the template entry.
*/
@@ -1885,10 +2375,11 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
if(*pbMustBeFreed == 1)
free(pRes);
*pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
+ return UCHAR_CONSTANT("**OUT OF MEMORY**");
}
/* now copy */
memcpy(pBuf, pFld, iLen);
+ bufLen = iLen;
pBuf[iLen] = '\0'; /* terminate it */
if(*pbMustBeFreed == 1)
free(pRes);
@@ -1901,12 +2392,12 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
if(*pbMustBeFreed == 1)
free(pRes);
*pbMustBeFreed = 0;
- return "**FIELD NOT FOUND**";
+ return UCHAR_CONSTANT("**FIELD NOT FOUND**");
}
} else if(pTpe->data.field.iFromPos != 0 || pTpe->data.field.iToPos != 0) {
/* we need to obtain a private copy */
int iFrom, iTo;
- char *pSb;
+ uchar *pSb;
iFrom = pTpe->data.field.iFromPos;
iTo = pTpe->data.field.iToPos;
/* need to zero-base to and from (they are 1-based!) */
@@ -1914,42 +2405,55 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
--iFrom;
if(iTo > 0)
--iTo;
- iLen = iTo - iFrom + 1; /* the +1 is for an actual char, NOT \0! */
- pBufStart = pBuf = malloc((iLen + 1) * sizeof(char));
- if(pBuf == NULL) {
- if(*pbMustBeFreed == 1)
- free(pRes);
- *pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
- }
- pSb = pRes;
- if(iFrom) {
- /* skip to the start of the substring (can't do pointer arithmetic
- * because the whole string might be smaller!!)
- */
- while(*pSb && iFrom) {
- --iFrom;
+ if(bufLen == -1)
+ bufLen = ustrlen(pRes);
+ if(iFrom == 0 && iTo >= bufLen) {
+ /* in this case, the requested string is a superset of what we already have,
+ * so there is no need to do any processing. This is a frequent case for size-limited
+ * fields like TAG in the default forwarding template (so it is a useful optimization
+ * to check for this condition ;)). -- rgerhards, 2009-07-09
+ */
+ ; /*DO NOTHING*/
+ } else {
+ iLen = iTo - iFrom + 1; /* the +1 is for an actual char, NOT \0! */
+ pBufStart = pBuf = malloc((iLen + 1) * sizeof(char));
+ if(pBuf == NULL) {
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ *pbMustBeFreed = 0;
+ return UCHAR_CONSTANT("**OUT OF MEMORY**");
+ }
+ pSb = pRes;
+ if(iFrom) {
+ /* skip to the start of the substring (can't do pointer arithmetic
+ * because the whole string might be smaller!!)
+ */
+ while(*pSb && iFrom) {
+ --iFrom;
+ ++pSb;
+ }
+ }
+ /* OK, we are at the begin - now let's copy... */
+ bufLen = iLen;
+ while(*pSb && iLen) {
+ *pBuf++ = *pSb;
++pSb;
+ --iLen;
}
+ *pBuf = '\0';
+ bufLen -= iLen; /* subtract remaining length if the string was smaller! */
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ pRes = pBufStart;
+ *pbMustBeFreed = 1;
}
- /* OK, we are at the begin - now let's copy... */
- while(*pSb && iLen) {
- *pBuf++ = *pSb;
- ++pSb;
- --iLen;
- }
- *pBuf = '\0';
- if(*pbMustBeFreed == 1)
- free(pRes);
- pRes = pBufStart;
- *pbMustBeFreed = 1;
#ifdef FEATURE_REGEXP
} else {
/* Check for regular expressions */
if (pTpe->data.field.has_regex != 0) {
if (pTpe->data.field.has_regex == 2)
/* Could not compile regex before! */
- return "**NO MATCH** **BAD REGULAR EXPRESSION**";
+ return UCHAR_CONSTANT("**NO MATCH** **BAD REGULAR EXPRESSION**");
dbgprintf("string to match for regex is: %s\n", pRes);
@@ -1962,7 +2466,7 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
*/
while(!bFound) {
int iREstat;
- iREstat = regexp.regexec(&pTpe->data.field.re, pRes + iOffs, nmatch, pmatch, 0);
+ iREstat = regexp.regexec(&pTpe->data.field.re, (char*)(pRes + iOffs), nmatch, pmatch, 0);
dbgprintf("regexec return is %d\n", iREstat);
if(iREstat == 0) {
if(pmatch[0].rm_so == -1) {
@@ -1990,11 +2494,11 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
*pbMustBeFreed = 0;
}
if(pTpe->data.field.nomatchAction == TPL_REGEX_NOMATCH_USE_DFLTSTR)
- return "**NO MATCH**";
+ return UCHAR_CONSTANT("**NO MATCH**");
else if(pTpe->data.field.nomatchAction == TPL_REGEX_NOMATCH_USE_ZERO)
- return "0";
+ return UCHAR_CONSTANT("0");
else
- return "";
+ return UCHAR_CONSTANT("");
}
} else {
/* Match- but did it match the one we wanted? */
@@ -2006,28 +2510,29 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
*pbMustBeFreed = 0;
}
if(pTpe->data.field.nomatchAction == TPL_REGEX_NOMATCH_USE_DFLTSTR)
- return "**NO MATCH**";
+ return UCHAR_CONSTANT("**NO MATCH**");
else
- return "";
+ return UCHAR_CONSTANT("");
}
}
/* OK, we have a usable match - we now need to malloc pB */
int iLenBuf;
- char *pB;
+ uchar *pB;
iLenBuf = pmatch[pTpe->data.field.iSubMatchToUse].rm_eo
- pmatch[pTpe->data.field.iSubMatchToUse].rm_so;
- pB = (char *) malloc((iLenBuf + 1) * sizeof(char));
+ pB = malloc((iLenBuf + 1) * sizeof(uchar));
if (pB == NULL) {
if (*pbMustBeFreed == 1)
free(pRes);
*pbMustBeFreed = 0;
- return "**OUT OF MEMORY ALLOCATING pBuf**";
+ return UCHAR_CONSTANT("**OUT OF MEMORY**");
}
/* Lets copy the matched substring to the buffer */
memcpy(pB, pRes + iOffs + pmatch[pTpe->data.field.iSubMatchToUse].rm_so, iLenBuf);
+ bufLen = iLenBuf - 1;
pB[iLenBuf] = '\0';/* terminate string, did not happen before */
if (*pbMustBeFreed == 1)
@@ -2045,7 +2550,7 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
free(pRes);
*pbMustBeFreed = 0;
}
- return "***REGEXP NOT AVAILABLE***";
+ return UCHAR_CONSTANT("***REGEXP NOT AVAILABLE***");
}
}
#endif /* #ifdef FEATURE_REGEXP */
@@ -2053,28 +2558,13 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
/* now check if we need to do our "SP if first char is non-space" hack logic */
if(*pRes && pTpe->data.field.options.bSPIffNo1stSP) {
- char *pB;
- uchar cFirst = *pRes;
-
/* here, we always destruct the buffer and return a new one */
- pB = (char *) malloc(2 * sizeof(char));
- if(pB == NULL) {
- if(*pbMustBeFreed == 1)
- free(pRes);
- *pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
- }
- pRes = pB;
- *pbMustBeFreed = 1;
-
- if(cFirst == ' ') {
- /* if we have a SP, we must return an empty string */
- *pRes = '\0'; /* empty */
- } else {
- /* if it is no SP, we need to return one */
- *pRes = ' ';
- *(pRes+1) = '\0';
- }
+ uchar cFirst = *pRes; /* save first char */
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ pRes = (cFirst == ' ') ? UCHAR_CONSTANT("") : UCHAR_CONSTANT(" ");
+ bufLen = (cFirst == ' ') ? 0 : 1;
+ *pbMustBeFreed = 0;
}
if(*pRes) {
@@ -2083,21 +2573,22 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
*/
if(pTpe->data.field.eCaseConv != tplCaseConvNo) {
/* we need to obtain a private copy */
- int iBufLen = strlen(pRes);
- char *pBStart;
- char *pB;
- char *pSrc;
- pBStart = pB = malloc((iBufLen + 1) * sizeof(char));
+ if(bufLen == -1)
+ bufLen = ustrlen(pRes);
+ uchar *pBStart;
+ uchar *pB;
+ uchar *pSrc;
+ pBStart = pB = malloc((bufLen + 1) * sizeof(char));
if(pB == NULL) {
if(*pbMustBeFreed == 1)
free(pRes);
*pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
+ return UCHAR_CONSTANT("**OUT OF MEMORY**");
}
pSrc = pRes;
while(*pSrc) {
*pB++ = (pTpe->data.field.eCaseConv == tplCaseConvUpper) ?
- (char)toupper((int)*pSrc) : (char)tolower((int)*pSrc);
+ (uchar)toupper((int)*pSrc) : (uchar)tolower((int)*pSrc);
/* currently only these two exist */
++pSrc;
}
@@ -2121,10 +2612,10 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
*/
if(pTpe->data.field.options.bDropCC) {
int iLenBuf = 0;
- char *pSrc = pRes;
- char *pDstStart;
- char *pDst;
- char bDropped = 0;
+ uchar *pSrc = pRes;
+ uchar *pDstStart;
+ uchar *pDst;
+ uchar bDropped = 0;
while(*pSrc) {
if(!iscntrl((int) *pSrc++))
@@ -2139,7 +2630,7 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
if(*pbMustBeFreed == 1)
free(pRes);
*pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
+ return UCHAR_CONSTANT("**OUT OF MEMORY**");
}
for(pSrc = pRes; *pSrc; pSrc++) {
if(!iscntrl((int) *pSrc))
@@ -2149,12 +2640,13 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
if(*pbMustBeFreed == 1)
free(pRes);
pRes = pDstStart;
+ bufLen = iLenBuf;
*pbMustBeFreed = 1;
}
} else if(pTpe->data.field.options.bSpaceCC) {
- char *pSrc;
- char *pDstStart;
- char *pDst;
+ uchar *pSrc;
+ uchar *pDstStart;
+ uchar *pDst;
if(*pbMustBeFreed == 1) {
/* in this case, we already work on dynamic
@@ -2167,12 +2659,14 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
*pDst = ' ';
}
} else {
- pDst = pDstStart = malloc(strlen(pRes) + 1);
+ if(bufLen == -1)
+ bufLen = ustrlen(pRes);
+ pDst = pDstStart = malloc(bufLen + 1);
if(pDst == NULL) {
if(*pbMustBeFreed == 1)
free(pRes);
*pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
+ return UCHAR_CONSTANT("**OUT OF MEMORY**");
}
for(pSrc = pRes; *pSrc; pSrc++) {
if(iscntrl((int) *pSrc))
@@ -2192,7 +2686,7 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
*/
int iNumCC = 0;
int iLenBuf = 0;
- char *pB;
+ uchar *pB;
for(pB = pRes ; *pB ; ++pB) {
++iLenBuf;
@@ -2202,21 +2696,21 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
if(iNumCC > 0) { /* if 0, there is nothing to escape, so we are done */
/* OK, let's do the escaping... */
- char *pBStart;
- char szCCEsc[8]; /* buffer for escape sequence */
+ uchar *pBStart;
+ uchar szCCEsc[8]; /* buffer for escape sequence */
int i;
iLenBuf += iNumCC * 4;
- pBStart = pB = malloc((iLenBuf + 1) * sizeof(char));
+ pBStart = pB = malloc((iLenBuf + 1) * sizeof(uchar));
if(pB == NULL) {
if(*pbMustBeFreed == 1)
free(pRes);
*pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
+ return UCHAR_CONSTANT("**OUT OF MEMORY**");
}
while(*pRes) {
if(iscntrl((int) *pRes)) {
- snprintf(szCCEsc, sizeof(szCCEsc), "#%3.3d", *pRes);
+ snprintf((char*)szCCEsc, sizeof(szCCEsc), "#%3.3d", *pRes);
for(i = 0 ; i < 4 ; ++i)
*pB++ = szCCEsc[i];
} else {
@@ -2228,6 +2722,7 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
if(*pbMustBeFreed == 1)
free(pRes);
pRes = pBStart;
+ bufLen = -1;
*pbMustBeFreed = 1;
}
}
@@ -2239,10 +2734,10 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
if(pTpe->data.field.options.bSecPathDrop || pTpe->data.field.options.bSecPathReplace) {
if(pTpe->data.field.options.bSecPathDrop) {
int iLenBuf = 0;
- char *pSrc = pRes;
- char *pDstStart;
- char *pDst;
- char bDropped = 0;
+ uchar *pSrc = pRes;
+ uchar *pDstStart;
+ uchar *pDst;
+ uchar bDropped = 0;
while(*pSrc) {
if(*pSrc++ != '/')
@@ -2257,7 +2752,7 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
if(*pbMustBeFreed == 1)
free(pRes);
*pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
+ return UCHAR_CONSTANT("**OUT OF MEMORY**");
}
for(pSrc = pRes; *pSrc; pSrc++) {
if(*pSrc != '/')
@@ -2267,12 +2762,13 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
if(*pbMustBeFreed == 1)
free(pRes);
pRes = pDstStart;
+ bufLen = -1; /* TODO: can we do better? */
*pbMustBeFreed = 1;
}
} else {
- char *pSrc;
- char *pDstStart;
- char *pDst;
+ uchar *pSrc;
+ uchar *pDstStart;
+ uchar *pDst;
if(*pbMustBeFreed == 1) {
/* here, again, we can modify the string as we already obtained
@@ -2285,12 +2781,14 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
*pDst++ = '_';
}
} else {
- pDst = pDstStart = malloc(strlen(pRes) + 1);
+ if(bufLen == -1)
+ bufLen = ustrlen(pRes);
+ pDst = pDstStart = malloc(bufLen + 1);
if(pDst == NULL) {
if(*pbMustBeFreed == 1)
free(pRes);
*pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
+ return UCHAR_CONSTANT("**OUT OF MEMORY**");
}
for(pSrc = pRes; *pSrc; pSrc++) {
if(*pSrc == '/')
@@ -2310,44 +2808,49 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
/* check for "." and ".." (note the parenthesis in the if condition!) */
if((*pRes == '.') && (*(pRes + 1) == '\0' || (*(pRes + 1) == '.' && *(pRes + 2) == '\0'))) {
- char *pTmp = pRes;
+ uchar *pTmp = pRes;
if(*(pRes + 1) == '\0')
- pRes = "_";
+ pRes = UCHAR_CONSTANT("_");
else
- pRes = "_.";;
+ pRes = UCHAR_CONSTANT("_.");;
if(*pbMustBeFreed == 1)
free(pTmp);
*pbMustBeFreed = 0;
} else if(*pRes == '\0') {
if(*pbMustBeFreed == 1)
free(pRes);
- pRes = "_";
+ pRes = UCHAR_CONSTANT("_");
+ bufLen = 1;
*pbMustBeFreed = 0;
}
}
/* Now drop last LF if present (pls note that this must not be done
- * if bEscapeCC was set!
+ * if bEscapeCC was set)!
*/
if(pTpe->data.field.options.bDropLastLF && !pTpe->data.field.options.bEscapeCC) {
- int iLn = strlen(pRes);
- char *pB;
+ int iLn;
+ uchar *pB;
+ if(bufLen == -1)
+ bufLen = ustrlen(pRes);
+ iLn = bufLen;
if(iLn > 0 && *(pRes + iLn - 1) == '\n') {
/* we have a LF! */
/* check if we need to obtain a private copy */
if(*pbMustBeFreed == 0) {
/* ok, original copy, need a private one */
- pB = malloc((iLn + 1) * sizeof(char));
+ pB = malloc((iLn + 1) * sizeof(uchar));
if(pB == NULL) {
*pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
+ return UCHAR_CONSTANT("**OUT OF MEMORY**");
}
memcpy(pB, pRes, iLn - 1);
pRes = pB;
*pbMustBeFreed = 1;
}
*(pRes + iLn - 1) = '\0'; /* drop LF ;) */
+ --bufLen;
}
}
@@ -2358,17 +2861,20 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
*/
if(pTpe->data.field.options.bCSV) {
/* we need to obtain a private copy, as we need to at least add the double quotes */
- int iBufLen = strlen(pRes);
- char *pBStart;
- char *pDst;
- char *pSrc;
+ int iBufLen;
+ uchar *pBStart;
+ uchar *pDst;
+ uchar *pSrc;
+ if(bufLen == -1)
+ bufLen = ustrlen(pRes);
+ iBufLen = bufLen;
/* the malloc may be optimized, we currently use the worst case... */
- pBStart = pDst = malloc((2 * iBufLen + 3) * sizeof(char));
+ pBStart = pDst = malloc((2 * iBufLen + 3) * sizeof(uchar));
if(pDst == NULL) {
if(*pbMustBeFreed == 1)
free(pRes);
*pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
+ return UCHAR_CONSTANT("**OUT OF MEMORY**");
}
pSrc = pRes;
*pDst++ = '"'; /* starting quote */
@@ -2382,10 +2888,15 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
if(*pbMustBeFreed == 1)
free(pRes);
pRes = pBStart;
+ bufLen = -1;
*pbMustBeFreed = 1;
}
- /*dbgprintf("MsgGetProp(\"%s\"): \"%s\"\n", pName, pRes); only for verbose debug logging */
+ if(bufLen == -1)
+ bufLen = ustrlen(pRes);
+ *pPropLen = bufLen;
+
+ ENDfunc
return(pRes);
}
@@ -2400,8 +2911,10 @@ msgGetMsgVar(msg_t *pThis, cstr_t *pstrPropName, var_t **ppVar)
{
DEFiRet;
var_t *pVar;
+ size_t propLen;
uchar *pszProp = NULL;
cstr_t *pstrProp;
+ propid_t propid;
unsigned short bMustBeFreed = 0;
ISOBJ_TYPE_assert(pThis, msg);
@@ -2413,7 +2926,9 @@ msgGetMsgVar(msg_t *pThis, cstr_t *pstrPropName, var_t **ppVar)
CHKiRet(var.ConstructFinalize(pVar));
/* always call MsgGetProp() without a template specifier */
- pszProp = (uchar*) MsgGetProp(pThis, NULL, pstrPropName, &bMustBeFreed);
+ /* TODO: optimize propNameToID() call -- rgerhards, 2009-06-26 */
+ propNameToID(pstrPropName, &propid);
+ pszProp = (uchar*) MsgGetProp(pThis, NULL, propid, &propLen, &bMustBeFreed);
/* now create a string object out of it and hand that over to the var */
CHKiRet(rsCStrConstructFromszStr(&pstrProp, pszProp));
@@ -2428,8 +2943,6 @@ finalize_it:
RETiRet;
}
-
-
/* This function can be used as a generic way to set properties.
* We have to handle a lot of legacy, so our return value is not always
* 100% correct (called functions do not always provide one, should
@@ -2439,6 +2952,9 @@ finalize_it:
#define isProp(name) !rsCStrSzStrCmp(pProp->pcsName, (uchar*) name, sizeof(name) - 1)
rsRetVal MsgSetProperty(msg_t *pThis, var_t *pProp)
{
+ prop_t *myProp;
+ prop_t *propRcvFrom = NULL;
+ prop_t *propRcvFromIP = NULL;
DEFiRet;
ISOBJ_TYPE_assert(pThis, msg);
@@ -2452,27 +2968,34 @@ rsRetVal MsgSetProperty(msg_t *pThis, var_t *pProp)
pThis->iFacility = pProp->val.num;
} else if(isProp("msgFlags")) {
pThis->msgFlags = pProp->val.num;
+ } else if(isProp("offMSG")) {
+ MsgSetMSGoffs(pThis, pProp->val.num);
} else if(isProp("pszRawMsg")) {
- MsgSetRawMsg(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ MsgSetRawMsg(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr), cstrLen(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")) {
/*IGNORE*/; /* this *was* a property, but does no longer exist */
} else if(isProp("pszTAG")) {
- MsgSetTAG(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ MsgSetTAG(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr), cstrLen(pProp->val.pStr));
} else if(isProp("pszInputName")) {
- MsgSetInputName(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr), rsCStrLen(pProp->val.pStr));
+ /* we need to create a property */
+ CHKiRet(prop.Construct(&myProp));
+ CHKiRet(prop.SetString(myProp, rsCStrGetSzStrNoNULL(pProp->val.pStr), rsCStrLen(pProp->val.pStr)));
+ CHKiRet(prop.ConstructFinalize(myProp));
+ MsgSetInputName(pThis, myProp);
+ prop.Destruct(&myProp);
} else if(isProp("pszRcvFromIP")) {
- MsgSetRcvFromIP(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ MsgSetRcvFromIPStr(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr), rsCStrLen(pProp->val.pStr), &propRcvFromIP);
+ prop.Destruct(&propRcvFromIP);
} else if(isProp("pszRcvFrom")) {
- MsgSetRcvFrom(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ MsgSetRcvFromStr(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr), rsCStrLen(pProp->val.pStr), &propRcvFrom);
+ prop.Destruct(&propRcvFrom);
} else if(isProp("pszHOSTNAME")) {
- MsgSetHOSTNAME(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ MsgSetHOSTNAME(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr), rsCStrLen(pProp->val.pStr));
} else if(isProp("pCSStrucData")) {
MsgSetStructuredData(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
} else if(isProp("pCSAPPNAME")) {
@@ -2487,8 +3010,11 @@ rsRetVal MsgSetProperty(msg_t *pThis, var_t *pProp)
memcpy(&pThis->tRcvdAt, &pProp->val.vSyslogTime, sizeof(struct syslogTime));
} else if(isProp("tTIMESTAMP")) {
memcpy(&pThis->tTIMESTAMP, &pProp->val.vSyslogTime, sizeof(struct syslogTime));
+ } else if(isProp("pszMSG")) {
+ dbgprintf("no longer supported property pszMSG silently ignored\n");
}
+finalize_it:
RETiRet;
}
#undef isProp
@@ -2532,6 +3058,7 @@ BEGINObjClassInit(msg, 1, OBJ_IS_CORE_MODULE)
CHKiRet(objUse(var, CORE_COMPONENT));
CHKiRet(objUse(datetime, CORE_COMPONENT));
CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(prop, CORE_COMPONENT));
/* set our own handlers */
OBJSetMethodHandler(objMethod_SERIALIZE, MsgSerialize);
diff --git a/runtime/msg.h b/runtime/msg.h
index fe9f87fa..b006cbec 100644
--- a/runtime/msg.h
+++ b/runtime/msg.h
@@ -3,7 +3,7 @@
*
* File begun on 2007-07-13 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 the rsyslog runtime library.
*
@@ -33,6 +33,7 @@
#include "syslogd-types.h"
#include "template.h"
+
/* rgerhards 2004-11-08: The following structure represents a
* syslog message.
*
@@ -47,16 +48,20 @@
* will be decremented. If it is 1, however, the object is actually
* destroyed. To make this work, it is vital that MsgAddRef() is
* called each time a "copy" is stored somewhere.
+ *
+ * WARNING: this structure is not calloc()ed, so be careful when
+ * adding new fields. You need to initialize them in
+ * msgBaseConstruct(). That function header comment also describes
+ * why this is the case.
*/
struct msg {
BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
- pthread_mutexattr_t mutAttr;
- 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. */
+ pthread_mutex_t mut;
+ bool bDoLock; /* use the mutex? */
+ bool bParseHOSTNAME; /* should the hostname be parsed from the message? */
short iRefCount; /* reference counter (0 = unused) */
- short bParseHOSTNAME; /* should the hostname be parsed from the message? */
/* background: the hostname is not present on "regular" messages
* received via UNIX domain sockets from the same machine. However,
* it is available when we have a forwarder (e.g. rfc3195d) using local
@@ -64,43 +69,35 @@ struct msg {
* resolve all these issues... rgerhards, 2005-10-06
*/
short iSeverity; /* the severity 0..7 */
- uchar *pszSeverity; /* severity as string... */
- int iLenSeverity; /* ... and its length. */
- uchar *pszSeverityStr; /* severity name... */
- int iLenSeverityStr; /* ... and its length. */
short iFacility; /* Facility code 0 .. 23*/
- uchar *pszFacility; /* Facility as string... */
- int iLenFacility; /* ... and its length. */
- uchar *pszFacilityStr; /* facility name... */
- int iLenFacilityStr; /* ... and its length. */
- 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 */
+ short offMSG; /* offset at which the MSG part starts in pszRawMsg */
+ short iProtocolVersion;/* protocol version of message received 0 - legacy, 1 syslog-protocol) */
+ int msgFlags; /* flags associated with this message */
int iLenRawMsg; /* length of raw message */
- uchar *pszMSG; /* the MSG part itself */
int iLenMSG; /* Length of the MSG part */
- uchar *pszUxTradMsg; /* the traditional UNIX message */
- int iLenUxTradMsg;/* Length of the traditional UNIX message */
- uchar *pszTAG; /* pointer to tag value */
int iLenTAG; /* Length of the TAG part */
- uchar *pszHOSTNAME; /* HOSTNAME from syslog message */
int iLenHOSTNAME; /* Length of HOSTNAME */
- uchar *pszRcvFrom; /* System message was received from */
- int iLenRcvFrom; /* Length of pszRcvFrom */
- uchar *pszRcvFromIP; /* IP of system message was received from */
- int iLenRcvFromIP; /* Length of pszRcvFromIP */
- uchar *pszInputName; /* name of the input module that submitted this message */
- int iLenInputName; /* Length of pszInputName */
- short iProtocolVersion;/* protocol version of message received 0 - legacy, 1 syslog-protocol) */
+ uchar *pszRawMsg; /* message as it was received on the wire. This is important in case we
+ * need to preserve cryptographic verifiers. */
+ uchar *pszHOSTNAME; /* HOSTNAME from syslog message */
+ char *pszRcvdAt3164; /* time as RFC3164 formatted string (always 15 charcters) */
+ char *pszRcvdAt3339; /* time as RFC3164 formatted string (32 charcters at most) */
+ char *pszRcvdAt_MySQL; /* rcvdAt as MySQL formatted string (always 14 charcters) */
+ char *pszRcvdAt_PgSQL; /* rcvdAt as PgSQL formatted string (always 21 characters) */
+ char *pszTIMESTAMP3164; /* TIMESTAMP as RFC3164 formatted string (always 15 charcters) */
+ char *pszTIMESTAMP3339; /* TIMESTAMP as RFC3339 formatted string (32 charcters at most) */
+ char *pszTIMESTAMP_MySQL;/* TIMESTAMP as MySQL formatted string (always 14 charcters) */
+ char *pszTIMESTAMP_PgSQL;/* TIMESTAMP as PgSQL formatted string (always 21 characters) */
cstr_t *pCSProgName; /* the (BSD) program name */
cstr_t *pCSStrucData; /* STRUCTURED-DATA */
cstr_t *pCSAPPNAME; /* APP-NAME */
cstr_t *pCSPROCID; /* PROCID */
cstr_t *pCSMSGID; /* MSGID */
+ prop_t *pInputName; /* input name property */
+ prop_t *pRcvFrom; /* name of system message was received from */
+ prop_t *pRcvFromIP; /* IP of system message was received from */
+ ruleset_t *pRuleset; /* ruleset to be used for processing this message */
time_t ttGenTime; /* time msg object was generated, same as tRcvdAt, but a Unix timestamp.
While this field looks redundant, it is required because a Unix timestamp
is used at later processing stages (namely in the output arena). Thanks to
@@ -109,20 +106,18 @@ struct msg {
enough to reliable, but I prefer to leave the subtle things to the OS, where
it obviously is solved in way or another...). */
struct syslogTime tRcvdAt;/* time the message entered this program */
- char *pszRcvdAt3164; /* time as RFC3164 formatted string (always 15 charcters) */
- char *pszRcvdAt3339; /* time as RFC3164 formatted string (32 charcters at most) */
- char *pszRcvdAt_SecFrac;/* time just as fractional seconds (6 charcters) */
- char *pszRcvdAt_MySQL; /* rcvdAt as MySQL formatted string (always 14 charcters) */
- char *pszRcvdAt_PgSQL; /* rcvdAt as PgSQL formatted string (always 21 characters) */
struct syslogTime tTIMESTAMP;/* (parsed) value of the timestamp */
- char *pszTIMESTAMP3164; /* TIMESTAMP as RFC3164 formatted string (always 15 charcters) */
- char *pszTIMESTAMP3339; /* TIMESTAMP as RFC3339 formatted string (32 charcters at most) */
- char *pszTIMESTAMP_MySQL;/* TIMESTAMP as MySQL formatted string (always 14 charcters) */
- 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 */
- /* now follow fixed-size buffers to safe some time otherwise used for allocs */
-
+ /* some fixed-size buffers to save malloc()/free() for frequently used fields (from the default templates) */
+ uchar szRawMsg[CONF_RAWMSG_BUFSIZE]; /* most messages are small, and these are stored here (without malloc/free!) */
+ uchar szHOSTNAME[CONF_HOSTNAME_BUFSIZE];
+ union {
+ uchar *pszTAG; /* pointer to tag value */
+ uchar szBuf[CONF_TAG_BUFSIZE];
+ } TAG;
+ char pszTimestamp3164[16];
+ char pszTimestamp3339[33];
+ char pszTIMESTAMP_SecFrac[7]; /* Note: a pointer is 64 bits/8 char, so this is actually fewer than a pointer! */
+ char pszRcvdAt_SecFrac[7]; /* same as above. Both are fractional seconds for their respective timestamp */
};
@@ -146,45 +141,47 @@ rsRetVal msgDestruct(msg_t **ppM);
msg_t* MsgDup(msg_t* pOld);
msg_t *MsgAddRef(msg_t *pM);
void setProtocolVersion(msg_t *pM, int iNewVersion);
-void MsgSetInputName(msg_t *pMsg, uchar*, size_t);
+void MsgSetInputName(msg_t *pMsg, prop_t*);
rsRetVal MsgSetAPPNAME(msg_t *pMsg, char* pszAPPNAME);
rsRetVal MsgSetPROCID(msg_t *pMsg, char* pszPROCID);
rsRetVal MsgSetMSGID(msg_t *pMsg, char* pszMSGID);
-void MsgAssignTAG(msg_t *pMsg, uchar *pBuf);
-void MsgSetTAG(msg_t *pMsg, char* pszTAG);
+void MsgSetTAG(msg_t *pMsg, uchar* pszBuf, size_t lenBuf);
+void MsgSetRuleset(msg_t *pMsg, ruleset_t*);
rsRetVal MsgSetFlowControlType(msg_t *pMsg, flowControl_t eFlowCtl);
rsRetVal MsgSetStructuredData(msg_t *pMsg, char* pszStrucData);
-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);
+void MsgSetRcvFrom(msg_t *pMsg, prop_t*);
+void MsgSetRcvFromStr(msg_t *pMsg, uchar* pszRcvFrom, int, prop_t **);
+rsRetVal MsgSetRcvFromIP(msg_t *pMsg, prop_t*);
+rsRetVal MsgSetRcvFromIPStr(msg_t *pThis, uchar *psz, int len, prop_t **ppProp);
+void MsgSetHOSTNAME(msg_t *pMsg, uchar* pszHOSTNAME, int lenHOSTNAME);
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 *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
- cstr_t *pCSPropName, unsigned short *pbMustBeFreed);
+void MsgSetMSGoffs(msg_t *pMsg, short offs);
+void MsgSetRawMsgWOSize(msg_t *pMsg, char* pszRawMsg);
+void MsgSetRawMsg(msg_t *pMsg, char* pszRawMsg, size_t lenMsg);
+rsRetVal MsgReplaceMSG(msg_t *pThis, uchar* pszMSG, int lenMSG);
+uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
+ propid_t propID, size_t *pPropLen, 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);
+uchar *getRcvFrom(msg_t *pM);
+
/* TODO: remove these five (so far used in action.c) */
-char *getMSG(msg_t *pM);
+uchar *getMSG(msg_t *pM);
char *getHOSTNAME(msg_t *pM);
-char *getPROCID(msg_t *pM);
-char *getAPPNAME(msg_t *pM);
+char *getPROCID(msg_t *pM, bool bLockMutex);
+char *getAPPNAME(msg_t *pM, bool bLockMutex);
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 *getProgramName(msg_t *pM, bool bLockMutex);
+int getProgramNameLen(msg_t *pM, bool bLockMutex);
uchar *getRcvFrom(msg_t *pM);
+rsRetVal propNameToID(cstr_t *pCSPropName, propid_t *pPropID);
+uchar *propIDToName(propid_t propID);
-#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
@@ -195,6 +192,23 @@ int MsgSetUxTradMsg(msg_t *pMsg, char* pszUxTradMsg);
extern void (*funcMsgPrepareEnqueue)(msg_t *pMsg);
#define MsgPrepareEnqueue(pMsg) funcMsgPrepareEnqueue(pMsg)
+
+/* ------------------------------ some inline functions ------------------------------ */
+
+/* set raw message size. This is needed in some cases where a trunctation is necessary
+ * but the raw message must not be newly set. The most important (and currently only)
+ * use case is if we remove trailing LF or NUL characters. Note that the size can NOT
+ * be extended, only shrunk!
+ * rgerhards, 2009-08-26
+ */
+static inline void
+MsgSetRawMsgSize(msg_t *pMsg, size_t newLen)
+{
+ assert(newLen <= (size_t) pMsg->iLenRawMsg);
+ pMsg->iLenRawMsg = newLen;
+}
+
+
#endif /* #ifndef MSG_H_INCLUDED */
/* vim:set ai:
*/
diff --git a/runtime/nsd_gtls.c b/runtime/nsd_gtls.c
index 19dc8678..79ceffb3 100644
--- a/runtime/nsd_gtls.c
+++ b/runtime/nsd_gtls.c
@@ -710,16 +710,16 @@ gtlsGetCN(nsd_gtls_t *pThis, gnutls_x509_crt *pCert, cstr_t **ppstrCN)
}
/* we found a common name, now extract it */
- CHKiRet(rsCStrConstruct(&pstrCN));
+ CHKiRet(cstrConstruct(&pstrCN));
while(szDN[i] != '\0' && szDN[i] != ',') {
if(szDN[i] == '\\') {
/* hex escapes are not implemented */
++i; /* escape char processed */
if(szDN[i] == '\0')
ABORT_FINALIZE(RS_RET_CERT_INVALID_DN);
- CHKiRet(rsCStrAppendChar(pstrCN, szDN[i]));
+ CHKiRet(cstrAppendChar(pstrCN, szDN[i]));
} else {
- CHKiRet(rsCStrAppendChar(pstrCN, szDN[i]));
+ CHKiRet(cstrAppendChar(pstrCN, szDN[i]));
}
++i; /* char processed */
}
@@ -734,7 +734,7 @@ gtlsGetCN(nsd_gtls_t *pThis, gnutls_x509_crt *pCert, cstr_t **ppstrCN)
finalize_it:
if(iRet != RS_RET_OK) {
if(pstrCN != NULL)
- rsCStrDestruct(&pstrCN);
+ cstrDestruct(&pstrCN);
}
RETiRet;
@@ -761,7 +761,7 @@ gtlsChkPeerFingerprint(nsd_gtls_t *pThis, gnutls_x509_crt *pCert)
size = sizeof(fingerprint);
CHKgnutls(gnutls_x509_crt_get_fingerprint(*pCert, GNUTLS_DIG_SHA1, fingerprint, &size));
CHKiRet(GenFingerprintStr(fingerprint, size, &pstrFingerprint));
- dbgprintf("peer's certificate SHA1 fingerprint: %s\n", rsCStrGetSzStr(pstrFingerprint));
+ dbgprintf("peer's certificate SHA1 fingerprint: %s\n", cstrGetSzStr(pstrFingerprint));
/* now search through the permitted peers to see if we can find a permitted one */
bFoundPositiveMatch = 0;
@@ -779,7 +779,7 @@ gtlsChkPeerFingerprint(nsd_gtls_t *pThis, gnutls_x509_crt *pCert)
if(pThis->bReportAuthErr == 1) {
errno = 0;
errmsg.LogError(0, RS_RET_INVALID_FINGERPRINT, "error: peer fingerprint '%s' unknown - we are "
- "not permitted to talk to it", rsCStrGetSzStr(pstrFingerprint));
+ "not permitted to talk to it", cstrGetSzStr(pstrFingerprint));
pThis->bReportAuthErr = 0;
}
ABORT_FINALIZE(RS_RET_INVALID_FINGERPRINT);
@@ -787,7 +787,7 @@ gtlsChkPeerFingerprint(nsd_gtls_t *pThis, gnutls_x509_crt *pCert)
finalize_it:
if(pstrFingerprint != NULL)
- rsCStrDestruct(&pstrFingerprint);
+ cstrDestruct(&pstrFingerprint);
RETiRet;
}
@@ -874,10 +874,10 @@ gtlsChkPeerName(nsd_gtls_t *pThis, gnutls_x509_crt *pCert)
/* if we did not succeed so far, we try the CN part of the DN... */
CHKiRet(gtlsGetCN(pThis, pCert, &pstrCN));
if(pstrCN != NULL) { /* NULL if there was no CN present */
- dbgprintf("gtls now checking auth for CN '%s'\n", rsCStrGetSzStr(pstrCN));
- snprintf((char*)lnBuf, sizeof(lnBuf), "CN: %s; ", rsCStrGetSzStr(pstrCN));
+ dbgprintf("gtls now checking auth for CN '%s'\n", cstrGetSzStr(pstrCN));
+ snprintf((char*)lnBuf, sizeof(lnBuf), "CN: %s; ", cstrGetSzStr(pstrCN));
CHKiRet(rsCStrAppendStr(pStr, lnBuf));
- CHKiRet(gtlsChkOnePeerName(pThis, rsCStrGetSzStr(pstrCN), &bFoundPositiveMatch));
+ CHKiRet(gtlsChkOnePeerName(pThis, cstrGetSzStr(pstrCN), &bFoundPositiveMatch));
}
}
@@ -888,7 +888,7 @@ gtlsChkPeerName(nsd_gtls_t *pThis, gnutls_x509_crt *pCert)
errno = 0;
errmsg.LogError(0, RS_RET_INVALID_FINGERPRINT, "error: peer name not authorized - "
"not permitted to talk to it. Names: %s",
- rsCStrGetSzStr(pStr));
+ cstrGetSzStr(pStr));
pThis->bReportAuthErr = 0;
}
ABORT_FINALIZE(RS_RET_INVALID_FINGERPRINT);
@@ -1010,8 +1010,8 @@ gtlsChkPeerCertValidity(nsd_gtls_t *pThis)
errmsg.LogError(0, NO_ERRCODE, "not permitted to talk to peer, certificate invalid: %s",
pszErrCause);
gtlsGetCertInfo(pThis, &pStr);
- errmsg.LogError(0, NO_ERRCODE, "invalid cert info: %s", rsCStrGetSzStr(pStr));
- rsCStrDestruct(&pStr);
+ errmsg.LogError(0, NO_ERRCODE, "invalid cert info: %s", cstrGetSzStr(pStr));
+ cstrDestruct(&pStr);
ABORT_FINALIZE(RS_RET_CERT_INVALID);
}
@@ -1032,8 +1032,8 @@ gtlsChkPeerCertValidity(nsd_gtls_t *pThis)
else if(ttCert > ttNow) {
errmsg.LogError(0, RS_RET_CERT_NOT_YET_ACTIVE, "not permitted to talk to peer: certificate %d not yet active", i);
gtlsGetCertInfo(pThis, &pStr);
- errmsg.LogError(0, RS_RET_CERT_NOT_YET_ACTIVE, "invalid cert info: %s", rsCStrGetSzStr(pStr));
- rsCStrDestruct(&pStr);
+ errmsg.LogError(0, RS_RET_CERT_NOT_YET_ACTIVE, "invalid cert info: %s", cstrGetSzStr(pStr));
+ cstrDestruct(&pStr);
ABORT_FINALIZE(RS_RET_CERT_NOT_YET_ACTIVE);
}
@@ -1043,8 +1043,8 @@ gtlsChkPeerCertValidity(nsd_gtls_t *pThis)
else if(ttCert < ttNow) {
errmsg.LogError(0, RS_RET_CERT_EXPIRED, "not permitted to talk to peer: certificate %d expired", i);
gtlsGetCertInfo(pThis, &pStr);
- errmsg.LogError(0, RS_RET_CERT_EXPIRED, "invalid cert info: %s", rsCStrGetSzStr(pStr));
- rsCStrDestruct(&pStr);
+ errmsg.LogError(0, RS_RET_CERT_EXPIRED, "invalid cert info: %s", cstrGetSzStr(pStr));
+ cstrDestruct(&pStr);
ABORT_FINALIZE(RS_RET_CERT_EXPIRED);
}
gnutls_x509_crt_deinit(cert);
diff --git a/runtime/obj-types.h b/runtime/obj-types.h
index 78829f94..e1b54d4f 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
@@ -292,6 +293,15 @@ rsRetVal objName##ClassExit(void) \
ISOBJ_TYPE_assert(pThis, OBJ); \
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave);
+/* note: there was a long-time bug in the macro below that lead to *ppThis = NULL
+ * only when the object was actually destructed. I discovered this issue during
+ * introduction of the pRcvFrom property in msg_t, but it potentially had other
+ * effects, too. I am not sure if some experienced instability resulted from this
+ * bug OR if its fix will cause harm to so-far "correctly" running code. The later
+ * may very well be. Thus I will change it only for the current branch and also
+ * the beta, but not in all old builds. Let's see how things evolve.
+ * rgerhards, 2009-06-30
+ */
#define ENDobjDestruct(OBJ) \
goto finalize_it; /* prevent compiler warning ;) */ \
/* no more code here! */ \
@@ -299,8 +309,8 @@ rsRetVal objName##ClassExit(void) \
if(pThis != NULL) { \
obj.DestructObjSelf((obj_t*) pThis); \
free(pThis); \
- *ppThis = NULL; \
} \
+ *ppThis = NULL; \
pthread_setcancelstate(iCancelStateSave, NULL); \
RETiRet; \
}
diff --git a/runtime/obj.c b/runtime/obj.c
index 355c0f97..aebea332 100644
--- a/runtime/obj.c
+++ b/runtime/obj.c
@@ -75,6 +75,7 @@
#include <string.h>
#include <ctype.h>
#include <assert.h>
+#include <pthread.h>
/* how many objects are supported by rsyslogd? */
#define OBJ_NUM_IDS 100 /* TODO change to a linked list? info: 16 were currently in use 2008-02-29 */
@@ -87,13 +88,17 @@
#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 */
+static pthread_mutex_t mutObjGlobalOp; /* mutex to guard global operations of the object system */
/* cookies for serialized lines */
@@ -144,8 +149,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 +181,7 @@ InfoDestruct(objInfo_t **ppThis)
pThis = *ppThis;
assert(pThis != NULL);
- if(pThis->pszName != NULL)
- free(pThis->pszName);
+ free(pThis->pszName);
free(pThis);
*ppThis = NULL;
@@ -205,9 +209,7 @@ DestructObjSelf(obj_t *pThis)
DEFiRet;
ISOBJ_assert(pThis);
- if(pThis->pszName != NULL) {
- free(pThis->pszName);
- }
+ free(pThis->pszName);
RETiRet;
}
@@ -228,20 +230,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;
@@ -259,7 +261,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:
@@ -284,7 +286,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:
@@ -320,31 +322,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:
@@ -377,23 +379,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;
@@ -410,12 +412,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;
@@ -423,7 +425,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
@@ -440,11 +442,11 @@ objDeserializeEmbedStr(cstr_t **ppStr, strm_t *pStrm)
assert(ppStr != NULL);
- CHKiRet(rsCStrConstruct(&pStr));
+ CHKiRet(cstrConstruct(&pStr));
NEXTC;
while(c != ':') {
- CHKiRet(rsCStrAppendChar(pStr, c));
+ CHKiRet(cstrAppendChar(pStr, c));
NEXTC;
}
CHKiRet(cstrFinalize(pStr));
@@ -453,7 +455,7 @@ objDeserializeEmbedStr(cstr_t **ppStr, strm_t *pStrm)
finalize_it:
if(iRet != RS_RET_OK && pStr != NULL)
- rsCStrDestruct(&pStr);
+ cstrDestruct(&pStr);
RETiRet;
}
@@ -508,11 +510,11 @@ static rsRetVal objDeserializeStr(cstr_t **ppCStr, int iLen, strm_t *pStrm)
assert(ppCStr != NULL);
assert(iLen >= 0);
- CHKiRet(rsCStrConstruct(&pCStr));
+ CHKiRet(cstrConstruct(&pCStr));
NEXTC;
for(i = 0 ; i < iLen ; ++i) {
- CHKiRet(rsCStrAppendChar(pCStr, c));
+ CHKiRet(cstrAppendChar(pCStr, c));
NEXTC;
}
CHKiRet(cstrFinalize(pCStr));
@@ -524,7 +526,7 @@ static rsRetVal objDeserializeStr(cstr_t **ppCStr, int iLen, strm_t *pStrm)
finalize_it:
if(iRet != RS_RET_OK && pCStr != NULL)
- rsCStrDestruct(&pCStr);
+ cstrDestruct(&pCStr);
RETiRet;
}
@@ -617,16 +619,16 @@ 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);
}
/* get the property name first */
- CHKiRet(rsCStrConstruct(&pProp->pcsName));
+ CHKiRet(cstrConstruct(&pProp->pcsName));
NEXTC;
while(c != ':') {
- CHKiRet(rsCStrAppendChar(pProp->pcsName, c));
+ CHKiRet(cstrAppendChar(pProp->pcsName, c));
NEXTC;
}
CHKiRet(cstrFinalize(pProp->pcsName));
@@ -718,7 +720,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);
@@ -803,7 +805,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));
@@ -948,13 +950,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;
@@ -1057,7 +1054,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;
}
@@ -1096,7 +1093,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;
}
@@ -1132,6 +1129,7 @@ UseObj(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf)
/* DEV debug only: dbgprintf("source file %s requests object '%s', ifIsLoaded %d\n", srcFile, pObjName, pIf->ifIsLoaded); */
+ d_pthread_mutex_lock(&mutObjGlobalOp);
if(pIf->ifIsLoaded == 1) {
ABORT_FINALIZE(RS_RET_OK); /* we are already set */
@@ -1172,6 +1170,8 @@ UseObj(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf)
pIf->ifIsLoaded = 1; /* we are happy */
finalize_it:
+ d_pthread_mutex_unlock(&mutObjGlobalOp);
+
if(pStr != NULL)
rsCStrDestruct(&pStr);
@@ -1193,15 +1193,16 @@ ReleaseObj(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf)
/* dev debug only dbgprintf("source file %s releasing object '%s', ifIsLoaded %d\n", srcFile, pObjName, pIf->ifIsLoaded); */
+ d_pthread_mutex_lock(&mutObjGlobalOp);
if(pObjFile == NULL)
FINALIZE; /* if it is not a lodable module, we do not need to do anything... */
if(pIf->ifIsLoaded == 0) {
- ABORT_FINALIZE(RS_RET_OK); /* we are not loaded - this is perfectly OK... */
+ FINALIZE; /* we are not loaded - this is perfectly OK... */
} else if(pIf->ifIsLoaded == 2) {
pIf->ifIsLoaded = 0; /* clean up */
- ABORT_FINALIZE(RS_RET_OK); /* we had a load error and can not continue */
+ FINALIZE; /* we had a load error and can not/must not continue */
}
CHKiRet(rsCStrConstructFromszStr(&pStr, pObjName));
@@ -1213,6 +1214,8 @@ ReleaseObj(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf)
pIf->ifIsLoaded = 0; /* indicated "no longer valid" */
finalize_it:
+ d_pthread_mutex_unlock(&mutObjGlobalOp);
+
if(pStr != NULL)
rsCStrDestruct(&pStr);
@@ -1278,14 +1281,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();
@@ -1304,8 +1308,9 @@ objClassExit(void)
rsRetVal
objClassInit(modInfo_t *pModInfo)
{
- DEFiRet;
+ pthread_mutexattr_t mutAttr;
int i;
+ DEFiRet;
/* first, initialize the object system itself. This must be done
* before any other object is created.
@@ -1314,17 +1319,27 @@ objClassInit(modInfo_t *pModInfo)
arrObjInfo[i] = NULL;
}
+ /* the mutex must be recursive, because objects may call into other
+ * object identifieres recursively.
+ */
+ pthread_mutexattr_init(&mutAttr);
+ pthread_mutexattr_settype(&mutAttr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&mutObjGlobalOp, &mutAttr);
+
/* request objects we use */
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 dc04203b..419d29cc 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 objInfo_t *pObjInfoOBJ = NULL; \
+ static objInfo_t __attribute__((unused)) *pObjInfoOBJ = NULL; \
DEFobjCurrIf(obj)
@@ -77,11 +77,13 @@
/* the next macro MUST be called in Constructors: */
#ifndef NDEBUG /* this means if debug... */
# define objConstructSetObjInfo(pThis) \
- ASSERT(((obj_t*) (pThis))->pObjInfo == NULL); \
((obj_t*) (pThis))->pObjInfo = pObjInfoOBJ; \
+ ((obj_t*) (pThis))->pszName = NULL; \
((obj_t*) (pThis))->iObjCooCKiE = 0xBADEFEE
#else
-# define objConstructSetObjInfo(pThis) ((obj_t*) (pThis))->pObjInfo = pObjInfoOBJ
+# define objConstructSetObjInfo(pThis) \
+ ((obj_t*) (pThis))->pObjInfo = pObjInfoOBJ; \
+ ((obj_t*) (pThis))->pszName = NULL
#endif
#define objDestruct(pThis) (((obj_t*) (pThis))->pObjInfo->objMethods[objMethod_DESTRUCT])(&pThis)
#define objSerialize(pThis) (((obj_t*) (pThis))->pObjInfo->objMethods[objMethod_SERIALIZE])
diff --git a/runtime/parser.c b/runtime/parser.c
index 7eff0801..466066e7 100644
--- a/runtime/parser.c
+++ b/runtime/parser.c
@@ -114,10 +114,7 @@ static inline rsRetVal uncompressMessage(msg_t *pMsg)
"Message ignored.", ret);
FINALIZE; /* unconditional exit, nothing left to do... */
}
- free(pMsg->pszRawMsg);
- pMsg->pszRawMsg = deflateBuf;
- pMsg->iLenRawMsg = iLenDefBuf;
- deflateBuf = NULL; /* logically "freed" - caller is now responsible */
+ MsgSetRawMsg(pMsg, (char*)deflateBuf, iLenDefBuf);
}
finalize_it:
if(deflateBuf != NULL)
@@ -165,6 +162,9 @@ sanitizeMessage(msg_t *pMsg)
size_t iSrc;
size_t iDst;
size_t iMaxLine;
+ size_t maxDest;
+ bool bUpdatedLen = FALSE;
+ uchar szSanBuf[32*1024]; /* buffer used for sanitizing a string */
assert(pMsg != NULL);
assert(pMsg->iLenRawMsg > 0);
@@ -179,6 +179,7 @@ sanitizeMessage(msg_t *pMsg)
/* remove NUL character at end of message (see comment in function header) */
if(pszMsg[lenMsg-1] == '\0') {
DBGPRINTF("dropped NUL at very end of message\n");
+ bUpdatedLen = TRUE;
lenMsg--;
}
@@ -189,6 +190,7 @@ sanitizeMessage(msg_t *pMsg)
*/
if(bDropTrailingLF && pszMsg[lenMsg-1] == '\n') {
DBGPRINTF("dropped LF at very end of message (DropTrailingLF is set)\n");
+ bUpdatedLen = TRUE;
lenMsg--;
}
@@ -199,77 +201,55 @@ sanitizeMessage(msg_t *pMsg)
*/
int bNeedSanitize = 0;
for(iSrc = 0 ; iSrc < lenMsg ; iSrc++) {
- if(pszMsg[iSrc] < 32) {
+ if(iscntrl(pszMsg[iSrc])) {
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;
+
+ if(!bNeedSanitize) {
+ if(bUpdatedLen == TRUE)
+ MsgSetRawMsgSize(pMsg, lenMsg);
FINALIZE;
}
/* now copy over the message and sanitize it */
- /* TODO: can we get cheaper memory alloc? {alloca()?}*/
iMaxLine = glbl.GetMaxLine();
- CHKmalloc(pDst = malloc(sizeof(uchar) * (iMaxLine + 1)));
+ maxDest = lenMsg * 4; /* message can grow at most four-fold */
+ if(maxDest > iMaxLine)
+ maxDest = iMaxLine; /* but not more than the max size! */
+ if(maxDest < sizeof(szSanBuf))
+ pDst = szSanBuf;
+ else
+ CHKmalloc(pDst = malloc(sizeof(uchar) * (iMaxLine + 1)));
iSrc = iDst = 0;
- while(iSrc < lenMsg && iDst < iMaxLine) {
- if(pszMsg[iSrc] == '\0') { /* guard against \0 characters... */
- /* changed to the sequence (somewhat) proposed in
- * draft-ietf-syslog-protocol-19. rgerhards, 2006-11-30
+ while(iSrc < lenMsg && iDst < maxDest - 3) { /* leave some space if last char must be escaped */
+ if(iscntrl((int) pszMsg[iSrc])) {
+ /* note: \0 must always be escaped, the rest of the code currently
+ * can not handle it! -- rgerhards, 2009-08-26
*/
- if(iDst + 3 < iMaxLine) { /* do we have space? */
- pDst[iDst++] = cCCEscapeChar;
- pDst[iDst++] = '0';
- pDst[iDst++] = '0';
- pDst[iDst++] = '0';
- } /* if we do not have space, we simply ignore the '\0'... */
- /* log an error? Very questionable... rgerhards, 2006-11-30 */
- /* decided: we do not log an error, it won't help... rger, 2007-06-21 */
- } else if(bEscapeCCOnRcv && iscntrl((int) pszMsg[iSrc])) {
+ if(pszMsg[iSrc] == '\0' || bEscapeCCOnRcv) {
/* we are configured to escape control characters. Please note
* that this most probably break non-western character sets like
* Japanese, Korean or Chinese. rgerhards, 2007-07-17
- * Note: sysklogd logs octal values only for DEL and CCs above 127.
- * For others, it logs ^n where n is the control char converted to an
- * alphabet character. We like consistency and thus escape it to octal
- * in all cases. If someone complains, we may change the mode. At least
- * we known now what's going on.
- * rgerhards, 2007-07-17
*/
- if(iDst + 3 < iMaxLine) { /* do we have space? */
- pDst[iDst++] = cCCEscapeChar;
- pDst[iDst++] = '0' + ((pszMsg[iSrc] & 0300) >> 6);
- pDst[iDst++] = '0' + ((pszMsg[iSrc] & 0070) >> 3);
- pDst[iDst++] = '0' + ((pszMsg[iSrc] & 0007));
- } /* again, if we do not have space, we ignore the char - see comment at '\0' */
+ pDst[iDst++] = cCCEscapeChar;
+ pDst[iDst++] = '0' + ((pszMsg[iSrc] & 0300) >> 6);
+ pDst[iDst++] = '0' + ((pszMsg[iSrc] & 0070) >> 3);
+ pDst[iDst++] = '0' + ((pszMsg[iSrc] & 0007));
+ }
} else {
pDst[iDst++] = pszMsg[iSrc];
}
++iSrc;
}
- pDst[iDst] = '\0'; /* space *is* reserved for this! */
-
- /* we have a sanitized string. Let's save it now */
- free(pMsg->pszRawMsg);
- if((pMsg->pszRawMsg = malloc((iDst+1) * sizeof(uchar))) == NULL) {
- /* when we get no new buffer, we use what we already have ;) */
- pMsg->pszRawMsg = pDst;
- } else {
- /* trim buffer */
- memcpy(pMsg->pszRawMsg, pDst, iDst+1);
- free(pDst); /* too big! */
- pMsg->iLenRawMsg = iDst;
- }
+
+ MsgSetRawMsg(pMsg, (char*)pDst, iDst); /* save sanitized string */
+
+ if(pDst != szSanBuf)
+ free(pDst);
finalize_it:
RETiRet;
@@ -295,7 +275,7 @@ rsRetVal parseMsg(msg_t *pMsg)
CHKiRet(sanitizeMessage(pMsg));
/* we needed to sanitize first, because we otherwise do not have a C-string we can print... */
- DBGPRINTF("msg parser: flags %x, from '%s', msg '%s'\n", pMsg->msgFlags, pMsg->pszRcvFrom, pMsg->pszRawMsg);
+ DBGPRINTF("msg parser: flags %x, from '%s', msg '%s'\n", pMsg->msgFlags, getRcvFrom(pMsg), pMsg->pszRawMsg);
/* pull PRI */
lenMsg = pMsg->iLenRawMsg;
@@ -309,11 +289,8 @@ rsRetVal parseMsg(msg_t *pMsg)
*/
pri = 0;
while(--lenMsg > 0 && 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))
@@ -323,9 +300,6 @@ rsRetVal parseMsg(msg_t *pMsg)
pMsg->iSeverity = LOG_PRI(pri);
MsgSetAfterPRIOffs(pMsg, msg - pMsg->pszRawMsg);
- if(pMsg->bParseHOSTNAME == 0)
- MsgSetHOSTNAME(pMsg, pMsg->pszRcvFrom);
-
/* rger 2005-11-24 (happy thanksgiving!): we now need to check if we have
* a traditional syslog message or one formatted according to syslog-protocol.
* We need to apply different parsers depending on that. We use the
@@ -349,7 +323,6 @@ rsRetVal parseMsg(msg_t *pMsg)
/* finalize message object */
pMsg->msgFlags &= ~NEEDS_PARSING; /* this message is now parsed */
- MsgPrepareEnqueue(pMsg); /* "historical" name - prepare for multi-threading */
finalize_it:
RETiRet;
diff --git a/runtime/prop.c b/runtime/prop.c
new file mode 100644
index 00000000..d188b2ed
--- /dev/null
+++ b/runtime/prop.c
@@ -0,0 +1,247 @@
+/* prop.c - rsyslog's prop object
+ *
+ * This object is meant to support message properties that are stored
+ * seperately from the message. The main intent is to support properties
+ * that are "constant" during a period of time, so that many messages may
+ * contain a reference to the same property. It is important, though, that
+ * properties are destroyed when they are no longer needed.
+ *
+ * Please note that this is a performance-critical part of the software and
+ * as such we may use some methods in here which do not look elegant, but
+ * which are fast...
+ *
+ * Module begun 2009-06-17 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 <assert.h>
+#include <string.h>
+
+#include "rsyslog.h"
+#include "obj.h"
+#include "obj-types.h"
+#include "unicode-helper.h"
+#include "atomic.h"
+#include "prop.h"
+
+/* static data */
+DEFobjStaticHelpers
+
+
+/* Standard-Constructor
+ */
+BEGINobjConstruct(prop) /* be sure to specify the object type also in END macro! */
+ pThis->iRefCount = 1;
+ENDobjConstruct(prop)
+
+
+/* destructor for the prop object */
+BEGINobjDestruct(prop) /* be sure to specify the object type also in END and CODESTART macros! */
+ int currRefCount;
+CODESTARTobjDestruct(prop)
+ currRefCount = ATOMIC_DEC_AND_FETCH(pThis->iRefCount);
+ if(currRefCount == 0) {
+ /* (only) in this case we need to actually destruct the object */
+ if(pThis->len >= CONF_PROP_BUFSIZE)
+ free(pThis->szVal.psz);
+ } else {
+ pThis = NULL; /* tell framework NOT to destructing the object! */
+ }
+ENDobjDestruct(prop)
+
+/* set string, we make our own private copy! This MUST only be called BEFORE
+ * ConstructFinalize()!
+ */
+static rsRetVal SetString(prop_t *pThis, uchar *psz, int len)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, prop);
+ if(pThis->len >= CONF_PROP_BUFSIZE)
+ free(pThis->szVal.psz);
+ pThis->len = len;
+ if(len < CONF_PROP_BUFSIZE) {
+ memcpy(pThis->szVal.sz, psz, len + 1);
+ } else {
+ CHKmalloc(pThis->szVal.psz = malloc(len + 1));
+ memcpy(pThis->szVal.psz, psz, len + 1);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* get string length */
+static int GetStringLen(prop_t *pThis)
+{
+ return pThis->len;
+}
+
+
+/* get string */
+static rsRetVal GetString(prop_t *pThis, uchar **ppsz, int *plen)
+{
+ BEGINfunc
+ ISOBJ_TYPE_assert(pThis, prop);
+ if(pThis->len < CONF_PROP_BUFSIZE) {
+ *ppsz = pThis->szVal.sz;
+ } else {
+ *ppsz = pThis->szVal.psz;
+ }
+ *plen = pThis->len;
+ ENDfunc
+ return RS_RET_OK;
+}
+
+
+/* ConstructionFinalizer
+ * rgerhards, 2008-01-09
+ */
+static rsRetVal
+propConstructFinalize(prop_t __attribute__((unused)) *pThis)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, prop);
+ RETiRet;
+}
+
+
+/* add a new reference. It is VERY IMPORTANT to call this function whenever
+ * the property is handed over to some entitiy that later call Destruct() on it.
+ */
+static rsRetVal AddRef(prop_t *pThis)
+{
+ ATOMIC_INC(pThis->iRefCount);
+ return RS_RET_OK;
+}
+
+
+/* this is a "do it all in one shot" function that creates a new property,
+ * assigns the provided string to it and finalizes the property. Among the
+ * convenience, it is alos (very, very) slightly faster.
+ * rgerhards, 2009-07-01
+ */
+static rsRetVal CreateStringProp(prop_t **ppThis, uchar* psz, int len)
+{
+ DEFiRet;
+ propConstruct(ppThis);
+ SetString(*ppThis, psz, len);
+ propConstructFinalize(*ppThis);
+ RETiRet;
+}
+
+/* another one-stop function, quite useful: it takes a property pointer and
+ * a string. If the string is already contained in the property, nothing happens.
+ * If the string is different (or the pointer NULL), the current property
+ * is destructed and a new one created. This can be used to get a specific
+ * name in those cases where there is a good chance that the property
+ * immediatly previously processed already contained the value we need - in
+ * which case we save us all the creation overhead by just reusing the already
+ * existing property).
+ * rgerhards, 2009-07-01
+ */
+rsRetVal CreateOrReuseStringProp(prop_t **ppThis, uchar *psz, int len)
+{
+ uchar *pszPrev;
+ int lenPrev;
+ DEFiRet;
+ assert(ppThis != NULL);
+
+ if(*ppThis == NULL) {
+ /* we need to create a property */
+ CHKiRet(CreateStringProp(ppThis, psz, len));
+ } else {
+ /* already exists, check if we can re-use it */
+ GetString(*ppThis, &pszPrev, &lenPrev);
+ if(len != lenPrev || ustrcmp(psz, pszPrev)) {
+ /* different, need to discard old & create new one */
+ propDestruct(ppThis);
+ CHKiRet(CreateStringProp(ppThis, psz, len));
+ } /* else we can re-use the existing one! */
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* debugprint for the prop object */
+BEGINobjDebugPrint(prop) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDebugPrint(prop)
+ dbgprintf("prop object %p - no further debug info implemented\n", pThis);
+ENDobjDebugPrint(prop)
+
+
+/* queryInterface function
+ * rgerhards, 2008-02-21
+ */
+BEGINobjQueryInterface(prop)
+CODESTARTobjQueryInterface(prop)
+ if(pIf->ifVersion != propCURR_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 = propConstruct;
+ pIf->ConstructFinalize = propConstructFinalize;
+ pIf->Destruct = propDestruct;
+ pIf->DebugPrint = propDebugPrint;
+ pIf->SetString = SetString;
+ pIf->GetString = GetString;
+ pIf->GetStringLen = GetStringLen;
+ pIf->AddRef = AddRef;
+ pIf->CreateStringProp = CreateStringProp;
+ pIf->CreateOrReuseStringProp = CreateOrReuseStringProp;
+
+finalize_it:
+ENDobjQueryInterface(prop)
+
+
+/* Exit the prop class.
+ * rgerhards, 2009-04-06
+ */
+BEGINObjClassExit(prop, OBJ_IS_CORE_MODULE) /* class, version */
+// objRelease(errmsg, CORE_COMPONENT);
+ENDObjClassExit(prop)
+
+
+/* Initialize the prop class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-02-19
+ */
+BEGINObjClassInit(prop, 1, OBJ_IS_CORE_MODULE) /* class, version */
+ /* request objects we use */
+// CHKiRet(objUse(errmsg, CORE_COMPONENT));
+
+ /* set our own handlers */
+ OBJSetMethodHandler(objMethod_DEBUGPRINT, propDebugPrint);
+ OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, propConstructFinalize);
+ENDObjClassInit(prop)
+
+/* vi:set ai:
+ */
diff --git a/runtime/prop.h b/runtime/prop.h
new file mode 100644
index 00000000..e3519664
--- /dev/null
+++ b/runtime/prop.h
@@ -0,0 +1,58 @@
+/* The prop object.
+ *
+ * This implements props 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_PROP_H
+#define INCLUDED_PROP_H
+
+/* the prop object */
+struct prop_s {
+ BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
+ int iRefCount; /* reference counter */
+ union {
+ uchar *psz; /* stored string */
+ uchar sz[CONF_PROP_BUFSIZE];
+ } szVal;
+ int len; /* we use int intentionally, otherwise we may get some troubles... */
+};
+
+/* interfaces */
+BEGINinterface(prop) /* name must also be changed in ENDinterface macro! */
+ INTERFACEObjDebugPrint(prop);
+ rsRetVal (*Construct)(prop_t **ppThis);
+ rsRetVal (*ConstructFinalize)(prop_t *pThis);
+ rsRetVal (*Destruct)(prop_t **ppThis);
+ rsRetVal (*SetString)(prop_t *pThis, uchar* psz, int len);
+ rsRetVal (*GetString)(prop_t *pThis, uchar** ppsz, int *plen);
+ int (*GetStringLen)(prop_t *pThis);
+ rsRetVal (*AddRef)(prop_t *pThis);
+ rsRetVal (*CreateStringProp)(prop_t **ppThis, uchar* psz, int len);
+ rsRetVal (*CreateOrReuseStringProp)(prop_t **ppThis, uchar *psz, int len);
+ENDinterface(prop)
+#define propCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+
+
+/* prototypes */
+PROTOTYPEObj(prop);
+
+#endif /* #ifndef INCLUDED_PROP_H */
diff --git a/runtime/queue.c b/runtime/queue.c
index 4e017e84..101052a1 100644
--- a/runtime/queue.c
+++ b/runtime/queue.c
@@ -8,7 +8,11 @@
* (and in the web doc set on http://www.rsyslog.com/doc). Be sure to read it
* if you are getting aquainted to the object.
*
- * Copyright 2008 Rainer Gerhards and Adiscon GmbH.
+ * NOTE: as of 2009-04-22, I have begin to remove the qqueue* prefix from static
+ * function names - this makes it really hard to read and does not provide much
+ * benefit, at least I (now) think so...
+ *
+ * Copyright 2008, 2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of the rsyslog runtime library.
*
@@ -49,71 +53,171 @@
#include "obj.h"
#include "wtp.h"
#include "wti.h"
+#include "msg.h"
#include "atomic.h"
+#include "msg.h" /* TODO: remove once we remove MsgAddRef() call */
#ifdef OS_SOLARIS
# include <sched.h>
-# define pthread_yield() sched_yield()
#endif
/* static data */
DEFobjStaticHelpers
DEFobjCurrIf(glbl)
+DEFobjCurrIf(strm)
/* forward-definitions */
-rsRetVal qqueueChkPersist(qqueue_t *pThis);
-static rsRetVal qqueueSetEnqOnly(qqueue_t *pThis, int bEnqOnly, int bLockMutex);
-static rsRetVal qqueueRateLimiter(qqueue_t *pThis);
+static rsRetVal qqueueChkPersist(qqueue_t *pThis, int nUpdates);
+static rsRetVal SetEnqOnly(qqueue_t *pThis, int bEnqOnly, int bLockMutex);
+static rsRetVal RateLimiter(qqueue_t *pThis);
static int qqueueChkStopWrkrDA(qqueue_t *pThis);
+static rsRetVal GetDeqBatchSize(qqueue_t *pThis, int *pVal);
static int qqueueIsIdleDA(qqueue_t *pThis);
-static rsRetVal qqueueConsumerDA(qqueue_t *pThis, wti_t *pWti, int iCancelStateSave);
-static rsRetVal qqueueConsumerCancelCleanup(void *arg1, void *arg2);
-static rsRetVal qqueueUngetObj(qqueue_t *pThis, obj_t *pUsr, int bLockMutex);
+static rsRetVal ConsumerDA(qqueue_t *pThis, wti_t *pWti);
+static rsRetVal batchProcessed(qqueue_t *pThis, wti_t *pWti);
/* some constants for queuePersist () */
#define QUEUE_CHECKPOINT 1
#define QUEUE_NO_CHECKPOINT 0
+/***********************************************************************
+ * we need a private data structure, the "to-delete" list. As C does
+ * not provide any partly private data structures, we implement this
+ * structure right here inside the module.
+ * Note that this list must always be kept sorted based on a unique
+ * dequeue ID (which is monotonically increasing).
+ * rgerhards, 2009-05-18
+ ***********************************************************************/
+
+/* generate next uniqueue dequeue ID. Note that uniqueness is only required
+ * on a per-queue basis and while this instance runs. So a stricly monotonically
+ * increasing counter is sufficient (if enough bits are used).
+ */
+static inline qDeqID getNextDeqID(qqueue_t *pQueue)
+{
+ ISOBJ_TYPE_assert(pQueue, qqueue);
+ return pQueue->deqIDAdd++;
+}
+
+
+/* return the top element of the to-delete list or NULL, if the
+ * list is empty.
+ */
+static inline toDeleteLst_t *tdlPeek(qqueue_t *pQueue)
+{
+ ISOBJ_TYPE_assert(pQueue, qqueue);
+ return pQueue->toDeleteLst;
+}
+
+
+/* remove the top element of the to-delete list. Nothing but the
+ * element itself is destroyed. Must not be called when the list
+ * is empty.
+ */
+static inline rsRetVal tdlPop(qqueue_t *pQueue)
+{
+ toDeleteLst_t *pRemove;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pQueue, qqueue);
+ assert(pQueue->toDeleteLst != NULL);
+
+ pRemove = pQueue->toDeleteLst;
+ pQueue->toDeleteLst = pQueue->toDeleteLst->pNext;
+ free(pRemove);
+
+ RETiRet;
+}
+
+
+/* Add a new to-delete list entry. The function allocates the data
+ * structure, populates it with the values provided and links the new
+ * element into the correct place inside the list.
+ */
+static inline rsRetVal tdlAdd(qqueue_t *pQueue, qDeqID deqID, int nElemDeq)
+{
+ toDeleteLst_t *pNew;
+ toDeleteLst_t *pPrev;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pQueue, qqueue);
+ assert(pQueue->toDeleteLst != NULL);
+
+ CHKmalloc(pNew = malloc(sizeof(toDeleteLst_t)));
+ pNew->deqID = deqID;
+ pNew->nElemDeq = nElemDeq;
+
+ /* now find right spot */
+ for( pPrev = pQueue->toDeleteLst
+ ; pPrev != NULL && deqID > pPrev->deqID
+ ; pPrev = pPrev->pNext) {
+ /*JUST SEARCH*/;
+ }
+
+ if(pPrev == NULL) {
+ pNew->pNext = pQueue->toDeleteLst;
+ pQueue->toDeleteLst = pNew;
+ } else {
+ pNew->pNext = pPrev->pNext;
+ pPrev->pNext = pNew;
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
/* methods */
-/* get the overall queue size, which includes ungotten objects. Must only be called
+/* get the physical queue size. Must only be called
* while mutex is locked!
* rgerhards, 2008-01-29
*/
static inline int
-qqueueGetOverallQueueSize(qqueue_t *pThis)
+getPhysicalQueueSize(qqueue_t *pThis)
{
-#if 0 /* leave a bit in for debugging -- rgerhards, 2008-01-30 */
-BEGINfunc
-dbgoprint((obj_t*) pThis, "queue size: %d (regular %d, ungotten %d)\n",
- pThis->iQueueSize + pThis->iUngottenObjs, pThis->iQueueSize, pThis->iUngottenObjs);
-ENDfunc
-#endif
- return pThis->iQueueSize + pThis->iUngottenObjs;
+ return pThis->iQueueSize;
}
+/* get the logical queue size (that is store size minus logically dequeued elements).
+ * Must only be called while mutex is locked!
+ * rgerhards, 2009-05-19
+ */
+static inline int
+getLogicalQueueSize(qqueue_t *pThis)
+{
+ return pThis->iQueueSize - pThis->nLogDeq;
+}
+
+
+
/* This function drains the queue in cases where this needs to be done. The most probable
* reason is a HUP which needs to discard data (because the queue is configured to be lossy).
* During a shutdown, this is typically not needed, as the OS frees up ressources and does
* this much quicker than when we clean up ourselvs. -- rgerhards, 2008-10-21
* This function returns void, as it makes no sense to communicate an error back, even if
* it happens.
+ * This functions works "around" the regular deque mechanism, because it is only used to
+ * clean up (in cases where message loss is acceptable).
*/
static inline void queueDrain(qqueue_t *pThis)
{
void *pUsr;
-
ASSERT(pThis != NULL);
+ BEGINfunc
+ DBGOPRINT((obj_t*) pThis, "queue (type %d) will lose %d messages, destroying...\n", pThis->qType, pThis->iQueueSize);
/* iQueueSize is not decremented by qDel(), so we need to do it ourselves */
- while(pThis->iQueueSize-- > 0) {
- pThis->qDel(pThis, &pUsr);
+ while(ATOMIC_DEC_AND_FETCH(pThis->iQueueSize) > 0) {
+ pThis->qDeq(pThis, &pUsr);
if(pUsr != NULL) {
objDestruct(pUsr);
}
+ pThis->qDel(pThis);
}
+ ENDfunc
}
@@ -136,37 +240,17 @@ static inline rsRetVal qqueueAdviseMaxWorkers(qqueue_t *pThis)
/* if we have not yet reached the high water mark, there is no need to start a
* worker. -- rgerhards, 2008-01-26
*/
- if(qqueueGetOverallQueueSize(pThis) >= pThis->iHighWtrMrk || pThis->bQueueStarted == 0) {
+ if(getLogicalQueueSize(pThis) >= pThis->iHighWtrMrk || pThis->bQueueStarted == 0) {
wtpAdviseMaxWorkers(pThis->pWtpDA, 1); /* disk queues have always one worker */
}
+ }
+ /* regular workers always run */
+ if(pThis->qType == QUEUETYPE_DISK || pThis->iMinMsgsPerWrkr == 0) {
+ iMaxWorkers = 1;
} else {
- if(pThis->qType == QUEUETYPE_DISK || pThis->iMinMsgsPerWrkr == 0) {
- iMaxWorkers = 1;
- } else {
- iMaxWorkers = qqueueGetOverallQueueSize(pThis) / pThis->iMinMsgsPerWrkr + 1;
- }
- wtpAdviseMaxWorkers(pThis->pWtpReg, iMaxWorkers); /* disk queues have always one worker */
+ iMaxWorkers = getLogicalQueueSize(pThis) / pThis->iMinMsgsPerWrkr + 1;
}
- }
-
- RETiRet;
-}
-
-
-/* wait until we have a fully initialized DA queue. Sometimes, we need to
- * sync with it, as we expect it for some function.
- * rgerhards, 2008-02-27
- */
-static rsRetVal
-qqueueWaitDAModeInitialized(qqueue_t *pThis)
-{
- DEFiRet;
-
- ISOBJ_TYPE_assert(pThis, qqueue);
- ASSERT(pThis->bRunsDA);
-
- while(pThis->bRunsDA != 2) {
- d_pthread_cond_wait(&pThis->condDAReady, pThis->mut);
+ wtpAdviseMaxWorkers(pThis->pWtpReg, iMaxWorkers); /* disk queues have always one worker */
}
RETiRet;
@@ -184,45 +268,16 @@ qqueueWaitDAModeInitialized(qqueue_t *pThis)
* rgerhards, 2008-01-15
*/
static rsRetVal
-qqueueTurnOffDAMode(qqueue_t *pThis)
+TurnOffDAMode(qqueue_t *pThis)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, qqueue);
ASSERT(pThis->bRunsDA);
-
- /* at this point, we need a fully initialized DA queue. So if it isn't, we finally need
- * to wait for its startup... -- rgerhards, 2008-01-25
- */
- qqueueWaitDAModeInitialized(pThis);
-
- /* if we need to pull any data that we still need from the (child) disk queue,
- * now would be the time to do so. At present, we do not need this, but I'd like to
- * keep that comment if future need arises.
- */
-
- /* we need to check if the DA queue is empty because the DA worker may simply have
- * terminated do to no new messages arriving. That does not, however, mean that the
- * DA queue is empty. If there is still data in that queue, we do nothing and leave
- * that for a later incarnation of this function (it will be called multiple times
- * during the lifetime of DA-mode, depending on how often the DA worker receives an
- * inactivity timeout. -- rgerhards, 2008-01-25
- */
- if(pThis->pqDA->iQueueSize == 0) {
+ if(getLogicalQueueSize(pThis->pqDA) == 0) {
pThis->bRunsDA = 0; /* tell the world we are back in non-DA mode */
- /* we destruct the queue object, which will also shutdown the queue worker. As the queue is empty,
- * this will be quick.
- */
- qqueueDestruct(&pThis->pqDA); /* and now we are ready to destruct the DA queue */
- dbgoprint((obj_t*) pThis, "disk-assistance has been turned off, disk queue was empty (iRet %d)\n",
+ DBGOPRINT((obj_t*) pThis, "disk-assistance has been turned off, disk queue was empty (iRet %d)\n",
iRet);
- /* now we need to check if the regular queue has some messages. This may be the case
- * when it is waiting that the high water mark is reached again. If so, we need to start up
- * a regular worker. -- rgerhards, 2008-01-26
- */
- if(qqueueGetOverallQueueSize(pThis) > 0) {
- qqueueAdviseMaxWorkers(pThis);
- }
}
RETiRet;
@@ -244,9 +299,9 @@ qqueueChkIsDA(qqueue_t *pThis)
ISOBJ_TYPE_assert(pThis, qqueue);
if(pThis->pszFilePrefix != NULL) {
pThis->bIsDA = 1;
- dbgoprint((obj_t*) pThis, "is disk-assisted, disk will be used on demand\n");
+ DBGOPRINT((obj_t*) pThis, "is disk-assisted, disk will be used on demand\n");
} else {
- dbgoprint((obj_t*) pThis, "is NOT disk-assisted\n");
+ DBGOPRINT((obj_t*) pThis, "is NOT disk-assisted\n");
}
RETiRet;
@@ -265,16 +320,13 @@ qqueueChkIsDA(qqueue_t *pThis)
* rgerhards, 2008-01-15
*/
static rsRetVal
-qqueueStartDA(qqueue_t *pThis)
+StartDA(qqueue_t *pThis)
{
DEFiRet;
uchar pszDAQName[128];
ISOBJ_TYPE_assert(pThis, qqueue);
- if(pThis->bRunsDA == 2) /* check if already in (fully initialized) DA mode... */
- FINALIZE; /* ... then we are already done! */
-
/* create message queue */
CHKiRet(qqueueConstruct(&pThis->pqDA, QUEUETYPE_DISK , 1, 0, pThis->pConsumer));
@@ -293,13 +345,18 @@ qqueueStartDA(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(qqueueSetEnqOnly(pThis->pqDA, pThis->bDAEnqOnly, MUTEX_ALREADY_LOCKED));
+ CHKiRet(SetEnqOnly(pThis->pqDA, pThis->bDAEnqOnly, MUTEX_ALREADY_LOCKED));
CHKiRet(qqueueSetiDeqtWinFromHr(pThis->pqDA, pThis->iDeqtWinFromHr));
CHKiRet(qqueueSetiDeqtWinToHr(pThis->pqDA, pThis->iDeqtWinToHr));
CHKiRet(qqueueSetiHighWtrMrk(pThis->pqDA, 0));
CHKiRet(qqueueSetiDiscardMrk(pThis->pqDA, 0));
+
+ // experimental: XXX
+ CHKiRet(qqueueSettoWrkShutdown(pThis->pqDA, 0));
+
if(pThis->toQShutdown == 0) {
CHKiRet(qqueueSettoQShutdown(pThis->pqDA, 0)); /* if the user really wants... */
} else {
@@ -315,19 +372,9 @@ qqueueStartDA(qqueue_t *pThis)
if(iRet != RS_RET_OK && iRet != RS_RET_FILE_NOT_FOUND)
FINALIZE; /* something is wrong */
- /* as we are right now starting DA mode because we are so busy, it is
- * extremely unlikely that any regular worker is sleeping on empty queue. HOWEVER,
- * we want to be on the safe side, and so we awake anyone that is waiting
- * on one. So even if the scheduler plays badly with us, things should be
- * quite well. -- rgerhards, 2008-01-15
- */
- wtpWakeupWrkr(pThis->pWtpReg); /* awake all workers, but not ourselves ;) */
-
- pThis->bRunsDA = 2; /* we are now in DA mode, but not fully initialized */
- pThis->bChildIsDone = 0;/* set to 1 when child's worker detect queue is finished */
- pthread_cond_broadcast(&pThis->condDAReady); /* signal we are now initialized and ready to go ;) */
+ //pthread_cond_broadcast(&pThis->condDAReady); /* signal we are now initialized and ready to go ;) */
- dbgoprint((obj_t*) pThis, "is now running in disk assisted mode, disk queue 0x%lx\n",
+ DBGOPRINT((obj_t*) pThis, "is now running in disk assisted mode, disk queue 0x%lx\n",
qqueueGetID(pThis->pqDA));
finalize_it:
@@ -335,7 +382,7 @@ finalize_it:
if(pThis->pqDA != NULL) {
qqueueDestruct(&pThis->pqDA);
}
- dbgoprint((obj_t*) pThis, "error %d creating disk queue - giving up.\n", iRet);
+ DBGOPRINT((obj_t*) pThis, "error %d creating disk queue - giving up.\n", iRet);
pThis->bIsDA = 0;
}
@@ -349,8 +396,8 @@ finalize_it:
* If this function fails (should not happen), DA mode is not turned on.
* rgerhards, 2008-01-16
*/
-static inline rsRetVal
-qqueueInitDA(qqueue_t *pThis, int bEnqOnly, int bLockMutex)
+static rsRetVal
+InitDA(qqueue_t *pThis, int bEnqOnly, int bLockMutex)
{
DEFiRet;
DEFVARS_mutexProtection;
@@ -363,17 +410,18 @@ qqueueInitDA(qqueue_t *pThis, int bEnqOnly, int bLockMutex)
* is intentional. We assume that when we need it once, we may also need it on another
* occasion. Ressources used are quite minimal when no worker is running.
* rgerhards, 2008-01-24
+ * NOTE: this is the DA worker *pool*, not the DA queue!
*/
if(pThis->pWtpDA == NULL) {
- lenBuf = snprintf((char*)pszBuf, sizeof(pszBuf), "%s:DA", obj.GetName((obj_t*) pThis));
+ lenBuf = snprintf((char*)pszBuf, sizeof(pszBuf), "%s:DAwpool", obj.GetName((obj_t*) pThis));
CHKiRet(wtpConstruct (&pThis->pWtpDA));
CHKiRet(wtpSetDbgHdr (pThis->pWtpDA, pszBuf, lenBuf));
CHKiRet(wtpSetpfChkStopWrkr (pThis->pWtpDA, (rsRetVal (*)(void *pUsr, int)) qqueueChkStopWrkrDA));
- CHKiRet(wtpSetpfIsIdle (pThis->pWtpDA, (rsRetVal (*)(void *pUsr, int)) qqueueIsIdleDA));
- CHKiRet(wtpSetpfDoWork (pThis->pWtpDA, (rsRetVal (*)(void *pUsr, void *pWti, int)) qqueueConsumerDA));
- CHKiRet(wtpSetpfOnWorkerCancel (pThis->pWtpDA, (rsRetVal (*)(void *pUsr, void*pWti)) qqueueConsumerCancelCleanup));
- CHKiRet(wtpSetpfOnWorkerStartup (pThis->pWtpDA, (rsRetVal (*)(void *pUsr)) qqueueStartDA));
- CHKiRet(wtpSetpfOnWorkerShutdown(pThis->pWtpDA, (rsRetVal (*)(void *pUsr)) qqueueTurnOffDAMode));
+ CHKiRet(wtpSetpfGetDeqBatchSize (pThis->pWtpDA, (rsRetVal (*)(void *pUsr, int*)) GetDeqBatchSize));
+ CHKiRet(wtpSetpfIsIdle (pThis->pWtpDA, (rsRetVal (*)(void *pUsr, wtp_t*)) qqueueIsIdleDA));
+ CHKiRet(wtpSetpfDoWork (pThis->pWtpDA, (rsRetVal (*)(void *pUsr, void *pWti)) ConsumerDA));
+ CHKiRet(wtpSetpfObjProcessed (pThis->pWtpDA, (rsRetVal (*)(void *pUsr, wti_t *pWti)) batchProcessed));
+ CHKiRet(wtpSetpfOnWorkerShutdown(pThis->pWtpDA, (rsRetVal (*)(void *pUsr)) TurnOffDAMode));
CHKiRet(wtpSetpmutUsr (pThis->pWtpDA, pThis->mut));
CHKiRet(wtpSetpcondBusy (pThis->pWtpDA, &pThis->notEmpty));
CHKiRet(wtpSetiNumWorkerThreads (pThis->pWtpDA, 1));
@@ -384,14 +432,20 @@ qqueueInitDA(qqueue_t *pThis, int bEnqOnly, int bLockMutex)
/* if we reach this point, we have a "good" DA worker pool */
/* indicate we now run in DA mode - this is reset by the DA worker if it fails */
- pThis->bRunsDA = 1;
pThis->bDAEnqOnly = bEnqOnly;
+ /* now construct the actual queue (if it does not already exist) */
+ if(pThis->pqDA == NULL) {
+ CHKiRet(StartDA(pThis));
+ }
+
+ pThis->bRunsDA = 1;
+
/* now we must now adivse the wtp that we need one worker. If none is yet active,
* that will also start one up. If we forgot that step, everything would be stalled
* until the next enqueue request.
*/
- wtpAdviseMaxWorkers(pThis->pWtpDA, 1); /* DA queues alsways have just one worker max */
+ wtpAdviseMaxWorkers(pThis->pWtpDA, 1); /* DA queues always have just one worker max */
finalize_it:
END_MTX_PROTECTED_OPERATIONS(pThis->mut);
@@ -405,15 +459,15 @@ finalize_it:
* complete.
* rgerhards, 2008-01-14
*/
-static inline rsRetVal
-qqueueChkStrtDA(qqueue_t *pThis)
+static rsRetVal
+ChkStrtDA(qqueue_t *pThis)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, qqueue);
/* if we do not hit the high water mark, we have nothing to do */
- if(qqueueGetOverallQueueSize(pThis) != pThis->iHighWtrMrk)
+ if(getPhysicalQueueSize(pThis) != pThis->iHighWtrMrk)
ABORT_FINALIZE(RS_RET_OK);
if(pThis->bRunsDA) {
@@ -426,16 +480,16 @@ qqueueChkStrtDA(qqueue_t *pThis)
* terminated due to the inactivity timeout, thus we need to advise the pool that
* we need at least one).
*/
- dbgoprint((obj_t*) pThis, "%d entries - passed high water mark in DA mode, send notify\n",
- qqueueGetOverallQueueSize(pThis));
+ DBGOPRINT((obj_t*) pThis, "%d entries - passed high water mark in DA mode, send notify\n",
+ getPhysicalQueueSize(pThis));
qqueueAdviseMaxWorkers(pThis);
} else {
/* this is the case when we are currently not running in DA mode. So it is time
* to turn it back on.
*/
- dbgoprint((obj_t*) pThis, "%d entries - passed high water mark for disk-assisted mode, initiating...\n",
- qqueueGetOverallQueueSize(pThis));
- qqueueInitDA(pThis, QUEUE_MODE_ENQDEQ, MUTEX_ALREADY_LOCKED); /* initiate DA mode */
+ DBGOPRINT((obj_t*) pThis, "%d entries - passed high water mark for disk-assisted mode, initiating...\n",
+ getPhysicalQueueSize(pThis));
+ InitDA(pThis, QUEUE_MODE_ENQDEQ, MUTEX_ALREADY_LOCKED); /* initiate DA mode */
}
finalize_it:
@@ -466,6 +520,7 @@ static rsRetVal qConstructFixedArray(qqueue_t *pThis)
ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
}
+ pThis->tVars.farray.deqhead = 0;
pThis->tVars.farray.head = 0;
pThis->tVars.farray.tail = 0;
@@ -483,9 +538,7 @@ static rsRetVal qDestructFixedArray(qqueue_t *pThis)
ASSERT(pThis != NULL);
queueDrain(pThis); /* discard any remaining queue entries */
-
- if(pThis->tVars.farray.pBuf != NULL)
- free(pThis->tVars.farray.pBuf);
+ free(pThis->tVars.farray.pBuf);
RETiRet;
}
@@ -504,76 +557,57 @@ static rsRetVal qAddFixedArray(qqueue_t *pThis, void* in)
RETiRet;
}
-static rsRetVal qDelFixedArray(qqueue_t *pThis, void **out)
+
+static rsRetVal qDeqFixedArray(qqueue_t *pThis, void **out)
{
DEFiRet;
ASSERT(pThis != NULL);
- *out = (void*) pThis->tVars.farray.pBuf[pThis->tVars.farray.head];
+ *out = (void*) pThis->tVars.farray.pBuf[pThis->tVars.farray.deqhead];
- pThis->tVars.farray.head++;
- if (pThis->tVars.farray.head == pThis->iMaxQueueSize)
- pThis->tVars.farray.head = 0;
+ pThis->tVars.farray.deqhead++;
+ if (pThis->tVars.farray.deqhead == pThis->iMaxQueueSize)
+ pThis->tVars.farray.deqhead = 0;
RETiRet;
}
-/* -------------------- linked list -------------------- */
-
-/* first some generic functions which are also used for the unget linked list */
-
-static inline rsRetVal qqueueAddLinkedList(qLinkedList_t **ppRoot, qLinkedList_t **ppLast, void* pUsr)
+static rsRetVal qDelFixedArray(qqueue_t *pThis)
{
DEFiRet;
- qLinkedList_t *pEntry;
-
- ASSERT(ppRoot != NULL);
- ASSERT(ppLast != NULL);
-
- if((pEntry = (qLinkedList_t*) malloc(sizeof(qLinkedList_t))) == NULL) {
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- }
- pEntry->pNext = NULL;
- pEntry->pUsr = pUsr;
+ ASSERT(pThis != NULL);
- if(*ppRoot == NULL) {
- *ppRoot = *ppLast = pEntry;
- } else {
- (*ppLast)->pNext = pEntry;
- *ppLast = pEntry;
- }
+ pThis->tVars.farray.head++;
+ if (pThis->tVars.farray.head == pThis->iMaxQueueSize)
+ pThis->tVars.farray.head = 0;
-finalize_it:
RETiRet;
}
-static inline rsRetVal qqueueDelLinkedList(qLinkedList_t **ppRoot, qLinkedList_t **ppLast, obj_t **ppUsr)
+
+/* reset the logical dequeue pointer to the physical dequeue position.
+ * This is only needed after we cancelled workers (during queue shutdown).
+ */
+static rsRetVal
+qUnDeqAllFixedArray(qqueue_t *pThis)
{
DEFiRet;
- qLinkedList_t *pEntry;
- ASSERT(ppRoot != NULL);
- ASSERT(ppLast != NULL);
- ASSERT(ppUsr != NULL);
- ASSERT(*ppRoot != NULL);
-
- pEntry = *ppRoot;
- *ppUsr = pEntry->pUsr;
+ ISOBJ_TYPE_assert(pThis, qqueue);
- if(*ppRoot == *ppLast) {
- *ppRoot = NULL;
- *ppLast = NULL;
- } else {
- *ppRoot = pEntry->pNext;
- }
- free(pEntry);
+ DBGOPRINT((obj_t*) pThis, "resetting FixedArray deq index to %ld (was %ld), logical dequeue count %d\n",
+ pThis->tVars.farray.head, pThis->tVars.farray.deqhead, pThis->nLogDeq);
+
+ pThis->tVars.farray.deqhead = pThis->tVars.farray.head;
+ pThis->nLogDeq = 0;
RETiRet;
}
-/* end generic functions which are also used for the unget linked list */
+
+/* -------------------- linked list -------------------- */
static rsRetVal qConstructLinkedList(qqueue_t *pThis)
@@ -582,8 +616,9 @@ static rsRetVal qConstructLinkedList(qqueue_t *pThis)
ASSERT(pThis != NULL);
- pThis->tVars.linklist.pRoot = 0;
- pThis->tVars.linklist.pLast = 0;
+ pThis->tVars.linklist.pDeqRoot = NULL;
+ pThis->tVars.linklist.pDelRoot = NULL;
+ pThis->tVars.linklist.pLast = NULL;
qqueueChkIsDA(pThis);
@@ -606,54 +641,79 @@ static rsRetVal qDestructLinkedList(qqueue_t __attribute__((unused)) *pThis)
static rsRetVal qAddLinkedList(qqueue_t *pThis, void* pUsr)
{
- DEFiRet;
-
- iRet = qqueueAddLinkedList(&pThis->tVars.linklist.pRoot, &pThis->tVars.linklist.pLast, pUsr);
-#if 0
qLinkedList_t *pEntry;
+ DEFiRet;
- ASSERT(pThis != NULL);
- if((pEntry = (qLinkedList_t*) malloc(sizeof(qLinkedList_t))) == NULL) {
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- }
+ CHKmalloc((pEntry = (qLinkedList_t*) malloc(sizeof(qLinkedList_t))));
pEntry->pNext = NULL;
pEntry->pUsr = pUsr;
- if(pThis->tVars.linklist.pRoot == NULL) {
- pThis->tVars.linklist.pRoot = pThis->tVars.linklist.pLast = pEntry;
+ if(pThis->tVars.linklist.pDelRoot == NULL) {
+ pThis->tVars.linklist.pDelRoot = pThis->tVars.linklist.pDeqRoot = pThis->tVars.linklist.pLast = pEntry;
} else {
pThis->tVars.linklist.pLast->pNext = pEntry;
pThis->tVars.linklist.pLast = pEntry;
}
+ if(pThis->tVars.linklist.pDeqRoot == NULL) {
+ pThis->tVars.linklist.pDeqRoot = pEntry;
+ }
+
finalize_it:
-#endif
RETiRet;
}
-static rsRetVal qDelLinkedList(qqueue_t *pThis, obj_t **ppUsr)
+
+static rsRetVal qDeqLinkedList(qqueue_t *pThis, obj_t **ppUsr)
{
- DEFiRet;
- iRet = qqueueDelLinkedList(&pThis->tVars.linklist.pRoot, &pThis->tVars.linklist.pLast, ppUsr);
-#if 0
qLinkedList_t *pEntry;
+ DEFiRet;
- ASSERT(pThis != NULL);
- ASSERT(pThis->tVars.linklist.pRoot != NULL);
-
- pEntry = pThis->tVars.linklist.pRoot;
+ pEntry = pThis->tVars.linklist.pDeqRoot;
+ ISOBJ_TYPE_assert(pEntry->pUsr, msg);
*ppUsr = pEntry->pUsr;
+ pThis->tVars.linklist.pDeqRoot = pEntry->pNext;
+
+ RETiRet;
+}
+
+
+static rsRetVal qDelLinkedList(qqueue_t *pThis)
+{
+ qLinkedList_t *pEntry;
+ DEFiRet;
- if(pThis->tVars.linklist.pRoot == pThis->tVars.linklist.pLast) {
- pThis->tVars.linklist.pRoot = NULL;
- pThis->tVars.linklist.pLast = NULL;
+ pEntry = pThis->tVars.linklist.pDelRoot;
+
+ if(pThis->tVars.linklist.pDelRoot == pThis->tVars.linklist.pLast) {
+ pThis->tVars.linklist.pDelRoot = pThis->tVars.linklist.pDeqRoot = pThis->tVars.linklist.pLast = NULL;
} else {
- pThis->tVars.linklist.pRoot = pEntry->pNext;
+ pThis->tVars.linklist.pDelRoot = pEntry->pNext;
}
+
free(pEntry);
-#endif
+ RETiRet;
+}
+
+
+/* reset the logical dequeue pointer to the physical dequeue position.
+ * This is only needed after we cancelled workers (during queue shutdown).
+ */
+static rsRetVal
+qUnDeqAllLinkedList(qqueue_t *pThis)
+{
+ DEFiRet;
+
+ ASSERT(pThis != NULL);
+
+ DBGOPRINT((obj_t*) pThis, "resetting LinkedList deq ptr to %p (was %p), logical dequeue count %d\n",
+ pThis->tVars.linklist.pDelRoot, pThis->tVars.linklist.pDeqRoot, pThis->nLogDeq);
+
+ pThis->tVars.linklist.pDeqRoot = pThis->tVars.linklist.pDelRoot;
+ pThis->nLogDeq = 0;
+
RETiRet;
}
@@ -667,7 +727,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;
}
@@ -697,10 +757,10 @@ qqueueHaveQIF(qqueue_t *pThis)
/* check if the file exists */
if(stat((char*) pszQIFNam, &stat_buf) == -1) {
if(errno == ENOENT) {
- dbgoprint((obj_t*) pThis, "no .qi file found\n");
+ DBGOPRINT((obj_t*) pThis, "no .qi file found\n");
ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND);
} else {
- dbgoprint((obj_t*) pThis, "error %d trying to access .qi file\n", errno);
+ DBGOPRINT((obj_t*) pThis, "error %d trying to access .qi file\n", errno);
ABORT_FINALIZE(RS_RET_IO_ERROR);
}
}
@@ -722,8 +782,6 @@ qqueueTryLoadPersistedInfo(qqueue_t *pThis)
uchar pszQIFNam[MAXFNAME];
size_t lenQIFNam;
struct stat stat_buf;
- int iUngottenObjs;
- obj_t *pUsr;
ISOBJ_TYPE_assert(pThis, qqueue);
@@ -734,44 +792,41 @@ qqueueTryLoadPersistedInfo(qqueue_t *pThis)
/* check if the file exists */
if(stat((char*) pszQIFNam, &stat_buf) == -1) {
if(errno == ENOENT) {
- dbgoprint((obj_t*) pThis, "clean startup, no .qi file found\n");
+ DBGOPRINT((obj_t*) pThis, "clean startup, no .qi file found\n");
ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND);
} else {
- dbgoprint((obj_t*) pThis, "error %d trying to access .qi file\n", errno);
+ DBGOPRINT((obj_t*) pThis, "error %d trying to access .qi file\n", errno);
ABORT_FINALIZE(RS_RET_IO_ERROR);
}
}
/* 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));
- /* then the ungotten object queue */
- iUngottenObjs = pThis->iUngottenObjs;
- pThis->iUngottenObjs = 0; /* will be incremented when we add objects! */
-
- while(iUngottenObjs > 0) {
- /* fill the queue from disk */
- CHKiRet(obj.Deserialize((void*) &pUsr, (uchar*)"msg", psQIF, NULL, NULL));
- qqueueUngetObj(pThis, pUsr, MUTEX_ALREADY_LOCKED);
- --iUngottenObjs; /* one less */
- }
-
- /* and now the stream objects (some order as when persisted!) */
+ /* then the stream objects (same order as when persisted!) */
CHKiRet(obj.Deserialize(&pThis->tVars.disk.pWrite, (uchar*) "strm", psQIF,
(rsRetVal(*)(obj_t*,void*))qqueueLoadPersStrmInfoFixup, pThis));
- CHKiRet(obj.Deserialize(&pThis->tVars.disk.pRead, (uchar*) "strm", psQIF,
+ CHKiRet(obj.Deserialize(&pThis->tVars.disk.pReadDel, (uchar*) "strm", psQIF,
(rsRetVal(*)(obj_t*,void*))qqueueLoadPersStrmInfoFixup, pThis));
- CHKiRet(strmSeekCurrOffs(pThis->tVars.disk.pWrite));
- CHKiRet(strmSeekCurrOffs(pThis->tVars.disk.pRead));
+ /* create a duplicate for the read "pointer".
+ */
+
+ 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(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.
@@ -780,10 +835,10 @@ 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",
+ DBGOPRINT((obj_t*) pThis, "error %d reading .qi file - can not read persisted info (if any)\n",
iRet);
}
@@ -815,24 +870,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.pRead));
- CHKiRet(strmSetbDeleteOnClose(pThis->tVars.disk.pRead, 1));
- CHKiRet(strmSetDir(pThis->tVars.disk.pRead, glbl.GetWorkDir(), strlen((char*)glbl.GetWorkDir())));
- CHKiRet(strmSetiMaxFiles(pThis->tVars.disk.pRead, 10000000));
- CHKiRet(strmSettOperationsMode(pThis->tVars.disk.pRead, STREAMMODE_READ));
- CHKiRet(strmSetsType(pThis->tVars.disk.pRead, STREAMTYPE_FILE_CIRCULAR));
- CHKiRet(strmConstructFinalize(pThis->tVars.disk.pRead));
-
-
- CHKiRet(strmSetFName(pThis->tVars.disk.pWrite, pThis->pszFilePrefix, pThis->lenFilePrefix));
- CHKiRet(strmSetFName(pThis->tVars.disk.pRead, 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
@@ -840,8 +905,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.pRead, 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;
@@ -854,8 +920,9 @@ static rsRetVal qDestructDisk(qqueue_t *pThis)
ASSERT(pThis != NULL);
- strmDestruct(&pThis->tVars.disk.pWrite);
- strmDestruct(&pThis->tVars.disk.pRead);
+ strm.Destruct(&pThis->tVars.disk.pWrite);
+ strm.Destruct(&pThis->tVars.disk.pReadDeq);
+ strm.Destruct(&pThis->tVars.disk.pReadDel);
RETiRet;
}
@@ -867,10 +934,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;
@@ -880,23 +947,37 @@ static rsRetVal qAddDisk(qqueue_t *pThis, void* pUsr)
*/
objDestruct(pUsr);
- dbgoprint((obj_t*) pThis, "write wrote %lld octets to disk, queue disk size now %lld octets\n",
+ DBGOPRINT((obj_t*) pThis, "write wrote %lld octets to disk, queue disk size now %lld octets\n",
nWriteCount, pThis->tVars.disk.sizeOnDisk);
finalize_it:
RETiRet;
}
-static rsRetVal qDelDisk(qqueue_t *pThis, void **ppUsr)
+
+static rsRetVal qDeqDisk(qqueue_t *pThis, void **ppUsr)
+{
+ DEFiRet;
+
+ CHKiRet(obj.Deserialize(ppUsr, (uchar*) "msg", pThis->tVars.disk.pReadDeq, NULL, NULL));
+
+finalize_it:
+ RETiRet;
+}
+
+
+static rsRetVal qDelDisk(qqueue_t *pThis)
{
+ obj_t *pDummyObj; /* we need to deserialize it... */
DEFiRet;
int64 offsIn;
int64 offsOut;
- CHKiRet(strmGetCurrOffset(pThis->tVars.disk.pRead, &offsIn));
- CHKiRet(obj.Deserialize(ppUsr, (uchar*) "msg", pThis->tVars.disk.pRead, NULL, NULL));
- CHKiRet(strmGetCurrOffset(pThis->tVars.disk.pRead, &offsOut));
+ CHKiRet(strm.GetCurrOffset(pThis->tVars.disk.pReadDel, &offsIn));
+ CHKiRet(obj.Deserialize(&pDummyObj, (uchar*) "msg", pThis->tVars.disk.pReadDel, NULL, NULL));
+ objDestruct(pDummyObj);
+ 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
@@ -908,7 +989,7 @@ static rsRetVal qDelDisk(qqueue_t *pThis, void **ppUsr)
} else {
pThis->tVars.disk.sizeOnDisk -= pThis->tVars.disk.bytesRead;
pThis->tVars.disk.bytesRead = offsOut;
- dbgoprint((obj_t*) pThis, "a file has been deleted, now %lld octets disk space used\n", pThis->tVars.disk.sizeOnDisk);
+ DBGOPRINT((obj_t*) pThis, "a file has been deleted, now %lld octets disk space used\n", pThis->tVars.disk.sizeOnDisk);
/* awake possibly waiting enq process */
pthread_cond_signal(&pThis->notFull); /* we hold the mutex while we are in here! */
}
@@ -917,6 +998,17 @@ finalize_it:
RETiRet;
}
+
+/* This is a dummy function for disks - we do not need to reset anything
+ * because everything is already persisted...
+ */
+static rsRetVal
+qUnDeqAllDisk(__attribute__((unused)) qqueue_t *pThis)
+{
+ return RS_RET_OK;
+}
+
+
/* -------------------- direct (no queueing) -------------------- */
static rsRetVal qConstructDirect(qqueue_t __attribute__((unused)) *pThis)
{
@@ -931,6 +1023,8 @@ static rsRetVal qDestructDirect(qqueue_t __attribute__((unused)) *pThis)
static rsRetVal qAddDirect(qqueue_t *pThis, void* pUsr)
{
+ batch_t singleBatch;
+ batch_obj_t batchObj;
DEFiRet;
ASSERT(pThis != NULL);
@@ -940,70 +1034,33 @@ static rsRetVal qAddDirect(qqueue_t *pThis, void* pUsr)
* mode the consumer probably has a lot to convey (which get's lost in the other modes
* because they are asynchronous. But direct mode is deliberately synchronous.
* rgerhards, 2008-02-12
+ * We use our knowledge about the batch_t structure below, but without that, we
+ * pay a too-large performance toll... -- rgerhards, 2009-04-22
*/
- iRet = pThis->pConsumer(pThis->pUsr, pUsr);
+ batchObj.state = BATCH_STATE_RDY;
+ batchObj.pUsrp = (obj_t*) pUsr;
+ singleBatch.nElem = 1; /* there always is only one in direct mode */
+ singleBatch.pElem = &batchObj;
+ iRet = pThis->pConsumer(pThis->pUsr, &singleBatch);
+ objDestruct(pUsr);
RETiRet;
}
-static rsRetVal qDelDirect(qqueue_t __attribute__((unused)) *pThis, __attribute__((unused)) void **out)
+
+static rsRetVal qDelDirect(qqueue_t __attribute__((unused)) *pThis)
{
return RS_RET_OK;
}
-
-/* --------------- end type-specific handlers -------------------- */
-
-
-/* unget a user pointer that has been dequeued. This functionality is especially important
- * for consumer cancel cleanup handlers. To support it, a short list of ungotten user pointers
- * is maintened in memory.
- * rgerhards, 2008-01-20
- */
static rsRetVal
-qqueueUngetObj(qqueue_t *pThis, obj_t *pUsr, int bLockMutex)
+qUnDeqAllDirect(__attribute__((unused)) qqueue_t *pThis)
{
- DEFiRet;
- DEFVARS_mutexProtection;
-
- ISOBJ_TYPE_assert(pThis, qqueue);
- ISOBJ_assert(pUsr); /* TODO: we aborted right at this place at least 3 times -- race? 2008-02-28, -03-10, -03-15
- The second time I noticed it the queue was in destruction with NO worker threads
- running. The pUsr ptr was totally off and provided no clue what it may be pointing
- at (except that it looked like the static data pool). Both times, the abort happend
- inside an action queue */
-
- dbgoprint((obj_t*) pThis, "ungetting user object %s\n", obj.GetName(pUsr));
- BEGIN_MTX_PROTECTED_OPERATIONS(pThis->mut, bLockMutex);
- iRet = qqueueAddLinkedList(&pThis->pUngetRoot, &pThis->pUngetLast, pUsr);
- ++pThis->iUngottenObjs; /* indicate one more */
- END_MTX_PROTECTED_OPERATIONS(pThis->mut);
-
- RETiRet;
+ return RS_RET_OK;
}
-/* dequeues a user pointer from the ungotten queue. Pointers from there should always be
- * dequeued first.
- *
- * This function must only be called when the mutex is locked!
- *
- * rgerhards, 2008-01-29
- */
-static rsRetVal
-qqueueGetUngottenObj(qqueue_t *pThis, obj_t **ppUsr)
-{
- DEFiRet;
-
- ISOBJ_TYPE_assert(pThis, qqueue);
- ASSERT(ppUsr != NULL);
-
- iRet = qqueueDelLinkedList(&pThis->pUngetRoot, &pThis->pUngetLast, ppUsr);
- --pThis->iUngottenObjs; /* indicate one less */
- dbgoprint((obj_t*) pThis, "dequeued ungotten user object %s\n", obj.GetName(*ppUsr));
-
- RETiRet;
-}
+/* --------------- end type-specific handlers -------------------- */
/* generic code to add a queue entry
@@ -1022,7 +1079,8 @@ qqueueAdd(qqueue_t *pThis, void *pUsr)
if(pThis->qType != QUEUETYPE_DIRECT) {
ATOMIC_INC(pThis->iQueueSize);
- dbgoprint((obj_t*) pThis, "entry added, size now %d entries\n", pThis->iQueueSize);
+ DBGOPRINT((obj_t*) pThis, "entry added, size now log %d, phys %d entries\n",
+ getLogicalQueueSize(pThis), getPhysicalQueueSize(pThis));
}
finalize_it:
@@ -1030,12 +1088,10 @@ finalize_it:
}
-/* generic code to remove a queue entry
- * rgerhards, 2008-01-29: we must first see if there is any object in the
- * ungotten list and, if so, dequeue it first.
+/* generic code to dequeue a queue entry
*/
static rsRetVal
-qqueueDel(qqueue_t *pThis, void *pUsr)
+qqueueDeq(qqueue_t *pThis, void **ppUsr)
{
DEFiRet;
@@ -1046,53 +1102,36 @@ qqueueDel(qqueue_t *pThis, void *pUsr)
* If we decrement, however, we may lose a message. But that is better than
* losing the whole process because it loops... -- rgerhards, 2008-01-03
*/
- if(pThis->iUngottenObjs > 0) {
- iRet = qqueueGetUngottenObj(pThis, (obj_t**) pUsr);
- } else {
- iRet = pThis->qDel(pThis, pUsr);
- ATOMIC_DEC(pThis->iQueueSize);
- }
+ iRet = pThis->qDeq(pThis, ppUsr);
+ ATOMIC_INC(pThis->nLogDeq);
- dbgoprint((obj_t*) pThis, "entry deleted, state %d, size now %d entries\n",
- iRet, pThis->iQueueSize);
+// DBGOPRINT((obj_t*) pThis, "entry deleted, size now log %d, phys %d entries\n",
+// getLogicalQueueSize(pThis), getPhysicalQueueSize(pThis));
RETiRet;
}
-/* This function shuts down all worker threads and waits until they
- * have terminated. If they timeout, they are cancelled. Parameters have been set
- * before this function is called so that DA queues will be fully persisted to
- * disk (if configured to do so).
- * rgerhards, 2008-01-24
- * Please note that this function shuts down BOTH the parent AND the child queue
- * in DA case. This is necessary because their timeouts are tightly coupled. Most
- * importantly, the timeouts would be applied twice (or logic be extremely
- * complex) if each would have its own shutdown. The function does not self check
- * this condition - the caller must make sure it is not called with a parent.
+/* Try to terminate queue worker threads within the regular shutdown interval.
+ * Both the regular and DA queue (if it exists) is waited for, but on the same timeout.
+ * After this function returns, the workers must either be finished or some force
+ * to finish them must be applied.
+ * This function also instructs the DA worker pool (if it exists) to terminate. This is done
+ * in preparation of final queue shutdown.
+ * rgerhards, 2009-05-27
*/
-static rsRetVal qqueueShutdownWorkers(qqueue_t *pThis)
+static rsRetVal
+tryShutdownWorkersWithinQueueTimeout(qqueue_t *pThis)
{
- DEFiRet;
- DEFVARS_mutexProtection;
struct timespec tTimeout;
rsRetVal iRetLocal;
+ DEFiRet;
ISOBJ_TYPE_assert(pThis, qqueue);
ASSERT(pThis->pqParent == NULL); /* detect invalid calling sequence */
- dbgoprint((obj_t*) pThis, "initiating worker thread shutdown sequence\n");
-
- /* we reduce the low water mark in any case. This is not absolutely necessary, but
- * it is useful because we enable DA mode at several spots below and so we do not need
- * to think about the low water mark each time.
- */
- pThis->iHighWtrMrk = 1; /* if we do not do this, the DA queue will not stop! */
- pThis->iLowWtrMrk = 0;
-
- /* first try to shutdown the queue within the regular shutdown period */
- BEGIN_MTX_PROTECTED_OPERATIONS(pThis->mut, LOCK_MUTEX); /* some workers may be running in parallel! */
- if(qqueueGetOverallQueueSize(pThis) > 0) {
+ d_pthread_mutex_lock(pThis->mut); /* some workers may be running in parallel! */
+ if(getPhysicalQueueSize(pThis) > 0) {
if(pThis->bRunsDA) {
/* We may have waited on the low water mark. As it may have changed, we
* see if we reactivate the worker.
@@ -1100,7 +1139,7 @@ static rsRetVal qqueueShutdownWorkers(qqueue_t *pThis)
wtpAdviseMaxWorkers(pThis->pWtpDA, 1);
}
}
- END_MTX_PROTECTED_OPERATIONS(pThis->mut);
+ d_pthread_mutex_unlock(pThis->mut);
/* Now wait for the queue's workers to shut down. Note that we run into the code even if we just found
* out there are no active workers - that doesn't matter: the wtp knows about that and so will
@@ -1119,151 +1158,212 @@ static rsRetVal qqueueShutdownWorkers(qqueue_t *pThis)
* shutdown of both the regular and DA queue on *the same* timeout.
*/
timeoutComp(&tTimeout, pThis->toQShutdown);
- dbgoprint((obj_t*) pThis, "trying shutdown of regular workers\n");
+ DBGOPRINT((obj_t*) pThis, "trying shutdown of regular workers\n");
iRetLocal = wtpShutdownAll(pThis->pWtpReg, wtpState_SHUTDOWN, &tTimeout);
if(iRetLocal == RS_RET_TIMED_OUT) {
- dbgoprint((obj_t*) pThis, "regular shutdown timed out on primary queue (this is OK)\n");
+ DBGOPRINT((obj_t*) pThis, "regular shutdown timed out on primary queue (this is OK)\n");
} else {
- /* OK, the regular queue is now shut down. So we can now wait for the DA queue (if running DA) */
- dbgoprint((obj_t*) pThis, "regular queue workers shut down.\n");
- BEGIN_MTX_PROTECTED_OPERATIONS(pThis->mut, LOCK_MUTEX); /* some workers may be running in parallel! */
- if(pThis->bRunsDA) {
- END_MTX_PROTECTED_OPERATIONS(pThis->mut);
- dbgoprint((obj_t*) pThis, "we have a DA queue (0x%lx), requesting its shutdown.\n",
- qqueueGetID(pThis->pqDA));
- /* we use the same absolute timeout as above, so we do not use more than the configured
- * timeout interval!
- */
- dbgoprint((obj_t*) pThis, "trying shutdown of DA workers\n");
- iRetLocal = wtpShutdownAll(pThis->pWtpDA, wtpState_SHUTDOWN, &tTimeout);
- if(iRetLocal == RS_RET_TIMED_OUT) {
- dbgoprint((obj_t*) pThis, "shutdown timed out on DA queue (this is OK)\n");
- }
- } else {
- END_MTX_PROTECTED_OPERATIONS(pThis->mut);
- }
+ DBGOPRINT((obj_t*) pThis, "regular queue workers shut down.\n");
}
- /* when we reach this point, both queues are either empty or the regular queue shutdown timeout
- * has expired. Now we need to check if we are configured to not loose messages. If so, we need
- * to persist the queue to disk (this is only possible if the queue is DA-enabled). We must also
- * set the primary queue to SHUTDOWN_IMMEDIATE, as it shall now terminate as soon as its consumer
- * is done. This is especially important as we otherwise may interfere with queue order while the
- * DA consumer is running. -- rgerhards, 2008-01-27
- * Note: there was a note that we should not wait eternally on the DA worker if we run in
- * enqueue-only note. I have reviewed the code and think there is no need for this check. Howerver,
- * I'd like to keep this note in here should we happen to run into some related trouble.
- * rgerhards, 2008-01-28
- */
- wtpSetState(pThis->pWtpReg, wtpState_SHUTDOWN_IMMEDIATE); /* set primary queue to shutdown only */
-
- /* at this stage, we need to have the DA worker properly initialized and running (if there is one) */
- if(pThis->bRunsDA)
- qqueueWaitDAModeInitialized(pThis);
-
- BEGIN_MTX_PROTECTED_OPERATIONS(pThis->mut, LOCK_MUTEX); /* some workers may be running in parallel! */
- /* optimize parameters for shutdown of DA-enabled queues */
- if(pThis->bIsDA && qqueueGetOverallQueueSize(pThis) > 0 && pThis->bSaveOnShutdown) {
- /* switch to enqueue-only mode so that no more actions happen */
- if(pThis->bRunsDA == 0) {
- qqueueInitDA(pThis, QUEUE_MODE_ENQONLY, MUTEX_ALREADY_LOCKED); /* switch to DA mode */
+ /* OK, the worker for the regular queue is processed, on the the DA queue regular worker. */
+ if(pThis->pqDA != NULL) {
+ DBGOPRINT((obj_t*) pThis, "we have a DA queue (0x%lx), requesting its shutdown.\n",
+ qqueueGetID(pThis->pqDA));
+ /* we use the same absolute timeout as above, so we do not use more than the configured
+ * timeout interval!
+ */
+ DBGOPRINT((obj_t*) pThis, "trying shutdown of regular worker of DA queue\n");
+ iRetLocal = wtpShutdownAll(pThis->pqDA->pWtpReg, wtpState_SHUTDOWN, &tTimeout);
+ if(iRetLocal == RS_RET_TIMED_OUT) {
+ DBGOPRINT((obj_t*) pThis, "shutdown timed out on DA queue worker (this is OK)\n");
} else {
- /* TODO: RACE: we may reach this point when the DA worker has been initialized (state 1)
- * but is not yet running (state 2). In this case, pThis->pqDA is NULL! rgerhards, 2008-02-27
- */
- qqueueSetEnqOnly(pThis->pqDA, QUEUE_MODE_ENQONLY, MUTEX_ALREADY_LOCKED); /* switch to enqueue-only mode */
+ DBGOPRINT((obj_t*) pThis, "DA queue worker shut down.\n");
}
- END_MTX_PROTECTED_OPERATIONS(pThis->mut);
- /* make sure we do not timeout before we are done */
- dbgoprint((obj_t*) pThis, "bSaveOnShutdown configured, eternal timeout set\n");
- timeoutComp(&tTimeout, QUEUE_TIMEOUT_ETERNAL);
- /* and run the primary queue's DA worker to drain the queue */
- iRetLocal = wtpShutdownAll(pThis->pWtpDA, wtpState_SHUTDOWN, &tTimeout);
- if(iRetLocal != RS_RET_OK) {
- dbgoprint((obj_t*) pThis, "unexpected iRet state %d after trying to shut down primary queue in disk save mode, "
- "continuing, but results are unpredictable\n", iRetLocal);
+ /* we also instruct the DA worker pool to shutdown ASAP. If we need it for persisting
+ * the queue, it is restarted at a later stage. We don't care here if a timeout happens.
+ */
+ DBGOPRINT((obj_t*) pThis, "trying shutdown of main queue DA worker pool\n");
+ iRetLocal = wtpShutdownAll(pThis->pWtpDA, wtpState_SHUTDOWN_IMMEDIATE, &tTimeout);
+ if(iRetLocal == RS_RET_TIMED_OUT) {
+ DBGOPRINT((obj_t*) pThis, "shutdown timed out on main queue DA worker pool (this is OK)\n");
+ } else {
+ DBGOPRINT((obj_t*) pThis, "main queue DA worker pool shut down on first try.\n");
}
- } else {
- END_MTX_PROTECTED_OPERATIONS(pThis->mut);
}
- /* now the primary queue is either empty, persisted to disk - or set to loose messages. So we
- * can now request immediate shutdown of any remaining workers. Note that if bSaveOnShutdown was set,
- * the queue is now empty. If regular workers are still running, and try to pull the next message,
- * they will automatically terminate as there no longer is any message left to process.
+ RETiRet;
+}
+
+
+/* Try to shut down regular and DA queue workers, within the action timeout
+ * period. Note that the main queue DA worker is still unaffected (and may shuffle
+ * data to the disk queue while we terminate the other workers). Not finishing
+ * processing all messages is now OK (but they may be preserved later, depending
+ * on bSaveOnShutdown setting).
+ * rgerhards, 2009-05-27
+ */
+static rsRetVal
+tryShutdownWorkersWithinActionTimeout(qqueue_t *pThis)
+{
+ struct timespec tTimeout;
+ rsRetVal iRetLocal;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, qqueue);
+ ASSERT(pThis->pqParent == NULL); /* detect invalid calling sequence */
+
+ /* instruct workers to finish ASAP, even if still work exists */
+ /* note that we modify bEnqOnly directly, because going through the method would
+ * startup some workers again. So this is OK here. -- rgerhards, 2009-05-28
*/
- BEGIN_MTX_PROTECTED_OPERATIONS(pThis->mut, LOCK_MUTEX); /* some workers may be running in parallel! */
- if(qqueueGetOverallQueueSize(pThis) > 0) {
- timeoutComp(&tTimeout, pThis->toActShutdown);
- if(wtpGetCurNumWrkr(pThis->pWtpReg, LOCK_MUTEX) > 0) {
- END_MTX_PROTECTED_OPERATIONS(pThis->mut);
- dbgoprint((obj_t*) pThis, "trying immediate shutdown of regular workers\n");
- iRetLocal = wtpShutdownAll(pThis->pWtpReg, wtpState_SHUTDOWN_IMMEDIATE, &tTimeout);
- if(iRetLocal == RS_RET_TIMED_OUT) {
- dbgoprint((obj_t*) pThis, "immediate shutdown timed out on primary queue (this is acceptable and "
- "triggers cancellation)\n");
- } else if(iRetLocal != RS_RET_OK) {
- dbgoprint((obj_t*) pThis, "unexpected iRet state %d after trying immediate shutdown of the primary queue "
- "in disk save mode. Continuing, but results are unpredictable\n", iRetLocal);
- }
- /* we need to re-aquire the mutex for the next check in this case! */
- BEGIN_MTX_PROTECTED_OPERATIONS(pThis->mut, LOCK_MUTEX); /* some workers may be running in parallel! */
+ pThis->bEnqOnly = 1;
+ /* need to set this so that the DA queue begins shutdown in parallel! */
+ if(pThis->pqDA != NULL) {
+ pThis->pqDA->bEnqOnly = 1;
+ wtpSetState(pThis->pqDA->pWtpReg, wtpState_SHUTDOWN_IMMEDIATE);
+ }
+
+ /* now give the queue workers a last chance to gracefully shut down (based on action timeout setting) */
+ timeoutComp(&tTimeout, pThis->toActShutdown);
+ DBGOPRINT((obj_t*) pThis, "trying immediate shutdown of regular workers (if any)\n");
+ iRetLocal = wtpShutdownAll(pThis->pWtpReg, wtpState_SHUTDOWN_IMMEDIATE, &tTimeout);
+ if(iRetLocal == RS_RET_TIMED_OUT) {
+ DBGOPRINT((obj_t*) pThis, "immediate shutdown timed out on primary queue (this is acceptable and "
+ "triggers cancellation)\n");
+ } else if(iRetLocal != RS_RET_OK) {
+ DBGOPRINT((obj_t*) pThis, "unexpected iRet state %d after trying immediate shutdown of the primary queue "
+ "in disk save mode. Continuing, but results are unpredictable\n", iRetLocal);
+ }
+
+ if(pThis->pqDA != NULL) {
+ /* and now the same for the DA queue */
+ DBGOPRINT((obj_t*) pThis, "trying immediate shutdown of DA queue workers\n");
+ iRetLocal = wtpShutdownAll(pThis->pqDA->pWtpReg, wtpState_SHUTDOWN_IMMEDIATE, &tTimeout);
+ if(iRetLocal == RS_RET_TIMED_OUT) {
+ DBGOPRINT((obj_t*) pThis, "immediate shutdown timed out on DA queue (this is acceptable "
+ "and triggers cancellation)\n");
+ } else if(iRetLocal != RS_RET_OK) {
+ DBGOPRINT((obj_t*) pThis, "unexpected iRet state %d after trying immediate shutdown of the DA "
+ "queue in disk save mode. Continuing, but results are unpredictable\n", iRetLocal);
}
- if(pThis->bIsDA && wtpGetCurNumWrkr(pThis->pWtpDA, LOCK_MUTEX) > 0) {
- /* and now the same for the DA queue */
- END_MTX_PROTECTED_OPERATIONS(pThis->mut);
- dbgoprint((obj_t*) pThis, "trying immediate shutdown of DA workers\n");
- iRetLocal = wtpShutdownAll(pThis->pWtpDA, wtpState_SHUTDOWN_IMMEDIATE, &tTimeout);
- if(iRetLocal == RS_RET_TIMED_OUT) {
- dbgoprint((obj_t*) pThis, "immediate shutdown timed out on DA queue (this is acceptable and "
- "triggers cancellation)\n");
- } else if(iRetLocal != RS_RET_OK) {
- dbgoprint((obj_t*) pThis, "unexpected iRet state %d after trying immediate shutdown of the DA queue "
- "in disk save mode. Continuing, but results are unpredictable\n", iRetLocal);
- }
+ /* and now we need to check the DA worker itself (the one that shuffles data to the disk). This
+ * is necessary because we may be in a situation where the DA queue regular worker and the
+ * main queue worker stopped rather quickly. In this case, there is almost no time (and
+ * probably no thread switch!) between the point where we instructed the main queue DA
+ * worker to shutdown and this code location. In consequence, it may not even have
+ * noticed that it should should down, less acutally done this. So we provide it with a
+ * fixed 100ms timeout to try complete its work, what usually should be sufficient.
+ * rgerhards, 2009-10-06
+ */
+ timeoutComp(&tTimeout, 100);
+ DBGOPRINT((obj_t*) pThis, "last try for regular shutdown of main queue DA worker pool\n");
+ iRetLocal = wtpShutdownAll(pThis->pWtpDA, wtpState_SHUTDOWN_IMMEDIATE, &tTimeout);
+ if(iRetLocal == RS_RET_TIMED_OUT) {
+ DBGOPRINT((obj_t*) pThis, "shutdown timed out on main queue DA worker pool "
+ "(this is not good, but probably OK)\n");
} else {
- END_MTX_PROTECTED_OPERATIONS(pThis->mut);
+ DBGOPRINT((obj_t*) pThis, "main queue DA worker pool shut down.\n");
}
- } else {
- END_MTX_PROTECTED_OPERATIONS(pThis->mut);
}
+ RETiRet;
+}
+
+
+/* This function cancels all remaining regular workers for both the main and the DA
+ * queue. The main queue's DA worker pool continues to run (if it exists and is active).
+ * rgerhards, 2009-05-29
+ */
+static rsRetVal
+cancelWorkers(qqueue_t *pThis)
+{
+ rsRetVal iRetLocal;
+ DEFiRet;
+
/* Now queue workers should have terminated. If not, we need to cancel them as we have applied
* all timeout setting. If any worker in any queue still executes, its consumer is possibly
- * long-running and cancelling is the only way to get rid of it. Note that the
- * cancellation handler will probably re-queue a user pointer, so the queue's enqueue
- * function is still needed (what is no problem as we do not yet destroy the queue - but I
- * thought it's a good idea to mention that fact). -- rgerhards, 2008-01-25
+ * long-running and cancelling is the only way to get rid of it.
*/
- dbgoprint((obj_t*) pThis, "checking to see if we need to cancel any worker threads of the primary queue\n");
+ DBGOPRINT((obj_t*) pThis, "checking to see if we need to cancel any worker threads of the primary queue\n");
iRetLocal = wtpCancelAll(pThis->pWtpReg); /* returns immediately if all threads already have terminated */
if(iRetLocal != RS_RET_OK) {
- dbgoprint((obj_t*) pThis, "unexpected iRet state %d trying to cancel primary queue worker "
+ DBGOPRINT((obj_t*) pThis, "unexpected iRet state %d trying to cancel primary queue worker "
"threads, continuing, but results are unpredictable\n", iRetLocal);
}
-
- /* TODO: think: do we really need to do this here? Can't it happen on DA queue destruction? If we
- * disable it, we get an assertion... I think this is OK, as we need to have a certain order and
- * canceling the DA workers here ensures that order. But in any instant, we may have a look at this
- * code after we have reaced the milestone. -- rgerhards, 2008-01-27
- */
/* ... and now the DA queue, if it exists (should always be after the primary one) */
if(pThis->pqDA != NULL) {
- dbgoprint((obj_t*) pThis, "checking to see if we need to cancel any worker threads of the DA queue\n");
+ DBGOPRINT((obj_t*) pThis, "checking to see if we need to cancel any worker threads of the DA queue\n");
iRetLocal = wtpCancelAll(pThis->pqDA->pWtpReg); /* returns immediately if all threads already have terminated */
if(iRetLocal != RS_RET_OK) {
- dbgoprint((obj_t*) pThis, "unexpected iRet state %d trying to cancel DA queue worker "
+ DBGOPRINT((obj_t*) pThis, "unexpected iRet state %d trying to cancel DA queue worker "
"threads, continuing, but results are unpredictable\n", iRetLocal);
}
+
+ /* finally, we cancel the main queue's DA worker pool, if it still is running. It may be
+ * restarted later to persist the queue. But we stop it, because otherwise we get into
+ * big trouble when resetting the logical dequeue pointer. This operation can only be
+ * done when *no* worker is running. So time for a shutdown... -- rgerhards, 2009-05-28
+ */
+ DBGOPRINT((obj_t*) pThis, "checking to see if we need to cancel the main queue's DA worker pool\n");
+ iRetLocal = wtpCancelAll(pThis->pWtpDA); /* returns immediately if all threads already have terminated */
+ }
+
+ RETiRet;
+}
+
+
+/* This function shuts down all worker threads and waits until they
+ * have terminated. If they timeout, they are cancelled.
+ * rgerhards, 2008-01-24
+ * Please note that this function shuts down BOTH the parent AND the child queue
+ * in DA case. This is necessary because their timeouts are tightly coupled. Most
+ * importantly, the timeouts would be applied twice (or logic be extremely
+ * complex) if each would have its own shutdown. The function does not self check
+ * this condition - the caller must make sure it is not called with a parent.
+ * rgerhards, 2009-05-26: we do NO longer persist the queue here if bSaveOnShutdown
+ * is set. This must be handled by the caller. Not doing that cleans up the queue
+ * shutdown considerably. Also, older engines had a potential hang condition when
+ * the DA queue was already started and the DA worker configured for infinite
+ * retries and the action was during retry processing. This was a design issue,
+ * which is solved as of now. Note that the shutdown now may take a little bit
+ * longer, because we no longer can persist the queue in parallel to waiting
+ * on worker timeouts.
+ */
+static rsRetVal
+ShutdownWorkers(qqueue_t *pThis)
+{
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, qqueue);
+ ASSERT(pThis->pqParent == NULL); /* detect invalid calling sequence */
+
+ DBGOPRINT((obj_t*) pThis, "initiating worker thread shutdown sequence\n");
+
+ /* we reduce the low water mark in any case. This is not absolutely necessary, but
+ * it is useful because we enable DA mode at several spots below and so we do not need
+ * to think about the low water mark each time.
+ */
+ pThis->iHighWtrMrk = 1; /* if we do not do this, the DA queue will not stop! */
+ pThis->iLowWtrMrk = 0;
+
+ CHKiRet(tryShutdownWorkersWithinQueueTimeout(pThis));
+
+ if(getPhysicalQueueSize(pThis) > 0) {
+ CHKiRet(tryShutdownWorkersWithinActionTimeout(pThis));
}
+ CHKiRet(cancelWorkers(pThis));
+
/* ... finally ... all worker threads have terminated :-)
* Well, more precisely, they *are in termination*. Some cancel cleanup handlers
- * may still be running.
+ * may still be running. Note that the main queue's DA worker may still be running.
*/
- dbgoprint((obj_t*) pThis, "worker threads terminated, remaining queue size %d.\n", qqueueGetOverallQueueSize(pThis));
+ DBGOPRINT((obj_t*) pThis, "worker threads terminated, remaining queue size log %d, phys %d.\n",
+ getLogicalQueueSize(pThis), getPhysicalQueueSize(pThis));
+finalize_it:
RETiRet;
}
@@ -1275,7 +1375,7 @@ static rsRetVal qqueueShutdownWorkers(qqueue_t *pThis)
* to modify some parameters before the queue is actually started.
*/
rsRetVal qqueueConstruct(qqueue_t **ppThis, queueType_t qType, int iWorkerThreads,
- int iMaxQueueSize, rsRetVal (*pConsumer)(void*,void*))
+ int iMaxQueueSize, rsRetVal (*pConsumer)(void*, batch_t*))
{
DEFiRet;
qqueue_t *pThis;
@@ -1290,7 +1390,6 @@ rsRetVal qqueueConstruct(qqueue_t **ppThis, queueType_t qType, int iWorkerThread
/* we have an object, so let's fill the properties */
objConstructSetObjInfo(pThis);
- pThis->bOptimizeUniProc = glbl.GetOptimizeUniProc();
if((pThis->pszSpoolDir = (uchar*) strdup((char*)glbl.GetWorkDir())) == NULL)
ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
@@ -1301,10 +1400,12 @@ rsRetVal qqueueConstruct(qqueue_t **ppThis, queueType_t qType, int iWorkerThread
pThis->lenSpoolDir = strlen((char*)pThis->pszSpoolDir);
pThis->iMaxFileSize = 1024 * 1024; /* default is 1 MiB */
pThis->iQueueSize = 0;
+ pThis->nLogDeq = 0;
pThis->iMaxQueueSize = iMaxQueueSize;
pThis->pConsumer = pConsumer;
pThis->iNumWorkerThreads = iWorkerThreads;
pThis->iDeqtWinToHr = 25; /* disable time-windowed dequeuing by default */
+ pThis->iDeqBatchSize = 8; /* conservative default, should still provide good performance */
pThis->pszFilePrefix = NULL;
pThis->qType = qType;
@@ -1315,19 +1416,25 @@ rsRetVal qqueueConstruct(qqueue_t **ppThis, queueType_t qType, int iWorkerThread
pThis->qConstruct = qConstructFixedArray;
pThis->qDestruct = qDestructFixedArray;
pThis->qAdd = qAddFixedArray;
+ pThis->qDeq = qDeqFixedArray;
pThis->qDel = qDelFixedArray;
+ pThis->qUnDeqAll = qUnDeqAllFixedArray;
break;
case QUEUETYPE_LINKEDLIST:
pThis->qConstruct = qConstructLinkedList;
pThis->qDestruct = qDestructLinkedList;
pThis->qAdd = qAddLinkedList;
- pThis->qDel = (rsRetVal (*)(qqueue_t*,void**)) qDelLinkedList;
+ pThis->qDeq = (rsRetVal (*)(qqueue_t*,void**)) qDeqLinkedList;
+ pThis->qDel = (rsRetVal (*)(qqueue_t*)) qDelLinkedList;
+ pThis->qUnDeqAll = qUnDeqAllLinkedList;
break;
case QUEUETYPE_DISK:
pThis->qConstruct = qConstructDisk;
pThis->qDestruct = qDestructDisk;
pThis->qAdd = qAddDisk;
+ pThis->qDeq = qDeqDisk;
pThis->qDel = qDelDisk;
+ pThis->qUnDeqAll = qUnDeqAllDisk;
/* special handling */
pThis->iNumWorkerThreads = 1; /* we need exactly one worker */
break;
@@ -1336,6 +1443,7 @@ rsRetVal qqueueConstruct(qqueue_t **ppThis, queueType_t qType, int iWorkerThread
pThis->qDestruct = qDestructDirect;
pThis->qAdd = qAddDirect;
pThis->qDel = qDelDirect;
+ pThis->qUnDeqAll = qUnDeqAllDirect;
break;
}
@@ -1345,36 +1453,6 @@ finalize_it:
}
-/* cancellation cleanup handler for queueWorker ()
- * Updates admin structure and frees ressources.
- * Params:
- * arg1 - user pointer (in this case a qqueue_t)
- * arg2 - user data pointer (in this case a queue data element, any object [queue's pUsr ptr!])
- * Note that arg2 may be NULL, in which case no dequeued but unprocessed pUsr exists!
- * rgerhards, 2008-01-16
- */
-static rsRetVal
-qqueueConsumerCancelCleanup(void *arg1, void *arg2)
-{
- DEFiRet;
-
- qqueue_t *pThis = (qqueue_t*) arg1;
- obj_t *pUsr = (obj_t*) arg2;
-
- ISOBJ_TYPE_assert(pThis, qqueue);
-
- if(pUsr != NULL) {
- /* make sure the data element is not lost */
- dbgoprint((obj_t*) pThis, "cancelation cleanup handler consumer called, we need to unget one user data element\n");
- CHKiRet(qqueueUngetObj(pThis, pUsr, LOCK_MUTEX));
- }
-
-finalize_it:
- RETiRet;
-}
-
-
-
/* This function checks if the provided message shall be discarded and does so, if needed.
* In DA mode, we do not discard any messages as we assume the disk subsystem is fast enough to
* provide real-time creation of spool files.
@@ -1400,12 +1478,12 @@ static int qqueueChkDiscardMsg(qqueue_t *pThis, int iQueueSize, int bRunsDA, voi
if(pThis->iDiscardMrk > 0 && iQueueSize >= pThis->iDiscardMrk && bRunsDA == 0) {
iRetLocal = objGetSeverity(pUsr, &iSeverity);
if(iRetLocal == RS_RET_OK && iSeverity >= pThis->iDiscardSeverity) {
- dbgoprint((obj_t*) pThis, "queue nearly full (%d entries), discarded severity %d message\n",
+ DBGOPRINT((obj_t*) pThis, "queue nearly full (%d entries), discarded severity %d message\n",
iQueueSize, iSeverity);
objDestruct(pUsr);
ABORT_FINALIZE(RS_RET_QUEUE_FULL);
} else {
- dbgoprint((obj_t*) pThis, "queue nearly full (%d entries), but could not drop msg "
+ DBGOPRINT((obj_t*) pThis, "queue nearly full (%d entries), but could not drop msg "
"(iRet: %d, severity %d)\n", iQueueSize, iRetLocal, iSeverity);
}
}
@@ -1415,78 +1493,189 @@ finalize_it:
}
-/* dequeue the queued object for the queue consumers.
- * rgerhards, 2008-10-21
+/* Finally remove n elements from the queue store.
*/
-static rsRetVal
-qqueueDequeueConsumable(qqueue_t *pThis, wti_t *pWti, int iCancelStateSave)
+static inline rsRetVal
+DoDeleteBatchFromQStore(qqueue_t *pThis, int nElem)
+{
+ int i;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, qqueue);
+
+ /* now send delete request to storage driver */
+ for(i = 0 ; i < nElem ; ++i) {
+ pThis->qDel(pThis);
+ }
+
+ /* iQueueSize is not decremented by qDel(), so we need to do it ourselves */
+ ATOMIC_SUB(pThis->iQueueSize, nElem);
+ ATOMIC_SUB(pThis->nLogDeq, nElem);
+dbgprintf("delete batch from store, new sizes: log %d, phys %d\n", getLogicalQueueSize(pThis), getPhysicalQueueSize(pThis));
+ ++pThis->deqIDDel; /* one more batch dequeued */
+
+ RETiRet;
+}
+
+
+/* remove messages from the physical queue store that are fully processed. This is
+ * controlled via the to-delete list. We can only delete those elements, that are
+ * at the current physical tail of the queue. If the batch is from another position,
+ * we schedule it for deletion, but actual deletion will happen at a later call
+ * of this function here. We always delete as much as possible, which includes
+ * picking up things from the to-delete list.
+ */
+static inline rsRetVal
+DeleteBatchFromQStore(qqueue_t *pThis, batch_t *pBatch)
{
+ toDeleteLst_t *pTdl;
+ qDeqID deqIDDel;
DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, qqueue);
+ assert(pBatch != NULL);
+
+ pTdl = tdlPeek(pThis); /* get current head element */
+ if(pTdl == NULL) { /* to-delete list empty */
+ DoDeleteBatchFromQStore(pThis, pBatch->nElemDeq);
+ } else if(pBatch->deqID == pThis->deqIDDel) {
+ deqIDDel = pThis->deqIDDel;
+ pTdl = tdlPeek(pThis);
+ while(pTdl != NULL && deqIDDel == pTdl->deqID) {
+ DoDeleteBatchFromQStore(pThis, pTdl->nElemDeq);
+ tdlPop(pThis);
+ ++deqIDDel;
+ pTdl = tdlPeek(pThis);
+ }
+ } else {
+ /* can not delete, insert into to-delete list */
+ dbgprintf("not at head of to-delete list, enqueue %d\n", (int) pBatch->deqID);
+ CHKiRet(tdlAdd(pThis, pBatch->deqID, pBatch->nElemDeq));
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Delete a batch of processed user objects from the queue, which includes
+ * destructing the objects themself.
+ * rgerhards, 2009-05-13
+ */
+static inline rsRetVal
+DeleteProcessedBatch(qqueue_t *pThis, batch_t *pBatch)
+{
+ int i;
void *pUsr;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, qqueue);
+ assert(pBatch != NULL);
+
+ for(i = 0 ; i < pBatch->nElem ; ++i) {
+ pUsr = pBatch->pElem[i].pUsrp;
+ objDestruct(pUsr);
+ }
+
+ iRet = DeleteBatchFromQStore(pThis, pBatch);
+
+ pBatch->nElem = pBatch->nElemDeq = 0; /* reset batch */
+
+ RETiRet;
+}
+
+
+/* dequeue as many user pointers as are available, until we hit the configured
+ * upper limit of pointers.
+ * This must only be called when the queue mutex is LOOKED, otherwise serious
+ * malfunction will happen.
+ */
+static inline rsRetVal
+DequeueConsumableElements(qqueue_t *pThis, wti_t *pWti, int *piRemainingQueueSize)
+{
+ int nDequeued;
+ int nDiscarded;
+ int nDeleted;
int iQueueSize;
- int bRunsDA; /* cache for early mutex release */
-
- /* dequeue element (still protected from mutex) */
- iRet = qqueueDel(pThis, &pUsr);
- qqueueChkPersist(pThis);
- iQueueSize = qqueueGetOverallQueueSize(pThis); /* cache this for after mutex release */
- bRunsDA = pThis->bRunsDA; /* cache this for after mutex release */
-
- /* We now need to save the user pointer for the cancel cleanup handler, BUT ONLY
- * if we could successfully obtain a user pointer. Otherwise, we would bring the
- * cancel cleanup handler into big troubles (and we did ;)). Note that we can
- * NOT set the variable further below, as this may lead to an object leak. We
- * may get cancelled before we reach that part of the code, so the only
- * solution is to do it here. -- rgerhards, 2008-02-27
- */
- if(iRet == RS_RET_OK) {
- pWti->pUsrp = pUsr;
+ void *pUsr;
+ rsRetVal localRet;
+ DEFiRet;
+
+ nDeleted = pWti->batch.nElemDeq;
+ DeleteProcessedBatch(pThis, &pWti->batch);
+
+ nDequeued = nDiscarded = 0;
+ while((iQueueSize = getLogicalQueueSize(pThis)) > 0 && nDequeued < pThis->iDeqBatchSize) {
+dbgprintf("DequeueConsumableElements, index %d\n", nDequeued);
+ CHKiRet(qqueueDeq(pThis, &pUsr));
+
+ /* check if we should discard this element */
+ localRet = qqueueChkDiscardMsg(pThis, pThis->iQueueSize, pThis->bRunsDA, pUsr);
+ if(localRet == RS_RET_QUEUE_FULL) {
+ ++nDiscarded;
+ continue;
+ } else if(localRet != RS_RET_OK) {
+ ABORT_FINALIZE(localRet);
+ }
+
+ /* all well, use this element */
+ pWti->batch.pElem[nDequeued].pUsrp = pUsr;
+ pWti->batch.pElem[nDequeued].state = BATCH_STATE_RDY;
+ ++nDequeued;
}
+ /* it is sufficient to persist only when the bulk of work is done */
+ qqueueChkPersist(pThis, nDequeued+nDiscarded+nDeleted);
+
+ pWti->batch.nElem = nDequeued;
+ pWti->batch.nElemDeq = nDequeued + nDiscarded;
+ pWti->batch.deqID = getNextDeqID(pThis);
+ *piRemainingQueueSize = iQueueSize;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* dequeue the queued object for the queue consumers.
+ * rgerhards, 2008-10-21
+ * I made a radical change - we now dequeue multiple elements, and store these objects in
+ * an array of user pointers. We expect that this increases performance.
+ * rgerhards, 2009-04-22
+ */
+static rsRetVal
+DequeueConsumable(qqueue_t *pThis, wti_t *pWti)
+{
+ DEFiRet;
+ int iQueueSize = 0; /* keep the compiler happy... */
+
+ /* dequeue element batch (still protected from mutex) */
+ iRet = DequeueConsumableElements(pThis, pWti, &iQueueSize);
+
/* awake some flow-controlled sources if we can do this right now */
/* TODO: this could be done better from a performance point of view -- do it only if
* we have someone waiting for the condition (or only when we hit the watermark right
* on the nail [exact value]) -- rgerhards, 2008-03-14
+ * now that we dequeue batches of pointers, this is much less an issue...
+ * rgerhards, 2009-04-22
*/
- if(iQueueSize < pThis->iFullDlyMrk) {
+ if(iQueueSize < pThis->iFullDlyMrk / 2) {
pthread_cond_broadcast(&pThis->belowFullDlyWtrMrk);
}
- if(iQueueSize < pThis->iLightDlyMrk) {
+ if(iQueueSize < pThis->iLightDlyMrk / 2) {
pthread_cond_broadcast(&pThis->belowLightDlyWtrMrk);
}
- /* rgerhards, 2008-09-30: I reversed the order of cond_signal und mutex_unlock
- * as of the pthreads recommendation on predictable scheduling behaviour. I don't see
- * any problems caused by this, but I add this comment in case some will be seen
- * in the next time.
- */
+ // TODO: MULTI: check physical queue size?
pthread_cond_signal(&pThis->notFull);
- d_pthread_mutex_unlock(pThis->mut);
- pthread_setcancelstate(iCancelStateSave, NULL);
/* WE ARE NO LONGER PROTECTED BY THE MUTEX */
- /* do actual processing (the lengthy part, runs in parallel)
- * If we had a problem while dequeing, we do not call the consumer,
- * but we otherwise ignore it. This is in the hopes that it will be
- * self-healing. However, this is really not a good thing.
- * rgerhards, 2008-01-03
- */
- if(iRet != RS_RET_OK)
- FINALIZE;
-
- /* we are running in normal, non-disk-assisted mode do a quick check if we need to drain the queue.
- * In DA mode, we do not discard any messages as we assume the disk subsystem is fast enough to
- * provide real-time creation of spool files.
- * Note: It is OK to use the cached iQueueSize here, because it does not hurt if it is slightly wrong.
- */
- CHKiRet(qqueueChkDiscardMsg(pThis, iQueueSize, bRunsDA, pUsr));
-
-finalize_it:
if(iRet != RS_RET_OK && iRet != RS_RET_DISCARDMSG) {
- dbgoprint((obj_t*) pThis, "error %d dequeueing element - ignoring, but strange things "
+ DBGOPRINT((obj_t*) pThis, "error %d dequeueing element - ignoring, but strange things "
"may happen\n", iRet);
}
+
RETiRet;
}
@@ -1529,7 +1718,7 @@ finalize_it:
* but you get the idea from the code above.
*/
static rsRetVal
-qqueueRateLimiter(qqueue_t *pThis)
+RateLimiter(qqueue_t *pThis)
{
DEFiRet;
int iDelay;
@@ -1578,7 +1767,7 @@ qqueueRateLimiter(qqueue_t *pThis)
}
if(iDelay > 0) {
- dbgoprint((obj_t*) pThis, "outside dequeue time window, delaying %d seconds\n", iDelay);
+ DBGOPRINT((obj_t*) pThis, "outside dequeue time window, delaying %d seconds\n", iDelay);
srSleep(iDelay, 0);
}
@@ -1586,37 +1775,88 @@ qqueueRateLimiter(qqueue_t *pThis)
}
+/* This dequeues the next batch.
+ * rgerhards, 2009-05-20
+ */
+static inline rsRetVal
+DequeueForConsumer(qqueue_t *pThis, wti_t *pWti)
+{
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, qqueue);
+ ISOBJ_TYPE_assert(pWti, wti);
+
+dbgprintf("YYY: deqeueu for consumer");
+ CHKiRet(DequeueConsumable(pThis, pWti));
+
+ if(pWti->batch.nElem == 0)
+ ABORT_FINALIZE(RS_RET_IDLE);
+
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* This is called when a batch is processed and the worker does not
+ * ask for another batch (e.g. because it is to be terminated)
+ * rgerhards, 2009-05-27
+ */
+static rsRetVal
+batchProcessed(qqueue_t *pThis, wti_t *pWti)
+{
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, qqueue);
+ ISOBJ_TYPE_assert(pWti, wti);
+dbgprintf("XXX: batchProcessed deletes %d records\n", pWti->batch.nElemDeq);
+
+ DeleteProcessedBatch(pThis, &pWti->batch);
+ qqueueChkPersist(pThis, pWti->batch.nElemDeq);
+
+ RETiRet;
+}
+
/* This is the queue consumer in the regular (non-DA) case. It is
* protected by the queue mutex, but MUST release it as soon as possible.
* rgerhards, 2008-01-21
*/
static rsRetVal
-qqueueConsumerReg(qqueue_t *pThis, wti_t *pWti, int iCancelStateSave)
+ConsumerReg(qqueue_t *pThis, wti_t *pWti)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, qqueue);
ISOBJ_TYPE_assert(pWti, wti);
- CHKiRet(qqueueDequeueConsumable(pThis, pWti, iCancelStateSave));
- CHKiRet(pThis->pConsumer(pThis->pUsr, pWti->pUsrp));
+ CHKiRet(DequeueForConsumer(pThis, pWti));
+
+ /* we now have a non-idle batch of work, so we can release the queue mutex and process it */
+ d_pthread_mutex_unlock(pThis->mut);
+
+ CHKiRet(pThis->pConsumer(pThis->pUsr, &pWti->batch));
/* we now need to check if we should deliberately delay processing a bit
* and, if so, do that. -- rgerhards, 2008-01-30
*/
+//TODO: MULTIQUEUE: the following setting is no longer correct - need to think about how to do that...
if(pThis->iDeqSlowdown) {
- dbgoprint((obj_t*) pThis, "sleeping %d microseconds as requested by config params\n",
+ DBGOPRINT((obj_t*) pThis, "sleeping %d microseconds as requested by config params\n",
pThis->iDeqSlowdown);
srSleep(pThis->iDeqSlowdown / 1000000, pThis->iDeqSlowdown % 1000000);
}
+ /* now we are done, but need to re-aquire the mutex */
+ d_pthread_mutex_lock(pThis->mut);
+
finalize_it:
+dbgprintf("XXX: regular consumer finished, iret=%d, szlog %d sz phys %d\n", iRet, getLogicalQueueSize(pThis), getPhysicalQueueSize(pThis));
RETiRet;
}
-/* This is a special consumer to feed the disk-queue in disk-assited mode.
+/* This is a special consumer to feed the disk-queue in disk-assisted mode.
* When active, our own queue more or less acts as a memory buffer to the disk.
* So this consumer just needs to drain the memory queue and submit entries
* to the disk queue. The disk queue will then call the actual consumer from
@@ -1626,18 +1866,33 @@ finalize_it:
* rgerhards, 2008-01-14
*/
static rsRetVal
-qqueueConsumerDA(qqueue_t *pThis, wti_t *pWti, int iCancelStateSave)
+ConsumerDA(qqueue_t *pThis, wti_t *pWti)
{
+ int i;
DEFiRet;
ISOBJ_TYPE_assert(pThis, qqueue);
ISOBJ_TYPE_assert(pWti, wti);
- CHKiRet(qqueueDequeueConsumable(pThis, pWti, iCancelStateSave));
- CHKiRet(qqueueEnqObj(pThis->pqDA, eFLOWCTL_NO_DELAY, pWti->pUsrp));
+ CHKiRet(DequeueForConsumer(pThis, pWti));
+
+ /* we now have a non-idle batch of work, so we can release the queue mutex and process it */
+ d_pthread_mutex_unlock(pThis->mut);
+
+ /* iterate over returned results and enqueue them in DA queue */
+ for(i = 0 ; i < pWti->batch.nElem ; i++) {
+ /* TODO: we must add a generic "addRef" mechanism, because the disk queue enqueue destructs
+ * the message. So far, we simply assume we always have msg_t, what currently is always the case.
+ * rgerhards, 2009-05-28
+ */
+ CHKiRet(qqueueEnqObj(pThis->pqDA, eFLOWCTL_NO_DELAY, (obj_t*)MsgAddRef((msg_t*)(pWti->batch.pElem[i].pUsrp))));
+ }
+
+ /* now we are done, but need to re-aquire the mutex */
+ d_pthread_mutex_lock(pThis->mut);
finalize_it:
- dbgoprint((obj_t*) pThis, "DAConsumer returns with iRet %d\n", iRet);
+ DBGOPRINT((obj_t*) pThis, "DAConsumer returns with iRet %d\n", iRet);
RETiRet;
}
@@ -1647,20 +1902,17 @@ finalize_it:
* If we are a child, we have done our duty when the queue is empty. In that case,
* we can terminate.
* Version for the DA worker thread. NOTE: the pThis->bRunsDA is different from
- * the DA queue
+ * the DA queue.
+ * If our queue is in destruction, we drain to the DA queue and so we shall not terminate
+ * until we have done so.
*/
-static int
+static rsRetVal
qqueueChkStopWrkrDA(qqueue_t *pThis)
{
- /* if our queue is in destruction, we drain to the DA queue and so we shall not terminate
- * until we have done so.
- */
- int bStopWrkr;
-
- BEGINfunc
+ DEFiRet;
if(pThis->bEnqOnly) {
- bStopWrkr = 1;
+ iRet = RS_RET_TERMINATE_WHEN_IDLE;
} else {
if(pThis->bRunsDA) {
ASSERT(pThis->pqDA != NULL);
@@ -1668,19 +1920,21 @@ qqueueChkStopWrkrDA(qqueue_t *pThis)
&& pThis->pqDA->sizeOnDiskMax > 0
&& pThis->pqDA->tVars.disk.sizeOnDisk > pThis->pqDA->sizeOnDiskMax) {
/* this queue can never grow, so we can give up... */
- bStopWrkr = 1;
- } else if(qqueueGetOverallQueueSize(pThis) < pThis->iHighWtrMrk && pThis->bQueueStarted == 1) {
- bStopWrkr = 1;
- } else {
- bStopWrkr = 0;
+ iRet = RS_RET_TERMINATE_NOW;
+ } else if(getPhysicalQueueSize(pThis) < pThis->iHighWtrMrk && pThis->bQueueStarted == 1) {
+dbgprintf("XXX: terminate_NOW DA worker: queue size %d, high water mark %d\n", getPhysicalQueueSize(pThis), pThis->iHighWtrMrk);
+ iRet = RS_RET_TERMINATE_NOW;
+RUNLOG_STR("XXX: re-start reg worker");
+qqueueAdviseMaxWorkers(pThis);
+RUNLOG_STR("XXX: done re-start reg worker");
}
} else {
- bStopWrkr = 1;
+ // experimental iRet = RS_RET_TERMINATE_NOW;
+ ;
}
}
- ENDfunc
- return bStopWrkr;
+ RETiRet;
}
@@ -1691,38 +1945,50 @@ qqueueChkStopWrkrDA(qqueue_t *pThis)
* Version for the regular worker thread. NOTE: the pThis->bRunsDA is different from
* the DA queue
*/
-static int
-qqueueChkStopWrkrReg(qqueue_t *pThis)
+static rsRetVal
+ChkStopWrkrReg(qqueue_t *pThis)
{
- return pThis->bEnqOnly || pThis->bRunsDA || (pThis->pqParent != NULL && qqueueGetOverallQueueSize(pThis) == 0);
+ DEFiRet;
+ if(pThis->bEnqOnly) {
+ iRet = RS_RET_TERMINATE_NOW;
+ } else if(pThis->pqParent != NULL) {
+ iRet = RS_RET_TERMINATE_WHEN_IDLE;
+ }
+
+ RETiRet;
+}
+
+
+/* return the configured "deq max at once" interval
+ * rgerhards, 2009-04-22
+ */
+static rsRetVal
+GetDeqBatchSize(qqueue_t *pThis, int *pVal)
+{
+ DEFiRet;
+ assert(pVal != NULL);
+ *pVal = pThis->iDeqBatchSize;
+if(pThis->pqParent != NULL)
+ *pVal = 16;
+ RETiRet;
}
/* must only be called when the queue mutex is locked, else results
- * are not stable! DA queue version
+ * are not stable! DA worker version (pThis *is* the *main* queue, not DA!)
*/
static int
qqueueIsIdleDA(qqueue_t *pThis)
{
- /* remember: iQueueSize is the DA queue size, not the main queue! */
- /* TODO: I think we need just a single function for DA and non-DA mode - but I leave it for now as is */
- return(qqueueGetOverallQueueSize(pThis) == 0 || (pThis->bRunsDA && qqueueGetOverallQueueSize(pThis) <= pThis->iLowWtrMrk));
+ return(getPhysicalQueueSize(pThis) <= pThis->iLowWtrMrk);
}
/* must only be called when the queue mutex is locked, else results
- * are not stable! Regular queue version
+ * are not stable! Regular worker version.
*/
static int
-qqueueIsIdleReg(qqueue_t *pThis)
-{
-#if 0 /* enable for performance testing */
- int ret;
- ret = qqueueGetOverallQueueSize(pThis) == 0 || (pThis->bRunsDA && qqueueGetOverallQueueSize(pThis) <= pThis->iLowWtrMrk);
- if(ret) fprintf(stderr, "queue is idle\n");
- return ret;
-#else
- /* regular code! */
- return(qqueueGetOverallQueueSize(pThis) == 0 || (pThis->bRunsDA && qqueueGetOverallQueueSize(pThis) <= pThis->iLowWtrMrk));
-#endif
+IsIdleReg(qqueue_t *pThis)
+{
+ return(getPhysicalQueueSize(pThis) == 0);
}
@@ -1740,14 +2006,13 @@ qqueueIsIdleReg(qqueue_t *pThis)
* I am telling this, because I, too, always get confused by those...
*/
static rsRetVal
-qqueueRegOnWrkrShutdown(qqueue_t *pThis)
+RegOnWrkrShutdown(qqueue_t *pThis)
{
DEFiRet;
ISOBJ_TYPE_assert(pThis, qqueue);
if(pThis->pqParent != NULL) {
- pThis->pqParent->bChildIsDone = 1; /* indicate we are done */
if(pThis->pqParent->pWtpDA != NULL) { /* see comment in function header from 2008-02-27 */
wtpAdviseMaxWorkers(pThis->pqParent->pWtpDA, 1); /* reactivate DA worker (always 1) */
}
@@ -1757,28 +2022,11 @@ qqueueRegOnWrkrShutdown(qqueue_t *pThis)
}
-/* The following function is called when a regular queue worker starts up. We need this
- * hook to indicate in the parent queue (if we are a child) that we are not done yet.
- */
-static rsRetVal
-qqueueRegOnWrkrStartup(qqueue_t *pThis)
-{
- DEFiRet;
-
- ISOBJ_TYPE_assert(pThis, qqueue);
-
- if(pThis->pqParent != NULL) {
- pThis->pqParent->bChildIsDone = 0;
- }
-
- RETiRet;
-}
-
-
/* start up the queue - it must have been constructed and parameters defined
* before.
*/
-rsRetVal qqueueStart(qqueue_t *pThis) /* this is the ConstructionFinalizer */
+rsRetVal
+qqueueStart(qqueue_t *pThis) /* this is the ConstructionFinalizer */
{
DEFiRet;
rsRetVal iRetLocal;
@@ -1800,7 +2048,7 @@ rsRetVal qqueueStart(qqueue_t *pThis) /* this is the ConstructionFinalizer */
pthread_mutex_init(pThis->mut, NULL);
} else {
/* child queue, we need to use parent's mutex */
- dbgoprint((obj_t*) pThis, "I am a child\n");
+ DBGOPRINT((obj_t*) pThis, "I am a child\n");
pThis->mut = pThis->pqParent->mut;
}
@@ -1814,11 +2062,12 @@ rsRetVal qqueueStart(qqueue_t *pThis) /* this is the ConstructionFinalizer */
/* call type-specific constructor */
CHKiRet(pThis->qConstruct(pThis)); /* this also sets bIsDA */
- dbgoprint((obj_t*) pThis, "type %d, enq-only %d, disk assisted %d, maxFileSz %lld, qsize %d, child %d, "
- "full delay %d, light delay %d starting\n",
+ DBGOPRINT((obj_t*) pThis, "type %d, enq-only %d, disk assisted %d, maxFileSz %lld, lqsize %d, pqsize %d, child %d, "
+ "full delay %d, light delay %d, deq batch size %d starting\n",
pThis->qType, pThis->bEnqOnly, pThis->bIsDA, pThis->iMaxFileSize,
- qqueueGetOverallQueueSize(pThis), pThis->pqParent == NULL ? 0 : 1,
- pThis->iFullDlyMrk, pThis->iLightDlyMrk);
+ getLogicalQueueSize(pThis), getPhysicalQueueSize(pThis),
+ pThis->pqParent == NULL ? 0 : 1, pThis->iFullDlyMrk, pThis->iLightDlyMrk,
+ pThis->iDeqBatchSize);
if(pThis->qType == QUEUETYPE_DIRECT)
FINALIZE; /* with direct queues, we are already finished... */
@@ -1829,13 +2078,13 @@ rsRetVal qqueueStart(qqueue_t *pThis) /* this is the ConstructionFinalizer */
lenBuf = snprintf((char*)pszBuf, sizeof(pszBuf), "%s:Reg", obj.GetName((obj_t*) pThis));
CHKiRet(wtpConstruct (&pThis->pWtpReg));
CHKiRet(wtpSetDbgHdr (pThis->pWtpReg, pszBuf, lenBuf));
- CHKiRet(wtpSetpfRateLimiter (pThis->pWtpReg, (rsRetVal (*)(void *pUsr)) qqueueRateLimiter));
- CHKiRet(wtpSetpfChkStopWrkr (pThis->pWtpReg, (rsRetVal (*)(void *pUsr, int)) qqueueChkStopWrkrReg));
- CHKiRet(wtpSetpfIsIdle (pThis->pWtpReg, (rsRetVal (*)(void *pUsr, int)) qqueueIsIdleReg));
- CHKiRet(wtpSetpfDoWork (pThis->pWtpReg, (rsRetVal (*)(void *pUsr, void *pWti, int)) qqueueConsumerReg));
- CHKiRet(wtpSetpfOnWorkerCancel (pThis->pWtpReg, (rsRetVal (*)(void *pUsr, void*pWti))qqueueConsumerCancelCleanup));
- CHKiRet(wtpSetpfOnWorkerStartup (pThis->pWtpReg, (rsRetVal (*)(void *pUsr)) qqueueRegOnWrkrStartup));
- CHKiRet(wtpSetpfOnWorkerShutdown(pThis->pWtpReg, (rsRetVal (*)(void *pUsr)) qqueueRegOnWrkrShutdown));
+ CHKiRet(wtpSetpfRateLimiter (pThis->pWtpReg, (rsRetVal (*)(void *pUsr)) RateLimiter));
+ CHKiRet(wtpSetpfChkStopWrkr (pThis->pWtpReg, (rsRetVal (*)(void *pUsr, int)) ChkStopWrkrReg));
+ CHKiRet(wtpSetpfGetDeqBatchSize (pThis->pWtpReg, (rsRetVal (*)(void *pUsr, int*)) GetDeqBatchSize));
+ CHKiRet(wtpSetpfIsIdle (pThis->pWtpReg, (rsRetVal (*)(void *pUsr, wtp_t*)) IsIdleReg));
+ CHKiRet(wtpSetpfDoWork (pThis->pWtpReg, (rsRetVal (*)(void *pUsr, void *pWti)) ConsumerReg));
+ CHKiRet(wtpSetpfObjProcessed (pThis->pWtpReg, (rsRetVal (*)(void *pUsr, wti_t *pWti)) batchProcessed));
+ CHKiRet(wtpSetpfOnWorkerShutdown(pThis->pWtpReg, (rsRetVal (*)(void *pUsr)) RegOnWrkrShutdown));
CHKiRet(wtpSetpmutUsr (pThis->pWtpReg, pThis->mut));
CHKiRet(wtpSetpcondBusy (pThis->pWtpReg, &pThis->notEmpty));
CHKiRet(wtpSetiNumWorkerThreads (pThis->pWtpReg, pThis->iNumWorkerThreads));
@@ -1850,18 +2099,18 @@ rsRetVal qqueueStart(qqueue_t *pThis) /* this is the ConstructionFinalizer */
*/
iRetLocal = qqueueHaveQIF(pThis);
if(iRetLocal == RS_RET_OK) {
- dbgoprint((obj_t*) pThis, "on-disk queue present, needs to be reloaded\n");
- qqueueInitDA(pThis, QUEUE_MODE_ENQDEQ, LOCK_MUTEX); /* initiate DA mode */
+ DBGOPRINT((obj_t*) pThis, "on-disk queue present, needs to be reloaded\n");
+ InitDA(pThis, QUEUE_MODE_ENQDEQ, LOCK_MUTEX); /* initiate DA mode */
bInitialized = 1; /* we are done */
} else {
/* TODO: use logerror? -- rgerhards, 2008-01-16 */
- dbgoprint((obj_t*) pThis, "error %d trying to access on-disk queue files, starting without them. "
+ DBGOPRINT((obj_t*) pThis, "error %d trying to access on-disk queue files, starting without them. "
"Some data may be lost\n", iRetLocal);
}
}
- if(!bInitialized) {
- dbgoprint((obj_t*) pThis, "queue starts up without (loading) any DA disk state (this is normal for the DA "
+ if(Debug && !bInitialized) {
+ DBGOPRINT((obj_t*) pThis, "queue starts up without (loading) any DA disk state (this is normal for the DA "
"queue itself!)\n");
}
@@ -1889,12 +2138,11 @@ static rsRetVal qqueuePersist(qqueue_t *pThis, int bIsCheckpoint)
strm_t *psQIF = NULL; /* Queue Info File */
uchar pszQIFNam[MAXFNAME];
size_t lenQIFNam;
- obj_t *pUsr;
ASSERT(pThis != NULL);
if(pThis->qType != QUEUETYPE_DISK) {
- if(qqueueGetOverallQueueSize(pThis) > 0) {
+ if(getPhysicalQueueSize(pThis) > 0) {
/* This error code is OK, but we will probably not implement this any time
* The reason is that persistence happens via DA queues. But I would like to
* leave the code as is, as we so have a hook in case we need one.
@@ -1905,28 +2153,28 @@ static rsRetVal qqueuePersist(qqueue_t *pThis, int bIsCheckpoint)
FINALIZE; /* if the queue is empty, we are happy and done... */
}
- dbgoprint((obj_t*) pThis, "persisting queue to disk, %d entries...\n", qqueueGetOverallQueueSize(pThis));
+ DBGOPRINT((obj_t*) pThis, "persisting queue to disk, %d entries...\n", getPhysicalQueueSize(pThis));
/* Construct file name */
lenQIFNam = snprintf((char*)pszQIFNam, sizeof(pszQIFNam) / sizeof(uchar), "%s/%s.qi",
(char*) glbl.GetWorkDir(), (char*)pThis->pszFilePrefix);
- if((bIsCheckpoint != QUEUE_CHECKPOINT) && (qqueueGetOverallQueueSize(pThis) == 0)) {
+ if((bIsCheckpoint != QUEUE_CHECKPOINT) && (getPhysicalQueueSize(pThis) == 0)) {
if(pThis->bNeedDelQIF) {
unlink((char*)pszQIFNam);
pThis->bNeedDelQIF = 0;
}
/* indicate spool file needs to be deleted */
- CHKiRet(strmSetbDeleteOnClose(pThis->tVars.disk.pRead, 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
@@ -1936,29 +2184,19 @@ static rsRetVal qqueuePersist(qqueue_t *pThis, int bIsCheckpoint)
*/
CHKiRet(obj.BeginSerializePropBag(psQIF, (obj_t*) pThis));
objSerializeSCALAR(psQIF, iQueueSize, INT);
- objSerializeSCALAR(psQIF, iUngottenObjs, INT);
objSerializeSCALAR(psQIF, tVars.disk.sizeOnDisk, INT64);
objSerializeSCALAR(psQIF, tVars.disk.bytesRead, INT64);
CHKiRet(obj.EndSerialize(psQIF));
- /* now we must persist all objects on the ungotten queue - they can not go to
- * to the regular files. -- rgerhards, 2008-01-29
- */
- while(pThis->iUngottenObjs > 0) {
- CHKiRet(qqueueGetUngottenObj(pThis, &pUsr));
- CHKiRet((objSerialize(pUsr))(pUsr, psQIF));
- objDestruct(pUsr);
- }
-
/* now persist the stream info */
- CHKiRet(strmSerialize(pThis->tVars.disk.pWrite, psQIF));
- CHKiRet(strmSerialize(pThis->tVars.disk.pRead, 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.pRead, 0));
+ CHKiRet(strm.SetbDeleteOnClose(pThis->tVars.disk.pReadDel, 0));
}
/* we have persisted the queue object. So whenever it comes to an empty queue,
@@ -1968,28 +2206,69 @@ static rsRetVal qqueuePersist(qqueue_t *pThis, int bIsCheckpoint)
finalize_it:
if(psQIF != NULL)
- strmDestruct(&psQIF);
+ strm.Destruct(&psQIF);
RETiRet;
}
/* check if we need to persist the current queue info. If an
- * error occurs, thus should be ignored by caller (but we still
+ * error occurs, this should be ignored by caller (but we still
* abide to our regular call interface)...
* rgerhards, 2008-01-13
+ * nUpdates is the number of updates since the last call to this function.
+ * It may be > 1 due to batches. -- rgerhards, 2009-05-12
*/
-rsRetVal qqueueChkPersist(qqueue_t *pThis)
+static rsRetVal qqueueChkPersist(qqueue_t *pThis, int nUpdates)
{
DEFiRet;
-
ISOBJ_TYPE_assert(pThis, qqueue);
+ assert(nUpdates >= 0);
- if(pThis->iPersistUpdCnt && ++pThis->iUpdsSincePersist >= pThis->iPersistUpdCnt) {
+ if(nUpdates == 0)
+ FINALIZE;
+
+ pThis->iUpdsSincePersist += nUpdates;
+ if(pThis->iPersistUpdCnt && pThis->iUpdsSincePersist >= pThis->iPersistUpdCnt) {
qqueuePersist(pThis, QUEUE_CHECKPOINT);
pThis->iUpdsSincePersist = 0;
}
+finalize_it:
+ RETiRet;
+}
+
+
+/* persist a queue with all data elements to disk - this is used to handle
+ * bSaveOnShutdown. We utilize the DA worker to do this. This must only
+ * be called after all workers have been shut down and if bSaveOnShutdown
+ * is actually set. Note that this function may potentially run long,
+ * depending on the queue configuration (e.g. store on remote machine).
+ * rgerhards, 2009-05-26
+ */
+static inline rsRetVal
+DoSaveOnShutdown(qqueue_t *pThis)
+{
+ struct timespec tTimeout;
+ rsRetVal iRetLocal;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, qqueue);
+
+ InitDA(pThis, QUEUE_MODE_ENQONLY, LOCK_MUTEX); /* switch to DA mode */
+dbgprintf("after InitDA, queue log %d, phys %d\n", getLogicalQueueSize(pThis), getPhysicalQueueSize(pThis));
+ /* make sure we do not timeout before we are done */
+ DBGOPRINT((obj_t*) pThis, "bSaveOnShutdown configured, infinite timeout set\n");
+ timeoutComp(&tTimeout, QUEUE_TIMEOUT_ETERNAL);
+ /* and run the primary queue's DA worker to drain the queue */
+ iRetLocal = wtpShutdownAll(pThis->pWtpDA, wtpState_SHUTDOWN, &tTimeout);
+ DBGOPRINT((obj_t*) pThis, "end queue persistence run, iRet %d, queue size log %d, phys %d\n",
+ iRetLocal, getLogicalQueueSize(pThis), getPhysicalQueueSize(pThis));
+ if(iRetLocal != RS_RET_OK) {
+ DBGOPRINT((obj_t*) pThis, "unexpected iRet state %d after trying to shut down primary queue in disk save mode, "
+ "continuing, but results are unpredictable\n", iRetLocal);
+ }
+
RETiRet;
}
@@ -1999,14 +2278,24 @@ BEGINobjDestruct(qqueue) /* be sure to specify the object type also in END and C
CODESTARTobjDestruct(qqueue)
pThis->bQueueInDestruction = 1; /* indicate we are in destruction (modifies some behaviour) */
- /* shut down all workers (handles *all* of the persistence logic)
- * See function head comment of queueShutdownWorkers () on why we don't call it
- * We also do not need to shutdown workers when we are in enqueue-only mode or we are a
+ /* shut down all workers
+ * We do not need to shutdown workers when we are in enqueue-only mode or we are a
* direct queue - because in both cases we have none... ;)
* with a child! -- rgerhards, 2008-01-28
*/
if(pThis->qType != QUEUETYPE_DIRECT && !pThis->bEnqOnly && pThis->pqParent == NULL)
- qqueueShutdownWorkers(pThis);
+ ShutdownWorkers(pThis);
+
+ /* now all workers are terminated. Messages may exist. Also, some logically dequeued
+ * messages may never have been processed because their worker was terminated. So
+ * we need to reset the logical dequeue pointer, persist the queue if configured to do
+ * so and then destruct everything. -- rgerhards, 2009-05-26
+ */
+ CHKiRet(pThis->qUnDeqAll(pThis));
+
+ if(pThis->bIsDA && getPhysicalQueueSize(pThis) > 0 && pThis->bSaveOnShutdown) {
+ CHKiRet(DoSaveOnShutdown(pThis));
+ }
/* finally destruct our (regular) worker thread pool
* Note: currently pWtpReg is never NULL, but if we optimize our logic, this may happen,
@@ -2042,7 +2331,7 @@ CODESTARTobjDestruct(qqueue)
* if need arises (what I doubt...) -- rgerhards, 2008-01-25
*/
CHKiRet_Hdlr(qqueuePersist(pThis, QUEUE_NO_CHECKPOINT)) {
- dbgoprint((obj_t*) pThis, "error %d persisting queue - data lost!\n", iRet);
+ DBGOPRINT((obj_t*) pThis, "error %d persisting queue - data lost!\n", iRet);
}
/* finally, clean up some simple things... */
@@ -2061,11 +2350,8 @@ CODESTARTobjDestruct(qqueue)
/* type-specific destructor */
iRet = pThis->qDestruct(pThis);
- if(pThis->pszFilePrefix != NULL)
- free(pThis->pszFilePrefix);
-
- if(pThis->pszSpoolDir != NULL)
- free(pThis->pszSpoolDir);
+ free(pThis->pszFilePrefix);
+ free(pThis->pszSpoolDir);
ENDobjDestruct(qqueue)
@@ -2079,8 +2365,8 @@ qqueueSetFilePrefix(qqueue_t *pThis, uchar *pszPrefix, size_t iLenPrefix)
{
DEFiRet;
- if(pThis->pszFilePrefix != NULL)
- free(pThis->pszFilePrefix);
+ free(pThis->pszFilePrefix);
+ pThis->pszFilePrefix = NULL;
if(pszPrefix == NULL) /* just unset the prefix! */
ABORT_FINALIZE(RS_RET_OK);
@@ -2115,41 +2401,23 @@ finalize_it:
}
-/* enqueue a new user data element
- * Enqueues the new element and awakes worker thread.
+/* enqueue a single data object.
+ * Note that the queue mutex MUST already be locked when this function is called.
+ * rgerhards, 2009-06-16
*/
-rsRetVal
-qqueueEnqObj(qqueue_t *pThis, flowControl_t flowCtlType, void *pUsr)
+static inline rsRetVal
+doEnqSingleObj(qqueue_t *pThis, flowControl_t flowCtlType, void *pUsr)
{
DEFiRet;
- int iCancelStateSave;
struct timespec t;
- ISOBJ_TYPE_assert(pThis, qqueue);
-
/* first check if we need to discard this message (which will cause CHKiRet() to exit)
- * rgerhards, 2008-10-07: It is OK to do this outside of mutex protection. The iQueueSize
- * and bRunsDA parameters may not reflect the correct settings here, but they are
- * "good enough" in the sense that they can be used to drive the decision. Valgrind's
- * threading tools may point this access to be an error, but this is done
- * intentional. I do not see this causes problems to us.
*/
CHKiRet(qqueueChkDiscardMsg(pThis, pThis->iQueueSize, pThis->bRunsDA, pUsr));
- /* Please note that this function is not cancel-safe and consequently
- * sets the calling thread's cancelibility state to PTHREAD_CANCEL_DISABLE
- * during its execution. If that is not done, race conditions occur if the
- * thread is canceled (most important use case is input module termination).
- * rgerhards, 2008-01-08
- */
- if(pThis->qType != QUEUETYPE_DIRECT) {
- pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave);
- d_pthread_mutex_lock(pThis->mut);
- }
-
/* then check if we need to add an assistance disk queue */
if(pThis->bIsDA)
- CHKiRet(qqueueChkStrtDA(pThis));
+ CHKiRet(ChkStrtDA(pThis));
/* handle flow control
* There are two different flow control mechanisms: basic and advanced flow control.
@@ -2173,12 +2441,12 @@ qqueueEnqObj(qqueue_t *pThis, flowControl_t flowCtlType, void *pUsr)
*/
if(flowCtlType == eFLOWCTL_FULL_DELAY) {
while(pThis->iQueueSize >= pThis->iFullDlyMrk) {
- dbgoprint((obj_t*) pThis, "enqueueMsg: FullDelay mark reached for full delayable message - blocking.\n");
+ DBGOPRINT((obj_t*) pThis, "enqueueMsg: FullDelay mark reached for full delayable message - blocking.\n");
pthread_cond_wait(&pThis->belowFullDlyWtrMrk, pThis->mut); /* TODO error check? But what do then? */
}
} else if(flowCtlType == eFLOWCTL_LIGHT_DELAY) {
if(pThis->iQueueSize >= pThis->iLightDlyMrk) {
- dbgoprint((obj_t*) pThis, "enqueueMsg: LightDelay mark reached for light delayable message - blocking a bit.\n");
+ DBGOPRINT((obj_t*) pThis, "enqueueMsg: LightDelay mark reached for light delayable message - blocking a bit.\n");
timeoutComp(&t, 1000); /* 1000 millisconds = 1 second TODO: make configurable */
pthread_cond_timedwait(&pThis->belowLightDlyWtrMrk, pThis->mut, &t); /* TODO error check? But what do then? */
}
@@ -2192,10 +2460,10 @@ qqueueEnqObj(qqueue_t *pThis, flowControl_t flowCtlType, void *pUsr)
while( (pThis->iMaxQueueSize > 0 && pThis->iQueueSize >= pThis->iMaxQueueSize)
|| (pThis->qType == QUEUETYPE_DISK && pThis->sizeOnDiskMax != 0
&& pThis->tVars.disk.sizeOnDisk > pThis->sizeOnDiskMax)) {
- dbgoprint((obj_t*) pThis, "enqueueMsg: queue FULL - waiting to drain.\n");
+ DBGOPRINT((obj_t*) pThis, "enqueueMsg: queue FULL - waiting to drain.\n");
timeoutComp(&t, pThis->toEnq);
if(pthread_cond_timedwait(&pThis->notFull, pThis->mut, &t) != 0) {
- dbgoprint((obj_t*) pThis, "enqueueMsg: cond timeout, dropping message!\n");
+ DBGOPRINT((obj_t*) pThis, "enqueueMsg: cond timeout, dropping message!\n");
objDestruct(pUsr);
ABORT_FINALIZE(RS_RET_QUEUE_FULL);
}
@@ -2203,7 +2471,40 @@ qqueueEnqObj(qqueue_t *pThis, flowControl_t flowCtlType, void *pUsr)
/* and finally enqueue the message */
CHKiRet(qqueueAdd(pThis, pUsr));
- qqueueChkPersist(pThis);
+
+finalize_it:
+ RETiRet;
+}
+
+/* enqueue multiple user data elements at once. The aim is to provide a faster interface
+ * for object submission. Uses the multi_submit_t helper object.
+ * Please note that this function is not cancel-safe and consequently
+ * sets the calling thread's cancelibility state to PTHREAD_CANCEL_DISABLE
+ * during its execution. If that is not done, race conditions occur if the
+ * thread is canceled (most important use case is input module termination).
+ * rgerhards, 2009-06-16
+ */
+rsRetVal
+qqueueMultiEnqObj(qqueue_t *pThis, multi_submit_t *pMultiSub)
+{
+ int iCancelStateSave;
+ int i;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, qqueue);
+ assert(pMultiSub != NULL);
+
+ if(pThis->qType != QUEUETYPE_DIRECT) {
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave);
+ d_pthread_mutex_lock(pThis->mut);
+ }
+
+ for(i = 0 ; i < pMultiSub->nElem ; ++i) {
+dbgprintf("queueMultiEnq: %d\n", i);
+ CHKiRet(doEnqSingleObj(pThis, pMultiSub->ppMsgs[i]->flowCtlType, (void*)pMultiSub->ppMsgs[i]));
+ }
+
+ qqueueChkPersist(pThis, pMultiSub->nElem);
finalize_it:
if(pThis->qType != QUEUETYPE_DIRECT) {
@@ -2212,14 +2513,42 @@ finalize_it:
/* and release the mutex */
d_pthread_mutex_unlock(pThis->mut);
pthread_setcancelstate(iCancelStateSave, NULL);
- dbgoprint((obj_t*) pThis, "EnqueueMsg advised worker start\n");
- /* the following pthread_yield is experimental, but brought us performance
- * benefit. For details, please see http://kb.monitorware.com/post14216.html#p14216
- * rgerhards, 2008-10-09
- * but this is only true for uniprocessors, so we guard it with an optimize flag -- rgerhards, 2008-10-22
- */
- if(pThis->bOptimizeUniProc)
- pthread_yield();
+ DBGOPRINT((obj_t*) pThis, "MultiEnqObj advised worker start\n");
+ }
+
+ RETiRet;
+}
+
+
+/* enqueue a new user data element
+ * Enqueues the new element and awakes worker thread.
+ */
+rsRetVal
+qqueueEnqObj(qqueue_t *pThis, flowControl_t flowCtlType, void *pUsr)
+{
+ DEFiRet;
+ int iCancelStateSave;
+
+ ISOBJ_TYPE_assert(pThis, qqueue);
+
+ if(pThis->qType != QUEUETYPE_DIRECT) {
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave);
+ d_pthread_mutex_lock(pThis->mut);
+ }
+
+ CHKiRet(doEnqSingleObj(pThis, flowCtlType, pUsr));
+
+ qqueueChkPersist(pThis, 1);
+
+finalize_it:
+ if(pThis->qType != QUEUETYPE_DIRECT) {
+ /* make sure at least one worker is running. */
+ qqueueAdviseMaxWorkers(pThis);
+dbgprintf("YYY: call advise with mutex %p locked \n", pThis->mut);
+ /* and release the mutex */
+ d_pthread_mutex_unlock(pThis->mut);
+ pthread_setcancelstate(iCancelStateSave, NULL);
+ DBGOPRINT((obj_t*) pThis, "EnqueueMsg advised worker start\n");
}
RETiRet;
@@ -2235,7 +2564,7 @@ finalize_it:
* rgerhards, 2008-01-16
*/
static rsRetVal
-qqueueSetEnqOnly(qqueue_t *pThis, int bEnqOnly, int bLockMutex)
+SetEnqOnly(qqueue_t *pThis, int bEnqOnly, int bLockMutex)
{
DEFiRet;
DEFVARS_mutexProtection;
@@ -2257,7 +2586,7 @@ qqueueSetEnqOnly(qqueue_t *pThis, int bEnqOnly, int bLockMutex)
if(bEnqOnly == 1) {
/* switch to enqueue-only mode */
/* this means we need to terminate all workers - that's it... */
- dbgoprint((obj_t*) pThis, "switching to enqueue-only mode, terminating all worker threads\n");
+ DBGOPRINT((obj_t*) pThis, "switching to enqueue-only mode, terminating all worker threads\n");
if(pThis->pWtpReg != NULL)
wtpWakeupAllWrkr(pThis->pWtpReg);
if(pThis->pWtpDA != NULL)
@@ -2279,6 +2608,7 @@ finalize_it:
/* some simple object access methods */
+DEFpropSetMeth(qqueue, bSyncQueueFiles, int)
DEFpropSetMeth(qqueue, iPersistUpdCnt, int)
DEFpropSetMeth(qqueue, iDeqtWinFromHr, int)
DEFpropSetMeth(qqueue, iDeqtWinToHr, int)
@@ -2296,6 +2626,7 @@ DEFpropSetMeth(qqueue, iMinMsgsPerWrkr, int)
DEFpropSetMeth(qqueue, bSaveOnShutdown, int)
DEFpropSetMeth(qqueue, pUsr, void*)
DEFpropSetMeth(qqueue, iDeqSlowdown, int)
+DEFpropSetMeth(qqueue, iDeqBatchSize, int)
DEFpropSetMeth(qqueue, sizeOnDiskMax, int64)
@@ -2314,8 +2645,6 @@ static rsRetVal qqueueSetProperty(qqueue_t *pThis, var_t *pProp)
if(isProp("iQueueSize")) {
pThis->iQueueSize = pProp->val.num;
- } else if(isProp("iUngottenObjs")) {
- pThis->iUngottenObjs = pProp->val.num;
} else if(isProp("tVars.disk.sizeOnDisk")) {
pThis->tVars.disk.sizeOnDisk = pProp->val.num;
} else if(isProp("tVars.disk.bytesRead")) {
@@ -2340,6 +2669,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 a267862d..73c62b52 100644
--- a/runtime/queue.h
+++ b/runtime/queue.h
@@ -27,8 +27,18 @@
#include <pthread.h>
#include "obj.h"
#include "wtp.h"
+#include "batch.h"
#include "stream.h"
+/* support for the toDelete list */
+typedef struct toDeleteLst_s toDeleteLst_t;
+struct toDeleteLst_s {
+ qDeqID deqID;
+ int nElemDeq; /* numbe of elements that were dequeued and as such must now be discarded */
+ struct toDeleteLst_s *pNext;
+};
+
+
/* queue types */
typedef enum {
QUEUETYPE_FIXED_ARRAY = 0,/* a simple queue made out of a fixed (initially malloced) array fast but memoryhog */
@@ -44,25 +54,15 @@ typedef struct qLinkedList_S {
} qLinkedList_t;
-typedef struct qWrkThrd_s {
- pthread_t thrdID; /* thread ID */
- qWrkCmd_t tCurrCmd; /* current command to be carried out by worker */
- obj_t *pUsr; /* current user object being processed (or NULL if none) */
- struct queue_s *pQueue; /* my queue (important if only the work thread instance is passed! */
- int iThrd; /* my worker thread array index */
- pthread_cond_t condInitDone; /* signaled when the thread startup is done (once per thread existance) */
- pthread_mutex_t mut;
-} qWrkThrd_t; /* type for queue worker threads */
-
/* the queue object */
typedef struct queue_s {
BEGINobjInstance;
queueType_t qType;
- int bOptimizeUniProc; /* cache for the equally-named global setting, pulled at time of queue creation */
- int bEnqOnly; /* does queue run in enqueue-only mode (1) or not (0)? */
- int bSaveOnShutdown;/* persists everthing on shutdown (if DA!)? 1-yes, 0-no */
- int bQueueStarted; /* has queueStart() been called on this queue? 1-yes, 0-no */
- int bQueueInDestruction;/* 1 if queue is in destruction process, 0 otherwise */
+ int nLogDeq; /* number of elements currently logically dequeued */
+ bool bEnqOnly; /* does queue run in enqueue-only mode (1) or not (0)? */
+ bool bSaveOnShutdown;/* persists everthing on shutdown (if DA!)? 1-yes, 0-no */
+ bool bQueueStarted; /* has queueStart() been called on this queue? 1-yes, 0-no */
+ bool bQueueInDestruction;/* 1 if queue is in destruction process, 0 otherwise */
int iQueueSize; /* Current number of elements in the queue */
int iMaxQueueSize; /* how large can the queue grow? */
int iNumWorkerThreads;/* number of worker threads to use */
@@ -73,17 +73,20 @@ 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 */
+ bool 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 */
int iFullDlyMrk; /* if the queue is above this mark, FULL_DELAYable message are put on hold */
int iLightDlyMrk; /* if the queue is above this mark, LIGHT_DELAYable message are put on hold */
int iDiscardSeverity;/* messages of this severity above are discarded on too-full queue */
- int bNeedDelQIF; /* does the QIF file need to be deleted when queue becomes empty? */
+ bool bNeedDelQIF; /* does the QIF file need to be deleted when queue becomes empty? */
int toQShutdown; /* timeout for regular queue shutdown in ms */
int toActShutdown; /* timeout for long-running action shutdown in ms */
int toWrkShutdown; /* timeout for idle workers in ms, -1 means indefinite (0 is immediate) */
+ toDeleteLst_t *toDeleteLst;/* this queue's to-delete list */
int toEnq; /* enqueue timeout */
+ int iDeqBatchSize; /* max number of elements that shall be dequeued at once */
/* rate limiting settings (will be expanded) */
int iDeqSlowdown; /* slow down dequeue by specified nbr of microseconds */
/* end rate limiting */
@@ -97,18 +100,19 @@ typedef struct queue_s {
* applied to detect user configuration errors (and tell me how should we detect what
* the user really wanted...). -- rgerhards, 2008-04-02
*/
- /* ane dequeue time window */
- rsRetVal (*pConsumer)(void *,void*); /* user-supplied consumer function for dequeued messages */
+ /* end dequeue time window */
+ rsRetVal (*pConsumer)(void *,batch_t*); /* user-supplied consumer function for dequeued messages */
/* calling interface for pConsumer: arg1 is the global user pointer from this structure, arg2 is the
- * user pointer that was dequeued (actual sample: for actions, arg1 is the pAction and arg2 is pointer
- * to message)
- * rgerhards, 2008-01-28
+ * user pointer array that was dequeued (actual sample: for actions, arg1 is the pAction and arg2
+ * is pointer to an array of message message pointers)
*/
/* type-specific handlers (set during construction) */
rsRetVal (*qConstruct)(struct queue_s *pThis);
rsRetVal (*qDestruct)(struct queue_s *pThis);
rsRetVal (*qAdd)(struct queue_s *pThis, void *pUsr);
- rsRetVal (*qDel)(struct queue_s *pThis, void **ppUsr);
+ rsRetVal (*qDeq)(struct queue_s *pThis, void **ppUsr);
+ rsRetVal (*qDel)(struct queue_s *pThis);
+ rsRetVal (*qUnDeqAll)(struct queue_s *pThis);
/* end type-specific handler */
/* synchronization variables */
pthread_mutex_t mutThrdMgmt; /* mutex for the queue's thread management */
@@ -117,7 +121,6 @@ typedef struct queue_s {
pthread_cond_t belowFullDlyWtrMrk; /* below eFLOWCTL_FULL_DELAY watermark */
pthread_cond_t belowLightDlyWtrMrk; /* below eFLOWCTL_FULL_DELAY watermark */
pthread_cond_t condDAReady;/* signalled when the DA queue is fully initialized and ready for processing */
- int bChildIsDone; /* set to 1 when the child DA queue has finished processing, 0 otherwise */
int bThrdStateChanged; /* at least one thread state has changed if 1 */
/* end sync variables */
/* the following variables are always present, because they
@@ -132,32 +135,30 @@ typedef struct queue_s {
int iNumberFiles; /* how many files make up the queue? */
int64 iMaxFileSize; /* max size for a single queue file */
int64 sizeOnDiskMax; /* maximum size on disk allowed */
+ qDeqID deqIDAdd; /* next dequeue ID to use during add to queue store */
+ qDeqID deqIDDel; /* queue store delete position */
int bIsDA; /* is this queue disk assisted? */
int bRunsDA; /* is this queue actually *running* disk assisted? */
struct queue_s *pqDA; /* queue for disk-assisted modes */
struct queue_s *pqParent;/* pointer to the parent (if this is a child queue) */
int bDAEnqOnly; /* EnqOnly setting for DA queue */
- /* some data elements for the queueUngetObj() functionality. This list should always be short
- * and is always kept in memory
- */
- qLinkedList_t *pUngetRoot;
- qLinkedList_t *pUngetLast;
- int iUngottenObjs; /* number of objects currently in the "ungotten" list */
/* now follow queueing mode specific data elements */
union { /* different data elements based on queue type (qType) */
struct {
- long head, tail;
+ long deqhead, head, tail;
void** pBuf; /* the queued user data structure */
} farray;
struct {
- qLinkedList_t *pRoot;
+ qLinkedList_t *pDeqRoot;
+ qLinkedList_t *pDelRoot;
qLinkedList_t *pLast;
} linklist;
struct {
int64 sizeOnDisk; /* current amount of disk space used */
int64 bytesRead; /* number of bytes read from current (undeleted!) file */
- strm_t *pWrite; /* current file to be written */
- strm_t *pRead; /* current file to be read */
+ strm_t *pWrite; /* current file to be written */
+ strm_t *pReadDeq; /* current file for dequeueing */
+ strm_t *pReadDel; /* current file for deleting */
} disk;
} tVars;
} qqueue_t;
@@ -178,14 +179,16 @@ typedef struct queue_s {
/* prototypes */
rsRetVal qqueueDestruct(qqueue_t **ppThis);
+rsRetVal qqueueMultiEnqObj(qqueue_t *pThis, multi_submit_t *pMultiSub);
rsRetVal qqueueEnqObj(qqueue_t *pThis, flowControl_t flwCtlType, void *pUsr);
rsRetVal qqueueStart(qqueue_t *pThis);
rsRetVal qqueueSetMaxFileSize(qqueue_t *pThis, size_t iMaxFileSize);
rsRetVal qqueueSetFilePrefix(qqueue_t *pThis, uchar *pszPrefix, size_t iLenPrefix);
rsRetVal qqueueConstruct(qqueue_t **ppThis, queueType_t qType, int iWorkerThreads,
- int iMaxQueueSize, rsRetVal (*pConsumer)(void*,void*));
+ 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);
@@ -201,6 +204,7 @@ PROTOTYPEpropSetMeth(qqueue, bSaveOnShutdown, int);
PROTOTYPEpropSetMeth(qqueue, pUsr, void*);
PROTOTYPEpropSetMeth(qqueue, iDeqSlowdown, int);
PROTOTYPEpropSetMeth(qqueue, sizeOnDiskMax, int64);
+PROTOTYPEpropSetMeth(qqueue, iDeqBatchSize, int);
#define qqueueGetID(pThis) ((unsigned long) pThis)
#endif /* #ifndef QUEUE_H_INCLUDED */
diff --git a/runtime/rsyslog.c b/runtime/rsyslog.c
index 8df100a1..443d0f41 100644
--- a/runtime/rsyslog.c
+++ b/runtime/rsyslog.c
@@ -77,6 +77,9 @@
#include "conf.h"
#include "glbl.h"
#include "errmsg.h"
+#include "prop.h"
+#include "rule.h"
+#include "ruleset.h"
/* forward definitions */
static rsRetVal dfltErrLogger(int, uchar *errMsg);
@@ -144,20 +147,18 @@ rsrtInit(char **ppErrObj, obj_if_t *pObjIF)
* class immediately after it is initialized. And, of course, we load those classes
* first that we use ourselfs... -- rgerhards, 2008-03-07
*/
+ if(ppErrObj != NULL) *ppErrObj = "prop";
+ CHKiRet(propClassInit(NULL));
if(ppErrObj != NULL) *ppErrObj = "glbl";
CHKiRet(glblClassInit(NULL));
if(ppErrObj != NULL) *ppErrObj = "datetime";
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 +169,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 +213,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 835fff27..59e8458b 100644
--- a/runtime/rsyslog.h
+++ b/runtime/rsyslog.h
@@ -29,7 +29,18 @@
/* ############################################################# *
* # Config Settings # *
* ############################################################# */
-#define RS_STRINGBUF_ALLOC_INCREMENT 128
+#define RS_STRINGBUF_ALLOC_INCREMENT 128
+/* MAXSIZE are absolute maxima, while BUFSIZE are just values after which
+ * processing is more time-intense. The BUFSIZE params currently add their
+ * value to the fixed size of the message object.
+ */
+#define CONF_TAG_MAXSIZE 512 /* a value that is deemed far too large for any valid TAG */
+#define CONF_TAG_HOSTNAME 512 /* a value that is deemed far too large for any valid HOSTNAME */
+#define CONF_RAWMSG_BUFSIZE 101
+#define CONF_TAG_BUFSIZE 32
+#define CONF_HOSTNAME_BUFSIZE 32
+#define CONF_PROP_BUFSIZE 16 /* should be close to sizeof(ptr) or lighly above it */
+
/* ############################################################# *
* # End Config Settings # *
@@ -58,11 +69,29 @@
#endif
+/* the rsyslog core provides information about present feature to plugins
+ * asking it. Below are feature-test macros which must be used to query
+ * features. Note that this must be powers of two, so that multiple queries
+ * can be combined. -- rgerhards, 2009-04-27
+ */
+#define CORE_FEATURE_BATCHING 1
+/*#define CORE_FEATURE_whatever 2 ... and so on ... */
+
+/* some universal fixed size integer defines ... */
+typedef long long int64;
+typedef long long unsigned uint64;
+typedef int64 number_t; /* type to use for numbers - TODO: maybe an autoconf option? */
+typedef char intTiny; /* 0..127! */
+typedef unsigned char uintTiny; /* 0..255! */
+
/* define some base data types */
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;
@@ -74,9 +103,11 @@ typedef struct nsd_gsspi_s nsd_gsspi_t;
typedef struct nsd_nss_s nsd_nss_t;
typedef struct nsdsel_ptcp_s nsdsel_ptcp_t;
typedef struct nsdsel_gtls_s nsdsel_gtls_t;
+typedef struct wti_s wti_t;
typedef obj_t nsd_t;
typedef obj_t nsdsel_t;
typedef struct msg msg_t;
+typedef struct prop_s prop_t;
typedef struct interface_s interface_t;
typedef struct objInfo_s objInfo_t;
typedef enum rsRetVal_ rsRetVal; /**< friendly type for global return value */
@@ -88,16 +119,15 @@ typedef struct tcps_sess_s tcps_sess_t;
typedef struct strmsrv_s strmsrv_t;
typedef struct strms_sess_s strms_sess_t;
typedef struct vmstk_s vmstk_t;
+typedef struct batch_obj_s batch_obj_t;
+typedef struct batch_s batch_t;
+typedef struct wtp_s wtp_t;
typedef rsRetVal (*prsf_t)(struct vmstk_s*, int); /* pointer to a RainerScript function */
+typedef uint64 qDeqID; /* queue Dequeue order ID. 32 bits is considered dangerously few */
typedef struct tcpLstnPortList_s tcpLstnPortList_t; // TODO: rename?
typedef struct strmLstnPortList_s strmLstnPortList_t; // TODO: rename?
-/* some universal 64 bit define... */
-typedef long long int64;
-typedef long long unsigned uint64;
-typedef int64 number_t; /* type to use for numbers - TODO: maybe an autoconf option? */
-
#ifdef __hpux
typedef unsigned int u_int32_t; /* TODO: is this correct? */
typedef int socklen_t;
@@ -114,6 +144,72 @@ 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;
+
+
+/* multi-submit support.
+ * This is done via a simple data structure, which holds the number of elements
+ * as well as an array of to-be-submitted messages.
+ * rgerhards, 2009-06-16
+ */
+typedef struct multi_submit_s multi_submit_t;
+struct multi_submit_s {
+ short maxElem; /* maximum number of Elements */
+ short nElem; /* current number of Elements, points to the next one FREE */
+ msg_t **ppMsgs;
+};
+
+
+#ifndef _PATH_CONSOLE
+#define _PATH_CONSOLE "/dev/console"
+#endif
+
+/* properties are now encoded as (tiny) integers. I do not use an enum as I would like
+ * to keep the memory footprint small (and thus cache hits high).
+ * rgerhards, 2009-06-26
+ */
+typedef uintTiny propid_t;
+#define PROP_INVALID 0
+#define PROP_MSG 1
+#define PROP_TIMESTAMP 2
+#define PROP_HOSTNAME 3
+#define PROP_SYSLOGTAG 4
+#define PROP_RAWMSG 5
+#define PROP_INPUTNAME 6
+#define PROP_FROMHOST 7
+#define PROP_FROMHOST_IP 8
+#define PROP_PRI 9
+#define PROP_PRI_TEXT 10
+#define PROP_IUT 11
+#define PROP_SYSLOGFACILITY 12
+#define PROP_SYSLOGFACILITY_TEXT 13
+#define PROP_SYSLOGSEVERITY 14
+#define PROP_SYSLOGSEVERITY_TEXT 15
+#define PROP_TIMEGENERATED 16
+#define PROP_PROGRAMNAME 17
+#define PROP_PROTOCOL_VERSION 18
+#define PROP_STRUCTURED_DATA 19
+#define PROP_APP_NAME 20
+#define PROP_PROCID 21
+#define PROP_MSGID 22
+#define PROP_SYS_NOW 150
+#define PROP_SYS_YEAR 151
+#define PROP_SYS_MONTH 152
+#define PROP_SYS_DAY 153
+#define PROP_SYS_HOUR 154
+#define PROP_SYS_HHOUR 155
+#define PROP_SYS_QHOUR 156
+#define PROP_SYS_MINUTE 157
+#define PROP_SYS_MYHOSTNAME 158
+
/* The error codes below are orginally "borrowed" from
* liblogging. As such, we reserve values up to -2999
@@ -279,17 +375,28 @@ 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 */
+ RS_RET_VAR_NOT_FOUND = -2142, /**< variable not found */
RS_RET_EMPTY_MSG = -2143, /**< provided (raw) MSG is empty */
+ RS_RET_PEER_CLOSED_CONN = -2144, /**< remote peer closed connection (information, no error) */
+ RS_RET_NO_SRCNAME_TPL = -2150, /**< sourcename template was not specified where one was needed (omudpspoof spoof addr) */
+ RS_RET_HOST_NOT_SPECIFIED = -2151, /**< (target) host was not specified where it was needed */
+ RS_RET_ERR_LIBNET_INIT = -2152, /**< error initializing libnet */
/* RainerScript error messages (range 1000.. 1999) */
RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */
/* some generic error/status codes */
+ RS_RET_OK = 0, /**< operation successful */
RS_RET_OK_DELETE_LISTENTRY = 1, /**< operation successful, but callee requested the deletion of an entry (special state) */
RS_RET_TERMINATE_NOW = 2, /**< operation successful, function is requested to terminate (mostly used with threads) */
RS_RET_NO_RUN = 3, /**< operation successful, but function does not like to be executed */
- RS_RET_OK = 0 /**< operation successful */
+ RS_RET_IDLE = 4, /**< operation successful, but callee is idle (e.g. because queue is empty) */
+ RS_RET_TERMINATE_WHEN_IDLE = 5 /**< operation successful, function is requested to terminate when idle */
};
/* some helpful macros to work with srRetVals.
@@ -369,6 +476,10 @@ typedef enum rsObjectID rsObjID;
# define O_CLOEXEC 0
#endif
+/* some constants */
+#define MUTEX_ALREADY_LOCKED 0
+#define LOCK_MUTEX 1
+
/* 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..182d616a
--- /dev/null
+++ b/runtime/rule.c
@@ -0,0 +1,449 @@
+/* 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 "unicode-helper.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;
+ uchar *pszPropVal;
+ int bRet = 0;
+ size_t propLen;
+ 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, LOCK_MUTEX), getProgramNameLen(pMsg, LOCK_MUTEX)))
+ 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, LOCK_MUTEX));
+ 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.propID, &propLen, &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,
+ pszPropVal, ustrlen(pszPropVal)) == 0)
+ bRet = 1; /* process message! */
+ break;
+ case FIOP_STARTSWITH:
+ if(rsCStrSzStrStartsWithCStr(pRule->f_filterData.prop.pCSCompValue,
+ pszPropVal, ustrlen(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') ",
+ propIDToName(pRule->f_filterData.prop.propID), 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.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", propIDToName(pThis->f_filterData.prop.propID));
+ 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..99ac44e7
--- /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 {
+ fiop_t operation;
+ regex_t *regex_cache; /* cache for compiled REs, if such are used */
+ cstr_t *pCSCompValue; /* value to "compare" against */
+ bool isNegated;
+ propid_t propID; /* ID of the requested property */
+ } 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..5ac9a8fd
--- /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;
+}
+/* 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(&(pThis->llRules), 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;
+}
+/* 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));
+
+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)
+{
+ rsRetVal iRet;
+ ISOBJ_TYPE_assert(pData, rule);
+ iRet = rule.ProcessMsg((rule_t*) pData, (msg_t*) pParam);
+ return iRet;
+}
+
+
+/* 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 bfce4cbb..c4f73e16 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
@@ -108,20 +109,18 @@ int getSubString(uchar **ppSrc, char *pDst, size_t DstSize, char cSep);
#define mutex_cancelsafe_unlock(mut) pthread_cleanup_pop(1)
/* some useful constants */
-#define MUTEX_ALREADY_LOCKED 0
-#define LOCK_MUTEX 1
#define DEFVARS_mutexProtection\
- int iCancelStateSave; \
int bLockedOpIsLocked=0
#define BEGIN_MTX_PROTECTED_OPERATIONS(mut, bMustLock) \
if(bMustLock == LOCK_MUTEX) { \
- pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave); \
d_pthread_mutex_lock(mut); \
+ assert(bLockedOpIsLocked == 0); \
bLockedOpIsLocked = 1; \
}
#define END_MTX_PROTECTED_OPERATIONS(mut) \
if(bLockedOpIsLocked) { \
d_pthread_mutex_unlock(mut); \
- pthread_setcancelstate(iCancelStateSave, NULL); \
+ bLockedOpIsLocked = 0; \
}
+
#endif
diff --git a/runtime/srutils.c b/runtime/srutils.c
index d01ca20d..c403b312 100644
--- a/runtime/srutils.c
+++ b/runtime/srutils.c
@@ -366,6 +366,7 @@ int getNumberDigits(long lNum)
/* compute an absolute time timeout suitable for calls to pthread_cond_timedwait()
+ * iTimeout is in milliseconds
* rgerhards, 2008-01-14
*/
rsRetVal
@@ -375,11 +376,12 @@ timeoutComp(struct timespec *pt, long iTimeout)
assert(pt != NULL);
/* compute timeout */
clock_gettime(CLOCK_REALTIME, pt);
+ pt->tv_sec += iTimeout / 1000;
pt->tv_nsec += (iTimeout % 1000) * 1000000; /* think INTEGER arithmetic! */
if(pt->tv_nsec > 999999999) { /* overrun? */
pt->tv_nsec -= 1000000000;
+ ++pt->tv_sec;
}
- pt->tv_sec += iTimeout / 1000;
ENDfunc
return RS_RET_OK; /* so far, this is static... */
}
@@ -553,6 +555,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 1cff2da6..58f16cce 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,17 @@
* "driver").
*
* File begun on 2008-01-09 by RGerhards
+ * Large modifications in 2009-06 to support using it with omfile, including zip writer.
+ * Note that this file obtains the zlib wrapper object is needed, but it never frees it
+ * again. While this sounds like a leak (and one may argue it actually is), there is no
+ * harm associated with that. The reason is that strm is a core object, so it is terminated
+ * only when rsyslogd exists. As we could only release on termination (or else bear more
+ * overhead for keeping track of how many users we have), not releasing zlibw is OK, because
+ * it will be released when rsyslogd terminates. We may want to revisit this decision if
+ * it turns out to be problematic. Then, we need to quasi-refcount the number of accesses
+ * to the object.
*
- * 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,24 +47,191 @@
#include <unistd.h>
#include <sys/stat.h> /* required for HP UX */
#include <errno.h>
+#include <pthread.h>
#include "rsyslog.h"
#include "stringbuf.h"
#include "srUtils.h"
#include "obj.h"
#include "stream.h"
+#include "unicode-helper.h"
+#include "module-template.h"
+#if HAVE_SYS_PRCTL_H
+# include <sys/prctl.h>
+#endif
+
+#define inline
/* static data */
DEFobjStaticHelpers
+DEFobjCurrIf(zlibw)
+
+/* 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);
+static void *asyncWriterThread(void *pPtr);
+static rsRetVal doZipWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf);
+static rsRetVal strmPhysWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf);
+
/* methods */
-/* first, we define type-specific handlers. The provide a generic functionality,
+/* 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 = 0;
+ 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
@@ -64,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);
@@ -80,7 +253,7 @@ static rsRetVal strmOpenFile(strm_t *pThis)
pThis->pszFName, pThis->lenFName, pThis->iCurrFNum, pThis->iFileNumDigits));
} else {
if(pThis->pszDir == NULL) {
- if((pThis->pszCurrFName = (uchar*) strdup((char*) pThis->pszFName)) == NULL)
+ if((pThis->pszCurrFName = ustrdup(pThis->pszFName)) == NULL)
ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
} else {
CHKiRet(genFileName(&pThis->pszCurrFName, pThis->pszDir, pThis->lenDir,
@@ -88,34 +261,41 @@ 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;
- 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);
- }
+ 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;
}
+/* wait for the output writer thread to be done. This must be called before actions
+ * that require data to be persisted. May be called in non-async mode and is a null
+ * operation than. Must be called with the mutex locked.
+ */
+static inline void
+strmWaitAsyncWriterDone(strm_t *pThis)
+{
+ BEGINfunc
+ if(pThis->bAsyncWrite) {
+ /* awake writer thread and make it write out everything */
+ pthread_cond_signal(&pThis->notEmpty);
+ d_pthread_cond_wait(&pThis->isEmpty, &pThis->mut);
+ }
+ ENDfunc
+}
+
+
/* close a strm file
* Note that the bDeleteOnClose flag is honored. If it is set, the file will be
* deleted after close. This is in support for the qRead thread.
@@ -128,14 +308,33 @@ 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)
- strmFlush(pThis);
+ if(!pThis->bInClose && pThis->tOperationsMode != STREAMMODE_READ) {
+ pThis->bInClose = 1;
+ if(pThis->bAsyncWrite) {
+ strmFlush(pThis);
+ } else {
+ strmWaitAsyncWriterDone(pThis);
+ }
+ pThis->bInClose = 0;
+ }
- 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 */
@@ -234,10 +433,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;
@@ -295,7 +490,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;
@@ -329,7 +524,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);
@@ -351,7 +546,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;
@@ -360,19 +555,19 @@ strmReadLine(strm_t *pThis, cstr_t **ppCStr)
ASSERT(pThis != NULL);
ASSERT(ppCStr != NULL);
- CHKiRet(rsCStrConstruct(ppCStr));
+ CHKiRet(cstrConstruct(ppCStr));
/* now read the line */
CHKiRet(strmReadChar(pThis, &c));
while(c != '\n') {
- CHKiRet(rsCStrAppendChar(*ppCStr, c));
+ CHKiRet(cstrAppendChar(*ppCStr, c));
CHKiRet(strmReadChar(pThis, &c));
}
CHKiRet(cstrFinalize(*ppCStr));
finalize_it:
if(iRet != RS_RET_OK && *ppCStr != NULL)
- rsCStrDestruct(ppCStr);
+ cstrDestruct(ppCStr);
RETiRet;
}
@@ -383,26 +578,75 @@ 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;
+ int i;
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 */
+ 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 everything with a SINGLE api call!
+ * We add another 128 bytes to take care of the gzip header and "all eventualities".
+ */
+ CHKmalloc(pThis->pZipBuf = (Bytef*) malloc(sizeof(uchar) * pThis->sIOBufSize + 128));
+ }
+ }
+
+ /* 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 have a flush interval, we need to do async writes in any case */
+ if(pThis->iFlushInterval != 0) {
+ pThis->bAsyncWrite = 1;
+ }
+
+ /* if we work asynchronously, we need a couple of synchronization objects */
+ if(pThis->bAsyncWrite) {
+ pthread_mutex_init(&pThis->mut, 0);
+ pthread_cond_init(&pThis->notFull, 0);
+ pthread_cond_init(&pThis->notEmpty, 0);
+ pthread_cond_init(&pThis->isEmpty, 0);
+ pThis->iCnt = pThis->iEnq = pThis->iDeq = 0;
+ for(i = 0 ; i < STREAM_ASYNC_NUMBUFS ; ++i) {
+ CHKmalloc(pThis->asyncBuf[i].pBuf = (uchar*) malloc(sizeof(uchar) * pThis->sIOBufSize));
+ }
+ pThis->pIOBuf = pThis->asyncBuf[0].pBuf;
+ pThis->bStopWriter = 0;
+ if(pthread_create(&pThis->writerThreadID, NULL, asyncWriterThread, pThis) != 0)
+ DBGPRINTF("ERROR: stream %p cold not create writer thread\n", pThis);
+ } else {
+ /* we work synchronously, so we need to alloc a fixed pIOBuf */
+ CHKmalloc(pThis->pIOBuf = (uchar*) malloc(sizeof(uchar) * pThis->sIOBufSize));
}
finalize_it:
@@ -410,24 +654,57 @@ finalize_it:
}
+/* stop the writer thread (we MUST be runnnig asynchronously when this method
+ * is called!). Note that the mutex must be locked! -- rgerhards, 2009-07-06
+ */
+static inline void
+stopWriter(strm_t *pThis)
+{
+ BEGINfunc
+ pThis->bStopWriter = 1;
+ pthread_cond_signal(&pThis->notEmpty);
+ d_pthread_mutex_unlock(&pThis->mut);
+ pthread_join(pThis->writerThreadID, NULL);
+ ENDfunc
+}
+
+
/* destructor for the strm object */
BEGINobjDestruct(strm) /* be sure to specify the object type also in END and CODESTART macros! */
+ int i;
CODESTARTobjDestruct(strm)
- if(pThis->tOperationsMode == STREAMMODE_WRITE)
+ if(pThis->bAsyncWrite)
+ /* Note: mutex will be unlocked in stopWriter! */
+ d_pthread_mutex_lock(&pThis->mut);
+
+ if(pThis->tOperationsMode != STREAMMODE_READ)
strmFlush(pThis);
- /* ... then free resources */
+ if(pThis->bAsyncWrite) {
+ stopWriter(pThis);
+ pthread_mutex_destroy(&pThis->mut);
+ pthread_cond_destroy(&pThis->notFull);
+ pthread_cond_destroy(&pThis->notEmpty);
+ pthread_cond_destroy(&pThis->isEmpty);
+ for(i = 0 ; i < STREAM_ASYNC_NUMBUFS ; ++i) {
+ free(pThis->asyncBuf[i].pBuf);
+ }
+ } else {
+ free(pThis->pIOBuf);
+ }
+
+ /* Finally, we can free the resources.
+ * IMPORTANT: we MUST free this only AFTER the ansyncWriter has been stopped, else
+ * we get random errors...
+ */
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);
+ free(pThis->pszDir);
+ free(pThis->pZipBuf);
+ free(pThis->pszCurrFName);
+ free(pThis->pszFName);
+
ENDobjDestruct(strm)
@@ -443,6 +720,9 @@ static rsRetVal strmCheckNextOutputFile(strm_t *pThis)
if(pThis->fd == -1)
FINALIZE;
+ /* wait for output to be empty, so that our counts are correct */
+ strmWaitAsyncWriterDone(pThis);
+
if(pThis->iCurrOffs >= pThis->iMaxFileSize) {
dbgoprint((obj_t*) pThis, "max file size %ld reached for %d, now %ld - starting new file\n",
(long) pThis->iMaxFileSize, pThis->fd, (long) pThis->iCurrOffs);
@@ -453,47 +733,375 @@ finalize_it:
RETiRet;
}
+
+/* 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.
+ */
+#ifdef linux
+# define ERR_TTYHUP EIO
+#else
+# define ERR_TTYHUP EBADF
+#endif
+static rsRetVal
+tryTTYRecover(strm_t *pThis, int err)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strm);
+ if(err == ERR_TTYHUP) {
+ close(pThis->fd);
+ CHKiRet(doPhysOpen(pThis));
+ }
+
+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()! */
+
+ dbgoprint((obj_t*) pThis, "file %d write wrote %d bytes\n", pThis->fd, (int) iWritten);
+
+finalize_it:
+ *pLenBuf = iTotalWritten;
+ 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)
+static inline rsRetVal
+doWriteInternal(strm_t *pThis, uchar *pBuf, size_t lenBuf)
+{
+ DEFiRet;
+
+ ASSERT(pThis != NULL);
+
+ if(pThis->iZipLevel) {
+ CHKiRet(doZipWrite(pThis, pBuf, lenBuf));
+ } else {
+ /* write without zipping */
+ CHKiRet(strmPhysWrite(pThis, pBuf, lenBuf));
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* This function is called to "do" an async write call, what primarily means that
+ * the data is handed over to the writer thread (which will then do the actual write
+ * in parallel). Note that the stream mutex has already been locked by the
+ * strmWrite...() calls. Also note that we always have only a single producer,
+ * so we can simply serially assign the next free buffer to it and be sure that
+ * the very some producer comes back in sequence to submit the then-filled buffers.
+ * This also enables us to timout on partially written buffers. -- rgerhards, 2009-07-06
+ */
+static inline rsRetVal
+doAsyncWriteInternal(strm_t *pThis, size_t lenBuf)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strm);
+
+ while(pThis->iCnt >= STREAM_ASYNC_NUMBUFS)
+ d_pthread_cond_wait(&pThis->notFull, &pThis->mut);
+
+ pThis->asyncBuf[pThis->iEnq % STREAM_ASYNC_NUMBUFS].lenBuf = lenBuf;
+ pThis->pIOBuf = pThis->asyncBuf[++pThis->iEnq % STREAM_ASYNC_NUMBUFS].pBuf;
+
+ pThis->bDoTimedWait = 0; /* everything written, no need to timeout partial buffer writes */
+ if(++pThis->iCnt == 1)
+ pthread_cond_signal(&pThis->notEmpty);
+
+ RETiRet;
+}
+
+
+/* schedule writing to the stream. Depending on our concurrency settings,
+ * this either directly writes to the stream or schedules writing via
+ * the background thread. -- rgerhards, 2009-07-07
+ */
+static rsRetVal
+strmSchedWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf)
{
DEFiRet;
- int iWritten;
ASSERT(pThis != NULL);
- ASSERT(pBuf == pThis->pIOBuf || pThis->iBufPtr == 0);
+
+ if(pThis->bAsyncWrite) {
+ CHKiRet(doAsyncWriteInternal(pThis, lenBuf));
+ } else {
+ CHKiRet(doWriteInternal(pThis, pBuf, lenBuf));
+ }
+
+ pThis->iBufPtr = 0; /* we are at the begin of a new buffer */
+
+finalize_it:
+ RETiRet;
+}
+
+
+
+/* This is the writer thread for asynchronous mode.
+ * -- rgerhards, 2009-07-06
+ */
+static void*
+asyncWriterThread(void *pPtr)
+{
+ int iDeq;
+ struct timespec t;
+ bool bTimedOut = 0;
+ strm_t *pThis = (strm_t*) pPtr;
+ ISOBJ_TYPE_assert(pThis, strm);
+
+ BEGINfunc
+# if HAVE_PRCTL && defined PR_SET_NAME
+ if(prctl(PR_SET_NAME, "rs:asyn strmwr", 0, 0, 0) != 0) {
+ DBGPRINTF("prctl failed, not setting thread name for '%s'\n", "stream writer");
+ }
+#endif
+
+ while(1) { /* loop broken inside */
+ d_pthread_mutex_lock(&pThis->mut);
+ while(pThis->iCnt == 0) {
+ if(pThis->bStopWriter) {
+ pthread_cond_broadcast(&pThis->isEmpty);
+ d_pthread_mutex_unlock(&pThis->mut);
+ goto finalize_it; /* break main loop */
+ }
+ if(bTimedOut && pThis->iBufPtr > 0) {
+ /* if we timed out, we need to flush pending data */
+ strmFlush(pThis);
+ bTimedOut = 0;
+ continue; /* now we should have data */
+ }
+ bTimedOut = 0;
+ timeoutComp(&t, pThis->iFlushInterval * 2000); /* *1000 millisconds */
+ if(pThis->bDoTimedWait) {
+ if(pthread_cond_timedwait(&pThis->notEmpty, &pThis->mut, &t) != 0) {
+ int err = errno;
+ if(err == ETIMEDOUT) {
+ bTimedOut = 1;
+ } else {
+ bTimedOut = 1;
+ char errStr[1024];
+ rs_strerror_r(err, errStr, sizeof(errStr));
+ DBGPRINTF("stream async writer timeout with error (%d): %s - ignoring\n",
+ err, errStr);
+ }
+ }
+ } else {
+ d_pthread_cond_wait(&pThis->notEmpty, &pThis->mut);
+ }
+ }
+
+ bTimedOut = 0; /* we may have timed out, but there *is* work to do... */
+
+ iDeq = pThis->iDeq++ % STREAM_ASYNC_NUMBUFS;
+ doWriteInternal(pThis, pThis->asyncBuf[iDeq].pBuf, pThis->asyncBuf[iDeq].lenBuf);
+ // TODO: error check????? 2009-07-06
+
+ --pThis->iCnt;
+ if(pThis->iCnt < STREAM_ASYNC_NUMBUFS) {
+ pthread_cond_signal(&pThis->notFull);
+ if(pThis->iCnt == 0)
+ pthread_cond_broadcast(&pThis->isEmpty);
+ }
+ d_pthread_mutex_unlock(&pThis->mut);
+ }
+
+finalize_it:
+ ENDfunc
+ return NULL; /* to keep pthreads happy */
+}
+
+
+/* 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
+ */
+#undef SYNCCALL
+#if HAVE_FDATASYNC
+# define SYNCCALL(x) fdatasync(x)
+#else
+# define SYNCCALL(x) fsync(x)
+#endif
+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 = SYNCCALL(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;
+}
+#undef SYNCCALL
+
+/* 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
- */
- pThis->iBufPtr = 0;
+ iWritten = lenBuf;
+ CHKiRet(doWriteCall(pThis, pBuf, &iWritten));
+
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
+ * For the time being, we take a very conservative approach and do not run this
+ * method multithreaded. This is done in an effort to solve a segfault condition
+ * that seems to be related to the zip code. -- rgerhards, 2009-09-22
+ * TODO: make multithreaded again!
+ */
+static rsRetVal
+doZipWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf)
+{
+ z_stream zstrm;
+ int zRet; /* zlib return state */
+ bool bzInitDone = FALSE;
+ DEFiRet;
+ assert(pThis != NULL);
+ assert(pBuf != NULL);
+
+ /* allocate deflate state */
+ zstrm.zalloc = Z_NULL;
+ zstrm.zfree = Z_NULL;
+ zstrm.opaque = Z_NULL;
+ zstrm.next_in = (Bytef*) pBuf; /* as of zlib doc, this must be set BEFORE DeflateInit2 */
+ /* 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);
+ }
+ bzInitDone = TRUE;
+
+ /* now doing the compression */
+ zstrm.next_in = (Bytef*) pBuf; /* as of zlib doc, this must be set BEFORE DeflateInit2 */
+ zstrm.avail_in = lenBuf;
+ /* run deflate() on buffer until everything has been compressed */
+ 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 */
+ if(zstrm.avail_out == pThis->sIOBufSize)
+ break; /* this is valid, indicates end of compression --> see zlib howto */
+ 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 */
finalize_it:
- pThis->iBufPtr = 0; /* see comment above */
+ if(bzInitDone) {
+ zRet = zlibw.DeflateEnd(&zstrm);
+ if(zRet != Z_OK) {
+ DBGPRINTF("error %d returned from zlib/deflateEnd()\n", zRet);
+ }
+ }
RETiRet;
}
@@ -503,15 +1111,16 @@ 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) {
- iRet = strmWriteInternal(pThis, pThis->pIOBuf, pThis->iBufPtr);
+ if(pThis->tOperationsMode != STREAMMODE_READ && pThis->iBufPtr > 0) {
+ iRet = strmSchedWrite(pThis, pThis->pIOBuf, pThis->iBufPtr);
}
RETiRet;
@@ -545,7 +1154,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;
@@ -558,12 +1167,18 @@ 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;
ASSERT(pThis != NULL);
+ if(pThis->bAsyncWrite)
+ d_pthread_mutex_lock(&pThis->mut);
+
+ if(pThis->bDisabled)
+ ABORT_FINALIZE(RS_RET_STREAM_DISABLED);
+
/* if the buffer is full, we need to flush before we can write */
if(pThis->iBufPtr == pThis->sIOBufSize) {
CHKiRet(strmFlush(pThis));
@@ -573,12 +1188,19 @@ rsRetVal strmWriteChar(strm_t *pThis, uchar c)
pThis->iBufPtr++;
finalize_it:
+ if(pThis->bAsyncWrite)
+ d_pthread_mutex_unlock(&pThis->mut);
+
RETiRet;
}
-/* write an integer value (actually a long) to a stream object */
-rsRetVal strmWriteLong(strm_t *pThis, long i)
+/* write an integer value (actually a long) to a stream object
+ * Note that we do not need to lock the mutex here, because we call
+ * strmWrite(), which does the lock (aka: we must not lock it, else we
+ * would run into a recursive lock, resulting in a deadlock!)
+ */
+static rsRetVal strmWriteLong(strm_t *pThis, long i)
{
DEFiRet;
uchar szBuf[32];
@@ -593,45 +1215,67 @@ finalize_it:
}
-/* write memory buffer to a stream object
+/* write memory buffer to a stream object.
+ * process the data in chunks and copy it over to our buffer. The caller-provided data
+ * may theoritically be larger than our buffer. In that case, we do multiple copies. One
+ * may argue if it were more efficient to write out the caller-provided buffer in that case
+ * and earlier versions of rsyslog did this. However, this introduces a lot of complexity
+ * inside the buffered writer and potential performance bottlenecks when trying to solve
+ * it. Now keep in mind that we actually do (almost?) never have a case where the
+ * caller-provided buffer is larger than our one. So instead of optimizing a case
+ * which normally does not exist, we expect some degradation in its case but make us
+ * perform better in the regular cases. -- rgerhards, 2009-07-07
*/
-rsRetVal strmWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf)
+static rsRetVal
+strmWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf)
{
DEFiRet;
- size_t iPartial;
+ size_t iWrite;
+ size_t iOffset;
ASSERT(pThis != NULL);
ASSERT(pBuf != NULL);
- /* 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.
- * TODO: is it really? think about disk block sizes!
- */
- CHKiRet(strmFlush(pThis)); /* we need to flush first!!! */
- CHKiRet(strmWriteInternal(pThis, pBuf, lenBuf));
- } else {
- /* data fits into a buffer - we just need to see if it
- * fits into the current buffer...
- */
- if(pThis->iBufPtr + lenBuf > pThis->sIOBufSize) {
- /* nope, so we must split it */
- iPartial = pThis->sIOBufSize - pThis->iBufPtr; /* this fits in current buf */
- if(iPartial > 0) { /* the buffer was exactly full, can not write anything! */
- memcpy(pThis->pIOBuf + pThis->iBufPtr, pBuf, iPartial);
- pThis->iBufPtr += iPartial;
- }
+//DBGPRINTF("strmWrite(%p, '%65.65s', %ld);, disabled %d, sizelim %ld, size %lld\n", pThis, pBuf,lenBuf, pThis->bDisabled, pThis->iSizeLimit, pThis->iCurrOffs);
+ if(pThis->bAsyncWrite)
+ d_pthread_mutex_lock(&pThis->mut);
+
+ if(pThis->bDisabled)
+ ABORT_FINALIZE(RS_RET_STREAM_DISABLED);
+
+ iOffset = 0;
+ do {
+ if(pThis->iBufPtr == pThis->sIOBufSize) {
CHKiRet(strmFlush(pThis)); /* get a new buffer for rest of data */
- memcpy(pThis->pIOBuf, pBuf + iPartial, lenBuf - iPartial);
- pThis->iBufPtr = lenBuf - iPartial;
- } else {
- /* we have space, so we simply copy over the string */
- memcpy(pThis->pIOBuf + pThis->iBufPtr, pBuf, lenBuf);
- pThis->iBufPtr += lenBuf;
}
+ iWrite = pThis->sIOBufSize - pThis->iBufPtr; /* this fits in current buf */
+ if(iWrite > lenBuf)
+ iWrite = lenBuf;
+ memcpy(pThis->pIOBuf + pThis->iBufPtr, pBuf + iOffset, iWrite);
+ pThis->iBufPtr += iWrite;
+ iOffset += iWrite;
+ lenBuf -= iWrite;
+ } while(lenBuf > 0);
+
+ /* now check if the buffer right at the end of the write is full and, if so,
+ * write it. This seems more natural than waiting (hours?) for the next message...
+ */
+ if(pThis->iBufPtr == pThis->sIOBufSize) {
+ CHKiRet(strmFlush(pThis)); /* get a new buffer for rest of data */
}
finalize_it:
+ if(pThis->bAsyncWrite) {
+ if(pThis->bDoTimedWait == 0) {
+ /* we potentially have a partial buffer, so re-activate the
+ * writer thread that it can set and pick up timeouts.
+ */
+ pThis->bDoTimedWait = 1;
+ pthread_cond_signal(&pThis->notEmpty);
+ }
+ d_pthread_mutex_unlock(&pThis->mut);
+ }
+
RETiRet;
}
@@ -644,34 +1288,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;
@@ -685,7 +1322,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! */
@@ -701,7 +1338,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;
@@ -745,7 +1382,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);
@@ -753,7 +1390,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);
@@ -775,7 +1412,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;
@@ -811,6 +1448,46 @@ finalize_it:
}
+/* duplicate a stream object excluding dynamic properties. This function is
+ * primarily meant to provide a duplicate that later on can be used to access
+ * the data. This is needed, for example, for a restart of the disk queue.
+ * Note that ConstructFinalize() is NOT called. So our caller may change some
+ * properties before finalizing things.
+ * rgerhards, 2009-05-26
+ */
+rsRetVal
+strmDup(strm_t *pThis, strm_t **ppNew)
+{
+ strm_t *pNew = NULL;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, strm);
+ assert(ppNew != NULL);
+
+ CHKiRet(strmConstruct(&pNew));
+ pNew->sType = pThis->sType;
+ pNew->iCurrFNum = pThis->iCurrFNum;
+ CHKmalloc(pNew->pszFName = ustrdup(pThis->pszFName));
+ pNew->lenFName = pThis->lenFName;
+ CHKmalloc(pNew->pszDir = ustrdup(pThis->pszDir));
+ pNew->lenDir = pThis->lenDir;
+ pNew->tOperationsMode = pThis->tOperationsMode;
+ pNew->tOpenMode = pThis->tOpenMode;
+ pNew->iMaxFileSize = pThis->iMaxFileSize;
+ pNew->iMaxFiles = pThis->iMaxFiles;
+ pNew->iFileNumDigits = pThis->iFileNumDigits;
+ pNew->bDeleteOnClose = pThis->bDeleteOnClose;
+ pNew->iCurrOffs = pThis->iCurrOffs;
+
+ *ppNew = pNew;
+ pNew = NULL;
+
+finalize_it:
+ if(pNew != NULL)
+ strmDestruct(&pNew);
+
+ RETiRet;
+}
/* set a user write-counter. This counter is initialized to zero and
* receives the number of bytes written. It is accurate only after a
@@ -821,7 +1498,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;
@@ -841,8 +1518,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;
@@ -881,7 +1558,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;
@@ -909,8 +1586,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)
@@ -927,7 +1635,5 @@ BEGINObjClassInit(strm, 1, OBJ_IS_CORE_MODULE)
OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, strmConstructFinalize);
ENDObjClassInit(strm)
-
-/*
- * vi:set ai:
+/* vi:set ai:
*/
diff --git a/runtime/stream.h b/runtime/stream.h
index 371358ab..9577d704 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,12 +79,15 @@ 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;
+#define STREAM_ASYNC_NUMBUFS 2 /* must be a power of 2 -- TODO: make configurable */
/* The strm_t data structure */
typedef struct strm_s {
BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
@@ -71,61 +98,96 @@ 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 */
+ bool bDisabled; /* should file no longer be written to? (currently set only if omfile file size limit fails) */
+ bool 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 */
+ uchar *pIOBuf; /* the iobuffer currently in use to gather data */
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 */
+ bool bInClose; /* used to break "deadly close loops", tells us we are already inside a close */
+ int iZipLevel; /* zip level (0..9). If 0, zip is completely disabled */
+ Bytef *pZipBuf;
+ /* support for async flush procesing */
+ bool bAsyncWrite; /* do asynchronous writes (always if a flush interval is given) */
+ bool bStopWriter; /* shall writer thread terminate? */
+ bool bDoTimedWait; /* instruct writer thread to do a times wait to support flush timeouts */
+ 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 */
+ pthread_cond_t notFull;
+ pthread_cond_t notEmpty;
+ pthread_cond_t isEmpty;
+ unsigned short iEnq; /* this MUST be unsigned as we use module arithmetic (else invalid indexing happens!) */
+ unsigned short iDeq; /* this MUST be unsigned as we use module arithmetic (else invalid indexing happens!) */
+ short iCnt; /* current nbr of elements in buffer */
+ struct {
+ uchar *pBuf;
+ size_t lenBuf;
+ } asyncBuf[STREAM_ASYNC_NUMBUFS];
+ pthread_t writerThreadID;
+ int apcRequested; /* is an apc Requested? */
+ /* 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);
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 a2d9c599..93995b38 100644
--- a/runtime/stringbuf.c
+++ b/runtime/stringbuf.c
@@ -70,7 +70,6 @@ rsRetVal cstrConstruct(cstr_t **ppThis)
pThis->pszBuf = NULL;
pThis->iBufSize = 0;
pThis->iStrLen = 0;
- pThis->iAllocIncrement = RS_STRINGBUF_ALLOC_INCREMENT;
*ppThis = pThis;
finalize_it:
@@ -153,24 +152,24 @@ void rsCStrDestruct(cstr_t **ppThis)
* rgerhards, 2008-01-07
* changed to utilized realloc() -- rgerhards, 2009-06-16
*/
-static rsRetVal
+rsRetVal
rsCStrExtendBuf(cstr_t *pThis, size_t iMinNeeded)
{
uchar *pNewBuf;
- size_t iNewSize;
+ unsigned short iNewSize;
DEFiRet;
/* first compute the new size needed */
- if(iMinNeeded > pThis->iAllocIncrement) {
- /* we allocate "n" iAllocIncrements. Usually, that should
+ if(iMinNeeded > RS_STRINGBUF_ALLOC_INCREMENT) {
+ /* we allocate "n" ALLOC_INCREMENTs. Usually, that should
* leave some room after the absolutely needed one. It also
* reduces memory fragmentation. Note that all of this are
* integer operations (very important to understand what is
* going on)! Parenthesis are for better readibility.
*/
- iNewSize = ((iMinNeeded / pThis->iAllocIncrement) + 1) * pThis->iAllocIncrement;
+ iNewSize = (iMinNeeded / RS_STRINGBUF_ALLOC_INCREMENT + 1) * RS_STRINGBUF_ALLOC_INCREMENT;
} else {
- iNewSize = pThis->iBufSize + pThis->iAllocIncrement;
+ iNewSize = pThis->iBufSize + RS_STRINGBUF_ALLOC_INCREMENT;
}
iNewSize += pThis->iBufSize; /* add current size */
@@ -224,7 +223,7 @@ rsRetVal rsCStrAppendStr(cstr_t *pThis, uchar* psz)
/* append the contents of one cstr_t object to another
* rgerhards, 2008-02-25
*/
-rsRetVal rsCStrAppendCStr(cstr_t *pThis, cstr_t *pstrAppend)
+rsRetVal cstrAppendCStr(cstr_t *pThis, cstr_t *pstrAppend)
{
return rsCStrAppendStrWithLen(pThis, pstrAppend->pBuf, pstrAppend->iStrLen);
}
@@ -245,58 +244,10 @@ finalize_it:
}
-rsRetVal rsCStrAppendChar(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;
-
- /* check if we need to invalidate an sz representation! */
- if(pThis->pszBuf != NULL) {
- free(pThis->pszBuf);
- pThis->pszBuf = NULL;
- }
-
-finalize_it:
- RETiRet;
-}
-
-
-/* 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
- * created (this is NOT an error!). Property iAllocIncrement is
- * not modified by this function.
+ * created (this is NOT an error!).
* rgerhards, 2005-10-18
*/
rsRetVal rsCStrSetSzStr(cstr_t *pThis, uchar *pszNew)
@@ -314,7 +265,6 @@ rsRetVal rsCStrSetSzStr(cstr_t *pThis, uchar *pszNew)
pThis->iStrLen = strlen((char*)pszNew);
pThis->iBufSize = pThis->iStrLen;
pThis->pszBuf = NULL;
- /* iAllocIncrement is NOT modified! */
/* now save the new value */
if((pThis->pBuf = (uchar*) malloc(sizeof(uchar) * pThis->iStrLen)) == NULL) {
@@ -392,25 +342,6 @@ 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
@@ -458,38 +389,6 @@ finalize_it:
}
-/* 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;
- rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
-
- assert(pThis->bIsFinalized == 0);
-
- 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;
-}
-
-
-void rsCStrSetAllocIncrement(cstr_t *pThis, int iNewIncrement)
-{
- rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
- assert(iNewIncrement > 0);
- pThis->iAllocIncrement = iNewIncrement;
-}
-
-
/* return the length of the current string
* 2005-09-09 rgerhards
* Please note: this is only a function in a debug build.
@@ -497,7 +396,7 @@ void rsCStrSetAllocIncrement(cstr_t *pThis, int iNewIncrement)
* This is due to performance reasons.
*/
#ifndef NDEBUG
-int rsCStrLen(cstr_t *pThis)
+int cstrLen(cstr_t *pThis)
{
rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
return(pThis->iStrLen);
@@ -548,6 +447,27 @@ rsRetVal rsCStrTrimTrailingWhiteSpace(cstr_t *pThis)
return RS_RET_OK;
}
+/* Trim trailing whitespace from a given string
+ */
+rsRetVal cstrTrimTrailingWhiteSpace(cstr_t *pThis)
+{
+ register int i;
+ register uchar *pC;
+ rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
+
+ i = pThis->iStrLen;
+ pC = pThis->pBuf + i - 1;
+ while(i > 0 && isspace((int)*pC)) {
+ --pC;
+ --i;
+ }
+ /* i now is the new string length! */
+ pThis->iStrLen = i;
+ pThis->pBuf[pThis->iStrLen] = '0'; /* we always have this space */
+
+ return RS_RET_OK;
+}
+
/* compare two string objects - works like strcmp(), but operates
* on CStr objects. Please note that this version here is
* faster in the majority of cases, simply because it can
diff --git a/runtime/stringbuf.h b/runtime/stringbuf.h
index d28aee26..c5130238 100644
--- a/runtime/stringbuf.h
+++ b/runtime/stringbuf.h
@@ -1,5 +1,5 @@
-/*! \file stringbuf.h
- * \brief The counted string object
+/* stringbuf.h
+ * The counted string object
*
* This is the byte-counted string class for rsyslog. It is a replacement
* for classical \0 terminated string functions. We introduce it in
@@ -11,8 +11,7 @@
* \date 2005-09-07
* Initial version begun.
*
- * All functions in this "class" start with rsCStr (rsyslog Counted String).
- * Copyright 2005
+ * Copyright 2005-2009
* Rainer Gerhards and Adiscon GmbH. All Rights Reserved.
*
* This file is part of the rsyslog runtime library.
@@ -36,6 +35,8 @@
#ifndef _STRINGBUF_H_INCLUDED__
#define _STRINGBUF_H_INCLUDED__ 1
+#include <assert.h>
+
/**
* The dynamic string buffer object.
*/
@@ -48,8 +49,6 @@ typedef struct cstr_s
uchar *pszBuf; /**< pointer to the sz version of the string (after it has been created )*/
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;
@@ -67,14 +66,86 @@ rsRetVal rsCStrConstructFromCStr(cstr_t **ppThis, cstr_t *pFrom);
void rsCStrDestruct(cstr_t **ppThis);
#define cstrDestruct(x) rsCStrDestruct((x))
-/**
- * Append a character to an existing string. If necessary, the
- * method expands the string buffer.
- *
- * \param c Character to append to string.
+
+/* Append a character to the current string object. This may only be done until
+ * cstrFinalize() is called.
+ * rgerhards, 2009-06-16
*/
-rsRetVal rsCStrAppendChar(cstr_t *pThis, uchar c);
-rsRetVal cstrAppendChar(cstr_t *pThis, uchar c);
+rsRetVal rsCStrExtendBuf(cstr_t *pThis, size_t iMinNeeded); /* our helper, NOT a public interface! */
+static inline rsRetVal cstrAppendChar(cstr_t *pThis, uchar c)
+{
+ rsRetVal iRet = RS_RET_OK;
+
+ 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:
+ return iRet;
+}
+
+
+/* some inline functions for things that are really frequently called... */
+
+/* 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
+ */
+static inline rsRetVal
+cstrFinalize(cstr_t *pThis)
+{
+ rsRetVal iRet = RS_RET_OK;
+
+ if(pThis->iStrLen > 0) {
+ /* terminate string only if one exists */
+ CHKiRet(cstrAppendChar(pThis, '\0'));
+ --pThis->iStrLen; /* do NOT count the \0 byte */
+ }
+
+finalize_it:
+ return iRet;
+}
+
+
+/* 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
+ */
+static inline uchar* cstrGetSzStr(cstr_t *pThis)
+{
+ rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
+ return(pThis->pBuf);
+}
+
+
+/* Converts the CStr object to a classical sz string and returns that.
+ * Same restrictions as in cstrGetSzStr() applies (see there!). This
+ * function here guarantees that a valid string is returned, even if
+ * the CStr object currently holds a NULL pointer string buffer. If so,
+ * "" is returned.
+ * rgerhards 2005-10-19
+ * WARNING: The returned pointer MUST NOT be freed, as it may be
+ * obtained from that constant memory pool (in case of NULL!)
+ */
+static inline uchar* cstrGetSzStrNoNULL(cstr_t *pThis)
+{
+ rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
+ if(pThis->pBuf == NULL)
+ return (uchar*) "";
+ else
+ return cstrGetSzStr(pThis);
+}
+
/**
* Truncate "n" number of characters from the end of the
@@ -85,6 +156,7 @@ rsRetVal cstrAppendChar(cstr_t *pThis, uchar c);
rsRetVal rsCStrTruncate(cstr_t *pThis, size_t nTrunc);
rsRetVal rsCStrTrimTrailingWhiteSpace(cstr_t *pThis);
+rsRetVal cstrTrimTrailingWhiteSpace(cstr_t *pThis);
/**
* Append a string to the buffer. For performance reasons,
@@ -102,22 +174,6 @@ rsRetVal rsCStrAppendStr(cstr_t *pThis, uchar* psz);
*/
rsRetVal rsCStrAppendStrWithLen(cstr_t *pThis, uchar* psz, size_t iStrLen);
-/**
- * Set a new allocation incremet. This will influence
- * the allocation the next time the string will be expanded.
- * It can be set and changed at any time. If done immediately
- * after custructing the StrB object, this will also be
- * the inital allocation.
- *
- * \param iNewIncrement The new increment size
- *
- * \note It is possible to use a very low increment, e.g. 1 byte.
- * This can generate a considerable overhead. We highly
- * advise not to use an increment below 32 bytes, except
- * if you are very well aware why you are doing it ;)
- */
-void rsCStrSetAllocIncrement(cstr_t *pThis, int iNewIncrement);
-#define rsCStrGetAllocIncrement(pThis) ((pThis)->iAllocIncrement)
/**
* Append an integer to the string. No special formatting is
@@ -143,19 +199,22 @@ rsRetVal rsCStrSzStrMatchRegex(cstr_t *pCS1, uchar *psz, int iType, void *cache)
void rsCStrRegexDestruct(void *rc);
rsRetVal rsCStrConvertToNumber(cstr_t *pStr, number_t *pNumber);
rsRetVal rsCStrConvertToBool(cstr_t *pStr, number_t *pBool);
-rsRetVal rsCStrAppendCStr(cstr_t *pThis, cstr_t *pstrAppend);
+
+/* in migration */
+#define rsCStrAppendCStr(pThis, pstrAppend) cstrAppendCStr(pThis, pstrAppend)
/* new calling interface */
rsRetVal cstrFinalize(cstr_t *pThis);
rsRetVal cstrConvSzStrAndDestruct(cstr_t *pThis, uchar **ppSz, int bRetNULL);
-uchar* cstrGetSzStr(cstr_t *pThis);
+rsRetVal cstrAppendCStr(cstr_t *pThis, cstr_t *pstrAppend);
/* now come inline-like functions */
#ifdef NDEBUG
-# define rsCStrLen(x) ((int)((x)->iStrLen))
+# define cstrLen(x) ((int)((x)->iStrLen))
#else
- int rsCStrLen(cstr_t *pThis);
+ int cstrLen(cstr_t *pThis);
#endif
+#define rsCStrLen(s) cstrLen((s))
#define rsCStrGetBufBeg(x) ((x)->pBuf)
diff --git a/runtime/syslogd-types.h b/runtime/syslogd-types.h
index be0dfdd8..161ee06f 100644
--- a/runtime/syslogd-types.h
+++ b/runtime/syslogd-types.h
@@ -56,7 +56,8 @@
* applications I do not yet envision. -- rgerhards, 2007-07-24
*/
typedef enum _syslogFeature {
- sFEATURERepeatedMsgReduction = 1
+ sFEATURERepeatedMsgReduction = 1,
+ sFEATURENonCancelInputTermination = 2
} syslogFeature;
/* we define our own facility and severities */
@@ -76,25 +77,31 @@ enum _EHostnameCmpMode {
};
typedef enum _EHostnameCmpMode EHostnameCmpMode;
+/* time type numerical values for structure below */
+#define TIME_TYPE_UNINIT 0
+#define TIME_TYPE_RFC3164 1
+#define TIME_TYPE_RFC5424 2
/* rgerhards 2004-11-11: the following structure represents
* a time as it is used in syslog.
+ * rgerhards, 2009-06-23: packed structure for better cache performance
+ * (but left ultimate decision about packing to compiler)
*/
struct syslogTime {
- int timeType; /* 0 - unitinialized , 1 - RFC 3164, 2 - syslog-protocol */
- int year;
- int month;
- int day;
- int hour; /* 24 hour clock */
- int minute;
- int second;
- int secfrac; /* fractional seconds (must be 32 bit!) */
- int secfracPrecision;
+ intTiny timeType; /* 0 - unitinialized , 1 - RFC 3164, 2 - syslog-protocol */
+ intTiny month;
+ intTiny day;
+ intTiny hour; /* 24 hour clock */
+ intTiny minute;
+ intTiny second;
+ intTiny secfracPrecision;
+ intTiny OffsetMinute; /* UTC offset in minutes */
+ intTiny OffsetHour; /* UTC offset in hours
+ * full UTC offset minutes = OffsetHours*60 + OffsetMinute. Then use
+ * OffsetMode to know the direction.
+ */
char OffsetMode; /* UTC offset + or - */
- char OffsetHour; /* UTC offset in hours */
- int OffsetMinute; /* UTC offset in minutes */
- /* full UTC offset minutes = OffsetHours*60 + OffsetMinute. Then use
- * OffsetMode to know the direction.
- */
+ short year;
+ int secfrac; /* fractional seconds (must be 32 bit!) */
};
typedef struct syslogTime syslogTime_t;
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 8cbf9e12..d7cd52d5 100644
--- a/runtime/vm.c
+++ b/runtime/vm.c
@@ -82,6 +82,7 @@ rsfrAddFunction(uchar *szName, prsf_t rsf)
/* unique name, so add to head of list */
CHKmalloc(pEntry = calloc(1, sizeof(rsf_entry_t)));
CHKiRet(rsCStrConstructFromszStr(&pEntry->pName, szName));
+ CHKiRet(cstrFinalize(pEntry->pName));
pEntry->rsf = rsf;
pEntry->pNext = funcRegRoot;
funcRegRoot = pEntry;
@@ -167,7 +168,7 @@ rsfrRemoveAll(void)
while(pEntry != NULL) {
pEntryDel = pEntry;
pEntry = pEntry->pNext;
- rsCStrDestruct(&pEntryDel->pName);
+ cstrDestruct(&pEntryDel->pName);
free(pEntryDel);
}
funcRegRoot = NULL;
@@ -405,6 +406,7 @@ CODESTARTop(STRADD)
vmstk.PopString(pThis->pStk, &operand1);
CHKiRet(rsCStrAppendCStr(operand1->val.pStr, operand2->val.pStr));
+ CHKiRet(cstrFinalize(operand1->val.pStr));
/* we have a result, so let's push it */
vmstk.Push(pThis->pStk, operand1);
@@ -554,12 +556,12 @@ rsf_tolower(vmstk_t *pStk, int numOperands)
ABORT_FINALIZE(RS_RET_INVLD_NBR_ARGUMENTS);
/* pop args and do operaton */
- CHKiRet(rsCStrConstruct(&pcstr));
+ CHKiRet(cstrConstruct(&pcstr));
vmstk.PopString(pStk, &operand1);
- pSrc = rsCStrGetSzStr(operand1->val.pStr);
- iStrlen = strlen((char*)pSrc);
+ pSrc = cstrGetSzStr(operand1->val.pStr);
+ iStrlen = strlen((char*)pSrc); // TODO: use count from string!
while(iStrlen--) {
- CHKiRet(rsCStrAppendChar(pcstr, tolower(*pSrc++)));
+ CHKiRet(cstrAppendChar(pcstr, tolower(*pSrc++)));
}
/* Store result and cleanup */
diff --git a/runtime/vmop.c b/runtime/vmop.c
index acacfc9e..ea627220 100644
--- a/runtime/vmop.c
+++ b/runtime/vmop.c
@@ -125,7 +125,7 @@ Obj2Str(vmop_t *pThis, cstr_t *pstrPrg)
if(pThis->operand.pVar != NULL)
CHKiRet(var.Obj2Str(pThis->operand.pVar, pstrPrg));
}
- CHKiRet(rsCStrAppendChar(pstrPrg, '\n'));
+ CHKiRet(cstrAppendChar(pstrPrg, '\n'));
finalize_it:
RETiRet;
diff --git a/runtime/wti.c b/runtime/wti.c
index 544bffa7..53b695b0 100644
--- a/runtime/wti.c
+++ b/runtime/wti.c
@@ -39,10 +39,10 @@
#include <pthread.h>
#include <errno.h>
-#ifdef OS_SOLARIS
-# include <sched.h>
-# define pthread_yield() sched_yield()
-#endif
+/// TODO: check on solaris if this is any longer needed - I don't think so - rgerhards, 2009-09-20
+//#ifdef OS_SOLARIS
+//# include <sched.h>
+//#endif
#include "rsyslog.h"
#include "stringbuf.h"
@@ -51,6 +51,7 @@
#include "wti.h"
#include "obj.h"
#include "glbl.h"
+#include "atomic.h"
/* static data */
DEFobjStaticHelpers
@@ -75,85 +76,50 @@ wtiGetDbgHdr(wti_t *pThis)
}
-/* get the current worker state. For simplicity and speed, we have
- * NOT used our regular calling interface this time. I hope that won't
- * bite in the long term... -- rgerhards, 2008-01-17
- * TODO: may be performance optimized by atomic operations
+/* return the current worker processing state. For the sake of
+ * simplicity, we do not use the iRet interface. -- rgerhards, 2009-07-17
*/
-qWrkCmd_t
-wtiGetState(wti_t *pThis, int bLockMutex)
+bool
+wtiGetState(wti_t *pThis)
{
- DEFVARS_mutexProtection;
- qWrkCmd_t tCmd;
-
- BEGINfunc
- ISOBJ_TYPE_assert(pThis, wti);
-
- BEGIN_MTX_PROTECTED_OPERATIONS(&pThis->mut, bLockMutex);
- tCmd = pThis->tCurrCmd;
- END_MTX_PROTECTED_OPERATIONS(&pThis->mut);
-
- ENDfunc
- return tCmd;
+ return ATOMIC_FETCH_32BIT(pThis->bIsRunning);
}
-/* send a command to a specific thread
- * bActiveOnly specifies if the command should be sent only when the worker is
- * in an active state. -- rgerhards, 2008-01-20
+/* Set this thread to "always running" state (can not be unset)
+ * rgerhards, 2009-07-20
*/
rsRetVal
-wtiSetState(wti_t *pThis, qWrkCmd_t tCmd, int bActiveOnly, int bLockMutex)
+wtiSetAlwaysRunning(wti_t *pThis)
{
- DEFiRet;
- DEFVARS_mutexProtection;
-
ISOBJ_TYPE_assert(pThis, wti);
- assert(tCmd <= eWRKTHRD_SHUTDOWN_IMMEDIATE);
-
- BEGIN_MTX_PROTECTED_OPERATIONS(&pThis->mut, bLockMutex);
-
- /* all worker states must be followed sequentially, only termination can be set in any state */
- if( (bActiveOnly && (pThis->tCurrCmd < eWRKTHRD_RUN_CREATED))
- || (pThis->tCurrCmd > tCmd && !(tCmd == eWRKTHRD_TERMINATING || tCmd == eWRKTHRD_STOPPED))) {
- dbgprintf("%s: command %d can not be accepted in current %d processing state - ignored\n",
- wtiGetDbgHdr(pThis), tCmd, pThis->tCurrCmd);
- } else {
- dbgprintf("%s: receiving command %d\n", wtiGetDbgHdr(pThis), tCmd);
- /* we could replace this with a simple if, but we leave the switch in in case we need
- * to add something at a later stage. -- rgerhards, 2008-09-30
- */
- switch(tCmd) {
- case eWRKTHRD_TERMINATING:
- /* TODO: re-enable meaningful debug msg! (via function callback?)
- dbgprintf("%s: thread terminating with %d entries left in queue, %d workers running.\n",
- wtiGetDbgHdr(pThis->pQueue), pThis->pQueue->iQueueSize,
- pThis->pQueue->iCurNumWrkThrd);
- */
- pthread_cond_signal(&pThis->condExitDone);
- dbgprintf("%s: worker terminating\n", wtiGetDbgHdr(pThis));
- break;
- /* these cases just to satisfy the compiler, we do (yet) not act an them: */
- case eWRKTHRD_RUNNING:
- case eWRKTHRD_STOPPED:
- case eWRKTHRD_RUN_CREATED:
- case eWRKTHRD_RUN_INIT:
- case eWRKTHRD_SHUTDOWN:
- case eWRKTHRD_SHUTDOWN_IMMEDIATE:
- /* DO NOTHING */
- break;
- }
- pThis->tCurrCmd = tCmd; /* apply the new state */
- }
+ pThis->bAlwaysRunning = TRUE;
+ return RS_RET_OK;
+}
- END_MTX_PROTECTED_OPERATIONS(&pThis->mut);
- RETiRet;
+/* Set status (thread is running or not), actually an property of
+ * use for wtp, but we need to have it per thread instance (thus it
+ * is inside wti). -- rgerhards, 2009-07-17
+ */
+rsRetVal
+wtiSetState(wti_t *pThis, bool bNewVal)
+{
+ ISOBJ_TYPE_assert(pThis, wti);
+ if(bNewVal)
+ ATOMIC_STORE_1_TO_INT(pThis->bIsRunning);
+ else
+ ATOMIC_STORE_0_TO_INT(pThis->bIsRunning);
+ return RS_RET_OK;
}
-/* Cancel the thread. If the thread is already cancelled or termination,
- * we do not again cancel it. But it is save and legal to call wtiCancelThrd() in
- * such situations.
+/* Cancel the thread. If the thread is not running. But it is save and legal to
+ * call wtiCancelThrd() in such situations. This function only returns when the
+ * thread has terminated. Else we may get race conditions all over the code...
+ * Note that when waiting for the thread to terminate, we do a busy wait, checking
+ * progress every 10ms. It is very unlikely that we will ever cancel a thread
+ * and, if so, it will only happen at the end of the rsyslog run. So doing this
+ * kind of not optimal wait is considered preferable over using condition variables.
* rgerhards, 2008-02-26
*/
rsRetVal
@@ -163,17 +129,16 @@ wtiCancelThrd(wti_t *pThis)
ISOBJ_TYPE_assert(pThis, wti);
- d_pthread_mutex_lock(&pThis->mut);
-
- if(pThis->tCurrCmd >= eWRKTHRD_TERMINATING) {
+ if(wtiGetState(pThis)) {
dbgoprint((obj_t*) pThis, "canceling worker thread\n");
pthread_cancel(pThis->thrdID);
- wtiSetState(pThis, eWRKTHRD_TERMINATING, 0, MUTEX_ALREADY_LOCKED);
- pThis->pWtp->bThrdStateChanged = 1; /* indicate change, so harverster will be called */
+ /* now wait until the thread terminates... */
+ while(wtiGetState(pThis)) {
+//fprintf(stderr, "sleep loop for getState\n");
+ srSleep(0, 10000);
+ }
}
- d_pthread_mutex_unlock(&pThis->mut);
-
RETiRet;
}
@@ -181,37 +146,15 @@ wtiCancelThrd(wti_t *pThis)
/* Destructor */
BEGINobjDestruct(wti) /* be sure to specify the object type also in END and CODESTART macros! */
CODESTARTobjDestruct(wti)
- /* if we reach this point, we must make sure the associated worker has terminated. It is
- * the callers duty to make sure the worker already knows it shall terminate.
- * TODO: is it *really* the caller's duty? ...mmmhhhh.... smells bad... rgerhards, 2008-01-25
- */
- wtiProcessThrdChanges(pThis, LOCK_MUTEX); /* process state change one last time */
-
- d_pthread_mutex_lock(&pThis->mut);
- if(wtiGetState(pThis, MUTEX_ALREADY_LOCKED) != eWRKTHRD_STOPPED) {
- dbgprintf("%s: WARNING: worker %p shall be destructed but is still running (might be OK) - joining it\n",
- wtiGetDbgHdr(pThis), pThis);
- /* let's hope the caller actually instructed it to shutdown... */
- pthread_cond_wait(&pThis->condExitDone, &pThis->mut);
- wtiJoinThrd(pThis);
- }
- d_pthread_mutex_unlock(&pThis->mut);
-
/* actual destruction */
- pthread_cond_destroy(&pThis->condExitDone);
- pthread_mutex_destroy(&pThis->mut);
-
- if(pThis->pszDbgHdr != NULL)
- free(pThis->pszDbgHdr);
+ free(pThis->batch.pElem);
+ free(pThis->pszDbgHdr);
ENDobjDestruct(wti)
/* Standard-Constructor for the wti object
*/
BEGINobjConstruct(wti) /* be sure to specify the object type also in END macro! */
- pThis->bOptimizeUniProc = glbl.GetOptimizeUniProc();
- pthread_cond_init(&pThis->condExitDone, NULL);
- pthread_mutex_init(&pThis->mut, NULL);
ENDobjConstruct(wti)
@@ -222,81 +165,28 @@ rsRetVal
wtiConstructFinalize(wti_t *pThis)
{
DEFiRet;
+ int iDeqBatchSize;
ISOBJ_TYPE_assert(pThis, wti);
dbgprintf("%s: finalizing construction of worker instance data\n", wtiGetDbgHdr(pThis));
- /* initialize our thread instance descriptor */
- pThis->pUsrp = NULL;
- pThis->tCurrCmd = eWRKTHRD_STOPPED;
-
- RETiRet;
-}
-
-
-/* join a specific worker thread
- * we do not lock the mutex, because join will sync anyways...
- */
-rsRetVal
-wtiJoinThrd(wti_t *pThis)
-{
- DEFiRet;
+ /* initialize our thread instance descriptor (no concurrency here) */
+ pThis->bIsRunning = FALSE;
- ISOBJ_TYPE_assert(pThis, wti);
- dbgprintf("waiting for worker %s termination, current state %d\n", wtiGetDbgHdr(pThis), pThis->tCurrCmd);
- if (pThis->thrdID == 0) {
- dbgprintf("worker %s was already stopped\n", wtiGetDbgHdr(pThis));
- } else {
- pthread_join(pThis->thrdID, NULL);
- wtiSetState(pThis, eWRKTHRD_STOPPED, 0, MUTEX_ALREADY_LOCKED); /* back to virgin... */
- pThis->thrdID = 0; /* invalidate the thread ID so that we do not accidently find reused ones */
- dbgprintf("worker %s has stopped\n", wtiGetDbgHdr(pThis));
- }
-
- RETiRet;
-}
-
-/* check if we had a worker thread changes and, if so, act
- * on it. At a minimum, terminated threads are harvested (joined).
- */
-rsRetVal
-wtiProcessThrdChanges(wti_t *pThis, int bLockMutex)
-{
- DEFiRet;
- DEFVARS_mutexProtection;
-
- ISOBJ_TYPE_assert(pThis, wti);
-
- BEGIN_MTX_PROTECTED_OPERATIONS(&pThis->mut, bLockMutex);
- switch(pThis->tCurrCmd) {
- case eWRKTHRD_TERMINATING:
- /* we need to at least temporarily release the mutex, because otherwise
- * we may deadlock with the thread we intend to join (it aquires the mutex
- * during termination processing). -- rgerhards, 2008-02-26
- */
- END_MTX_PROTECTED_OPERATIONS(&pThis->mut);
- iRet = wtiJoinThrd(pThis);
- BEGIN_MTX_PROTECTED_OPERATIONS(&pThis->mut, bLockMutex);
- break;
- /* these cases just to satisfy the compiler, we do not act an them: */
- case eWRKTHRD_STOPPED:
- case eWRKTHRD_RUN_CREATED:
- case eWRKTHRD_RUN_INIT:
- case eWRKTHRD_RUNNING:
- case eWRKTHRD_SHUTDOWN:
- case eWRKTHRD_SHUTDOWN_IMMEDIATE:
- /* DO NOTHING */
- break;
- }
- END_MTX_PROTECTED_OPERATIONS(&pThis->mut);
+ /* we now alloc the array for user pointers. We obtain the max from the queue itself. */
+ CHKiRet(pThis->pWtp->pfGetDeqBatchSize(pThis->pWtp->pUsr, &iDeqBatchSize));
+ CHKmalloc(pThis->batch.pElem = calloc((size_t)iDeqBatchSize, sizeof(batch_obj_t)));
+finalize_it:
RETiRet;
}
/* cancellation cleanup handler for queueWorker ()
* Updates admin structure and frees ressources.
+ * Keep in mind that cancellation is disabled if we run into
+ * the cancel cleanup handler (and have been cancelled).
* rgerhards, 2008-01-16
*/
static void
@@ -304,7 +194,6 @@ wtiWorkerCancelCleanup(void *arg)
{
wti_t *pThis = (wti_t*) arg;
wtp_t *pWtp;
- int iCancelStateSave;
BEGINfunc
ISOBJ_TYPE_assert(pThis, wti);
@@ -313,129 +202,115 @@ wtiWorkerCancelCleanup(void *arg)
DBGPRINTF("%s: cancelation cleanup handler called.\n", wtiGetDbgHdr(pThis));
- /* call user supplied handler (that one e.g. requeues the element) */
- pWtp->pfOnWorkerCancel(pThis->pWtp->pUsr, pThis->pUsrp);
+ /* call user supplied handler */
+ pWtp->pfOnWorkerCancel(pThis->pWtp->pUsr, pThis->batch.pElem[0].pUsrp);
- pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave);
- d_pthread_mutex_lock(&pWtp->mut);
- wtiSetState(pThis, eWRKTHRD_TERMINATING, 0, MUTEX_ALREADY_LOCKED);
- /* TODO: sync access? I currently think it is NOT needed -- rgerhards, 2008-01-28 */
- pWtp->bThrdStateChanged = 1; /* indicate change, so harverster will be called */
+ ENDfunc
+}
- d_pthread_mutex_unlock(&pWtp->mut);
- pthread_setcancelstate(iCancelStateSave, NULL);
+
+/* wait for queue to become non-empty or timeout
+ * helper to wtiWorker. Note the the predicate is
+ * re-tested by the caller, so it is OK to NOT do it here.
+ * rgerhards, 2009-05-20
+ */
+static inline void
+doIdleProcessing(wti_t *pThis, wtp_t *pWtp, int *pbInactivityTOOccured)
+{
+ struct timespec t;
+
+ BEGINfunc
+ DBGPRINTF("%s: worker IDLE, waiting for work.\n", wtiGetDbgHdr(pThis));
+
+ pWtp->pfOnIdle(pWtp->pUsr, MUTEX_ALREADY_LOCKED);
+
+ if(pThis->bAlwaysRunning) {
+ /* never shut down any started worker */
+dbgprintf("YYY/ZZZ: wti Idle wait cond busy, mutex %p\n", pWtp->pmutUsr);
+ d_pthread_cond_wait(pWtp->pcondBusy, pWtp->pmutUsr);
+ } else {
+ timeoutComp(&t, pWtp->toWrkShutdown);/* get absolute timeout */
+ if(d_pthread_cond_timedwait(pWtp->pcondBusy, pWtp->pmutUsr, &t) != 0) {
+ DBGPRINTF("%s: inactivity timeout, worker terminating...\n", wtiGetDbgHdr(pThis));
+ *pbInactivityTOOccured = 1; /* indicate we had a timeout */
+ }
+ }
ENDfunc
}
/* generic worker thread framework
- *
- * Some special comments below, so that they do not clutter the main function code:
- *
- * On the use of pthread_testcancel():
- * Now make sure we can get canceled - it is not specified if pthread_setcancelstate() is
- * a cancellation point in itself. As we run most of the time without cancel enabled, I fear
- * we may never get cancelled if we do not create a cancellation point ourselfs.
- *
- * On the use of pthread_yield():
- * We yield to give the other threads a chance to obtain the mutex. If we do not
- * do that, this thread may very well aquire the mutex again before another thread
- * has even a chance to run. The reason is that mutex operations are free to be
- * implemented in the quickest possible way (and they typically are!). That is, the
- * mutex lock/unlock most probably just does an atomic memory swap and does not necessarily
- * schedule other threads waiting on the same mutex. That can lead to the same thread
- * aquiring the mutex ever and ever again while all others are starving for it. We
- * have exactly seen this behaviour when we deliberately introduced a long-running
- * test action which basically did a sleep. I understand that with real actions the
- * likelihood of this starvation condition is very low - but it could still happen
- * and would be very hard to debug. The yield() is a sure fix, its performance overhead
- * should be well accepted given the above facts. -- rgerhards, 2008-01-10
*/
#pragma GCC diagnostic ignored "-Wempty-body"
rsRetVal
wtiWorker(wti_t *pThis)
{
- DEFiRet;
- DEFVARS_mutexProtection;
- struct timespec t;
wtp_t *pWtp; /* our worker thread pool */
int bInactivityTOOccured = 0;
+ rsRetVal localRet;
+ rsRetVal terminateRet;
+ int iCancelStateSave;
+ DEFiRet;
ISOBJ_TYPE_assert(pThis, wti);
pWtp = pThis->pWtp; /* shortcut */
ISOBJ_TYPE_assert(pWtp, wtp);
dbgSetThrdName(pThis->pszDbgHdr);
- pThis->pUsrp = NULL;
pthread_cleanup_push(wtiWorkerCancelCleanup, pThis);
- BEGIN_MTX_PROTECTED_OPERATIONS(pWtp->pmutUsr, LOCK_MUTEX);
pWtp->pfOnWorkerStartup(pWtp->pUsr);
- END_MTX_PROTECTED_OPERATIONS(pWtp->pmutUsr);
/* now we have our identity, on to real processing */
while(1) { /* loop will be broken below - need to do mutex locks */
- /* process any pending thread requests */
- wtpProcessThrdChanges(pWtp);
- pthread_testcancel(); /* see big comment in function header */
-# if !defined(__hpux) /* pthread_yield is missing there! */
- if(pThis->bOptimizeUniProc)
- pthread_yield(); /* see big comment in function header */
-# endif
-
- /* if we have a rate-limiter set for this worker pool, let's call it. Please
- * keep in mind that the rate-limiter may hold us for an extended period
- * of time. -- rgerhards, 2008-04-02
- */
- if(pWtp->pfRateLimiter != NULL) {
+ if(pWtp->pfRateLimiter != NULL) { /* call rate-limiter, if defined */
pWtp->pfRateLimiter(pWtp->pUsr);
}
- wtpSetInactivityGuard(pThis->pWtp, 0, LOCK_MUTEX); /* must be set before usr mutex is locked! */
- BEGIN_MTX_PROTECTED_OPERATIONS(pWtp->pmutUsr, LOCK_MUTEX);
-
- if( (bInactivityTOOccured && pWtp->pfIsIdle(pWtp->pUsr, MUTEX_ALREADY_LOCKED))
- || wtpChkStopWrkr(pWtp, LOCK_MUTEX, MUTEX_ALREADY_LOCKED)) {
- END_MTX_PROTECTED_OPERATIONS(pWtp->pmutUsr);
- break; /* end worker thread run */
+dbgprintf("YYY/ZZZ: pre lock mutex\n");
+ d_pthread_mutex_lock(pWtp->pmutUsr);
+
+dbgprintf("YYY/ZZZ: wti locks mutex %p\n", pWtp->pmutUsr);
+ /* first check if we are in shutdown process (but evaluate a bit later) */
+ terminateRet = wtpChkStopWrkr(pWtp, MUTEX_ALREADY_LOCKED);
+ if(terminateRet == RS_RET_TERMINATE_NOW) {
+ /* we now need to free the old batch */
+ localRet = pWtp->pfObjProcessed(pWtp->pUsr, pThis);
+ dbgoprint((obj_t*) pThis, "terminating worker because of TERMINATE_NOW mode, del iRet %d\n",
+ localRet);
+ d_pthread_mutex_unlock(pWtp->pmutUsr);
+ break;
}
- bInactivityTOOccured = 0; /* reset for next run */
- /* if we reach this point, we are still protected by the mutex */
-
- if(pWtp->pfIsIdle(pWtp->pUsr, MUTEX_ALREADY_LOCKED)) {
- DBGPRINTF("%s: worker IDLE, waiting for work.\n", wtiGetDbgHdr(pThis));
- pWtp->pfOnIdle(pWtp->pUsr, MUTEX_ALREADY_LOCKED);
-
- if(pWtp->toWrkShutdown == -1) {
- /* never shut down any started worker */
- d_pthread_cond_wait(pWtp->pcondBusy, pWtp->pmutUsr);
- } else {
- timeoutComp(&t, pWtp->toWrkShutdown);/* get absolute timeout */
- if(d_pthread_cond_timedwait(pWtp->pcondBusy, pWtp->pmutUsr, &t) != 0) {
- DBGPRINTF("%s: inactivity timeout, worker terminating...\n", wtiGetDbgHdr(pThis));
- bInactivityTOOccured = 1; /* indicate we had a timeout */
- }
+ /* try to execute and process whatever we have */
+ /* Note that this function releases and re-aquires the mutex. The returned
+ * information on idle state must be processed before releasing the mutex again.
+ */
+ localRet = pWtp->pfDoWork(pWtp->pUsr, pThis);
+
+dbgprintf("YYY/ZZZ: wti loop locked mutex %p again\n", pWtp->pmutUsr);
+ if(localRet == RS_RET_IDLE) {
+ if(terminateRet == RS_RET_TERMINATE_WHEN_IDLE || bInactivityTOOccured) {
+ d_pthread_mutex_unlock(pWtp->pmutUsr);
+ break; /* end of loop */
}
- END_MTX_PROTECTED_OPERATIONS(pWtp->pmutUsr);
+ doIdleProcessing(pThis, pWtp, &bInactivityTOOccured);
+ d_pthread_mutex_unlock(pWtp->pmutUsr);
continue; /* request next iteration */
}
- /* if we reach this point, we have a non-empty queue (and are still protected by mutex) */
- pWtp->pfDoWork(pWtp->pUsr, pThis, iCancelStateSave);
+ d_pthread_mutex_unlock(pWtp->pmutUsr);
+
+ bInactivityTOOccured = 0; /* reset for next run */
}
/* indicate termination */
+ d_pthread_mutex_lock(pWtp->pmutUsr);
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave);
- d_pthread_mutex_lock(&pThis->mut);
pthread_cleanup_pop(0); /* remove cleanup handler */
-
pWtp->pfOnWorkerShutdown(pWtp->pUsr);
-
- wtiSetState(pThis, eWRKTHRD_TERMINATING, 0, MUTEX_ALREADY_LOCKED);
- pWtp->bThrdStateChanged = 1; /* indicate change, so harverster will be called */
- d_pthread_mutex_unlock(&pThis->mut);
pthread_setcancelstate(iCancelStateSave, NULL);
+ d_pthread_mutex_unlock(pWtp->pmutUsr);
RETiRet;
}
@@ -463,7 +338,6 @@ wtiSetDbgHdr(wti_t *pThis, uchar *pszMsg, size_t lenMsg)
if(pThis->pszDbgHdr != NULL) {
free(pThis->pszDbgHdr);
- pThis->pszDbgHdr = NULL;
}
if((pThis->pszDbgHdr = malloc(sizeof(uchar) * lenMsg + 1)) == NULL)
@@ -497,6 +371,5 @@ BEGINObjClassInit(wti, 1, OBJ_IS_CORE_MODULE) /* one is the object version (most
CHKiRet(objUse(glbl, CORE_COMPONENT));
ENDObjClassInit(wti)
-/*
- * vi:set ai:
+/* vi:set ai:
*/
diff --git a/runtime/wti.h b/runtime/wti.h
index 6b60b833..f466a053 100644
--- a/runtime/wti.h
+++ b/runtime/wti.h
@@ -1,6 +1,6 @@
/* Definition of the worker thread instance (wti) class.
*
- * Copyright 2008 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2008, 2009 by Rainer Gerhards and Adiscon GmbH.
*
* This file is part of the rsyslog runtime library.
*
@@ -27,22 +27,19 @@
#include <pthread.h>
#include "wtp.h"
#include "obj.h"
+#include "batch.h"
+
/* the worker thread instance class */
-typedef struct wti_s {
+struct wti_s {
BEGINobjInstance;
- int bOptimizeUniProc; /* cache for the equally-named global setting, pulled at time of queue creation */
- pthread_t thrdID; /* thread ID */
- qWrkCmd_t tCurrCmd; /* current command to be carried out by worker */
- obj_t *pUsrp; /* pointer to an object meaningful for current user pointer (e.g. queue pUsr data elemt) */
+ pthread_t thrdID; /* thread ID */
+ int bIsRunning; /* is this thread currently running? (must be int for atomic op!) */
+ bool bAlwaysRunning; /* should this thread always run? */
wtp_t *pWtp; /* my worker thread pool (important if only the work thread instance is passed! */
- pthread_cond_t condExitDone; /* signaled when the thread exit is done (once per thread existance) */
- pthread_mutex_t mut;
- int bShutdownRqtd; /* shutdown for this thread requested? 0 - no , 1 - yes */
+ batch_t batch; /* pointer to an object array meaningful for current user pointer (e.g. queue pUsr data elemt) */
uchar *pszDbgHdr; /* header string for debug messages */
-} wti_t;
-
-/* some symbolic constants for easier reference */
+};
/* prototypes */
@@ -50,12 +47,11 @@ rsRetVal wtiConstruct(wti_t **ppThis);
rsRetVal wtiConstructFinalize(wti_t *pThis);
rsRetVal wtiDestruct(wti_t **ppThis);
rsRetVal wtiWorker(wti_t *pThis);
-rsRetVal wtiProcessThrdChanges(wti_t *pThis, int bLockMutex);
rsRetVal wtiSetDbgHdr(wti_t *pThis, uchar *pszMsg, size_t lenMsg);
-rsRetVal wtiSetState(wti_t *pThis, qWrkCmd_t tCmd, int bActiveOnly, int bLockMutex);
-rsRetVal wtiJoinThrd(wti_t *pThis);
rsRetVal wtiCancelThrd(wti_t *pThis);
-qWrkCmd_t wtiGetState(wti_t *pThis, int bLockMutex);
+rsRetVal wtiSetAlwaysRunning(wti_t *pThis);
+rsRetVal wtiSetState(wti_t *pThis, bool bNew);
+bool wtiGetState(wti_t *pThis);
PROTOTYPEObjClassInit(wti);
PROTOTYPEpropSetMeth(wti, pszDbgHdr, uchar*);
PROTOTYPEpropSetMeth(wti, pWtp, wtp_t*);
diff --git a/runtime/wtp.c b/runtime/wtp.c
index 747ddb4a..47b99fe8 100644
--- a/runtime/wtp.c
+++ b/runtime/wtp.c
@@ -8,7 +8,7 @@
* (and in the web doc set on http://www.rsyslog.com/doc). Be sure to read it
* if you are getting aquainted to the object.
*
- * 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,18 +39,23 @@
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
-
-#ifdef OS_SOLARIS
-# include <sched.h>
-# define pthread_yield() sched_yield()
+#include <atomic.h>
+#if HAVE_SYS_PRCTL_H
+# include <sys/prctl.h>
#endif
+/// TODO: check on solaris if this is any longer needed - I don't think so - rgerhards, 2009-09-20
+//#ifdef OS_SOLARIS
+//# include <sched.h>
+//#endif
+
#include "rsyslog.h"
#include "stringbuf.h"
#include "srUtils.h"
#include "wtp.h"
#include "wti.h"
#include "obj.h"
+#include "unicode-helper.h"
#include "glbl.h"
/* static data */
@@ -78,18 +83,20 @@ wtpGetDbgHdr(wtp_t *pThis)
/* Not implemented dummy function for constructor */
-static rsRetVal NotImplementedDummy() { return RS_RET_OK; }
+static rsRetVal NotImplementedDummy() { return RS_RET_NOT_IMPLEMENTED; }
/* Standard-Constructor for the wtp object
*/
BEGINobjConstruct(wtp) /* be sure to specify the object type also in END macro! */
- pThis->bOptimizeUniProc = glbl.GetOptimizeUniProc();
- pthread_mutex_init(&pThis->mut, NULL);
- pthread_mutex_init(&pThis->mutThrdShutdwn, NULL);
+ pthread_mutex_init(&pThis->mutWtp, NULL);
pthread_cond_init(&pThis->condThrdTrm, NULL);
+ pthread_attr_init(&pThis->attrThrd);
+ pthread_attr_setdetachstate(&pThis->attrThrd, PTHREAD_CREATE_DETACHED);
/* set all function pointers to "not implemented" dummy so that we can safely call them */
pThis->pfChkStopWrkr = NotImplementedDummy;
+ pThis->pfGetDeqBatchSize = NotImplementedDummy;
pThis->pfIsIdle = NotImplementedDummy;
pThis->pfDoWork = NotImplementedDummy;
+ pThis->pfObjProcessed = NotImplementedDummy;
pThis->pfOnIdle = NotImplementedDummy;
pThis->pfOnWorkerCancel = NotImplementedDummy;
pThis->pfOnWorkerStartup = NotImplementedDummy;
@@ -111,13 +118,13 @@ wtpConstructFinalize(wtp_t *pThis)
ISOBJ_TYPE_assert(pThis, wtp);
- dbgprintf("%s: finalizing construction of worker thread pool\n", wtpGetDbgHdr(pThis));
+ DBGPRINTF("%s: finalizing construction of worker thread pool\n", wtpGetDbgHdr(pThis));
/* alloc and construct workers - this can only be done in finalizer as we previously do
* not know the max number of workers
*/
if((pThis->pWrkr = malloc(sizeof(wti_t*) * pThis->iNumWorkerThreads)) == NULL)
ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
-
+
for(i = 0 ; i < pThis->iNumWorkerThreads ; ++i) {
CHKiRet(wtiConstruct(&pThis->pWrkr[i]));
pWti = pThis->pWrkr[i];
@@ -137,8 +144,6 @@ finalize_it:
BEGINobjDestruct(wtp) /* be sure to specify the object type also in END and CODESTART macros! */
int i;
CODESTARTobjDestruct(wtp)
- wtpProcessThrdChanges(pThis); /* process thread changes one last time */
-
/* destruct workers */
for(i = 0 ; i < pThis->iNumWorkerThreads ; ++i)
wtiDestruct(&pThis->pWrkr[i]);
@@ -148,28 +153,13 @@ CODESTARTobjDestruct(wtp)
/* actual destruction */
pthread_cond_destroy(&pThis->condThrdTrm);
- pthread_mutex_destroy(&pThis->mut);
- pthread_mutex_destroy(&pThis->mutThrdShutdwn);
+ pthread_mutex_destroy(&pThis->mutWtp);
+ pthread_attr_destroy(&pThis->attrThrd);
- if(pThis->pszDbgHdr != NULL)
- free(pThis->pszDbgHdr);
+ free(pThis->pszDbgHdr);
ENDobjDestruct(wtp)
-/* wake up at least one worker thread.
- * rgerhards, 2008-01-20
- */
-rsRetVal
-wtpWakeupWrkr(wtp_t *pThis)
-{
- DEFiRet;
-
- /* TODO; mutex? I think not needed, as we do not need predictable exec order -- rgerhards, 2008-01-28 */
- ISOBJ_TYPE_assert(pThis, wtp);
- pthread_cond_signal(pThis->pcondBusy);
- RETiRet;
-}
-
/* wake up all worker threads.
* rgerhards, 2008-01-16
*/
@@ -184,99 +174,61 @@ wtpWakeupAllWrkr(wtp_t *pThis)
}
-/* check if we had any worker thread changes and, if so, act
- * on them. At a minimum, terminated threads are harvested (joined).
- * This function MUST NEVER block on the queue mutex!
- */
-rsRetVal
-wtpProcessThrdChanges(wtp_t *pThis)
-{
- DEFiRet;
- int i;
-
- ISOBJ_TYPE_assert(pThis, wtp);
-
- if(pThis->bThrdStateChanged == 0)
- FINALIZE;
-
- if(d_pthread_mutex_trylock(&(pThis->mutThrdShutdwn)) != 0) {
- /* another thread is already in the loop */
- FINALIZE;
- }
-
- /* Note: there is a left-over potential race condition below:
- * pThis->bThrdStateChanged may be re-set by another thread while
- * we work on it and thus the loop may terminate too early. However,
- * there are no really bad effects from that so I perfer - for this
- * version - to live with the problem as is. Not a good idea to
- * introduce that large change into the stable branch without very
- * good reason. -- rgerhards, 2009-04-02
- */
- do {
- /* reset the change marker */
- pThis->bThrdStateChanged = 0;
- /* go through all threads */
- for(i = 0 ; i < pThis->iNumWorkerThreads ; ++i) {
- wtiProcessThrdChanges(pThis->pWrkr[i], LOCK_MUTEX);
- }
- /* restart if another change occured while we were processing the changes */
- } while(pThis->bThrdStateChanged != 0);
-
- d_pthread_mutex_unlock(&(pThis->mutThrdShutdwn));
-
-finalize_it:
- RETiRet;
-}
-
-
-/* Sent a specific state for the worker thread pool.
- * rgerhards, 2008-01-21
+/* Sent a specific state for the worker thread pool. -- rgerhards, 2008-01-21
+ * We do not need to do atomic instructions as set operations are only
+ * called when terminating the pool, and then in strict sequence. So we
+ * can never overwrite each other. On the other hand, it also doesn't
+ * matter if the read operation obtains an older value, as we then simply
+ * do one more iteration, what is perfectly legal (during shutdown
+ * they are awoken in any case). -- rgerhards, 2009-07-20
*/
rsRetVal
wtpSetState(wtp_t *pThis, wtpState_t iNewState)
{
- DEFiRet;
-
ISOBJ_TYPE_assert(pThis, wtp);
pThis->wtpState = iNewState;
- /* TODO: must wakeup workers? seen to be not needed -- rgerhards, 2008-01-28 */
-
- RETiRet;
+ return RS_RET_OK;
}
/* check if the worker shall shutdown (1 = yes, 0 = no)
- * TODO: check if we can use atomic operations to enhance performance
* Note: there may be two mutexes locked, the bLockUsrMutex is the one in our "user"
* (e.g. the queue clas)
* rgerhards, 2008-01-21
*/
rsRetVal
-wtpChkStopWrkr(wtp_t *pThis, int bLockMutex, int bLockUsrMutex)
+wtpChkStopWrkr(wtp_t *pThis, int bLockUsrMutex)
{
DEFiRet;
- DEFVARS_mutexProtection;
+ wtpState_t wtpState;
ISOBJ_TYPE_assert(pThis, wtp);
+ /* we need a consistent value, but it doesn't really matter if it is changed
+ * right after the fetch - then we simply do one more iteration in the worker
+ */
+ wtpState = ATOMIC_FETCH_32BIT(pThis->wtpState);
- BEGIN_MTX_PROTECTED_OPERATIONS(&pThis->mut, bLockMutex);
- if( (pThis->wtpState == wtpState_SHUTDOWN_IMMEDIATE)
- || ((pThis->wtpState == wtpState_SHUTDOWN) && pThis->pfIsIdle(pThis->pUsr, bLockUsrMutex)))
- iRet = RS_RET_TERMINATE_NOW;
- END_MTX_PROTECTED_OPERATIONS(&pThis->mut);
+ if(wtpState == wtpState_SHUTDOWN_IMMEDIATE) {
+ ABORT_FINALIZE(RS_RET_TERMINATE_NOW);
+ } else if(wtpState == wtpState_SHUTDOWN) {
+ ABORT_FINALIZE(RS_RET_TERMINATE_WHEN_IDLE);
+ }
/* try customer handler if one was set and we do not yet have a definite result */
- if(iRet == RS_RET_OK && pThis->pfChkStopWrkr != NULL) {
+ if(pThis->pfChkStopWrkr != NULL) {
iRet = pThis->pfChkStopWrkr(pThis->pUsr, bLockUsrMutex);
}
+finalize_it:
RETiRet;
}
#pragma GCC diagnostic ignored "-Wempty-body"
/* Send a shutdown command to all workers and see if they terminate.
- * A timeout may be specified.
+ * A timeout may be specified. This function may also be called with
+ * the current number of workers being 0, in which case it does not
+ * shut down any worker.
* rgerhards, 2008-01-14
*/
rsRetVal
@@ -284,30 +236,22 @@ wtpShutdownAll(wtp_t *pThis, wtpState_t tShutdownCmd, struct timespec *ptTimeout
{
DEFiRet;
int bTimedOut;
- int iCancelStateSave;
ISOBJ_TYPE_assert(pThis, wtp);
wtpSetState(pThis, tShutdownCmd);
wtpWakeupAllWrkr(pThis);
- /* see if we need to harvest (join) any terminated threads (even in timeout case,
- * some may have terminated...
- */
- wtpProcessThrdChanges(pThis);
-
- /* and wait for their termination */
- pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave);
- d_pthread_mutex_lock(&pThis->mut);
- pthread_cleanup_push(mutexCancelCleanup, &pThis->mut);
- pthread_setcancelstate(iCancelStateSave, NULL);
+ /* wait for worker thread termination */
+ d_pthread_mutex_lock(&pThis->mutWtp);
+ pthread_cleanup_push(mutexCancelCleanup, &pThis->mutWtp);
bTimedOut = 0;
while(pThis->iCurNumWrkThrd > 0 && !bTimedOut) {
- dbgprintf("%s: waiting %ldms on worker thread termination, %d still running\n",
- wtpGetDbgHdr(pThis), timeoutVal(ptTimeout), pThis->iCurNumWrkThrd);
+ DBGPRINTF("%s: waiting %ldms on worker thread termination, %d still running\n",
+ wtpGetDbgHdr(pThis), timeoutVal(ptTimeout), ATOMIC_FETCH_32BIT(pThis->iCurNumWrkThrd));
- if(d_pthread_cond_timedwait(&pThis->condThrdTrm, &pThis->mut, ptTimeout) != 0) {
- dbgprintf("%s: timeout waiting on worker thread termination\n", wtpGetDbgHdr(pThis));
+ if(d_pthread_cond_timedwait(&pThis->condThrdTrm, &pThis->mutWtp, ptTimeout) != 0) {
+ DBGPRINTF("%s: timeout waiting on worker thread termination\n", wtpGetDbgHdr(pThis));
bTimedOut = 1; /* we exit the loop on timeout */
}
}
@@ -316,40 +260,11 @@ wtpShutdownAll(wtp_t *pThis, wtpState_t tShutdownCmd, struct timespec *ptTimeout
if(bTimedOut)
iRet = RS_RET_TIMED_OUT;
- /* see if we need to harvest (join) any terminated threads (even in timeout case,
- * some may have terminated...
- */
- wtpProcessThrdChanges(pThis);
-
RETiRet;
}
#pragma GCC diagnostic warning "-Wempty-body"
-/* indicate that a thread has terminated and awake anyone waiting on it
- * rgerhards, 2008-01-23
- */
-rsRetVal wtpSignalWrkrTermination(wtp_t *pThis)
-{
- DEFiRet;
- /* I leave the mutex code here out as it gives us deadlocks. I think it is not really
- * needed and we are on the safe side. I leave this comment in if practice proves us
- * wrong. The whole thing should be removed after half a year or year if we see there
- * actually is no issue (or revisit it from a theoretical POV).
- * rgerhards, 2008-01-28
- * revisited 2008-09-30, still a bit unclear, leave in
- */
- /*TODO: mutex or not mutex, that's the question ;)DEFVARS_mutexProtection;*/
-
- ISOBJ_TYPE_assert(pThis, wtp);
-
- /*BEGIN_MTX_PROTECTED_OPERATIONS(&pThis->mut, LOCK_MUTEX);*/
- pthread_cond_signal(&pThis->condThrdTrm); /* activate anyone waiting on thread shutdown */
- /*END_MTX_PROTECTED_OPERATIONS(&pThis->mut);*/
- RETiRet;
-}
-
-
/* Unconditionally cancel all running worker threads.
* rgerhards, 2008-01-14
*/
@@ -361,12 +276,8 @@ wtpCancelAll(wtp_t *pThis)
ISOBJ_TYPE_assert(pThis, wtp);
- /* process any pending thread requests so that we know who actually is still running */
- wtpProcessThrdChanges(pThis);
-
/* go through all workers and cancel those that are active */
for(i = 0 ; i < pThis->iNumWorkerThreads ; ++i) {
- dbgprintf("%s: try canceling worker thread %d\n", wtpGetDbgHdr(pThis), i);
wtiCancelThrd(pThis->pWrkr[i]);
}
@@ -374,39 +285,29 @@ wtpCancelAll(wtp_t *pThis)
}
-
-/* Set the Inactivity Guard
- * rgerhards, 2008-01-21
- */
-rsRetVal
-wtpSetInactivityGuard(wtp_t *pThis, int bNewState, int bLockMutex)
-{
- DEFiRet;
- DEFVARS_mutexProtection;
-
- BEGIN_MTX_PROTECTED_OPERATIONS(&pThis->mut, bLockMutex);
- pThis->bInactivityGuard = bNewState;
- END_MTX_PROTECTED_OPERATIONS(&pThis->mut);
-
- RETiRet;
-}
-
-
-/* cancellation cleanup handler for executing worker
- * decrements the worker counter
- * rgerhards, 2008-01-20
+/* cancellation cleanup handler for executing worker decrements the worker counter.
+ * This is also called when the the worker is normally shut down.
+ * rgerhards, 2009-07-20
*/
-void
+static void
wtpWrkrExecCancelCleanup(void *arg)
{
- wtp_t *pThis = (wtp_t*) arg;
+ wti_t *pWti = (wti_t*) arg;
+ wtp_t *pThis;
BEGINfunc
+ ISOBJ_TYPE_assert(pWti, wti);
+ pThis = pWti->pWtp;
ISOBJ_TYPE_assert(pThis, wtp);
- pThis->iCurNumWrkThrd--;
- wtpSignalWrkrTermination(pThis);
- dbgprintf("%s: thread CANCELED with %d workers running.\n", wtpGetDbgHdr(pThis), pThis->iCurNumWrkThrd);
+ /* the order of the next two statements is important! */
+ wtiSetState(pWti, WRKTHRD_STOPPED);
+ ATOMIC_DEC(pThis->iCurNumWrkThrd);
+
+ DBGPRINTF("%s: Worker thread %lx, terminated, num workers now %d\n",
+ wtpGetDbgHdr(pThis), (unsigned long) pWti, ATOMIC_FETCH_32BIT(pThis->iCurNumWrkThrd));
+
+ pthread_cond_broadcast(&pThis->condThrdTrm); /* activate anyone waiting on thread shutdown */
ENDfunc
}
@@ -419,12 +320,13 @@ wtpWrkrExecCancelCleanup(void *arg)
static void *
wtpWorker(void *arg) /* the arg is actually a wti object, even though we are in wtp! */
{
- DEFiRet;
- DEFVARS_mutexProtection;
+ uchar *pszDbgHdr;
+ uchar thrdName[32] = "rs:";
wti_t *pWti = (wti_t*) arg;
wtp_t *pThis;
sigset_t sigSet;
+ BEGINfunc
ISOBJ_TYPE_assert(pWti, wti);
pThis = pWti->pWtp;
ISOBJ_TYPE_assert(pThis, wtp);
@@ -432,39 +334,18 @@ wtpWorker(void *arg) /* the arg is actually a wti object, even though we are in
sigfillset(&sigSet);
pthread_sigmask(SIG_BLOCK, &sigSet, NULL);
- BEGIN_MTX_PROTECTED_OPERATIONS(&pThis->mut, LOCK_MUTEX);
-
- /* do some late initialization */
-
- pthread_cleanup_push(wtpWrkrExecCancelCleanup, pThis);
-
- /* finally change to RUNNING state. We need to check if we actually should still run,
- * because someone may have requested us to shut down even before we got a chance to do
- * our init. That would be a bad race... -- rgerhards, 2008-01-16
- */
- wtiSetState(pWti, eWRKTHRD_RUNNING, 0, MUTEX_ALREADY_LOCKED); /* we are running now! */
-
- do {
- END_MTX_PROTECTED_OPERATIONS(&pThis->mut);
-
- iRet = wtiWorker(pWti); /* just to make sure: this is NOT protected by the mutex! */
-
- BEGIN_MTX_PROTECTED_OPERATIONS(&pThis->mut, LOCK_MUTEX);
- } while(pThis->iCurNumWrkThrd == 1 && pThis->bInactivityGuard == 1);
- /* inactivity guard prevents shutdown of all workers while one should be running due to race
- * condition. It can lead to one more worker running than desired, but that is acceptable. After
- * all, that worker will shutdown itself due to inactivity timeout. If, however, none were running
- * when one was required, processing could come to a halt. -- rgerhards, 2008-01-21
- */
-
- pthread_cleanup_pop(0);
- pThis->iCurNumWrkThrd--;
- wtpSignalWrkrTermination(pThis);
-
- dbgprintf("%s: Worker thread %lx, terminated, num workers now %d\n",
- wtpGetDbgHdr(pThis), (unsigned long) pWti, pThis->iCurNumWrkThrd);
+# if HAVE_PRCTL && defined PR_SET_NAME
+ /* set thread name - we ignore if the call fails, has no harsh consequences... */
+ pszDbgHdr = wtpGetDbgHdr(pThis);
+ ustrncpy(thrdName+3, pszDbgHdr, 20);
+ if(prctl(PR_SET_NAME, thrdName, 0, 0, 0) != 0) {
+ DBGPRINTF("prctl failed, not setting thread name for '%s'\n", wtpGetDbgHdr(pThis));
+ }
+# endif
- END_MTX_PROTECTED_OPERATIONS(&pThis->mut);
+ pthread_cleanup_push(wtpWrkrExecCancelCleanup, pWti);
+ wtiWorker(pWti);
+ pthread_cleanup_pop(1);
ENDfunc
pthread_exit(0);
@@ -474,27 +355,20 @@ wtpWorker(void *arg) /* the arg is actually a wti object, even though we are in
/* start a new worker */
static rsRetVal
-wtpStartWrkr(wtp_t *pThis, int bLockMutex)
+wtpStartWrkr(wtp_t *pThis)
{
- DEFiRet;
- DEFVARS_mutexProtection;
wti_t *pWti;
int i;
int iState;
+ DEFiRet;
ISOBJ_TYPE_assert(pThis, wtp);
- wtpProcessThrdChanges(pThis); // TODO: Performance: this causes a lot of FUTEX calls
-
- BEGIN_MTX_PROTECTED_OPERATIONS(&pThis->mut, bLockMutex);
-
- pThis->iCurNumWrkThrd++;
+ d_pthread_mutex_lock(&pThis->mutWtp);
- /* find free spot in thread table. If we find at least one worker that is in initialization,
- * we do NOT start a new one. Let's give the other one a chance, first.
- */
+ /* find free spot in thread table. */
for(i = 0 ; i < pThis->iNumWorkerThreads ; ++i) {
- if(wtiGetState(pThis->pWrkr[i], LOCK_MUTEX) == eWRKTHRD_STOPPED) {
+ if(wtiGetState(pThis->pWrkr[i]) == WRKTHRD_STOPPED) {
break;
}
}
@@ -502,25 +376,20 @@ wtpStartWrkr(wtp_t *pThis, int bLockMutex)
if(i == pThis->iNumWorkerThreads)
ABORT_FINALIZE(RS_RET_NO_MORE_THREADS);
+ if(i == 0 || pThis->toWrkShutdown == -1) {
+ wtiSetAlwaysRunning(pThis->pWrkr[i]);
+ }
+
pWti = pThis->pWrkr[i];
- wtiSetState(pWti, eWRKTHRD_RUN_CREATED, 0, LOCK_MUTEX);
- iState = pthread_create(&(pWti->thrdID), NULL, wtpWorker, (void*) pWti);
- dbgprintf("%s: started with state %d, num workers now %d\n",
- wtpGetDbgHdr(pThis), iState, pThis->iCurNumWrkThrd);
-
- /* we try to give the starting worker a little boost. It won't help much as we still
- * hold the queue's mutex, but at least it has a chance to start on a single-CPU system.
- */
-# if !defined(__hpux) /* pthread_yield is missing there! */
- if(pThis->bOptimizeUniProc)
- pthread_yield();
-# endif
+ wtiSetState(pWti, WRKTHRD_RUNNING);
+ iState = pthread_create(&(pWti->thrdID), &pThis->attrThrd, wtpWorker, (void*) pWti);
+ ATOMIC_INC(pThis->iCurNumWrkThrd); /* we got one more! */
- /* indicate we just started a worker and would like to see it running */
- wtpSetInactivityGuard(pThis, 1, MUTEX_ALREADY_LOCKED);
+ DBGPRINTF("%s: started with state %d, num workers now %d\n",
+ wtpGetDbgHdr(pThis), iState, ATOMIC_FETCH_32BIT(pThis->iCurNumWrkThrd));
finalize_it:
- END_MTX_PROTECTED_OPERATIONS(&pThis->mut);
+ d_pthread_mutex_unlock(&pThis->mutWtp);
RETiRet;
}
@@ -537,38 +406,34 @@ rsRetVal
wtpAdviseMaxWorkers(wtp_t *pThis, int nMaxWrkr)
{
DEFiRet;
- DEFVARS_mutexProtection;
int nMissing; /* number workers missing to run */
int i;
ISOBJ_TYPE_assert(pThis, wtp);
+int nMaxWrkrTmp = nMaxWrkr;
if(nMaxWrkr == 0)
FINALIZE;
- BEGIN_MTX_PROTECTED_OPERATIONS(&pThis->mut, LOCK_MUTEX);
-
if(nMaxWrkr > pThis->iNumWorkerThreads) /* limit to configured maximum */
nMaxWrkr = pThis->iNumWorkerThreads;
- nMissing = nMaxWrkr - pThis->iCurNumWrkThrd;
+ nMissing = nMaxWrkr - ATOMIC_FETCH_32BIT(pThis->iCurNumWrkThrd);
+dbgprintf("wtpAdviseMaxWorkers, nmax: %d, curr %d, missing %d\n", nMaxWrkrTmp, pThis->iNumWorkerThreads, nMissing);
if(nMissing > 0) {
- dbgprintf("%s: high activity - starting %d additional worker thread(s).\n", wtpGetDbgHdr(pThis), nMissing);
+ DBGPRINTF("%s: high activity - starting %d additional worker thread(s).\n", wtpGetDbgHdr(pThis), nMissing);
/* start the rqtd nbr of workers */
for(i = 0 ; i < nMissing ; ++i) {
- CHKiRet(wtpStartWrkr(pThis, MUTEX_ALREADY_LOCKED));
- }
- } else {
- if(nMaxWrkr > 0) {
- dbgprintf("wtpAdviseMaxWorkers signals busy\n");
- wtpWakeupWrkr(pThis);
+ CHKiRet(wtpStartWrkr(pThis));
}
+ } else {
+dbgprintf("YYY: adivse signal cond busy");
+ pthread_cond_signal(pThis->pcondBusy);
}
finalize_it:
- END_MTX_PROTECTED_OPERATIONS(&pThis->mut);
RETiRet;
}
@@ -582,37 +447,16 @@ DEFpropSetMethPTR(wtp, pmutUsr, pthread_mutex_t)
DEFpropSetMethPTR(wtp, pcondBusy, pthread_cond_t)
DEFpropSetMethFP(wtp, pfChkStopWrkr, rsRetVal(*pVal)(void*, int))
DEFpropSetMethFP(wtp, pfRateLimiter, rsRetVal(*pVal)(void*))
-DEFpropSetMethFP(wtp, pfIsIdle, rsRetVal(*pVal)(void*, int))
-DEFpropSetMethFP(wtp, pfDoWork, rsRetVal(*pVal)(void*, void*, int))
+DEFpropSetMethFP(wtp, pfGetDeqBatchSize, rsRetVal(*pVal)(void*, int*))
+DEFpropSetMethFP(wtp, pfIsIdle, rsRetVal(*pVal)(void*, wtp_t*))
+DEFpropSetMethFP(wtp, pfDoWork, rsRetVal(*pVal)(void*, void*))
+DEFpropSetMethFP(wtp, pfObjProcessed, rsRetVal(*pVal)(void*, wti_t*))
DEFpropSetMethFP(wtp, pfOnIdle, rsRetVal(*pVal)(void*, int))
DEFpropSetMethFP(wtp, pfOnWorkerCancel, rsRetVal(*pVal)(void*, void*))
DEFpropSetMethFP(wtp, pfOnWorkerStartup, rsRetVal(*pVal)(void*))
DEFpropSetMethFP(wtp, pfOnWorkerShutdown, rsRetVal(*pVal)(void*))
-/* return the current number of worker threads.
- * TODO: atomic operation would bring a nice performance
- * enhancemcent
- * rgerhards, 2008-01-27
- */
-int
-wtpGetCurNumWrkr(wtp_t *pThis, int bLockMutex)
-{
- DEFVARS_mutexProtection;
- int iNumWrkr;
-
- BEGINfunc
- ISOBJ_TYPE_assert(pThis, wtp);
-
- BEGIN_MTX_PROTECTED_OPERATIONS(&pThis->mut, bLockMutex);
- iNumWrkr = pThis->iCurNumWrkThrd;
- END_MTX_PROTECTED_OPERATIONS(&pThis->mut);
-
- ENDfunc
- return iNumWrkr;
-}
-
-
/* set the debug header message
* The passed-in string is duplicated. So if the caller does not need
* it any longer, it must free it. Must be called only before object is finalized.
@@ -664,6 +508,5 @@ BEGINObjClassInit(wtp, 1, OBJ_IS_CORE_MODULE)
CHKiRet(objUse(glbl, CORE_COMPONENT));
ENDObjClassInit(wtp)
-/*
- * vi:set ai:
+/* vi:set ai:
*/
diff --git a/runtime/wtp.h b/runtime/wtp.h
index b9cb07c5..0505b91c 100644
--- a/runtime/wtp.h
+++ b/runtime/wtp.h
@@ -27,18 +27,9 @@
#include <pthread.h>
#include "obj.h"
-/* commands and states for worker threads. */
-typedef enum {
- eWRKTHRD_STOPPED = 0, /* worker thread is not running (either actually never ran or was shut down) */
- eWRKTHRD_TERMINATING = 1,/* worker thread has shut down, but some finalzing is still needed */
- /* ALL active states MUST be numerically higher than eWRKTHRD_TERMINATED and NONE must be lower! */
- eWRKTHRD_RUN_CREATED = 2,/* worker thread has been created, but not yet begun initialization (prob. not yet scheduled) */
- eWRKTHRD_RUN_INIT = 3, /* worker thread is initializing, but not yet fully running */
- eWRKTHRD_RUNNING = 4, /* worker thread is up and running and shall continue to do so */
- eWRKTHRD_SHUTDOWN = 5, /* worker thread is running but shall terminate when wtp is empty */
- eWRKTHRD_SHUTDOWN_IMMEDIATE = 6/* worker thread is running but shall terminate even if wtp is full */
- /* SHUTDOWN_IMMEDIATE MUST alsways be the numerically highest state! */
-} qWrkCmd_t;
+/* states for worker threads. */
+#define WRKTHRD_STOPPED FALSE
+#define WRKTHRD_RUNNING TRUE
/* possible states of a worker thread pool */
@@ -50,37 +41,36 @@ typedef enum {
/* the worker thread pool (wtp) object */
-typedef struct wtp_s {
+struct wtp_s {
BEGINobjInstance;
- int bOptimizeUniProc; /* cache for the equally-named global setting, pulled at time of queue creation */
wtpState_t wtpState;
int iNumWorkerThreads;/* number of worker threads to use */
int iCurNumWrkThrd;/* current number of active worker threads */
struct wti_s **pWrkr;/* array with control structure for the worker thread(s) associated with this wtp */
int toWrkShutdown; /* timeout for idle workers in ms, -1 means indefinite (0 is immediate) */
- int bInactivityGuard;/* prevents inactivity due to race condition */
rsRetVal (*pConsumer)(void *); /* user-supplied consumer function for dewtpd messages */
/* synchronization variables */
- pthread_mutex_t mutThrdShutdwn; /* mutex to guard thread shutdown processing */
- pthread_mutex_t mut; /* mutex for the wtp's thread management */
+ pthread_mutex_t mutWtp; /* mutex for the wtp's thread management */
pthread_cond_t condThrdTrm;/* signalled when threads terminate */
- int bThrdStateChanged; /* at least one thread state has changed if 1 */
/* end sync variables */
/* user objects */
- void *pUsr; /* pointer to user object */
+ void *pUsr; /* pointer to user object (in this case, the queue the wtp belongs to) */
+ pthread_attr_t attrThrd;/* attribute for new threads (created just once and cached here) */
pthread_mutex_t *pmutUsr;
pthread_cond_t *pcondBusy; /* condition the user will signal "busy again, keep runing" on (awakes worker) */
rsRetVal (*pfChkStopWrkr)(void *pUsr, int);
+ rsRetVal (*pfGetDeqBatchSize)(void *pUsr, int*); /* obtains max dequeue count from queue config */
+ rsRetVal (*pfObjProcessed)(void *pUsr, wti_t *pWti); /* indicate user object is processed */
rsRetVal (*pfRateLimiter)(void *pUsr);
- rsRetVal (*pfIsIdle)(void *pUsr, int);
- rsRetVal (*pfDoWork)(void *pUsr, void *pWti, int);
+ rsRetVal (*pfIsIdle)(void *pUsr, wtp_t *pWtp);
+ rsRetVal (*pfDoWork)(void *pUsr, void *pWti);
rsRetVal (*pfOnIdle)(void *pUsr, int);
rsRetVal (*pfOnWorkerCancel)(void *pUsr, void*pWti);
rsRetVal (*pfOnWorkerStartup)(void *pUsr);
rsRetVal (*pfOnWorkerShutdown)(void *pUsr);
/* end user objects */
uchar *pszDbgHdr; /* header string for debug messages */
-} wtp_t;
+};
/* some symbolic constants for easier reference */
@@ -91,21 +81,19 @@ rsRetVal wtpConstructFinalize(wtp_t *pThis);
rsRetVal wtpDestruct(wtp_t **ppThis);
rsRetVal wtpAdviseMaxWorkers(wtp_t *pThis, int nMaxWrkr);
rsRetVal wtpProcessThrdChanges(wtp_t *pThis);
-rsRetVal wtpSetInactivityGuard(wtp_t *pThis, int bNewState, int bLockMutex);
-rsRetVal wtpChkStopWrkr(wtp_t *pThis, int bLockMutex, int bLockUsrMutex);
+rsRetVal wtpChkStopWrkr(wtp_t *pThis, int bLockUsrMutex);
rsRetVal wtpSetState(wtp_t *pThis, wtpState_t iNewState);
-rsRetVal wtpWakeupWrkr(wtp_t *pThis);
rsRetVal wtpWakeupAllWrkr(wtp_t *pThis);
rsRetVal wtpCancelAll(wtp_t *pThis);
rsRetVal wtpSetDbgHdr(wtp_t *pThis, uchar *pszMsg, size_t lenMsg);
-rsRetVal wtpSignalWrkrTermination(wtp_t *pWtp);
rsRetVal wtpShutdownAll(wtp_t *pThis, wtpState_t tShutdownCmd, struct timespec *ptTimeout);
-int wtpGetCurNumWrkr(wtp_t *pThis, int bLockMutex);
PROTOTYPEObjClassInit(wtp);
PROTOTYPEpropSetMethFP(wtp, pfChkStopWrkr, rsRetVal(*pVal)(void*, int));
PROTOTYPEpropSetMethFP(wtp, pfRateLimiter, rsRetVal(*pVal)(void*));
-PROTOTYPEpropSetMethFP(wtp, pfIsIdle, rsRetVal(*pVal)(void*, int));
-PROTOTYPEpropSetMethFP(wtp, pfDoWork, rsRetVal(*pVal)(void*, void*, int));
+PROTOTYPEpropSetMethFP(wtp, pfGetDeqBatchSize, rsRetVal(*pVal)(void*, int*));
+PROTOTYPEpropSetMethFP(wtp, pfIsIdle, rsRetVal(*pVal)(void*, wtp_t*));
+PROTOTYPEpropSetMethFP(wtp, pfDoWork, rsRetVal(*pVal)(void*, void*));
+PROTOTYPEpropSetMethFP(wtp, pfObjProcessed, rsRetVal(*pVal)(void*, wti_t*));
PROTOTYPEpropSetMethFP(wtp, pfOnIdle, rsRetVal(*pVal)(void*, int));
PROTOTYPEpropSetMethFP(wtp, pfOnWorkerCancel, rsRetVal(*pVal)(void*,void*));
PROTOTYPEpropSetMethFP(wtp, pfOnWorkerStartup, rsRetVal(*pVal)(void*));
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 */