summaryrefslogtreecommitdiffstats
path: root/runtime
diff options
context:
space:
mode:
authorRainer Gerhards <rgerhards@adiscon.com>2009-08-21 10:19:56 +0200
committerRainer Gerhards <rgerhards@adiscon.com>2009-08-21 10:19:56 +0200
commitbd7542b51593739893922e8ec8fb6d7195d06cbe (patch)
treef7d0a85f593c2b71876365788535bd1b83c73dae /runtime
parent51aa94ea2c2ae57a6c7207aecf39dd4b578ad8f0 (diff)
parent675d46f5b59f64e378968baa5e0dec6810090287 (diff)
downloadrsyslog-bd7542b51593739893922e8ec8fb6d7195d06cbe.tar.gz
rsyslog-bd7542b51593739893922e8ec8fb6d7195d06cbe.tar.xz
rsyslog-bd7542b51593739893922e8ec8fb6d7195d06cbe.zip
Merge branch 'v4.3.2' (v4-beta) into v4-stablev4.4.0
Conflicts: ChangeLog configure.ac doc/manual.html tests/Makefile.am
Diffstat (limited to 'runtime')
-rw-r--r--runtime/Makefile.am8
-rw-r--r--runtime/cfsysline.c12
-rw-r--r--runtime/conf.c41
-rw-r--r--runtime/ctok.c4
-rw-r--r--runtime/datetime.c17
-rw-r--r--runtime/datetime.h4
-rw-r--r--runtime/expr.c4
-rw-r--r--runtime/module-template.h72
-rw-r--r--runtime/modules.c29
-rw-r--r--runtime/msg.c269
-rw-r--r--runtime/msg.h59
-rw-r--r--runtime/netstrm.c16
-rw-r--r--runtime/netstrm.h9
-rw-r--r--runtime/nsd.h8
-rw-r--r--runtime/nsd_gtls.c21
-rw-r--r--runtime/nsd_ptcp.c29
-rw-r--r--runtime/obj-types.h4
-rw-r--r--runtime/obj.c6
-rw-r--r--runtime/parser.c38
-rw-r--r--runtime/rsyslog.h24
-rw-r--r--runtime/stream.c2
-rw-r--r--runtime/stringbuf.c227
-rw-r--r--runtime/stringbuf.h27
-rw-r--r--runtime/strms_sess.c300
-rw-r--r--runtime/strms_sess.h75
-rw-r--r--runtime/strmsrv.c966
-rw-r--r--runtime/strmsrv.h112
-rw-r--r--runtime/unicode-helper.h54
-rw-r--r--runtime/vm.c252
-rw-r--r--runtime/vm.h5
-rw-r--r--runtime/vmop.c53
-rw-r--r--runtime/vmop.h11
-rw-r--r--runtime/vmprg.c28
-rw-r--r--runtime/vmprg.h4
-rw-r--r--runtime/vmstk.h4
35 files changed, 2346 insertions, 448 deletions
diff --git a/runtime/Makefile.am b/runtime/Makefile.am
index 2f0a1aa0..eeb656d6 100644
--- a/runtime/Makefile.am
+++ b/runtime/Makefile.am
@@ -7,6 +7,7 @@ pkglib_LTLIBRARIES =
librsyslog_la_SOURCES = \
rsyslog.c \
rsyslog.h \
+ unicode-helper.h \
atomic.h \
syslogd-types.h \
module-template.h \
@@ -120,6 +121,13 @@ lmnetstrms_la_CPPFLAGS = $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
lmnetstrms_la_LDFLAGS = -module -avoid-version
lmnetstrms_la_LIBADD =
+# generic stream server framework
+pkglib_LTLIBRARIES += lmstrmsrv.la
+lmstrmsrv_la_SOURCES = strmsrv.c strmsrv.h strms_sess.c strms_sess.h
+lmstrmsrv_la_CPPFLAGS = $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
+lmstrmsrv_la_LDFLAGS = -module -avoid-version
+lmstrmsrv_la_LIBADD =
+
# netstream drivers
# plain tcp driver - main driver
diff --git a/runtime/cfsysline.c b/runtime/cfsysline.c
index e1e4a6a4..0fb4247d 100644
--- a/runtime/cfsysline.c
+++ b/runtime/cfsysline.c
@@ -462,7 +462,7 @@ getWord(uchar **pp, cstr_t **ppStrB)
ASSERT(*pp != NULL);
ASSERT(ppStrB != NULL);
- CHKiRet(rsCStrConstruct(ppStrB));
+ CHKiRet(cstrConstruct(ppStrB));
skipWhiteSpace(pp); /* skip over any whitespace */
@@ -470,9 +470,9 @@ getWord(uchar **pp, cstr_t **ppStrB)
p = *pp;
while(*p && !isspace((int) *p)) {
- CHKiRet(rsCStrAppendChar(*ppStrB, *p++));
+ CHKiRet(cstrAppendChar(*ppStrB, *p++));
}
- CHKiRet(rsCStrFinish(*ppStrB));
+ CHKiRet(cstrFinalize(*ppStrB));
*pp = p;
@@ -506,7 +506,7 @@ static rsRetVal doGetWord(uchar **pp, rsRetVal (*pSetHdlr)(void*, uchar*), void
ASSERT(*pp != NULL);
CHKiRet(getWord(pp, &pStrB));
- CHKiRet(rsCStrConvSzStrAndDestruct(pStrB, &pNewVal, 0));
+ CHKiRet(cstrConvSzStrAndDestruct(pStrB, &pNewVal, 0));
pStrB = NULL;
/* we got the word, now set it */
@@ -525,7 +525,7 @@ static rsRetVal doGetWord(uchar **pp, rsRetVal (*pSetHdlr)(void*, uchar*), void
finalize_it:
if(iRet != RS_RET_OK) {
if(pStrB != NULL)
- rsCStrDestruct(&pStrB);
+ cstrDestruct(&pStrB);
}
RETiRet;
@@ -548,7 +548,7 @@ doSyslogName(uchar **pp, rsRetVal (*pSetHdlr)(void*, int), void *pVal, syslogNam
ASSERT(*pp != NULL);
CHKiRet(getWord(pp, &pStrB)); /* get word */
- iNewVal = decodeSyslogName(rsCStrGetSzStr(pStrB), pNameTable);
+ iNewVal = decodeSyslogName(cstrGetSzStr(pStrB), pNameTable);
if(pSetHdlr == NULL) {
/* we should set value directly to var */
diff --git a/runtime/conf.c b/runtime/conf.c
index 126ea2b7..a1d74b93 100644
--- a/runtime/conf.c
+++ b/runtime/conf.c
@@ -526,17 +526,15 @@ rsRetVal cflineParseTemplateName(uchar** pp, omodStringRequest_t *pOMSR, int iEn
tplName = (uchar*) strdup((char*)dfltTplName);
} else {
/* template specified, pick it up */
- if(rsCStrConstruct(&pStrB) != RS_RET_OK) {
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- }
+ CHKiRet(cstrConstruct(&pStrB));
/* now copy the string */
while(*p && *p != '#' && !isspace((int) *p)) {
- CHKiRet(rsCStrAppendChar(pStrB, *p));
+ CHKiRet(cstrAppendChar(pStrB, *p));
++p;
}
- CHKiRet(rsCStrFinish(pStrB));
- CHKiRet(rsCStrConvSzStrAndDestruct(pStrB, &tplName, 0));
+ CHKiRet(cstrFinalize(pStrB));
+ CHKiRet(cstrConvSzStrAndDestruct(pStrB, &tplName, 0));
}
iRet = OMSRsetEntry(pOMSR, iEntry, tplName, iTplOpts);
@@ -802,7 +800,6 @@ dbgprintf("calling expression parser, pp %p ('%s')\n", *pline, *pline);
/* debug support - print vmprg after construction (uncomment to use) */
/* vmprgDebugPrint(f->f_filterData.f_expr->pVmprg); */
- vmprgDebugPrint(f->f_filterData.f_expr->pVmprg);
/* we now need to skip whitespace to the action part, else we confuse
* the legacy rsyslog conf parser. -- rgerhards, 2008-02-25
@@ -926,7 +923,7 @@ static rsRetVal cflineProcessPropFilter(uchar **pline, register selector_t *f)
*/
static rsRetVal cflineProcessHostSelector(uchar **pline)
{
- rsRetVal iRet;
+ DEFiRet;
ASSERT(pline != NULL);
ASSERT(*pline != NULL);
@@ -952,21 +949,20 @@ static rsRetVal cflineProcessHostSelector(uchar **pline)
dbgprintf("resetting BSD-like hostname filter\n");
eDfltHostnameCmpMode = HN_NO_COMP;
if(pDfltHostnameCmp != NULL) {
- if((iRet = rsCStrSetSzStr(pDfltHostnameCmp, NULL)) != RS_RET_OK)
- return(iRet);
+ CHKiRet(rsCStrSetSzStr(pDfltHostnameCmp, NULL));
}
} else {
dbgprintf("setting BSD-like hostname filter to '%s'\n", *pline);
if(pDfltHostnameCmp == NULL) {
/* create string for parser */
- if((iRet = rsCStrConstructFromszStr(&pDfltHostnameCmp, *pline)) != RS_RET_OK)
- return(iRet);
+ CHKiRet(rsCStrConstructFromszStr(&pDfltHostnameCmp, *pline));
} else { /* string objects exists, just update... */
- if((iRet = rsCStrSetSzStr(pDfltHostnameCmp, *pline)) != RS_RET_OK)
- return(iRet);
+ CHKiRet(rsCStrSetSzStr(pDfltHostnameCmp, *pline));
}
}
- return RS_RET_OK;
+
+finalize_it:
+ RETiRet;
}
@@ -977,7 +973,7 @@ static rsRetVal cflineProcessHostSelector(uchar **pline)
*/
static rsRetVal cflineProcessTagSelector(uchar **pline)
{
- rsRetVal iRet;
+ DEFiRet;
ASSERT(pline != NULL);
ASSERT(*pline != NULL);
@@ -996,21 +992,20 @@ static rsRetVal cflineProcessTagSelector(uchar **pline)
if(**pline != '\0' && **pline == '*' && *(*pline+1) == '\0') {
dbgprintf("resetting programname filter\n");
if(pDfltProgNameCmp != NULL) {
- if((iRet = rsCStrSetSzStr(pDfltProgNameCmp, NULL)) != RS_RET_OK)
- return(iRet);
+ CHKiRet(rsCStrSetSzStr(pDfltProgNameCmp, NULL));
}
} else {
dbgprintf("setting programname filter to '%s'\n", *pline);
if(pDfltProgNameCmp == NULL) {
/* create string for parser */
- if((iRet = rsCStrConstructFromszStr(&pDfltProgNameCmp, *pline)) != RS_RET_OK)
- return(iRet);
+ CHKiRet(rsCStrConstructFromszStr(&pDfltProgNameCmp, *pline));
} else { /* string objects exists, just update... */
- if((iRet = rsCStrSetSzStr(pDfltProgNameCmp, *pline)) != RS_RET_OK)
- return(iRet);
+ CHKiRet(rsCStrSetSzStr(pDfltProgNameCmp, *pline));
}
}
- return RS_RET_OK;
+
+finalize_it:
+ RETiRet;
}
diff --git a/runtime/ctok.c b/runtime/ctok.c
index d2cd8bbd..263e656c 100644
--- a/runtime/ctok.c
+++ b/runtime/ctok.c
@@ -269,7 +269,7 @@ ctokGetVar(ctok_t *pThis, ctok_token_t *pToken)
}
CHKiRet(ctokUngetCharFromStream(pThis, c)); /* put not processed char back */
- CHKiRet(rsCStrFinish(pstrVal));
+ CHKiRet(cstrFinalize(pstrVal));
CHKiRet(var.SetString(pToken->pVar, pstrVal));
pstrVal = NULL;
@@ -319,7 +319,7 @@ ctokGetSimpStr(ctok_t *pThis, ctok_token_t *pToken)
}
CHKiRet(ctokGetCharFromStream(pThis, &c));
}
- CHKiRet(rsCStrFinish(pStrB));
+ CHKiRet(cstrFinalize(pstrVal));
CHKiRet(var.SetString(pToken->pVar, pstrVal));
pstrVal = NULL;
diff --git a/runtime/datetime.c b/runtime/datetime.c
index deb66eb4..40ab4e9c 100644
--- a/runtime/datetime.c
+++ b/runtime/datetime.c
@@ -150,13 +150,12 @@ static void getCurrTime(struct syslogTime *t, time_t *ttSeconds)
* \retval The number parsed.
*/
-static int srSLMGParseInt32(char** ppsz)
+static int srSLMGParseInt32(uchar** ppsz)
{
- int i;
+ register int i;
i = 0;
- while(isdigit((int) **ppsz))
- {
+ while(isdigit((int) **ppsz)) {
i = i * 10 + **ppsz - '0';
++(*ppsz);
}
@@ -172,9 +171,9 @@ static int srSLMGParseInt32(char** ppsz)
* could be obtained (restriction added 2008-09-16 by rgerhards).
*/
static rsRetVal
-ParseTIMESTAMP3339(struct syslogTime *pTime, char** ppszTS)
+ParseTIMESTAMP3339(struct syslogTime *pTime, uchar** ppszTS)
{
- char *pszTS = *ppszTS;
+ uchar *pszTS = *ppszTS;
/* variables to temporarily hold time information while we parse */
int year;
int month;
@@ -234,7 +233,7 @@ ParseTIMESTAMP3339(struct syslogTime *pTime, char** ppszTS)
/* Now let's see if we have secfrac */
if(*pszTS == '.') {
- char *pszStart = ++pszTS;
+ uchar *pszStart = ++pszTS;
secfrac = srSLMGParseInt32(&pszTS);
secfracPrecision = (int) (pszTS - pszStart);
} else {
@@ -307,7 +306,7 @@ finalize_it:
* time() call reduction ;).
*/
static rsRetVal
-ParseTIMESTAMP3164(struct syslogTime *pTime, char** ppszTS)
+ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS)
{
/* variables to temporarily hold time information while we parse */
int month;
@@ -317,7 +316,7 @@ ParseTIMESTAMP3164(struct syslogTime *pTime, char** ppszTS)
int minute;
int second;
/* end variables to temporarily hold time information while we parse */
- char *pszTS;
+ uchar *pszTS;
DEFiRet;
assert(ppszTS != NULL);
diff --git a/runtime/datetime.h b/runtime/datetime.h
index 0739588d..efb0a0af 100644
--- a/runtime/datetime.h
+++ b/runtime/datetime.h
@@ -36,8 +36,8 @@ 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, char** ppszTS);
- rsRetVal (*ParseTIMESTAMP3164)(struct syslogTime *pTime, char** pszTS);
+ rsRetVal (*ParseTIMESTAMP3339)(struct syslogTime *pTime, uchar** ppszTS);
+ rsRetVal (*ParseTIMESTAMP3164)(struct syslogTime *pTime, uchar** pszTS);
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);
diff --git a/runtime/expr.c b/runtime/expr.c
index 38ed1c68..e449d1c7 100644
--- a/runtime/expr.c
+++ b/runtime/expr.c
@@ -142,7 +142,9 @@ terminal(expr_t *pThis, ctok_t *tok)
* we have all relevant information)
*/
CHKiRet(ctok_token.UnlinkVar(pToken, &pVar));
- CHKiRet(vmprg.AddVarOperation(pThis->pVmprg, opcode_FUNC_CALL, pVar)); /* add to program */
+ CHKiRet(var.ConvToString(pVar)); /* make sure we have a string */
+ CHKiRet(vmprg.AddCallOperation(pThis->pVmprg, pVar->val.pStr)); /* add to program */
+ CHKiRet(var.Destruct(&pVar));
break;
case ctok_MSGVAR:
dbgoprint((obj_t*) pThis, "MSGVAR\n");
diff --git a/runtime/module-template.h b/runtime/module-template.h
index 6f7d877c..3e963199 100644
--- a/runtime/module-template.h
+++ b/runtime/module-template.h
@@ -39,7 +39,8 @@
#define DEF_OMOD_STATIC_DATA \
DEF_MOD_STATIC_DATA \
- DEFobjCurrIf(obj)
+ DEFobjCurrIf(obj) \
+ static __attribute__((unused)) int bCoreSupportsBatching;
#define DEF_IMOD_STATIC_DATA \
DEF_MOD_STATIC_DATA \
DEFobjCurrIf(obj)
@@ -160,6 +161,37 @@ static rsRetVal isCompatibleWithFeature(syslogFeature __attribute__((unused)) eF
RETiRet;\
}
+
+/* beginTransaction()
+ * introduced in v4.3.3 -- rgerhards, 2009-04-27
+ */
+#define BEGINbeginTransaction \
+static rsRetVal beginTransaction(instanceData __attribute__((unused)) *pData)\
+{\
+ DEFiRet;
+
+#define CODESTARTbeginTransaction /* currently empty, but may be extended */
+
+#define ENDbeginTransaction \
+ RETiRet;\
+}
+
+
+/* endTransaction()
+ * introduced in v4.3.3 -- rgerhards, 2009-04-27
+ */
+#define BEGINendTransaction \
+static rsRetVal endTransaction(instanceData __attribute__((unused)) *pData)\
+{\
+ DEFiRet;
+
+#define CODESTARTendTransaction /* currently empty, but may be extended */
+
+#define ENDendTransaction \
+ RETiRet;\
+}
+
+
/* doAction()
*/
#define BEGINdoAction \
@@ -324,6 +356,18 @@ static rsRetVal queryEtryPt(uchar *name, rsRetVal (**pEtryPoint)())\
*pEtryPoint = tryResume;\
}
+
+/* the following definition is queryEtryPt block that must be added
+ * if an output module supports the transactional interface.
+ * rgerhards, 2009-04-27
+ */
+#define CODEqueryEtryPt_TXIF_OMOD_QUERIES \
+ else if(!strcmp((char*) name, "beginTransaction")) {\
+ *pEtryPoint = beginTransaction;\
+ } else if(!strcmp((char*) name, "endTransaction")) {\
+ *pEtryPoint = endTransaction;\
+ }
+
/* 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.
@@ -393,6 +437,32 @@ finalize_it:\
}
+/* now come some check functions, which enable a standard way of obtaining feature
+ * information from the core. feat is the to-be-tested feature and featVar is a
+ * variable that receives the result (0-not support, 1-supported).
+ * This must be a macro, so that it is put into the output's code. Otherwise, we
+ * would need to rely on a library entry point, which is what we intend to avoid ;)
+ * rgerhards, 2009-04-27
+ */
+#define INITChkCoreFeature(featVar, feat) \
+{ \
+ rsRetVal MACRO_Ret; \
+ rsRetVal (*pQueryCoreFeatureSupport)(int*, unsigned); \
+ int bSupportsIt; \
+ featVar = 0; \
+ MACRO_Ret = pHostQueryEtryPt((uchar*)"queryCoreFeatureSupport", &pQueryCoreFeatureSupport); \
+ if(MACRO_Ret == RS_RET_OK) { \
+ /* found entry point, so let's see if core supports it */ \
+ CHKiRet((*pQueryCoreFeatureSupport)(&bSupportsIt, feat)); \
+ if(bSupportsIt) \
+ featVar = 1; \
+ } else if(MACRO_Ret != RS_RET_ENTRY_POINT_NOT_FOUND) { \
+ ABORT_FINALIZE(MACRO_Ret); /* Something else went wrong, what is not acceptable */ \
+ } \
+}
+
+
+
/* definitions for host API queries */
#define CODEmodInit_QueryRegCFSLineHdlr \
CHKiRet(pHostQueryEtryPt((uchar*)"regCfSysLineHdlr", &omsdRegCFSLineHdlr));
diff --git a/runtime/modules.c b/runtime/modules.c
index 9fdb48e7..32ae659f 100644
--- a/runtime/modules.c
+++ b/runtime/modules.c
@@ -40,6 +40,7 @@
#include <time.h>
#include <assert.h>
#include <errno.h>
+#include <pthread.h>
#ifdef OS_BSD
# include "libgen.h"
#endif
@@ -61,6 +62,14 @@
DEFobjStaticHelpers
DEFobjCurrIf(errmsg)
+/* we must ensure that only one thread at one time tries to load or unload
+ * modules, otherwise we may see race conditions. This first came up with
+ * imdiag/imtcp, which both use the same stream drivers. Below is the mutex
+ * for that handling.
+ * rgerhards, 2009-05-25
+ */
+static pthread_mutex_t mutLoadUnload;
+
static modInfo_t *pLoadedModules = NULL; /* list of currently-loaded modules */
static modInfo_t *pLoadedModulesLast = NULL; /* tail-pointer */
@@ -479,6 +488,8 @@ modUnlinkAndDestroy(modInfo_t **ppThis)
pThis = *ppThis;
assert(pThis != NULL);
+ pthread_mutex_lock(&mutLoadUnload);
+
/* first check if we are permitted to unload */
if(pThis->eType == eMOD_LIB) {
if(pThis->uRefCnt > 0) {
@@ -513,6 +524,7 @@ modUnlinkAndDestroy(modInfo_t **ppThis)
moduleDestruct(pThis);
finalize_it:
+ pthread_mutex_unlock(&mutLoadUnload);
RETiRet;
}
@@ -587,6 +599,8 @@ Load(uchar *pModName)
assert(pModName != NULL);
dbgprintf("Requested to load module '%s'\n", pModName);
+ pthread_mutex_lock(&mutLoadUnload);
+
iModNameLen = strlen((char *) pModName);
if(iModNameLen > 3 && !strcmp((char *) pModName + iModNameLen - 3, ".so")) {
iModNameLen -= 3;
@@ -696,6 +710,7 @@ Load(uchar *pModName)
}
finalize_it:
+ pthread_mutex_unlock(&mutLoadUnload);
RETiRet;
}
@@ -791,6 +806,15 @@ BEGINObjClassExit(module, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MA
CODESTARTObjClassExit(module)
/* release objects we no longer need */
objRelease(errmsg, CORE_COMPONENT);
+ /* We have a problem in our reference counting, which leads to this function
+ * being called too early. This usually is no problem, but if we destroy
+ * the mutex object, we get into trouble. So rather than finding the root cause,
+ * we do not release the mutex right now and have a very, very slight leak.
+ * We know that otherwise no bad effects happen, so this acceptable for the
+ * time being. -- rgerhards, 2009-05-25
+ *
+ * TODO: add again: pthread_mutex_destroy(&mutLoadUnload);
+ */
# ifdef DEBUG
modUsrPrintAll(); /* debug aid - TODO: integrate with debug.c, at least the settings! */
@@ -833,6 +857,7 @@ ENDobjQueryInterface(module)
*/
BEGINAbstractObjClassInit(module, 1, OBJ_IS_CORE_MODULE) /* class, version - CHANGE class also in END MACRO! */
uchar *pModPath;
+ pthread_mutexattr_t mutAttr;
/* use any module load path specified in the environment */
if((pModPath = (uchar*) getenv("RSYSLOG_MODDIR")) != NULL) {
@@ -850,6 +875,10 @@ BEGINAbstractObjClassInit(module, 1, OBJ_IS_CORE_MODULE) /* class, version - CHA
SetModDir(glblModPath);
}
+ pthread_mutexattr_init(&mutAttr);
+ pthread_mutexattr_settype(&mutAttr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&mutLoadUnload, &mutAttr);
+
/* request objects we use */
CHKiRet(objUse(errmsg, CORE_COMPONENT));
ENDObjClassInit(module)
diff --git a/runtime/msg.c b/runtime/msg.c
index c965b1f2..22f81f5d 100644
--- a/runtime/msg.c
+++ b/runtime/msg.c
@@ -45,6 +45,7 @@
#include "glbl.h"
#include "regexp.h"
#include "atomic.h"
+#include "unicode-helper.h"
/* static data */
DEFobjStaticHelpers
@@ -103,6 +104,7 @@ static syslogCODE rs_facilitynames[] =
/* some forward declarations */
static int getAPPNAMELen(msg_t *pM);
+static int getProtocolVersion(msg_t *pM);
/* The following functions will support advanced output module
* multithreading, once this is implemented. Currently, we
@@ -341,52 +343,27 @@ CODESTARTobjDestruct(msg)
if(currRefCount == 0)
{
/* DEV Debugging Only! dbgprintf("msgDestruct\t0x%lx, RefCount now 0, doing DESTROY\n", (unsigned long)pThis); */
- if(pThis->pszUxTradMsg != NULL)
- free(pThis->pszUxTradMsg);
- if(pThis->pszRawMsg != NULL)
- free(pThis->pszRawMsg);
- if(pThis->pszTAG != NULL)
- free(pThis->pszTAG);
- if(pThis->pszHOSTNAME != NULL)
- free(pThis->pszHOSTNAME);
- if(pThis->pszInputName != NULL)
- free(pThis->pszInputName);
- if(pThis->pszRcvFrom != NULL)
- free(pThis->pszRcvFrom);
- if(pThis->pszRcvFromIP != NULL)
- free(pThis->pszRcvFromIP);
- if(pThis->pszMSG != NULL)
- free(pThis->pszMSG);
- if(pThis->pszFacility != NULL)
- free(pThis->pszFacility);
- if(pThis->pszFacilityStr != NULL)
- free(pThis->pszFacilityStr);
- if(pThis->pszSeverity != NULL)
- free(pThis->pszSeverity);
- if(pThis->pszSeverityStr != NULL)
- free(pThis->pszSeverityStr);
- if(pThis->pszRcvdAt3164 != NULL)
- free(pThis->pszRcvdAt3164);
- if(pThis->pszRcvdAt3339 != NULL)
- free(pThis->pszRcvdAt3339);
- if(pThis->pszRcvdAt_SecFrac != NULL)
- free(pThis->pszRcvdAt_SecFrac);
- if(pThis->pszRcvdAt_MySQL != NULL)
- free(pThis->pszRcvdAt_MySQL);
- if(pThis->pszRcvdAt_PgSQL != NULL)
- free(pThis->pszRcvdAt_PgSQL);
- if(pThis->pszTIMESTAMP3164 != NULL)
- free(pThis->pszTIMESTAMP3164);
- if(pThis->pszTIMESTAMP3339 != NULL)
- free(pThis->pszTIMESTAMP3339);
- if(pThis->pszTIMESTAMP_SecFrac != NULL)
- free(pThis->pszTIMESTAMP_SecFrac);
- if(pThis->pszTIMESTAMP_MySQL != NULL)
- free(pThis->pszTIMESTAMP_MySQL);
- if(pThis->pszTIMESTAMP_PgSQL != NULL)
- free(pThis->pszTIMESTAMP_PgSQL);
- if(pThis->pszPRI != NULL)
- free(pThis->pszPRI);
+ 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);
+ 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)
rsCStrDestruct(&pThis->pCSProgName);
if(pThis->pCSStrucData != NULL)
@@ -461,14 +438,18 @@ msg_t* MsgDup(msg_t* pOld)
pNew->msgFlags = pOld->msgFlags;
pNew->iProtocolVersion = pOld->iProtocolVersion;
pNew->ttGenTime = pOld->ttGenTime;
+ /* enable this, if someone actually uses UxTradMsg, delete after some time has
+ * passed and nobody complained -- rgerhards, 2009-06-16
+ pNew->offAfterPRI = pOld->offAfterPRI;
+ */
+ memcpy(pNew->bufPRI, pOld->bufPRI, pOld->iLenPRI);
+ pNew->iLenPRI = pOld->iLenPRI;
tmpCOPYSZ(Severity);
tmpCOPYSZ(SeverityStr);
tmpCOPYSZ(Facility);
tmpCOPYSZ(FacilityStr);
- tmpCOPYSZ(PRI);
tmpCOPYSZ(RawMsg);
tmpCOPYSZ(MSG);
- tmpCOPYSZ(UxTradMsg);
tmpCOPYSZ(TAG);
tmpCOPYSZ(HOSTNAME);
tmpCOPYSZ(RcvFrom);
@@ -517,10 +498,13 @@ static rsRetVal MsgSerialize(msg_t *pThis, strm_t *pStrm)
objSerializeSCALAR(pStrm, ttGenTime, INT);
objSerializeSCALAR(pStrm, tRcvdAt, SYSLOGTIME);
objSerializeSCALAR(pStrm, tTIMESTAMP, SYSLOGTIME);
+ /* enable this, if someone actually uses UxTradMsg, delete after some time has
+ * passed and nobody complained -- rgerhards, 2009-06-16
+ objSerializeSCALAR(pStrm, offsAfterPRI, SHORT);
+ */
objSerializePTR(pStrm, pszRawMsg, PSZ);
objSerializePTR(pStrm, pszMSG, PSZ);
- objSerializePTR(pStrm, pszUxTradMsg, PSZ);
objSerializePTR(pStrm, pszTAG, PSZ);
objSerializePTR(pStrm, pszHOSTNAME, PSZ);
objSerializePTR(pStrm, pszInputName, PSZ);
@@ -611,7 +595,7 @@ static rsRetVal aquirePROCIDFromTAG(msg_t *pM)
}
/* OK, finaally we could obtain a PROCID. So let's use it ;) */
- CHKiRet(rsCStrFinish(pM->pCSPROCID));
+ CHKiRet(cstrFinalize(pM->pCSPROCID));
finalize_it:
RETiRet;
@@ -653,7 +637,7 @@ static rsRetVal aquireProgramName(msg_t *pM)
; ++i) {
CHKiRet(rsCStrAppendChar(pM->pCSProgName, pM->pszTAG[i]));
}
- CHKiRet(rsCStrFinish(pM->pCSProgName));
+ CHKiRet(cstrFinalize(pM->pCSProgName));
}
finalize_it:
RETiRet;
@@ -694,7 +678,7 @@ void setProtocolVersion(msg_t *pM, int iNewVersion)
pM->iProtocolVersion = iNewVersion;
}
-int getProtocolVersion(msg_t *pM)
+static int getProtocolVersion(msg_t *pM)
{
assert(pM != NULL);
return(pM->iProtocolVersion);
@@ -713,7 +697,7 @@ int getMSGLen(msg_t *pM)
}
-char *getRawMsg(msg_t *pM)
+static char *getRawMsg(msg_t *pM)
{
if(pM == NULL)
return "";
@@ -724,16 +708,17 @@ char *getRawMsg(msg_t *pM)
return (char*)pM->pszRawMsg;
}
+
+/* enable this, if someone actually uses UxTradMsg, delete after some time has
+ * passed and nobody complained -- rgerhards, 2009-06-16
char *getUxTradMsg(msg_t *pM)
{
if(pM == NULL)
return "";
else
- if(pM->pszUxTradMsg == NULL)
- return "";
- else
- return (char*)pM->pszUxTradMsg;
+ return (char*)pM->pszRawMsg + pM->offAfterPRI;
}
+*/
char *getMSG(msg_t *pM)
{
@@ -747,44 +732,34 @@ char *getMSG(msg_t *pM)
}
-/* Get PRI value in text form */
-char *getPRI(msg_t *pM)
+/* Get PRI value as integer */
+static int getPRIi(msg_t *pM)
{
- int pri;
- BEGINfunc
+ assert(pM != NULL);
+ return (pM->iFacility << 3) + (pM->iSeverity);
+}
+
+/* Get PRI value in text form */
+static inline char *getPRI(msg_t *pM)
+{
if(pM == NULL)
return "";
+ /* there are some cases where bufPRI may not contain a valid string,
+ * and then we need to build it.
+ */
MsgLock(pM);
- if(pM->pszPRI == NULL) {
- /* OK, we need to construct it... we use a 5 byte buffer - as of
- * RFC 3164, it can't be longer. Should it still be, snprintf will truncate...
- * Note that we do not use the LOG_MAKEPRI macro. This macro
- * is a simple add of the two values under FreeBSD 7. So we implement
- * the logic in our own code. This is a change from a bug
- * report. -- rgerhards, 2008-07-14
- */
- pri = pM->iFacility * 8 + pM->iSeverity;
- if((pM->pszPRI = malloc(5)) == NULL) return "";
- pM->iLenPRI = snprintf((char*)pM->pszPRI, 5, "%d", pri);
+ if(pM->bufPRI[0] == '\0') {
+ snprintf((char*)pM->bufPRI, sizeof(pM->bufPRI), "<%d>", getPRIi(pM));
}
MsgUnlock(pM);
- ENDfunc
- return (char*)pM->pszPRI;
-}
-
-
-/* Get PRI value as integer */
-int getPRIi(msg_t *pM)
-{
- assert(pM != NULL);
- return (pM->iFacility << 3) + (pM->iSeverity);
+ return (char*)pM->bufPRI;
}
-char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt)
+static inline char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt)
{
BEGINfunc
if(pM == NULL)
@@ -862,7 +837,7 @@ char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt)
return "INVALID eFmt OPTION!";
}
-char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt)
+static inline char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt)
{
BEGINfunc
if(pM == NULL)
@@ -941,7 +916,7 @@ char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt)
}
-char *getSeverity(msg_t *pM)
+static inline char *getSeverity(msg_t *pM)
{
if(pM == NULL)
return "";
@@ -958,7 +933,7 @@ char *getSeverity(msg_t *pM)
}
-char *getSeverityStr(msg_t *pM)
+static inline char *getSeverityStr(msg_t *pM)
{
syslogCODE *c;
int val;
@@ -988,7 +963,7 @@ char *getSeverityStr(msg_t *pM)
return((char*)pM->pszSeverityStr);
}
-char *getFacility(msg_t *pM)
+static inline char *getFacility(msg_t *pM)
{
if(pM == NULL)
return "";
@@ -1007,7 +982,7 @@ char *getFacility(msg_t *pM)
return((char*)pM->pszFacility);
}
-char *getFacilityStr(msg_t *pM)
+static inline char *getFacilityStr(msg_t *pM)
{
syslogCODE *c;
int val;
@@ -1059,6 +1034,17 @@ MsgSetFlowControlType(msg_t *pMsg, flowControl_t eFlowCtl)
RETiRet;
}
+/* set offset after which PRI in raw msg starts
+ * rgerhards, 2009-06-16
+ */
+rsRetVal
+MsgSetAfterPRIOffs(msg_t *pMsg, short offs)
+{
+ assert(pMsg != NULL);
+ pMsg->offAfterPRI = offs;
+ return RS_RET_OK;
+}
+
/* rgerhards 2004-11-24: set APP-NAME in msg object
* TODO: revisit msg locking code!
@@ -1114,7 +1100,7 @@ finalize_it:
/* rgerhards, 2005-11-24
*/
-int getPROCIDLen(msg_t *pM)
+static inline int getPROCIDLen(msg_t *pM)
{
assert(pM != NULL);
MsgLock(pM);
@@ -1159,19 +1145,10 @@ finalize_it:
RETiRet;
}
-/* rgerhards, 2005-11-24
- */
-#if 0 /* This method is currently not called, be we like to preserve it */
-static int getMSGIDLen(msg_t *pM)
-{
- return (pM->pCSMSGID == NULL) ? 1 : rsCStrLen(pM->pCSMSGID);
-}
-#endif
-
/* rgerhards, 2005-11-24
*/
-char *getMSGID(msg_t *pM)
+static inline char *getMSGID(msg_t *pM)
{
return (pM->pCSMSGID == NULL) ? "-" : (char*) rsCStrGetSzStrNoNULL(pM->pCSMSGID);
}
@@ -1195,8 +1172,7 @@ void MsgAssignTAG(msg_t *pMsg, uchar *pBuf)
void MsgSetTAG(msg_t *pMsg, char* pszTAG)
{
assert(pMsg != NULL);
- if(pMsg->pszTAG != NULL)
- free(pMsg->pszTAG);
+ free(pMsg->pszTAG);
pMsg->iLenTAG = strlen(pszTAG);
if((pMsg->pszTAG = malloc(pMsg->iLenTAG + 1)) != NULL)
memcpy(pMsg->pszTAG, pszTAG, pMsg->iLenTAG + 1);
@@ -1253,7 +1229,7 @@ static int getTAGLen(msg_t *pM)
#endif
-char *getTAG(msg_t *pM)
+static inline char *getTAG(msg_t *pM)
{
char *ret;
@@ -1296,7 +1272,7 @@ char *getHOSTNAME(msg_t *pM)
}
-uchar *getInputName(msg_t *pM)
+static uchar *getInputName(msg_t *pM)
{
if(pM == NULL)
return (uchar*) "";
@@ -1308,15 +1284,15 @@ uchar *getInputName(msg_t *pM)
}
-char *getRcvFrom(msg_t *pM)
+uchar *getRcvFrom(msg_t *pM)
{
if(pM == NULL)
- return "";
+ return UCHAR_CONSTANT("");
else
if(pM->pszRcvFrom == NULL)
- return "";
+ return UCHAR_CONSTANT("");
else
- return (char*) pM->pszRcvFrom;
+ return pM->pszRcvFrom;
}
@@ -1363,7 +1339,7 @@ static int getStructuredDataLen(msg_t *pM)
/* get the "STRUCTURED-DATA" as sz string
* rgerhards, 2005-11-24
*/
-char *getStructuredData(msg_t *pM)
+static inline char *getStructuredData(msg_t *pM)
{
return (pM->pCSStrucData == NULL) ? "-" : (char*) rsCStrGetSzStrNoNULL(pM->pCSStrucData);
}
@@ -1489,14 +1465,13 @@ static int getAPPNAMELen(msg_t *pM)
}
/* rgerhards 2008-09-10: set pszInputName in msg object
+ * rgerhards, 2009-06-16
*/
-void MsgSetInputName(msg_t *pMsg, char* pszInputName)
+void MsgSetInputName(msg_t *pMsg, uchar* pszInputName, size_t lenInputName)
{
assert(pMsg != NULL);
- if(pMsg->pszInputName != NULL)
- free(pMsg->pszInputName);
-
- pMsg->iLenInputName = strlen(pszInputName);
+ free(pMsg->pszInputName);
+ pMsg->iLenInputName = lenInputName;
if((pMsg->pszInputName = malloc(pMsg->iLenInputName + 1)) != NULL) {
memcpy(pMsg->pszInputName, pszInputName, pMsg->iLenInputName + 1);
}
@@ -1504,13 +1479,12 @@ void MsgSetInputName(msg_t *pMsg, char* pszInputName)
/* rgerhards 2004-11-16: set pszRcvFrom in msg object
*/
-void MsgSetRcvFrom(msg_t *pMsg, char* pszRcvFrom)
+void MsgSetRcvFrom(msg_t *pMsg, uchar* pszRcvFrom)
{
assert(pMsg != NULL);
- if(pMsg->pszRcvFrom != NULL)
- free(pMsg->pszRcvFrom);
+ free(pMsg->pszRcvFrom);
- pMsg->iLenRcvFrom = strlen(pszRcvFrom);
+ pMsg->iLenRcvFrom = ustrlen(pszRcvFrom);
if((pMsg->pszRcvFrom = malloc(pMsg->iLenRcvFrom + 1)) != NULL) {
memcpy(pMsg->pszRcvFrom, pszRcvFrom, pMsg->iLenRcvFrom + 1);
}
@@ -1561,51 +1535,16 @@ 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, char* pszHOSTNAME)
+void MsgSetHOSTNAME(msg_t *pMsg, uchar* pszHOSTNAME)
{
assert(pMsg != NULL);
- if(pMsg->pszHOSTNAME != NULL)
- free(pMsg->pszHOSTNAME);
+ free(pMsg->pszHOSTNAME);
- pMsg->iLenHOSTNAME = strlen(pszHOSTNAME);
+ 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");
-}
-
-
-/* Set the UxTradMsg to a caller-provided string. This is thought
- * to be a heap buffer that the caller will no longer use. This
- * function is a performance optimization over MsgSetUxTradMsg().
- * rgerhards 2004-11-19
- */
-#if 0 /* This method is currently not called, be we like to preserve it */
-static void MsgAssignUxTradMsg(msg_t *pMsg, char *pBuf)
-{
- assert(pMsg != NULL);
- assert(pBuf != NULL);
- pMsg->iLenUxTradMsg = strlen(pBuf);
- pMsg->pszUxTradMsg = pBuf;
-}
-#endif
-
-
-/* rgerhards 2004-11-17: set the traditional Unix message in msg object
- */
-int MsgSetUxTradMsg(msg_t *pMsg, char* pszUxTradMsg)
-{
- assert(pMsg != NULL);
- assert(pszUxTradMsg != NULL);
- pMsg->iLenUxTradMsg = strlen(pszUxTradMsg);
- if(pMsg->pszUxTradMsg != NULL)
- free(pMsg->pszUxTradMsg);
- if((pMsg->pszUxTradMsg = malloc(pMsg->iLenUxTradMsg + 1)) != NULL)
- memcpy(pMsg->pszUxTradMsg, pszUxTradMsg, pMsg->iLenUxTradMsg + 1);
- else
- dbgprintf("Could not allocate memory for pszUxTradMsg buffer.");
-
- return(0);
+ DBGPRINTF("Could not allocate memory in MsgSetHOSTNAME()\n");
}
@@ -1787,12 +1726,15 @@ char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
pRes = getMSG(pMsg);
} else if(!strcmp((char*) pName, "rawmsg")) {
pRes = getRawMsg(pMsg);
+ /* enable this, if someone actually uses UxTradMsg, delete after some time has
+ * passed and nobody complained -- rgerhards, 2009-06-16
} else if(!strcmp((char*) pName, "uxtradmsg")) {
pRes = getUxTradMsg(pMsg);
+ */
} else if(!strcmp((char*) pName, "inputname")) {
pRes = (char*) getInputName(pMsg);
} else if(!strcmp((char*) pName, "fromhost")) {
- pRes = getRcvFrom(pMsg);
+ 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")) {
@@ -2512,20 +2454,25 @@ rsRetVal MsgSetProperty(msg_t *pThis, var_t *pProp)
pThis->msgFlags = pProp->val.num;
} else if(isProp("pszRawMsg")) {
MsgSetRawMsg(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ /* enable this, if someone actually uses UxTradMsg, delete after some time has
+ * passed and nobody complained -- rgerhards, 2009-06-16
+ } else if(isProp("offAfterPRI")) {
+ pThis->offAfterPRI = pProp->val.num;
+ */
} else if(isProp("pszMSG")) {
MsgSetMSG(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
} else if(isProp("pszUxTradMsg")) {
- MsgSetUxTradMsg(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ /*IGNORE*/; /* this *was* a property, but does no longer exist */
} else if(isProp("pszTAG")) {
MsgSetTAG(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
} else if(isProp("pszInputName")) {
- MsgSetInputName(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ MsgSetInputName(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr), rsCStrLen(pProp->val.pStr));
} else if(isProp("pszRcvFromIP")) {
MsgSetRcvFromIP(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr));
} else if(isProp("pszRcvFrom")) {
- MsgSetRcvFrom(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ MsgSetRcvFrom(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr));
} else if(isProp("pszHOSTNAME")) {
- MsgSetHOSTNAME(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
+ MsgSetHOSTNAME(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr));
} else if(isProp("pCSStrucData")) {
MsgSetStructuredData(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
} else if(isProp("pCSAPPNAME")) {
diff --git a/runtime/msg.h b/runtime/msg.h
index c8350626..fe9f87fa 100644
--- a/runtime/msg.h
+++ b/runtime/msg.h
@@ -51,7 +51,7 @@
struct msg {
BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
pthread_mutexattr_t mutAttr;
-short bDoLock; /* use the mutex? */
+ bool bDoLock; /* use the mutex? */
pthread_mutex_t mut;
flowControl_t flowCtlType; /**< type of flow control we can apply, for enqueueing, needs not to be persisted because
once data has entered the queue, this property is no longer needed. */
@@ -73,12 +73,13 @@ short bDoLock; /* use the mutex? */
int iLenFacility; /* ... and its length. */
uchar *pszFacilityStr; /* facility name... */
int iLenFacilityStr; /* ... and its length. */
- uchar *pszPRI; /* the PRI as a string */
+ uchar bufPRI[5]; /* PRI as string */
int iLenPRI; /* and its length */
uchar *pszRawMsg; /* message as it was received on the
* wire. This is important in case we
* need to preserve cryptographic verifiers.
*/
+ short offAfterPRI; /* offset, at which raw message WITHOUT PRI part starts in pszRawMsg */
int iLenRawMsg; /* length of raw message */
uchar *pszMSG; /* the MSG part itself */
int iLenMSG; /* Length of the MSG part */
@@ -120,6 +121,8 @@ short bDoLock; /* use the mutex? */
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 */
+
};
@@ -137,60 +140,52 @@ short bDoLock; /* use the mutex? */
/* function prototypes
*/
PROTOTYPEObjClassInit(msg);
-char* getProgramName(msg_t*);
rsRetVal msgConstruct(msg_t **ppThis);
rsRetVal msgConstructWithTime(msg_t **ppThis, struct syslogTime *stTime, time_t ttGenTime);
rsRetVal msgDestruct(msg_t **ppM);
msg_t* MsgDup(msg_t* pOld);
msg_t *MsgAddRef(msg_t *pM);
void setProtocolVersion(msg_t *pM, int iNewVersion);
-int getProtocolVersion(msg_t *pM);
-char *getProtocolVersionString(msg_t *pM);
-int getMSGLen(msg_t *pM);
-char *getRawMsg(msg_t *pM);
-char *getUxTradMsg(msg_t *pM);
-char *getMSG(msg_t *pM);
-char *getPRI(msg_t *pM);
-int getPRIi(msg_t *pM);
-char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt);
-char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt);
-char *getSeverity(msg_t *pM);
-char *getSeverityStr(msg_t *pM);
-char *getFacility(msg_t *pM);
-char *getFacilityStr(msg_t *pM);
-void MsgSetInputName(msg_t *pMsg, char*);
+void MsgSetInputName(msg_t *pMsg, uchar*, size_t);
rsRetVal MsgSetAPPNAME(msg_t *pMsg, char* pszAPPNAME);
-char *getAPPNAME(msg_t *pM);
rsRetVal MsgSetPROCID(msg_t *pMsg, char* pszPROCID);
-int getPROCIDLen(msg_t *pM);
-char *getPROCID(msg_t *pM);
rsRetVal MsgSetMSGID(msg_t *pMsg, char* pszMSGID);
void MsgAssignTAG(msg_t *pMsg, uchar *pBuf);
void MsgSetTAG(msg_t *pMsg, char* pszTAG);
rsRetVal MsgSetFlowControlType(msg_t *pMsg, flowControl_t eFlowCtl);
-char *getTAG(msg_t *pM);
-int getHOSTNAMELen(msg_t *pM);
-char *getHOSTNAME(msg_t *pM);
-char *getRcvFrom(msg_t *pM);
rsRetVal MsgSetStructuredData(msg_t *pMsg, char* pszStrucData);
-char *getStructuredData(msg_t *pM);
-int getProgramNameLen(msg_t *pM);
-char *getProgramName(msg_t *pM);
-void MsgSetRcvFrom(msg_t *pMsg, char* pszRcvFrom);
+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, char* pszHOSTNAME);
-int MsgSetUxTradMsg(msg_t *pMsg, char* pszUxTradMsg);
+void MsgSetHOSTNAME(msg_t *pMsg, uchar* pszHOSTNAME);
+rsRetVal MsgSetAfterPRIOffs(msg_t *pMsg, short offs);
void MsgSetMSG(msg_t *pMsg, char* pszMSG);
void MsgSetRawMsg(msg_t *pMsg, char* pszRawMsg);
void moveHOSTNAMEtoTAG(msg_t *pM);
-char *getMSGID(msg_t *pM);
char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
cstr_t *pCSPropName, unsigned short *pbMustBeFreed);
char *textpri(char *pRes, size_t pResLen, int pri);
rsRetVal msgGetMsgVar(msg_t *pThis, cstr_t *pstrPropName, var_t **ppVar);
rsRetVal MsgEnableThreadSafety(void);
+/* TODO: remove these five (so far used in action.c) */
+char *getMSG(msg_t *pM);
+char *getHOSTNAME(msg_t *pM);
+char *getPROCID(msg_t *pM);
+char *getAPPNAME(msg_t *pM);
+int getMSGLen(msg_t *pM);
+
+char *getHOSTNAME(msg_t *pM);
+int getHOSTNAMELen(msg_t *pM);
+char *getProgramName(msg_t *pM);
+int getProgramNameLen(msg_t *pM);
+uchar *getRcvFrom(msg_t *pM);
+
+#if 0
+char *getUxTradMsg(msg_t *pM);
+int MsgSetUxTradMsg(msg_t *pMsg, char* pszUxTradMsg);
+#endif
+
/* The MsgPrepareEnqueue() function is a macro for performance reasons.
* It needs one global variable to work. This is acceptable, as it gains
* us quite some performance and is fully abstracted using this header file.
diff --git a/runtime/netstrm.c b/runtime/netstrm.c
index ffa1c578..3658006f 100644
--- a/runtime/netstrm.c
+++ b/runtime/netstrm.c
@@ -17,7 +17,7 @@
* Rainer Gerhards and Adiscon GmbH have agreed to permit using the code
* under the terms of the GNU Lesser General Public License.
*
- * Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007-2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of the rsyslog runtime library.
*
@@ -233,6 +233,19 @@ Send(netstrm_t *pThis, uchar *pBuf, ssize_t *pLenBuf)
RETiRet;
}
+/* Enable Keep-Alive handling for those drivers that support it.
+ * rgerhards, 2009-06-02
+ */
+static rsRetVal
+EnableKeepAlive(netstrm_t *pThis)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, netstrm);
+ iRet = pThis->Drvr.EnableKeepAlive(pThis->pDrvrData);
+ RETiRet;
+}
+
+
/* check connection - slim wrapper for NSD driver function */
static void
@@ -337,6 +350,7 @@ CODESTARTobjQueryInterface(netstrm)
pIf->SetDrvrPermPeers = SetDrvrPermPeers;
pIf->CheckConnection = CheckConnection;
pIf->GetSock = GetSock;
+ pIf->EnableKeepAlive = EnableKeepAlive;
finalize_it:
ENDobjQueryInterface(netstrm)
diff --git a/runtime/netstrm.h b/runtime/netstrm.h
index 3ab790e8..f6931104 100644
--- a/runtime/netstrm.h
+++ b/runtime/netstrm.h
@@ -31,6 +31,7 @@ struct netstrm_s {
BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
nsd_t *pDrvrData; /**< the driver's data elements (at most other places, this is called pNsd) */
nsd_if_t Drvr; /**< our stream driver */
+ void *pUsr; /**< pointer to user-provided data structure */
netstrms_t *pNS; /**< pointer to our netstream subsystem object */
};
@@ -68,9 +69,13 @@ BEGINinterface(netstrm) /* name must also be changed in ENDinterface macro! */
* sockets - not really desirable, but not the end of the world... TODO: should be
* reconsidered when a new ACL system is build. -- rgerhards, 2008-12-01
*/
+ /* v4 */
+ rsRetVal (*EnableKeepAlive)(netstrm_t *pThis);
ENDinterface(netstrm)
-#define netstrmCURR_IF_VERSION 3 /* increment whenever you change the interface structure! */
-/* interface version 3 added GetRemAddr() */
+#define netstrmCURR_IF_VERSION 4 /* increment whenever you change the interface structure! */
+/* interface version 3 added GetRemAddr()
+ * interface version 4 added EnableKeepAlive() -- rgerhards, 2009-06-02
+ * */
/* prototypes */
PROTOTYPEObj(netstrm);
diff --git a/runtime/nsd.h b/runtime/nsd.h
index f0c9b9b6..8668c934 100644
--- a/runtime/nsd.h
+++ b/runtime/nsd.h
@@ -69,9 +69,13 @@ BEGINinterface(nsd) /* name must also be changed in ENDinterface macro! */
* sockets - not really desirable, but not the end of the world... TODO: should be
* reconsidered when a new ACL system is build. -- rgerhards, 2008-12-01
*/
+ /* v5 */
+ rsRetVal (*EnableKeepAlive)(nsd_t *pThis);
ENDinterface(nsd)
-#define nsdCURR_IF_VERSION 4 /* increment whenever you change the interface structure! */
-/* interface version 4 added GetRemAddr() */
+#define nsdCURR_IF_VERSION 5 /* increment whenever you change the interface structure! */
+/* interface version 4 added GetRemAddr()
+ * interface version 5 added EnableKeepAlive() -- rgerhards, 2009-06-02
+ */
/* interface for the select call */
BEGINinterface(nsdsel) /* name must also be changed in ENDinterface macro! */
diff --git a/runtime/nsd_gtls.c b/runtime/nsd_gtls.c
index 3a79a015..19dc8678 100644
--- a/runtime/nsd_gtls.c
+++ b/runtime/nsd_gtls.c
@@ -82,6 +82,7 @@ static gnutls_certificate_credentials xcred;
static gnutls_dh_params dh_params;
#ifdef DEBUG
+#if 0 /* uncomment, if needed some time again -- DEV Debug only */
/* This defines a log function to be provided to GnuTLS. It hopefully
* helps us track down hard to find problems.
* rgerhards, 2008-06-20
@@ -90,6 +91,7 @@ static void logFunction(int level, const char *msg)
{
dbgprintf("GnuTLS log msg, level %d: %s\n", level, msg);
}
+#endif
#endif /* #ifdef DEBUG */
@@ -333,7 +335,7 @@ gtlsGetCertInfo(nsd_gtls_t *pThis, cstr_t **ppStr)
gnutls_x509_crt_deinit(cert);
}
- CHKiRet(rsCStrFinish(pStr));
+ CHKiRet(cstrFinalize(pStr));
*ppStr = pStr;
finalize_it:
@@ -453,7 +455,7 @@ GenFingerprintStr(uchar *pFingerprint, size_t sizeFingerprint, cstr_t **ppStr)
snprintf((char*)buf, sizeof(buf), ":%2.2X", pFingerprint[i]);
CHKiRet(rsCStrAppendStrWithLen(pStr, buf, 3));
}
- CHKiRet(rsCStrFinish(pStr));
+ CHKiRet(cstrFinalize(pStr));
*ppStr = pStr;
@@ -721,7 +723,7 @@ gtlsGetCN(nsd_gtls_t *pThis, gnutls_x509_crt *pCert, cstr_t **ppstrCN)
}
++i; /* char processed */
}
- CHKiRet(rsCStrFinish(pstrCN));
+ CHKiRet(cstrFinalize(pstrCN));
/* we got it - we ignore the rest of the DN string (if any). So we may
* not detect if it contains more than one CN
@@ -882,7 +884,7 @@ gtlsChkPeerName(nsd_gtls_t *pThis, gnutls_x509_crt *pCert)
if(!bFoundPositiveMatch) {
dbgprintf("invalid peer name, not permitted to talk to it\n");
if(pThis->bReportAuthErr == 1) {
- CHKiRet(rsCStrFinish(pStr));
+ CHKiRet(cstrFinalize(pStr));
errno = 0;
errmsg.LogError(0, RS_RET_INVALID_FINGERPRINT, "error: peer name not authorized - "
"not permitted to talk to it. Names: %s",
@@ -1558,6 +1560,16 @@ finalize_it:
RETiRet;
}
+/* Enable KEEPALIVE handling on the socket.
+ * rgerhards, 2009-06-02
+ */
+static rsRetVal
+EnableKeepAlive(nsd_t *pNsd)
+{
+ return nsd_ptcp.EnableKeepAlive(pNsd);
+}
+
+
/* open a connection to a remote host (server). With GnuTLS, we always
* open a plain tcp socket and then, if in TLS mode, do a handshake on it.
@@ -1667,6 +1679,7 @@ CODESTARTobjQueryInterface(nsd_gtls)
pIf->GetRemoteHName = GetRemoteHName;
pIf->GetRemoteIP = GetRemoteIP;
pIf->GetRemAddr = GetRemAddr;
+ pIf->EnableKeepAlive = EnableKeepAlive;
finalize_it:
ENDobjQueryInterface(nsd_gtls)
diff --git a/runtime/nsd_ptcp.c b/runtime/nsd_ptcp.c
index cc531ca0..54ee0666 100644
--- a/runtime/nsd_ptcp.c
+++ b/runtime/nsd_ptcp.c
@@ -614,6 +614,34 @@ finalize_it:
}
+/* Enable KEEPALIVE handling on the socket.
+ * rgerhards, 2009-06-02
+ */
+static rsRetVal
+EnableKeepAlive(nsd_t *pNsd)
+{
+ nsd_ptcp_t *pThis = (nsd_ptcp_t*) pNsd;
+ int ret;
+ int optval;
+ socklen_t optlen;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, nsd_ptcp);
+
+ optval = 1;
+ optlen = sizeof(optval);
+ ret = setsockopt(pThis->sock, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen);
+ if(ret < 0) {
+ dbgprintf("EnableKeepAlive socket call returns error %d\n", ret);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+ dbgprintf("KEEPALIVE enabled for nsd %p\n", pThis);
+
+finalize_it:
+ RETiRet;
+}
+
+
/* open a connection to a remote host (server).
* rgerhards, 2008-03-19
*/
@@ -754,6 +782,7 @@ CODESTARTobjQueryInterface(nsd_ptcp)
pIf->GetRemoteHName = GetRemoteHName;
pIf->GetRemoteIP = GetRemoteIP;
pIf->CheckConnection = CheckConnection;
+ pIf->EnableKeepAlive = EnableKeepAlive;
finalize_it:
ENDobjQueryInterface(nsd_ptcp)
diff --git a/runtime/obj-types.h b/runtime/obj-types.h
index 914c2f2c..78829f94 100644
--- a/runtime/obj-types.h
+++ b/runtime/obj-types.h
@@ -280,7 +280,7 @@ rsRetVal objName##ClassExit(void) \
* rgerhards, 2008-01-30
*/
#define BEGINobjDestruct(OBJ) \
- rsRetVal OBJ##Destruct(OBJ##_t **ppThis) \
+ rsRetVal OBJ##Destruct(OBJ##_t __attribute__((unused)) **ppThis) \
{ \
DEFiRet; \
int iCancelStateSave; \
@@ -314,7 +314,7 @@ rsRetVal objName##ClassExit(void) \
#define PROTOTYPEObjDebugPrint(obj) rsRetVal obj##DebugPrint(obj##_t *pThis)
#define INTERFACEObjDebugPrint(obj) rsRetVal (*DebugPrint)(obj##_t *pThis)
#define BEGINobjDebugPrint(obj) \
- rsRetVal obj##DebugPrint(obj##_t *pThis) \
+ rsRetVal obj##DebugPrint(obj##_t __attribute__((unused)) *pThis) \
{ \
DEFiRet; \
diff --git a/runtime/obj.c b/runtime/obj.c
index 2a9df9ed..355c0f97 100644
--- a/runtime/obj.c
+++ b/runtime/obj.c
@@ -447,7 +447,7 @@ objDeserializeEmbedStr(cstr_t **ppStr, strm_t *pStrm)
CHKiRet(rsCStrAppendChar(pStr, c));
NEXTC;
}
- CHKiRet(rsCStrFinish(pStr));
+ CHKiRet(cstrFinalize(pStr));
*ppStr = pStr;
@@ -515,7 +515,7 @@ static rsRetVal objDeserializeStr(cstr_t **ppCStr, int iLen, strm_t *pStrm)
CHKiRet(rsCStrAppendChar(pCStr, c));
NEXTC;
}
- CHKiRet(rsCStrFinish(pCStr));
+ CHKiRet(cstrFinalize(pCStr));
/* check terminator */
if(c != ':') ABORT_FINALIZE(RS_RET_INVALID_DELIMITER);
@@ -629,7 +629,7 @@ static rsRetVal objDeserializeProperty(var_t *pProp, strm_t *pStrm)
CHKiRet(rsCStrAppendChar(pProp->pcsName, c));
NEXTC;
}
- CHKiRet(rsCStrFinish(pProp->pcsName));
+ CHKiRet(cstrFinalize(pProp->pcsName));
/* property type */
CHKiRet(objDeserializeNumber(&i, pStrm));
diff --git a/runtime/parser.c b/runtime/parser.c
index b4ab0a3e..0b45bfd5 100644
--- a/runtime/parser.c
+++ b/runtime/parser.c
@@ -191,6 +191,31 @@ sanitizeMessage(msg_t *pMsg)
lenMsg--;
}
+ /* it is much quicker to sweep over the message and see if it actually
+ * needs sanitation than to do the sanitation in any case. So we first do
+ * this and terminate when it is not needed - which is expectedly the case
+ * for the vast majority of messages. -- rgerhards, 2009-06-15
+ */
+ int bNeedSanitize = 0;
+ for(iSrc = 0 ; iSrc < lenMsg ; iSrc++) {
+ if(pszMsg[iSrc] < 32) {
+ if(pszMsg[iSrc] == '\0' || bEscapeCCOnRcv) {
+ bNeedSanitize = 1;
+ break;
+ }
+ }
+ }
+ if(bNeedSanitize == 0) {
+ /* what a shame - we do not have a \0 byte...
+ * TODO: think about adding it or otherwise be able to use it...
+ */
+ uchar *pRaw;
+ CHKmalloc(pRaw = realloc(pMsg->pszRawMsg, pMsg->iLenRawMsg + 1));
+ pRaw[pMsg->iLenRawMsg] = '\0';
+ pMsg->pszRawMsg = pRaw;
+ FINALIZE;
+ }
+
/* now copy over the message and sanitize it */
/* TODO: can we get cheaper memory alloc? {alloca()?}*/
iMaxLine = glbl.GetMaxLine();
@@ -259,6 +284,7 @@ rsRetVal parseMsg(msg_t *pMsg)
DEFiRet;
uchar *msg;
int pri;
+ int iPriText;
CHKiRet(sanitizeMessage(pMsg));
@@ -268,11 +294,19 @@ rsRetVal parseMsg(msg_t *pMsg)
/* pull PRI */
pri = DEFUPRI;
msg = pMsg->pszRawMsg;
+ iPriText = 0;
if(*msg == '<') {
+ /* while we process the PRI, we also fill the PRI textual representation
+ * inside the msg object. This may not be ideal from an OOP point of view,
+ * but it offers us performance...
+ */
pri = 0;
while(isdigit((int) *++msg)) {
+ pMsg->bufPRI[iPriText++ % 4] = *msg; /* mod 4 to guard against malformed messages! */
pri = 10 * pri + (*msg - '0');
}
+ pMsg->bufPRI[iPriText % 4] = '\0';
+ pMsg->iLenPRI = iPriText % 4;
if(*msg == '>')
++msg;
if(pri & ~(LOG_FACMASK|LOG_PRIMASK))
@@ -280,10 +314,10 @@ rsRetVal parseMsg(msg_t *pMsg)
}
pMsg->iFacility = LOG_FAC(pri);
pMsg->iSeverity = LOG_PRI(pri);
- MsgSetUxTradMsg(pMsg, (char*) msg);
+ MsgSetAfterPRIOffs(pMsg, msg - pMsg->pszRawMsg);
if(pMsg->bParseHOSTNAME == 0)
- MsgSetHOSTNAME(pMsg, (char*) pMsg->pszRcvFrom);
+ 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.
diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h
index a4a8a204..32177a9f 100644
--- a/runtime/rsyslog.h
+++ b/runtime/rsyslog.h
@@ -84,6 +84,14 @@ typedef rsRetVal (*errLogFunc_t)(uchar*); /* this is a trick to store a function
typedef struct permittedPeers_s permittedPeers_t; /* this should go away in the long term -- rgerhards, 2008-05-19 */
typedef struct permittedPeerWildcard_s permittedPeerWildcard_t; /* this should go away in the long term -- rgerhards, 2008-05-19 */
typedef struct tcpsrv_s tcpsrv_t;
+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 rsRetVal (*prsf_t)(struct vmstk_s*, int); /* pointer to a RainerScript function */
+
+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;
@@ -95,6 +103,8 @@ typedef unsigned int u_int32_t; /* TODO: is this correct? */
typedef int socklen_t;
#endif
+typedef char bool; /* I intentionally use char, to keep it slim so that many fit into the CPU cache! */
+
/* settings for flow control
* TODO: is there a better place for them? -- rgerhards, 2008-03-14
*/
@@ -258,7 +268,18 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth
RS_RET_FUNC_MISSING_EXPR = -2111, /**< no expression after comma in function call (rainerscript) */
RS_RET_INVLD_NBR_ARGUMENTS = -2112, /**< invalid number of arguments for function call (rainerscript) */
RS_RET_INVLD_FUNC = -2113, /**< invalid function name for function call (rainerscript) */
- RS_RET_NONFATAL_CONFIG_ERR = -2123, /**< non-fatal error during config processing */
+ RS_RET_DUP_FUNC_NAME = -2114, /**< duplicate function name (rainerscript) */
+ RS_RET_UNKNW_FUNC = -2115, /**< unkown function name (rainerscript) */
+ RS_RET_ERR_RLIM_NOFILE = -2116, /**< error setting max. nbr open files process limit */
+ RS_RET_ERR_CREAT_PIPE = -2117, /**< error during pipe creation */
+ RS_RET_ERR_FORK = -2118, /**< error during fork() */
+ RS_RET_ERR_WRITE_PIPE = -2119, /**< error writing to pipe */
+ RS_RET_RSCORE_TOO_OLD = -2120, /**< rsyslog core is too old for ... (eg this plugin) */
+ RS_RET_DEFER_COMMIT = -2121, /**< output plugin status: not yet committed (an OK state!) */
+ 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_FILENAME_INVALID = -2140, /**< filename invalid, not found, no access, ... */
/* RainerScript error messages (range 1000.. 1999) */
RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */
@@ -350,6 +371,7 @@ typedef enum rsObjectID rsObjID;
/* 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)));
+
#include "debug.h"
#include "obj.h"
diff --git a/runtime/stream.c b/runtime/stream.c
index f1f69cc8..1cff2da6 100644
--- a/runtime/stream.c
+++ b/runtime/stream.c
@@ -368,7 +368,7 @@ strmReadLine(strm_t *pThis, cstr_t **ppCStr)
CHKiRet(rsCStrAppendChar(*ppCStr, c));
CHKiRet(strmReadChar(pThis, &c));
}
- CHKiRet(rsCStrFinish(*ppCStr));
+ CHKiRet(cstrFinalize(*ppCStr));
finalize_it:
if(iRet != RS_RET_OK && *ppCStr != NULL)
diff --git a/runtime/stringbuf.c b/runtime/stringbuf.c
index 07256fab..a2d9c599 100644
--- a/runtime/stringbuf.c
+++ b/runtime/stringbuf.c
@@ -6,8 +6,9 @@
* Please see syslogd.c for license information.
* All functions in this "class" start with rsCStr (rsyslog Counted String).
* begun 2005-09-07 rgerhards
+ * did some optimization (read: bugs!) rgerhards, 2009-06-16
*
- * Copyright (C) 2007-2008 by Rainer Gerhards and Adiscon GmbH
+ * Copyright (C) 2007-2009 by Rainer Gerhards and Adiscon GmbH
*
* This file is part of the rsyslog runtime library.
*
@@ -40,6 +41,7 @@
#include "regexp.h"
#include "obj.h"
+uchar* rsCStrGetSzStr(cstr_t *pThis);
/* ################################################################# *
* private members *
@@ -54,15 +56,14 @@ DEFobjCurrIf(regexp)
* ################################################################# */
-rsRetVal rsCStrConstruct(cstr_t **ppThis)
+rsRetVal cstrConstruct(cstr_t **ppThis)
{
DEFiRet;
cstr_t *pThis;
ASSERT(ppThis != NULL);
- if((pThis = (cstr_t*) calloc(1, sizeof(cstr_t))) == NULL)
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ CHKmalloc(pThis = (cstr_t*) calloc(1, sizeof(cstr_t)));
rsSETOBJTYPE(pThis, OIDrsCStr);
pThis->pBuf = NULL;
@@ -89,7 +90,7 @@ rsRetVal rsCStrConstructFromszStr(cstr_t **ppThis, uchar *sz)
CHKiRet(rsCStrConstruct(&pThis));
- pThis->iBufSize = pThis->iStrLen = strlen((char*)(char *) sz);
+ pThis->iBufSize = pThis->iStrLen = strlen((char *) sz);
if((pThis->pBuf = (uchar*) malloc(sizeof(uchar) * pThis->iStrLen)) == NULL) {
RSFREEOBJ(pThis);
ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
@@ -137,24 +138,8 @@ void rsCStrDestruct(cstr_t **ppThis)
{
cstr_t *pThis = *ppThis;
- /* rgerhards 2005-10-19: The free of pBuf was contained in conditional compilation.
- * The code was only compiled if STRINGBUF_TRIM_ALLOCSIZE was set to 1. I honestly
- * do not know why it was so, I think it was an artifact. Anyhow, I have changed this
- * now. Should there any issue occur, this comment hopefully will shed some light
- * on what happened. I re-verified, and this function has never before been called
- * by anyone. So changing it can have no impact for obvious reasons...
- *
- * rgerhards, 2008-02-20: I changed the interface to the new calling conventions, where
- * the destructor receives a pointer to the object, so that it can set it to NULL.
- */
- if(pThis->pBuf != NULL) {
- free(pThis->pBuf);
- }
-
- if(pThis->pszBuf != NULL) {
- free(pThis->pszBuf);
- }
-
+ free(pThis->pBuf);
+ free(pThis->pszBuf);
RSFREEOBJ(pThis);
*ppThis = NULL;
}
@@ -166,12 +151,14 @@ void rsCStrDestruct(cstr_t **ppThis)
* allocated. In practice, a bit more is allocated because we envision that
* some more characters may be added after these.
* rgerhards, 2008-01-07
+ * changed to utilized realloc() -- rgerhards, 2009-06-16
*/
-static rsRetVal rsCStrExtendBuf(cstr_t *pThis, size_t iMinNeeded)
+static rsRetVal
+rsCStrExtendBuf(cstr_t *pThis, size_t iMinNeeded)
{
- DEFiRet;
uchar *pNewBuf;
size_t iNewSize;
+ DEFiRet;
/* first compute the new size needed */
if(iMinNeeded > pThis->iAllocIncrement) {
@@ -187,15 +174,9 @@ static rsRetVal rsCStrExtendBuf(cstr_t *pThis, size_t iMinNeeded)
}
iNewSize += pThis->iBufSize; /* add current size */
- /* and then allocate and copy over */
/* DEV debugging only: dbgprintf("extending string buffer, old %d, new %d\n", pThis->iBufSize, iNewSize); */
- if((pNewBuf = (uchar*) malloc(iNewSize * sizeof(uchar))) == NULL)
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- memcpy(pNewBuf, pThis->pBuf, pThis->iBufSize);
+ CHKmalloc(pNewBuf = (uchar*) realloc(pThis->pBuf, iNewSize * sizeof(uchar)));
pThis->iBufSize = iNewSize;
- if(pThis->pBuf != NULL) {
- free(pThis->pBuf);
- }
pThis->pBuf = pNewBuf;
finalize_it:
@@ -288,6 +269,29 @@ finalize_it:
}
+/* NEW VARIANT
+ * Append a character to the current string object. This may only be done until
+ * cstrFinalize() is called.
+ * rgerhards, 2009-06-16
+ */
+rsRetVal cstrAppendChar(cstr_t *pThis, uchar c)
+{
+ DEFiRet;
+
+ rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
+
+ if(pThis->iStrLen >= pThis->iBufSize) {
+ CHKiRet(rsCStrExtendBuf(pThis, 1)); /* need more memory! */
+ }
+
+ /* ok, when we reach this, we have sufficient memory */
+ *(pThis->pBuf + pThis->iStrLen++) = c;
+
+finalize_it:
+ RETiRet;
+}
+
+
/* Sets the string object to the classigal sz-string provided.
* Any previously stored vlaue is discarded. If a NULL pointer
* the the new value (pszNew) is provided, an empty string is
@@ -299,10 +303,8 @@ rsRetVal rsCStrSetSzStr(cstr_t *pThis, uchar *pszNew)
{
rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
- if(pThis->pBuf != NULL)
- free(pThis->pBuf);
- if(pThis->pszBuf != NULL)
- free(pThis->pszBuf);
+ free(pThis->pBuf);
+ free(pThis->pszBuf);
if(pszNew == NULL) {
pThis->iStrLen = 0;
pThis->iBufSize = 0;
@@ -390,35 +392,42 @@ uchar* rsCStrGetSzStr(cstr_t *pThis)
}
+/* NEW VERSION for interface without separate psz buffer! */
+/* Returns the cstr data as a classical C sz string. We use that the
+ * Finalizer did properly terminate our string (but we may stil be NULL).
+ * So it is vital that the finalizer is called BEFORe this function here!
+ * The caller must not free or otherwise manipulate the returned string and must not
+ * destroy the CStr object as long as the ascii string is used.
+ * This function may return NULL, if the string is currently NULL. This
+ * is a feature, not a bug. If you need non-NULL in any case, use
+ * cstrGetSzStrNoNULL() instead.
+ * Note that due to the new single-buffer interface this function almost does nothing!
+ * rgerhards, 2006-09-16
+ */
+uchar* cstrGetSzStr(cstr_t *pThis)
+{
+ rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
+ return(pThis->pBuf);
+}
+
+
/* Converts the CStr object to a classical zero-terminated C string,
* returns that string and destroys the CStr object. The returned string
* MUST be freed by the caller. The function might return NULL if
* no memory can be allocated.
*
- * TODO:
- * This function should at some time become special. The base idea is to
- * add one extra byte to the end of the regular buffer, so that we can
- * convert it to an szString without the need to copy. The extra memory
- * footprint is not hefty, but the performance gain is potentially large.
- * To get it done now, I am not doing the optimiziation right now.
- * rgerhards, 2005-09-07
+ * This is the NEW replacement for rsCStrConvSzStrAndDestruct which does
+ * no longer utilize a special buffer but soley works on pBuf (and also
+ * assumes that cstrFinalize had been called).
*
- * rgerhards, 2007-09-04: I have changed the interface of this function. It now
- * returns an rsRetVal, so that we can communicate back if we have an error.
- * Using the standard method is much better than returning NULL. Secondly, NULL
- * was not actually an error - it was in indication if the string was empty.
- * This was needed in some parts of the code, in others not. I have now added
- * a second parameter to specify what the caller needs. I hope these changes
- * will make it less likely that the function is called incorrectly, what
- * previously happend quite often and was the cause of a number of program
- * aborts. So the parameters are now:
+ * Parameters are as follows:
* pointer to the object, pointer to string-pointer to receive string and
* bRetNULL: 0 - must not return NULL on empty string, return "" in that
* case, 1 - return NULL instead of an empty string.
* PLEASE NOTE: the caller must free the memory returned in ppSz in any case
* (except, of course, if it is NULL).
*/
-rsRetVal rsCStrConvSzStrAndDestruct(cstr_t *pThis, uchar **ppSz, int bRetNULL)
+rsRetVal cstrConvSzStrAndDestruct(cstr_t *pThis, uchar **ppSz, int bRetNULL)
{
DEFiRet;
uchar* pRetBuf;
@@ -429,14 +438,13 @@ rsRetVal rsCStrConvSzStrAndDestruct(cstr_t *pThis, uchar **ppSz, int bRetNULL)
if(pThis->pBuf == NULL) {
if(bRetNULL == 0) {
- if((pRetBuf = malloc(sizeof(uchar))) == NULL)
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ CHKmalloc(pRetBuf = malloc(sizeof(uchar)));
*pRetBuf = '\0';
} else {
pRetBuf = NULL;
}
} else
- pRetBuf = rsCStrGetSzStr(pThis);
+ pRetBuf = pThis->pBuf;
*ppSz = pRetBuf;
@@ -445,60 +453,39 @@ finalize_it:
* that we can NOT use the rsCStrDestruct function as it would
* also free the sz String buffer, which we pass on to the user.
*/
- if(pThis->pBuf != NULL)
- free(pThis->pBuf);
RSFREEOBJ(pThis);
-
RETiRet;
}
-#if STRINGBUF_TRIM_ALLOCSIZE == 1
- /* Only in this mode, we need to trim the string. To do
- * so, we must allocate a new buffer of the exact
- * string size, and then copy the old one over.
- */
- /* WARNING
- * STRINGBUF_TRIM_ALLOCSIZE can, in theory, be used to trim
- * memory buffers. This part of the code was inherited from
- * liblogging (where it is used in a different context) but
- * never put to use in rsyslog. The reason is that it is hardly
- * imaginable where the extra performance cost is worth the save
- * in memory alloc. Then Anders Blomdel rightfully pointed out that
- * the code does not work at all - and nobody even know that it
- * probably shouldn't. Rather than removing, I deciced to somewhat
- * fix the code, so that this feature may be enabled if somebody
- * really has a need for it. Be warned, however, that I NEVER
- * tested the fix. So if you intend to use this feature, you must
- * do full testing before you rely on it. -- rgerhards, 2008-02-12
- */
-rsRetVal rsCStrFinish(cstr_t __attribute__((unused)) *pThis)
+/* Finalize the string object. This must be called after all data is added to it
+ * but before that data is used.
+ * rgerhards, 2009-06-16
+ */
+rsRetVal
+cstrFinalize(cstr_t *pThis)
{
DEFiRet;
- uchar* pBuf;
rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
+
+ assert(pThis->bIsFinalized == 0);
- if((pBuf = malloc((pThis->iStrLen) * sizeof(uchar))) == NULL)
- { /* OK, in this case we use the previous buffer. At least
- * we have it ;)
- */
- }
- else
- { /* got the new buffer, so let's use it */
- memcpy(pBuf, pThis->pBuf, pThis->iStrLen);
- pThis->pBuf = pBuf;
+ if(pThis->iStrLen > 0) {
+ /* terminate string only if one exists */
+ CHKiRet(cstrAppendChar(pThis, '\0'));
+ --pThis->iStrLen; /* do NOT count the \0 byte */
}
+ pThis->bIsFinalized = 1;
+finalize_it:
RETiRet;
}
-#endif /* #if STRINGBUF_TRIM_ALLOCSIZE == 1 */
void rsCStrSetAllocIncrement(cstr_t *pThis, int iNewIncrement)
{
rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
assert(iNewIncrement > 0);
-
pThis->iAllocIncrement = iNewIncrement;
}
@@ -1027,56 +1014,6 @@ int rsCStrCaseInsensitiveLocateInSzStr(cstr_t *pThis, uchar *sz)
}
-#if 0 /* read comment below why this is commented out. In short: for future use! */
-/* locate the first occurence of a standard sz string inside a rsCStr object.
- * Returns the offset (0-bound) of this first occurrence. If not found, -1 is
- * returned.
- * rgerhards 2005-09-19
- * WARNING: I accidently created this function (I later noticed I didn't relly
- * need it... I will not remove the function, as it probably is useful
- * some time later. However, it is not fully tested, so start with testing
- * it before you put it to first use).
- */
-int rsCStrLocateSzStr(cstr_t *pThis, uchar *sz)
-{
- int iLenSz;
- int i;
- int iMax;
- int bFound;
- rsCHECKVALIDOBJECT(pThis, OIDrsCStr);
-
- if(sz == NULL)
- return 0;
-
- iLenSz = strlen((char*)sz);
- if(iLenSz == 0)
- return 0;
-
- /* compute the largest index where a match could occur - after all,
- * the to-be-located string must be able to be present in the
- * searched string (it needs its size ;)).
- */
- iMax = pThis->iStrLen - iLenSz;
-
- bFound = 0;
- i = 0;
- while(i < iMax && !bFound) {
- int iCheck;
- uchar *pComp = pThis->pBuf + i;
- for(iCheck = 0 ; iCheck < iLenSz ; ++iCheck)
- if(*(pComp + iCheck) != *(sz + iCheck))
- break;
- if(iCheck == iLenSz)
- bFound = 1; /* found! - else it wouldn't be equal */
- else
- ++i; /* on to the next try */
- }
-
- return(bFound ? i : -1);
-}
-#endif /* end comment out */
-
-
/* our exit function. TODO: remove once converted to a class
* rgerhards, 2008-03-11
*/
@@ -1100,11 +1037,5 @@ finalize_it:
}
-/*
- * Local variables:
- * c-indent-level: 8
- * c-basic-offset: 8
- * tab-width: 8
- * End:
- * vi:set ai:
+/* vi:set ai:
*/
diff --git a/runtime/stringbuf.h b/runtime/stringbuf.h
index 684133bb..d28aee26 100644
--- a/runtime/stringbuf.h
+++ b/runtime/stringbuf.h
@@ -49,13 +49,15 @@ typedef struct cstr_s
size_t iBufSize; /**< current maximum size of the string buffer */
size_t iStrLen; /**< length of the string in characters. */
size_t iAllocIncrement; /**< the amount of bytes the string should be expanded if it needs to */
+ bool bIsFinalized; /**< is this object finished and ready for use? (a debug aid, may be removed later TODO 2009-06-16) */
} cstr_t;
/**
* Construct a rsCStr object.
*/
-rsRetVal rsCStrConstruct(cstr_t **ppThis);
+rsRetVal cstrConstruct(cstr_t **ppThis);
+#define rsCStrConstruct(x) cstrConstruct((x))
rsRetVal rsCStrConstructFromszStr(cstr_t **ppThis, uchar *sz);
rsRetVal rsCStrConstructFromCStr(cstr_t **ppThis, cstr_t *pFrom);
@@ -63,6 +65,7 @@ rsRetVal rsCStrConstructFromCStr(cstr_t **ppThis, cstr_t *pFrom);
* Destruct the string buffer object.
*/
void rsCStrDestruct(cstr_t **ppThis);
+#define cstrDestruct(x) rsCStrDestruct((x))
/**
* Append a character to an existing string. If necessary, the
@@ -71,6 +74,7 @@ void rsCStrDestruct(cstr_t **ppThis);
* \param c Character to append to string.
*/
rsRetVal rsCStrAppendChar(cstr_t *pThis, uchar c);
+rsRetVal cstrAppendChar(cstr_t *pThis, uchar c);
/**
* Truncate "n" number of characters from the end of the
@@ -123,10 +127,9 @@ rsRetVal rsCStrAppendInt(cstr_t *pThis, long i);
rsRetVal strExit(void); /* TODO: remove once we have a real object interface! */
-uchar* rsCStrGetSzStr(cstr_t *pThis);
+uchar* __attribute__((deprecated)) rsCStrGetSzStr(cstr_t *pThis);
uchar* rsCStrGetSzStrNoNULL(cstr_t *pThis);
rsRetVal rsCStrSetSzStr(cstr_t *pThis, uchar *pszNew);
-rsRetVal rsCStrConvSzStrAndDestruct(cstr_t *pThis, uchar **ppSz, int bRetNULL);
int rsCStrCStrCmp(cstr_t *pCS1, cstr_t *pCS2);
int rsCStrSzStrCmp(cstr_t *pCS1, uchar *psz, size_t iLenSz);
int rsCStrOffsetSzStrCmp(cstr_t *pCS1, size_t iOffset, uchar *psz, size_t iLenSz);
@@ -142,6 +145,11 @@ rsRetVal rsCStrConvertToNumber(cstr_t *pStr, number_t *pNumber);
rsRetVal rsCStrConvertToBool(cstr_t *pStr, number_t *pBool);
rsRetVal rsCStrAppendCStr(cstr_t *pThis, cstr_t *pstrAppend);
+/* new calling interface */
+rsRetVal cstrFinalize(cstr_t *pThis);
+rsRetVal cstrConvSzStrAndDestruct(cstr_t *pThis, uchar **ppSz, int bRetNULL);
+uchar* cstrGetSzStr(cstr_t *pThis);
+
/* now come inline-like functions */
#ifdef NDEBUG
# define rsCStrLen(x) ((int)((x)->iStrLen))
@@ -149,19 +157,6 @@ rsRetVal rsCStrAppendCStr(cstr_t *pThis, cstr_t *pstrAppend);
int rsCStrLen(cstr_t *pThis);
#endif
-#if STRINGBUF_TRIM_ALLOCSIZE != 1
-/* This is the normal case (see comment in rsCStrFinish!). In those cases, the function
- * simply needs to do nothing, so that we can save us the function call.
- * rgerhards, 2008-02-12
- */
-# define rsCStrFinish(pThis) RS_RET_OK
-#else
- /**
- * Finish the string buffer dynamic allocation.
- */
- rsRetVal rsCStrFinish(cstr_t *pThis);
-#endif
-
#define rsCStrGetBufBeg(x) ((x)->pBuf)
rsRetVal strInit();
diff --git a/runtime/strms_sess.c b/runtime/strms_sess.c
new file mode 100644
index 00000000..0aeebe03
--- /dev/null
+++ b/runtime/strms_sess.c
@@ -0,0 +1,300 @@
+/* strms_sess.c
+ *
+ * This implements a session of the strmsrv object. For general
+ * comments, see header of strmsrv.c.
+ *
+ * Copyright 2007, 2008, 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 <errno.h>
+#include <ctype.h>
+
+#include "rsyslog.h"
+#include "dirty.h"
+#include "module-template.h"
+#include "net.h"
+#include "strmsrv.h"
+#include "strms_sess.h"
+#include "obj.h"
+#include "errmsg.h"
+#include "netstrm.h"
+#include "msg.h"
+#include "datetime.h"
+
+
+/* static data */
+DEFobjStaticHelpers
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(netstrm)
+DEFobjCurrIf(datetime)
+
+static int iMaxLine; /* maximum size of a single message */
+
+/* forward definitions */
+static rsRetVal Close(strms_sess_t *pThis);
+
+
+/* Standard-Constructor */
+BEGINobjConstruct(strms_sess) /* be sure to specify the object type also in END macro! */
+ENDobjConstruct(strms_sess)
+
+
+/* ConstructionFinalizer
+ */
+static rsRetVal
+strms_sessConstructFinalize(strms_sess_t *pThis)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strms_sess);
+ if(pThis->pSrv->OnSessConstructFinalize != NULL) {
+ CHKiRet(pThis->pSrv->OnSessConstructFinalize(&pThis->pUsr));
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* destructor for the strms_sess object */
+BEGINobjDestruct(strms_sess) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDestruct(strms_sess)
+ if(pThis->pStrm != NULL)
+ netstrm.Destruct(&pThis->pStrm);
+
+ if(pThis->pSrv->pOnSessDestruct != NULL) {
+ pThis->pSrv->pOnSessDestruct(&pThis->pUsr);
+ }
+ /* now destruct our own properties */
+ free(pThis->fromHost);
+ free(pThis->fromHostIP);
+ENDobjDestruct(strms_sess)
+
+
+/* debugprint for the strms_sess object */
+BEGINobjDebugPrint(strms_sess) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDebugPrint(strms_sess)
+ENDobjDebugPrint(strms_sess)
+
+
+/* set property functions */
+/* set the hostname. Note that the caller *hands over* the string. That is,
+ * the caller no longer controls it once SetHost() has received it. Most importantly,
+ * the caller must not free it. -- rgerhards, 2008-04-24
+ */
+static rsRetVal
+SetHost(strms_sess_t *pThis, uchar *pszHost)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strms_sess);
+ free(pThis->fromHost);
+ pThis->fromHost = pszHost;
+ RETiRet;
+}
+
+/* set the remote host's IP. Note that the caller *hands over* the string. That is,
+ * the caller no longer controls it once SetHostIP() has received it. Most importantly,
+ * the caller must not free it. -- rgerhards, 2008-05-16
+ */
+static rsRetVal
+SetHostIP(strms_sess_t *pThis, uchar *pszHostIP)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strms_sess);
+ free(pThis->fromHostIP);
+ pThis->fromHostIP = pszHostIP;
+ RETiRet;
+}
+
+static rsRetVal
+SetStrm(strms_sess_t *pThis, netstrm_t *pStrm)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strms_sess);
+ pThis->pStrm = pStrm;
+ RETiRet;
+}
+
+
+/* set our parent, the strmsrv object */
+static rsRetVal
+SetStrmsrv(strms_sess_t *pThis, strmsrv_t *pSrv)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strms_sess);
+ ISOBJ_TYPE_assert(pSrv, strmsrv);
+ pThis->pSrv = pSrv;
+ RETiRet;
+}
+
+
+/* set our parent listener info*/
+static rsRetVal
+SetLstnInfo(strms_sess_t *pThis, strmLstnPortList_t *pLstnInfo)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strms_sess);
+ assert(pLstnInfo != NULL);
+ pThis->pLstnInfo = pLstnInfo;
+ RETiRet;
+}
+
+
+static rsRetVal
+SetUsrP(strms_sess_t *pThis, void *pUsr)
+{
+ DEFiRet;
+ pThis->pUsr = pUsr;
+ RETiRet;
+}
+
+
+static void *
+GetUsrP(strms_sess_t *pThis)
+{
+ return pThis->pUsr;
+}
+
+
+/* Closes a STRM session
+ * No attention is paid to the return code
+ * of close, so potential-double closes are not detected.
+ */
+static rsRetVal
+Close(strms_sess_t *pThis)
+{
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, strms_sess);
+ netstrm.Destruct(&pThis->pStrm);
+ free(pThis->fromHost);
+ pThis->fromHost = NULL; /* not really needed, but... */
+ free(pThis->fromHostIP);
+ pThis->fromHostIP = NULL; /* not really needed, but... */
+
+ RETiRet;
+}
+
+
+
+/* Processes the data received via a STRM session. If there
+ * is no other way to handle it, data is discarded.
+ * Input parameter data is the data received, iLen is its
+ * len as returned from recv(). iLen must be 1 or more (that
+ * is errors must be handled by caller!). iSTRMSess must be
+ * the index of the STRM session that received the data.
+ * rgerhards 2005-07-04
+ * And another change while generalizing. We now return either
+ * RS_RET_OK, which means the session should be kept open
+ * or anything else, which means it must be closed.
+ * rgerhards, 2008-03-01
+ */
+static rsRetVal
+DataRcvd(strms_sess_t *pThis, char *pData, size_t iLen)
+{
+ DEFiRet;
+ char *pEnd;
+
+ ISOBJ_TYPE_assert(pThis, strms_sess);
+ assert(pData != NULL);
+ assert(iLen > 0);
+
+ /* We now copy the message to the session buffer. */
+ pEnd = pData + iLen; /* this is one off, which is intensional */
+
+ while(pData < pEnd) {
+ CHKiRet(pThis->pSrv->OnCharRcvd(pThis, (uchar)*pData++));
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* queryInterface function
+ * rgerhards, 2008-02-29
+ */
+BEGINobjQueryInterface(strms_sess)
+CODESTARTobjQueryInterface(strms_sess)
+ if(pIf->ifVersion != strms_sessCURR_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->DebugPrint = strms_sessDebugPrint;
+ pIf->Construct = strms_sessConstruct;
+ pIf->ConstructFinalize = strms_sessConstructFinalize;
+ pIf->Destruct = strms_sessDestruct;
+
+ pIf->Close = Close;
+ pIf->DataRcvd = DataRcvd;
+
+ pIf->SetUsrP = SetUsrP;
+ pIf->GetUsrP = GetUsrP;
+ pIf->SetStrmsrv = SetStrmsrv;
+ pIf->SetLstnInfo = SetLstnInfo;
+ pIf->SetHost = SetHost;
+ pIf->SetHostIP = SetHostIP;
+ pIf->SetStrm = SetStrm;
+finalize_it:
+ENDobjQueryInterface(strms_sess)
+
+
+/* exit our class
+ * rgerhards, 2008-03-10
+ */
+BEGINObjClassExit(strms_sess, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */
+CODESTARTObjClassExit(strms_sess)
+ /* release objects we no longer need */
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(netstrm, LM_NETSTRMS_FILENAME);
+ objRelease(datetime, CORE_COMPONENT);
+ENDObjClassExit(strms_sess)
+
+
+/* Initialize our class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-02-29
+ */
+BEGINObjClassInit(strms_sess, 1, OBJ_IS_CORE_MODULE) /* class, version - CHANGE class also in END MACRO! */
+ /* request objects we use */
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(netstrm, LM_NETSTRMS_FILENAME));
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
+
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ iMaxLine = glbl.GetMaxLine(); /* get maximum size we currently support */
+ objRelease(glbl, CORE_COMPONENT);
+
+ /* set our own handlers */
+ OBJSetMethodHandler(objMethod_DEBUGPRINT, strms_sessDebugPrint);
+ OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, strms_sessConstructFinalize);
+ENDObjClassInit(strms_sess)
+
+/* vim:set ai:
+ */
diff --git a/runtime/strms_sess.h b/runtime/strms_sess.h
new file mode 100644
index 00000000..6483c0c3
--- /dev/null
+++ b/runtime/strms_sess.h
@@ -0,0 +1,75 @@
+/* Definitions for strms_sess class. This implements a session of the
+ * generic stream server.
+ *
+ * Copyright 2008, 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_STRMS_SESS_H
+#define INCLUDED_STRMS_SESS_H
+
+#include "obj.h"
+
+/* a forward-definition, we are somewhat cyclic */
+struct strmsrv_s;
+
+/* the strms_sess object */
+struct strms_sess_s {
+ BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
+ strmsrv_t *pSrv; /* pointer back to my server (e.g. for callbacks) */
+ strmLstnPortList_t *pLstnInfo; /* pointer back to listener info */
+ netstrm_t *pStrm;
+// uchar *pMsg; /* message (fragment) received */
+ uchar *fromHost;
+ uchar *fromHostIP;
+ void *pUsr; /* a user-pointer */
+};
+
+
+/* interfaces */
+BEGINinterface(strms_sess) /* name must also be changed in ENDinterface macro! */
+ INTERFACEObjDebugPrint(strms_sess);
+ rsRetVal (*Construct)(strms_sess_t **ppThis);
+ rsRetVal (*ConstructFinalize)(strms_sess_t __attribute__((unused)) *pThis);
+ rsRetVal (*Destruct)(strms_sess_t **ppThis);
+ rsRetVal (*Close)(strms_sess_t *pThis);
+ rsRetVal (*DataRcvd)(strms_sess_t *pThis, char *pData, size_t iLen);
+ /* set methods */
+ rsRetVal (*SetStrmsrv)(strms_sess_t *pThis, struct strmsrv_s *pSrv);
+ rsRetVal (*SetLstnInfo)(strms_sess_t *pThis, strmLstnPortList_t *pLstnInfo);
+ rsRetVal (*SetUsrP)(strms_sess_t*, void*);
+ void* (*GetUsrP)(strms_sess_t*);
+ rsRetVal (*SetHost)(strms_sess_t *pThis, uchar*);
+ rsRetVal (*SetHostIP)(strms_sess_t *pThis, uchar*);
+ rsRetVal (*SetStrm)(strms_sess_t *pThis, netstrm_t*);
+ rsRetVal (*SetOnMsgReceive)(strms_sess_t *pThis, rsRetVal (*OnMsgReceive)(strms_sess_t*, uchar*, int));
+ENDinterface(strms_sess)
+#define strms_sessCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+/* interface changes
+ * to version v2, rgerhards, 2009-05-22
+ * - Data structures changed
+ * - SetLstnInfo entry point added
+ */
+
+
+/* prototypes */
+PROTOTYPEObj(strms_sess);
+
+
+#endif /* #ifndef INCLUDED_STRMS_SESS_H */
diff --git a/runtime/strmsrv.c b/runtime/strmsrv.c
new file mode 100644
index 00000000..3dc53a97
--- /dev/null
+++ b/runtime/strmsrv.c
@@ -0,0 +1,966 @@
+/* strmsrv.c
+ *
+ * This builds a basic stream server. It handles connection creation but
+ * not any protocol. Instead, it calls a "data received" entry point of the
+ * caller with any data received, in which case the caller must react accordingly.
+ * This module works together with the netstream drivers.
+ *
+ * There are actually two classes within the stream server code: one is
+ * the strmsrv itself, the other one is its sessions. This is a helper
+ * class to strmsrv.
+ *
+ * File begun on 2009-06-01 by RGerhards based on strmsrv.c. Note that strmsrv is
+ * placed under LGPL, which is possible because I carefully evaluated and
+ * eliminated all those parts of strmsrv which were not written by me.
+ *
+ * TODO: I would consider it useful to migrate tcpsrv.c/tcps_sess.c to this stream
+ * class here. The requires a little bit redesign, but should not be too hard. The
+ * core idea, already begun here, is that we still support lots of callbacks, but
+ * provide "canned" implementations for standard cases. That way, most upper-layer
+ * modules can be kept rather simple and without any extra overhead. Note that
+ * to support this, tcps_sess.c would need to extract the message reception state
+ * machine to a separate module which then is called via the DoCharRcvd() interface
+ * of this class here. -- rgerhards, 2009-06-01
+ *
+ * Copyright 2007, 2008, 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 <errno.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include "rsyslog.h"
+#include "dirty.h"
+#include "cfsysline.h"
+#include "module-template.h"
+#include "net.h"
+#include "srUtils.h"
+#include "conf.h"
+#include "strmsrv.h"
+#include "obj.h"
+#include "glbl.h"
+#include "netstrms.h"
+#include "netstrm.h"
+#include "nssel.h"
+#include "errmsg.h"
+#include "unicode-helper.h"
+
+MODULE_TYPE_LIB
+
+/* defines */
+#define STRMSESS_MAX_DEFAULT 200 /* default for nbr of strm sessions if no number is given */
+#define STRMLSTN_MAX_DEFAULT 20 /* default for nbr of listeners */
+
+/* static data */
+DEFobjStaticHelpers
+DEFobjCurrIf(conf)
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(strms_sess)
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(net)
+DEFobjCurrIf(netstrms)
+DEFobjCurrIf(netstrm)
+DEFobjCurrIf(nssel)
+
+/* forward definitions */
+static rsRetVal create_strm_socket(strmsrv_t *pThis);
+
+/* standard callbacks, if the caller did not provide us with them (this helps keep us
+ * flexible while at the same time permits very simple upper-layer modules)
+ */
+/* this shall go into a specific ACL module! */
+static int
+isPermittedHost(struct sockaddr __attribute__((unused)) *addr, char __attribute__((unused)) *fromHostFQDN,
+ void __attribute__((unused)) *pUsrSrv, void __attribute__((unused)) *pUsrSess)
+{
+ return 1;
+}
+
+
+static rsRetVal
+doOpenLstnSocks(strmsrv_t *pSrv)
+{
+ ISOBJ_TYPE_assert(pSrv, strmsrv);
+ return create_strm_socket(pSrv);
+}
+
+
+static rsRetVal
+doRcvData(strms_sess_t *pSess, char *buf, size_t lenBuf, ssize_t *piLenRcvd)
+{
+ DEFiRet;
+ assert(pSess != NULL);
+ assert(piLenRcvd != NULL);
+
+ *piLenRcvd = lenBuf;
+ CHKiRet(netstrm.Rcv(pSess->pStrm, (uchar*) buf, piLenRcvd));
+finalize_it:
+ RETiRet;
+}
+
+static rsRetVal
+onRegularClose(strms_sess_t *pSess)
+{
+ DEFiRet;
+ assert(pSess != NULL);
+
+ /* process any incomplete frames left over */
+ //strms_sess.PrepareClose(pSess);
+ /* Session closed */
+ strms_sess.Close(pSess);
+ RETiRet;
+}
+
+
+static rsRetVal
+onErrClose(strms_sess_t *pSess)
+{
+ DEFiRet;
+ assert(pSess != NULL);
+
+ strms_sess.Close(pSess);
+ RETiRet;
+}
+
+/* ------------------------------ end callbacks ------------------------------ */
+
+/* add new listener port to listener port list
+ * rgerhards, 2009-05-21
+ */
+static inline rsRetVal
+addNewLstnPort(strmsrv_t *pThis, uchar *pszPort)
+{
+ strmLstnPortList_t *pEntry;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+
+ /* create entry */
+ CHKmalloc(pEntry = malloc(sizeof(strmLstnPortList_t)));
+ pEntry->pszPort = pszPort;
+ pEntry->pSrv = pThis;
+ CHKmalloc(pEntry->pszInputName = ustrdup(pThis->pszInputName));
+
+ /* and add to list */
+ pEntry->pNext = pThis->pLstnPorts;
+ pThis->pLstnPorts = pEntry;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* configure STRM listener settings.
+ * Note: pszPort is handed over to us - the caller MUST NOT free it!
+ * rgerhards, 2008-03-20
+ */
+static rsRetVal
+configureSTRMListen(strmsrv_t *pThis, uchar *pszPort)
+{
+ int i;
+ uchar *pPort = pszPort;
+ DEFiRet;
+
+ assert(pszPort != NULL);
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+
+ /* extract port */
+ i = 0;
+ while(isdigit((int) *pPort)) {
+ i = i * 10 + *pPort++ - '0';
+ }
+
+ if(i >= 0 && i <= 65535) {
+ CHKiRet(addNewLstnPort(pThis, pszPort));
+ } else {
+ errmsg.LogError(0, NO_ERRCODE, "Invalid STRM listen port %s - ignored.\n", pszPort);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Initialize the session table
+ * returns 0 if OK, somewhat else otherwise
+ */
+static rsRetVal
+STRMSessTblInit(strmsrv_t *pThis)
+{
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+ assert(pThis->pSessions == NULL);
+
+ dbgprintf("Allocating buffer for %d STRM sessions.\n", pThis->iSessMax);
+ if((pThis->pSessions = (strms_sess_t **) calloc(pThis->iSessMax, sizeof(strms_sess_t *))) == NULL) {
+ dbgprintf("Error: STRMSessInit() could not alloc memory for STRM session table.\n");
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* find a free spot in the session table. If the table
+ * is full, -1 is returned, else the index of the free
+ * entry (0 or higher).
+ */
+static int
+STRMSessTblFindFreeSpot(strmsrv_t *pThis)
+{
+ register int i;
+
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+
+ for(i = 0 ; i < pThis->iSessMax ; ++i) {
+ if(pThis->pSessions[i] == NULL)
+ break;
+ }
+
+ return((i < pThis->iSessMax) ? i : -1);
+}
+
+
+/* Get the next session index. Free session tables entries are
+ * skipped. This function is provided the index of the last
+ * session entry, or -1 if no previous entry was obtained. It
+ * returns the index of the next session or -1, if there is no
+ * further entry in the table. Please note that the initial call
+ * might as well return -1, if there is no session at all in the
+ * session table.
+ */
+static int
+STRMSessGetNxtSess(strmsrv_t *pThis, int iCurr)
+{
+ register int i;
+
+ BEGINfunc
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+ assert(pThis->pSessions != NULL);
+ for(i = iCurr + 1 ; i < pThis->iSessMax ; ++i) {
+ if(pThis->pSessions[i] != NULL)
+ break;
+ }
+
+ ENDfunc
+ return((i < pThis->iSessMax) ? i : -1);
+}
+
+
+/* De-Initialize STRM listner sockets.
+ * This function deinitializes everything, including freeing the
+ * session table. No STRM listen receive operations are permitted
+ * unless the subsystem is reinitialized.
+ * rgerhards, 2007-06-21
+ */
+static void deinit_strm_listener(strmsrv_t *pThis)
+{
+ int i;
+ strmLstnPortList_t *pEntry;
+ strmLstnPortList_t *pDel;
+
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+
+ if(pThis->pSessions != NULL) {
+ /* close all STRM connections! */
+ i = STRMSessGetNxtSess(pThis, -1);
+ while(i != -1) {
+ strms_sess.Destruct(&pThis->pSessions[i]);
+ /* now get next... */
+ i = STRMSessGetNxtSess(pThis, i);
+ }
+
+ /* we are done with the session table - so get rid of it... */
+ free(pThis->pSessions);
+ pThis->pSessions = NULL; /* just to make sure... */
+ }
+
+ /* free list of strm listen ports */
+ pEntry = pThis->pLstnPorts;
+ while(pEntry != NULL) {
+ free(pEntry->pszPort);
+ free(pEntry->pszInputName);
+ pDel = pEntry;
+ pEntry = pEntry->pNext;
+ free(pDel);
+ }
+
+ /* finally close our listen streams */
+ for(i = 0 ; i < pThis->iLstnMax ; ++i) {
+ netstrm.Destruct(pThis->ppLstn + i);
+ }
+}
+
+
+/* add a listen socket to our listen socket array. This is a callback
+ * invoked from the netstrm class. -- rgerhards, 2008-04-23
+ */
+static rsRetVal
+addStrmLstn(void *pUsr, netstrm_t *pLstn)
+{
+ strmLstnPortList_t *pPortList = (strmLstnPortList_t *) pUsr;
+ strmsrv_t *pThis = pPortList->pSrv;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+ ISOBJ_TYPE_assert(pLstn, netstrm);
+
+ if(pThis->iLstnMax >= STRMLSTN_MAX_DEFAULT)
+ ABORT_FINALIZE(RS_RET_MAX_LSTN_REACHED);
+
+ pThis->ppLstn[pThis->iLstnMax] = pLstn;
+ pThis->ppLstnPort[pThis->iLstnMax] = pPortList;
+ ++pThis->iLstnMax;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Initialize STRM listener socket for a single port
+ * rgerhards, 2009-05-21
+ */
+static inline rsRetVal
+initSTRMListener(strmsrv_t *pThis, strmLstnPortList_t *pPortEntry)
+{
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+ assert(pPortEntry != NULL);
+
+ /* TODO: add capability to specify local listen address! */
+ CHKiRet(netstrm.LstnInit(pThis->pNS, (void*)pPortEntry, addStrmLstn, pPortEntry->pszPort, NULL, pThis->iSessMax));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Initialize STRM sockets (for listener) and listens on them */
+static rsRetVal
+create_strm_socket(strmsrv_t *pThis)
+{
+ strmLstnPortList_t *pEntry;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+
+ /* init all configured ports */
+ pEntry = pThis->pLstnPorts;
+ while(pEntry != NULL) {
+ CHKiRet(initSTRMListener(pThis, pEntry));
+ pEntry = pEntry->pNext;
+ }
+
+ /* OK, we had success. Now it is also time to
+ * initialize our connections
+ */
+ if(STRMSessTblInit(pThis) != 0) {
+ /* OK, we are in some trouble - we could not initialize the
+ * session table, so we can not continue. We need to free all
+ * we have assigned so far, because we can not really use it...
+ */
+ errmsg.LogError(0, RS_RET_ERR, "Could not initialize STRM session table, suspending STRM message reception.");
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Accept new STRM connection; make entry in session table. If there
+ * is no more space left in the connection table, the new STRM
+ * connection is immediately dropped.
+ * ppSess has a pointer to the newly created session, if it succeeds.
+ * If it does not succeed, no session is created and ppSess is
+ * undefined. If the user has provided an OnSessAccept Callback,
+ * this one is executed immediately after creation of the
+ * session object, so that it can do its own initialization.
+ * rgerhards, 2008-03-02
+ */
+static rsRetVal
+SessAccept(strmsrv_t *pThis, strmLstnPortList_t *pLstnInfo, strms_sess_t **ppSess, netstrm_t *pStrm)
+{
+ DEFiRet;
+ strms_sess_t *pSess = NULL;
+ netstrm_t *pNewStrm = NULL;
+ int iSess = -1;
+ struct sockaddr_storage *addr;
+ uchar *fromHostFQDN = NULL;
+ uchar *fromHostIP = NULL;
+
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+ assert(pLstnInfo != NULL);
+
+ CHKiRet(netstrm.AcceptConnReq(pStrm, &pNewStrm));
+
+ /* Add to session list */
+ iSess = STRMSessTblFindFreeSpot(pThis);
+ if(iSess == -1) {
+ errno = 0;
+ errmsg.LogError(0, RS_RET_MAX_SESS_REACHED, "too many strm sessions - dropping incoming request");
+ ABORT_FINALIZE(RS_RET_MAX_SESS_REACHED);
+ }
+
+ if(pThis->bUseKeepAlive) {
+ CHKiRet(netstrm.EnableKeepAlive(pNewStrm));
+ }
+
+ /* we found a free spot and can construct our session object */
+ CHKiRet(strms_sess.Construct(&pSess));
+ CHKiRet(strms_sess.SetStrmsrv(pSess, pThis));
+ CHKiRet(strms_sess.SetLstnInfo(pSess, pLstnInfo));
+
+ /* get the host name */
+ CHKiRet(netstrm.GetRemoteHName(pNewStrm, &fromHostFQDN));
+ CHKiRet(netstrm.GetRemoteIP(pNewStrm, &fromHostIP));
+ CHKiRet(netstrm.GetRemAddr(pNewStrm, &addr));
+ /* TODO: check if we need to strip the domain name here -- rgerhards, 2008-04-24 */
+
+ /* Here we check if a host is permitted to send us messages. If it isn't, we do not further
+ * process the message but log a warning (if we are configured to do this).
+ * rgerhards, 2005-09-26
+ */
+ if(pThis->pIsPermittedHost != NULL
+ && !pThis->pIsPermittedHost((struct sockaddr*) addr, (char*) fromHostFQDN, pThis->pUsr, pSess->pUsr)) {
+ dbgprintf("%s is not an allowed sender\n", fromHostFQDN);
+ if(glbl.GetOption_DisallowWarning()) {
+ errno = 0;
+ errmsg.LogError(0, RS_RET_HOST_NOT_PERMITTED, "STRM message from disallowed sender %s discarded", fromHostFQDN);
+ }
+ ABORT_FINALIZE(RS_RET_HOST_NOT_PERMITTED);
+ }
+
+ /* OK, we have an allowed sender, so let's continue, what
+ * means we can finally fill in the session object.
+ */
+ CHKiRet(strms_sess.SetHost(pSess, fromHostFQDN));
+ fromHostFQDN = NULL; /* we handed this string over */
+ CHKiRet(strms_sess.SetHostIP(pSess, fromHostIP));
+ fromHostIP = NULL; /* we handed this string over */
+ CHKiRet(strms_sess.SetStrm(pSess, pNewStrm));
+ pNewStrm = NULL; /* prevent it from being freed in error handler, now done in strms_sess! */
+ CHKiRet(strms_sess.ConstructFinalize(pSess));
+
+ /* check if we need to call our callback */
+ if(pThis->pOnSessAccept != NULL) {
+ CHKiRet(pThis->pOnSessAccept(pThis, pSess));
+ }
+
+ *ppSess = pSess;
+ pThis->pSessions[iSess] = pSess;
+ pSess = NULL; /* this is now also handed over */
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pSess != NULL)
+ strms_sess.Destruct(&pSess);
+ if(pNewStrm != NULL)
+ netstrm.Destruct(&pNewStrm);
+ free(fromHostFQDN);
+ free(fromHostIP);
+ }
+
+ RETiRet;
+}
+
+
+static void
+RunCancelCleanup(void *arg)
+{
+ nssel_t **ppSel = (nssel_t**) arg;
+
+ if(*ppSel != NULL)
+ nssel.Destruct(ppSel);
+}
+
+
+/* This function is called to gather input. */
+#pragma GCC diagnostic ignored "-Wempty-body"
+static rsRetVal
+Run(strmsrv_t *pThis)
+{
+ DEFiRet;
+ int nfds;
+ int i;
+ int iSTRMSess;
+ int bIsReady;
+ strms_sess_t *pNewSess;
+ nssel_t *pSel;
+ ssize_t iRcvd;
+
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+
+ /* this is an endless loop - it is terminated by the framework canelling
+ * this thread. Thus, we also need to instantiate a cancel cleanup handler
+ * to prevent us from leaking anything. -- rgerharsd, 20080-04-24
+ */
+ pthread_cleanup_push(RunCancelCleanup, (void*) &pSel);
+ while(1) {
+ CHKiRet(nssel.Construct(&pSel));
+ // TODO: set driver
+ CHKiRet(nssel.ConstructFinalize(pSel));
+
+ /* Add the STRM listen sockets to the list of read descriptors. */
+ for(i = 0 ; i < pThis->iLstnMax ; ++i) {
+ CHKiRet(nssel.Add(pSel, pThis->ppLstn[i], NSDSEL_RD));
+ }
+
+ /* do the sessions */
+ iSTRMSess = STRMSessGetNxtSess(pThis, -1);
+ while(iSTRMSess != -1) {
+ /* TODO: access to pNsd is NOT really CLEAN, use method... */
+ CHKiRet(nssel.Add(pSel, pThis->pSessions[iSTRMSess]->pStrm, NSDSEL_RD));
+ /* now get next... */
+ iSTRMSess = STRMSessGetNxtSess(pThis, iSTRMSess);
+ }
+
+ /* wait for io to become ready */
+ CHKiRet(nssel.Wait(pSel, &nfds));
+
+ for(i = 0 ; i < pThis->iLstnMax ; ++i) {
+ CHKiRet(nssel.IsReady(pSel, pThis->ppLstn[i], NSDSEL_RD, &bIsReady, &nfds));
+ if(bIsReady) {
+ dbgprintf("New connect on NSD %p.\n", pThis->ppLstn[i]);
+ SessAccept(pThis, pThis->ppLstnPort[i], &pNewSess, pThis->ppLstn[i]);
+ --nfds; /* indicate we have processed one */
+ }
+ }
+
+ /* now check the sessions */
+ iSTRMSess = STRMSessGetNxtSess(pThis, -1);
+ while(nfds && iSTRMSess != -1) {
+ CHKiRet(nssel.IsReady(pSel, pThis->pSessions[iSTRMSess]->pStrm, NSDSEL_RD, &bIsReady, &nfds));
+ if(bIsReady) {
+ char buf[8*1024]; /* reception buffer - may hold a partial or multiple messages */
+ dbgprintf("netstream %p with new data\n", pThis->pSessions[iSTRMSess]->pStrm);
+
+ /* Receive message */
+ iRet = pThis->pRcvData(pThis->pSessions[iSTRMSess], buf, sizeof(buf), &iRcvd);
+ switch(iRet) {
+ case RS_RET_CLOSED:
+ pThis->pOnRegularClose(pThis->pSessions[iSTRMSess]);
+ strms_sess.Destruct(&pThis->pSessions[iSTRMSess]);
+ break;
+ case RS_RET_RETRY:
+ /* we simply ignore retry - this is not an error, but we also have not received anything */
+ break;
+ case RS_RET_OK:
+ /* valid data received, process it! */
+ if(strms_sess.DataRcvd(pThis->pSessions[iSTRMSess], buf, iRcvd) != RS_RET_OK) {
+ /* in this case, something went awfully wrong.
+ * We are instructed to terminate the session.
+ */
+ errmsg.LogError(0, NO_ERRCODE, "Tearing down STRM Session %d - see "
+ "previous messages for reason(s)\n", iSTRMSess);
+ pThis->pOnErrClose(pThis->pSessions[iSTRMSess]);
+ strms_sess.Destruct(&pThis->pSessions[iSTRMSess]);
+ }
+ break;
+ default:
+ errno = 0;
+ errmsg.LogError(0, iRet, "netstream session %p will be closed due to error\n",
+ pThis->pSessions[iSTRMSess]->pStrm);
+ pThis->pOnErrClose(pThis->pSessions[iSTRMSess]);
+ strms_sess.Destruct(&pThis->pSessions[iSTRMSess]);
+ break;
+ }
+ --nfds; /* indicate we have processed one */
+ }
+ iSTRMSess = STRMSessGetNxtSess(pThis, iSTRMSess);
+ }
+ CHKiRet(nssel.Destruct(&pSel));
+finalize_it: /* this is a very special case - this time only we do not exit the function,
+ * because that would not help us either. So we simply retry it. Let's see
+ * if that actually is a better idea. Exiting the loop wasn't we always
+ * crashed, which made sense (the rest of the engine was not prepared for
+ * that) -- rgerhards, 2008-05-19
+ */
+ /*EMPTY*/;
+ }
+
+ /* note that this point is usually not reached */
+ pthread_cleanup_pop(0); /* remove cleanup handler */
+
+ RETiRet;
+}
+#pragma GCC diagnostic warning "-Wempty-body"
+
+
+/* Standard-Constructor */
+BEGINobjConstruct(strmsrv) /* be sure to specify the object type also in END macro! */
+ pThis->iSessMax = STRMSESS_MAX_DEFAULT; /* TODO: useful default ;) */
+ /* set default callbacks (used if caller does not overwrite them) */
+ pThis->pIsPermittedHost = isPermittedHost;
+ pThis->OpenLstnSocks = doOpenLstnSocks;
+ pThis->pRcvData = doRcvData;
+ pThis->pOnRegularClose = onRegularClose;
+ pThis->pOnErrClose = onErrClose;
+ /* session specific callbacks */
+ //pThis->OnSessConstructFinalize =
+ //pThis->pOnSessDestruct =
+ENDobjConstruct(strmsrv)
+
+
+/* ConstructionFinalizer */
+static rsRetVal
+strmsrvConstructFinalize(strmsrv_t *pThis)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+
+ /* prepare network stream subsystem */
+ CHKiRet(netstrms.Construct(&pThis->pNS));
+ CHKiRet(netstrms.SetDrvrMode(pThis->pNS, pThis->iDrvrMode));
+ if(pThis->pszDrvrAuthMode != NULL)
+ CHKiRet(netstrms.SetDrvrAuthMode(pThis->pNS, pThis->pszDrvrAuthMode));
+ if(pThis->pPermPeers != NULL)
+ CHKiRet(netstrms.SetDrvrPermPeers(pThis->pNS, pThis->pPermPeers));
+ // TODO: set driver!
+ CHKiRet(netstrms.ConstructFinalize(pThis->pNS));
+
+ /* set up listeners */
+ CHKmalloc(pThis->ppLstn = calloc(STRMLSTN_MAX_DEFAULT, sizeof(netstrm_t*)));
+ CHKmalloc(pThis->ppLstnPort = calloc(STRMLSTN_MAX_DEFAULT, sizeof(strmLstnPortList_t*)));
+ iRet = pThis->OpenLstnSocks(pThis);
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pThis->pNS != NULL)
+ netstrms.Destruct(&pThis->pNS);
+ }
+ RETiRet;
+}
+
+
+/* destructor for the strmsrv object */
+BEGINobjDestruct(strmsrv) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDestruct(strmsrv)
+ if(pThis->OnDestruct != NULL)
+ pThis->OnDestruct(pThis->pUsr);
+
+ deinit_strm_listener(pThis);
+
+ if(pThis->pNS != NULL)
+ netstrms.Destruct(&pThis->pNS);
+ free(pThis->pszDrvrAuthMode);
+ free(pThis->ppLstn);
+ free(pThis->ppLstnPort);
+ free(pThis->pszInputName);
+ENDobjDestruct(strmsrv)
+
+
+/* debugprint for the strmsrv object */
+BEGINobjDebugPrint(strmsrv) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDebugPrint(strmsrv)
+ENDobjDebugPrint(strmsrv)
+
+/* set functions */
+static rsRetVal
+SetCBIsPermittedHost(strmsrv_t *pThis, int (*pCB)(struct sockaddr *addr, char *fromHostFQDN, void*, void*))
+{
+ DEFiRet;
+ pThis->pIsPermittedHost = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBOnSessAccept(strmsrv_t *pThis, rsRetVal (*pCB)(strmsrv_t*, strms_sess_t*))
+{
+ DEFiRet;
+ pThis->pOnSessAccept = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBOnDestruct(strmsrv_t *pThis, rsRetVal (*pCB)(void*))
+{
+ DEFiRet;
+ pThis->OnDestruct = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBOnSessConstructFinalize(strmsrv_t *pThis, rsRetVal (*pCB)(void*))
+{
+ DEFiRet;
+ pThis->OnSessConstructFinalize = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBOnSessDestruct(strmsrv_t *pThis, rsRetVal (*pCB)(void*))
+{
+ DEFiRet;
+ pThis->pOnSessDestruct = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBOnRegularClose(strmsrv_t *pThis, rsRetVal (*pCB)(strms_sess_t*))
+{
+ DEFiRet;
+ pThis->pOnRegularClose = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBOnErrClose(strmsrv_t *pThis, rsRetVal (*pCB)(strms_sess_t*))
+{
+ DEFiRet;
+ pThis->pOnErrClose = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetCBOpenLstnSocks(strmsrv_t *pThis, rsRetVal (*pCB)(strmsrv_t*))
+{
+ DEFiRet;
+ pThis->OpenLstnSocks = pCB;
+ RETiRet;
+}
+
+static rsRetVal
+SetUsrP(strmsrv_t *pThis, void *pUsr)
+{
+ DEFiRet;
+ pThis->pUsr = pUsr;
+ RETiRet;
+}
+
+static rsRetVal
+SetKeepAlive(strmsrv_t *pThis, int iVal)
+{
+ DEFiRet;
+ dbgprintf("keep-alive set to %d\n", iVal);
+ pThis->bUseKeepAlive = iVal;
+ RETiRet;
+}
+
+static rsRetVal
+SetOnCharRcvd(strmsrv_t *pThis, rsRetVal (*OnCharRcvd)(strms_sess_t*, uchar))
+{
+ DEFiRet;
+ assert(OnCharRcvd != NULL);
+ pThis->OnCharRcvd = OnCharRcvd;
+ RETiRet;
+}
+
+/* Set the input name to use -- rgerhards, 2008-12-10 */
+static rsRetVal
+SetInputName(strmsrv_t *pThis, uchar *name)
+{
+ uchar *pszName;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+ if(name == NULL)
+ pszName = NULL;
+ else
+ CHKmalloc(pszName = ustrdup(name));
+ free(pThis->pszInputName);
+ pThis->pszInputName = pszName;
+finalize_it:
+ RETiRet;
+}
+
+
+/* here follows a number of methods that shuffle authentication settings down
+ * to the drivers. Drivers not supporting these settings may return an error
+ * state.
+ * -------------------------------------------------------------------------- */
+
+/* set the driver mode -- rgerhards, 2008-04-30 */
+static rsRetVal
+SetDrvrMode(strmsrv_t *pThis, int iMode)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+ pThis->iDrvrMode = iMode;
+ RETiRet;
+}
+
+
+/* set the driver authentication mode -- rgerhards, 2008-05-19 */
+static rsRetVal
+SetDrvrAuthMode(strmsrv_t *pThis, uchar *mode)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+ CHKmalloc(pThis->pszDrvrAuthMode = ustrdup(mode));
+finalize_it:
+ RETiRet;
+}
+
+
+/* set the driver's permitted peers -- rgerhards, 2008-05-19 */
+static rsRetVal
+SetDrvrPermPeers(strmsrv_t *pThis, permittedPeers_t *pPermPeers)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+ pThis->pPermPeers = pPermPeers;
+ RETiRet;
+}
+
+
+/* End of methods to shuffle autentication settings to the driver.;
+
+ * -------------------------------------------------------------------------- */
+
+
+/* set max number of sessions
+ * this must be called before ConstructFinalize, or it will have no effect!
+ * rgerhards, 2009-04-09
+ */
+static rsRetVal
+SetSessMax(strmsrv_t *pThis, int iMax)
+{
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, strmsrv);
+ pThis->iSessMax = iMax;
+ RETiRet;
+}
+
+
+/* queryInterface function
+ * rgerhards, 2008-02-29
+ */
+BEGINobjQueryInterface(strmsrv)
+CODESTARTobjQueryInterface(strmsrv)
+ if(pIf->ifVersion != strmsrvCURR_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->DebugPrint = strmsrvDebugPrint;
+ pIf->Construct = strmsrvConstruct;
+ pIf->ConstructFinalize = strmsrvConstructFinalize;
+ pIf->Destruct = strmsrvDestruct;
+
+ pIf->configureSTRMListen = configureSTRMListen;
+ pIf->create_strm_socket = create_strm_socket;
+ pIf->Run = Run;
+
+ pIf->SetKeepAlive = SetKeepAlive;
+ pIf->SetUsrP = SetUsrP;
+ pIf->SetInputName = SetInputName;
+ pIf->SetSessMax = SetSessMax;
+ pIf->SetDrvrMode = SetDrvrMode;
+ pIf->SetDrvrAuthMode = SetDrvrAuthMode;
+ pIf->SetDrvrPermPeers = SetDrvrPermPeers;
+ pIf->SetCBIsPermittedHost = SetCBIsPermittedHost;
+ pIf->SetCBOpenLstnSocks = SetCBOpenLstnSocks;
+ pIf->SetCBOnSessAccept = SetCBOnSessAccept;
+ pIf->SetCBOnSessConstructFinalize = SetCBOnSessConstructFinalize;
+ pIf->SetCBOnSessDestruct = SetCBOnSessDestruct;
+ pIf->SetCBOnDestruct = SetCBOnDestruct;
+ pIf->SetCBOnRegularClose = SetCBOnRegularClose;
+ pIf->SetCBOnErrClose = SetCBOnErrClose;
+ pIf->SetOnCharRcvd = SetOnCharRcvd;
+
+finalize_it:
+ENDobjQueryInterface(strmsrv)
+
+
+/* exit our class
+ * rgerhards, 2008-03-10
+ */
+BEGINObjClassExit(strmsrv, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */
+CODESTARTObjClassExit(strmsrv)
+ /* release objects we no longer need */
+ objRelease(strms_sess, DONT_LOAD_LIB);
+ objRelease(conf, CORE_COMPONENT);
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(netstrms, DONT_LOAD_LIB);
+ objRelease(nssel, DONT_LOAD_LIB);
+ objRelease(netstrm, LM_NETSTRMS_FILENAME);
+ objRelease(net, LM_NET_FILENAME);
+ENDObjClassExit(strmsrv)
+
+
+/* Initialize our class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-02-29
+ */
+BEGINObjClassInit(strmsrv, 1, OBJ_IS_LOADABLE_MODULE) /* class, version - CHANGE class also in END MACRO! */
+ /* request objects we use */
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(net, LM_NET_FILENAME));
+ CHKiRet(objUse(netstrms, LM_NETSTRMS_FILENAME));
+ CHKiRet(objUse(netstrm, DONT_LOAD_LIB));
+ CHKiRet(objUse(nssel, DONT_LOAD_LIB));
+ CHKiRet(objUse(strms_sess, DONT_LOAD_LIB));
+ CHKiRet(objUse(conf, CORE_COMPONENT));
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+
+ /* set our own handlers */
+ OBJSetMethodHandler(objMethod_DEBUGPRINT, strmsrvDebugPrint);
+ OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, strmsrvConstructFinalize);
+ENDObjClassInit(strmsrv)
+
+
+/* --------------- here now comes the plumbing that makes as a library module --------------- */
+
+
+BEGINmodExit
+CODESTARTmodExit
+ /* de-init in reverse order! */
+ strmsrvClassExit();
+ strms_sessClassExit();
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_LIB_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+
+ /* Initialize all classes that are in our module - this includes ourselfs */
+ CHKiRet(strms_sessClassInit(pModInfo));
+ CHKiRet(strmsrvClassInit(pModInfo)); /* must be done after strms_sess, as we use it */
+ENDmodInit
+
+/* vim:set ai:
+ */
diff --git a/runtime/strmsrv.h b/runtime/strmsrv.h
new file mode 100644
index 00000000..86e529c3
--- /dev/null
+++ b/runtime/strmsrv.h
@@ -0,0 +1,112 @@
+/* Definitions for strmsrv class.
+ *
+ * Copyright 2008, 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_STRMSRV_H
+#define INCLUDED_STRMSRV_H
+
+#include "obj.h"
+#include "strms_sess.h"
+
+/* list of strm listen ports */
+struct strmLstnPortList_s {
+ uchar *pszPort; /**< the ports the listener shall listen on */
+ uchar *pszInputName; /**< value to be used as input name */
+ strmsrv_t *pSrv; /**< pointer to higher-level server instance */
+ strmLstnPortList_t *pNext; /**< next port or NULL */
+};
+
+
+/* the strmsrv object */
+struct strmsrv_s {
+ BEGINobjInstance; /**< Data to implement generic object - MUST be the first data element! */
+ int bUseKeepAlive; /**< use socket layer KEEPALIVE handling? */
+ netstrms_t *pNS; /**< pointer to network stream subsystem */
+ int iDrvrMode; /**< mode of the stream driver to use */
+ uchar *pszDrvrAuthMode; /**< auth mode of the stream driver to use */
+ uchar *pszInputName; /**< value to be used as input name */
+ permittedPeers_t *pPermPeers;/**< driver's permitted peers */
+ int iLstnMax; /**< max nbr of listeners currently supported */
+ netstrm_t **ppLstn; /**< our netstream listners */
+ strmLstnPortList_t **ppLstnPort; /**< pointer to relevant listen port description */
+ int iSessMax; /**< max number of sessions supported */
+ strmLstnPortList_t *pLstnPorts; /**< head pointer for listen ports */
+ int addtlFrameDelim; /**< additional frame delimiter for plain STRM syslog framing (e.g. to handle NetScreen) */
+ strms_sess_t **pSessions;/**< array of all of our sessions */
+ void *pUsr; /**< a user-settable pointer (provides extensibility for "derived classes")*/
+ /* callbacks */
+ int (*pIsPermittedHost)(struct sockaddr *addr, char *fromHostFQDN, void*pUsrSrv, void*pUsrSess);
+ rsRetVal (*pRcvData)(strms_sess_t*, char*, size_t, ssize_t *);
+ rsRetVal (*OpenLstnSocks)(struct strmsrv_s*);
+ rsRetVal (*pOnListenDeinit)(void*);
+ rsRetVal (*OnDestruct)(void*);
+ rsRetVal (*pOnRegularClose)(strms_sess_t *pSess);
+ rsRetVal (*pOnErrClose)(strms_sess_t *pSess);
+ /* session specific callbacks */
+ rsRetVal (*pOnSessAccept)(strmsrv_t *, strms_sess_t*);
+ rsRetVal (*OnSessConstructFinalize)(void*);
+ rsRetVal (*pOnSessDestruct)(void*);
+ rsRetVal (*OnCharRcvd)(strms_sess_t*, uchar);
+};
+
+
+/* interfaces */
+BEGINinterface(strmsrv) /* name must also be changed in ENDinterface macro! */
+ INTERFACEObjDebugPrint(strmsrv);
+ rsRetVal (*Construct)(strmsrv_t **ppThis);
+ rsRetVal (*ConstructFinalize)(strmsrv_t __attribute__((unused)) *pThis);
+ rsRetVal (*Destruct)(strmsrv_t **ppThis);
+ rsRetVal (*configureSTRMListen)(strmsrv_t*, uchar *pszPort);
+ //rsRetVal (*SessAccept)(strmsrv_t *pThis, strmLstnPortList_t*, strms_sess_t **ppSess, netstrm_t *pStrm);
+ rsRetVal (*create_strm_socket)(strmsrv_t *pThis);
+ rsRetVal (*Run)(strmsrv_t *pThis);
+ /* set methods */
+ rsRetVal (*SetAddtlFrameDelim)(strmsrv_t*, int);
+ rsRetVal (*SetInputName)(strmsrv_t*, uchar*);
+ rsRetVal (*SetKeepAlive)(strmsrv_t*, int);
+ rsRetVal (*SetUsrP)(strmsrv_t*, void*);
+ rsRetVal (*SetCBIsPermittedHost)(strmsrv_t*, int (*) (struct sockaddr *addr, char*, void*, void*));
+ rsRetVal (*SetCBOpenLstnSocks)(strmsrv_t *, rsRetVal (*)(strmsrv_t*));
+ rsRetVal (*SetCBOnDestruct)(strmsrv_t*, rsRetVal (*) (void*));
+ rsRetVal (*SetCBOnRegularClose)(strmsrv_t*, rsRetVal (*) (strms_sess_t*));
+ rsRetVal (*SetCBOnErrClose)(strmsrv_t*, rsRetVal (*) (strms_sess_t*));
+ rsRetVal (*SetDrvrMode)(strmsrv_t *pThis, int iMode);
+ rsRetVal (*SetDrvrAuthMode)(strmsrv_t *pThis, uchar *pszMode);
+ rsRetVal (*SetDrvrPermPeers)(strmsrv_t *pThis, permittedPeers_t*);
+ /* session specifics */
+ rsRetVal (*SetCBOnSessAccept)(strmsrv_t*, rsRetVal (*) (strmsrv_t*, strms_sess_t*));
+ rsRetVal (*SetCBOnSessDestruct)(strmsrv_t*, rsRetVal (*) (void*));
+ rsRetVal (*SetCBOnSessConstructFinalize)(strmsrv_t*, rsRetVal (*) (void*));
+ rsRetVal (*SetSessMax)(strmsrv_t *pThis, int iMaxSess);
+ rsRetVal (*SetOnCharRcvd)(strmsrv_t *pThis, rsRetVal (*OnMsgCharRcvd)(strms_sess_t*, uchar));
+ENDinterface(strmsrv)
+#define strmsrvCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+/* change for v?:
+ */
+
+
+/* prototypes */
+PROTOTYPEObj(strmsrv);
+
+/* the name of our library binary */
+#define LM_STRMSRV_FILENAME "lmstrmsrv"
+
+#endif /* #ifndef INCLUDED_STRMSRV_H */
diff --git a/runtime/unicode-helper.h b/runtime/unicode-helper.h
new file mode 100644
index 00000000..36d76a78
--- /dev/null
+++ b/runtime/unicode-helper.h
@@ -0,0 +1,54 @@
+/* This is the header file for unicode support.
+ *
+ * Currently, this is a dummy module.
+ * 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
+ *
+ * Begun 2009-05-21 RGerhards
+ *
+ * Copyright (C) 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 INCLUDED_UNICODE_HELPER_H
+#define INCLUDED_UNICODE_HELPER_H
+
+#include <string.h>
+
+static inline int ustrcmp(uchar *psz1, uchar *psz2)
+{
+ return strcmp((char*) psz1, (char*) psz2);
+}
+
+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))
+
+#endif /* multi-include protection */
+/* vim:set ai:
+ */
diff --git a/runtime/vm.c b/runtime/vm.c
index 23adea8b..8cbf9e12 100644
--- a/runtime/vm.c
+++ b/runtime/vm.c
@@ -27,6 +27,7 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
+#include <ctype.h>
#include "rsyslog.h"
#include "obj.h"
@@ -40,6 +41,142 @@ DEFobjCurrIf(vmstk)
DEFobjCurrIf(var)
DEFobjCurrIf(sysvar)
+/* ------------------------------ function registry code and structures ------------------------------ */
+
+/* we maintain a registry of known functions */
+/* currently, this is a singly-linked list, this shall become a binary
+ * tree when we add the real call interface. So far, entries are added
+ * at the root, only.
+ */
+typedef struct s_rsf_entry {
+ cstr_t *pName; /* function name */
+ prsf_t rsf; /* pointer to function code */
+ struct s_rsf_entry *pNext; /* Pointer to next element or NULL */
+} rsf_entry_t;
+rsf_entry_t *funcRegRoot = NULL;
+
+
+/* add a function to the function registry.
+ * The handed-over cstr_t* object must no longer be used by the caller.
+ * A duplicate function name is an error.
+ * rgerhards, 2009-04-06
+ */
+static rsRetVal
+rsfrAddFunction(uchar *szName, prsf_t rsf)
+{
+ rsf_entry_t *pEntry;
+ size_t lenName;
+ DEFiRet;
+
+ assert(szName != NULL);
+ assert(rsf != NULL);
+
+ /* first check if we have a duplicate name, with the current approach this means
+ * we need to go through the whole list.
+ */
+ lenName = strlen((char*)szName);
+ for(pEntry = funcRegRoot ; pEntry != NULL ; pEntry = pEntry->pNext)
+ if(!rsCStrSzStrCmp(pEntry->pName, szName, lenName))
+ ABORT_FINALIZE(RS_RET_DUP_FUNC_NAME);
+
+ /* unique name, so add to head of list */
+ CHKmalloc(pEntry = calloc(1, sizeof(rsf_entry_t)));
+ CHKiRet(rsCStrConstructFromszStr(&pEntry->pName, szName));
+ pEntry->rsf = rsf;
+ pEntry->pNext = funcRegRoot;
+ funcRegRoot = pEntry;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* find a function inside the function registry
+ * The caller provides a cstr_t with the function name and receives
+ * a function pointer back. If no function is found, an RS_RET_UNKNW_FUNC
+ * error is returned. So if the function returns with RS_RET_OK, the caller
+ * can savely assume the function pointer is valid.
+ * rgerhards, 2009-04-06
+ */
+static rsRetVal
+findRSFunction(cstr_t *pcsName, prsf_t *prsf)
+{
+ rsf_entry_t *pEntry;
+ rsf_entry_t *pFound;
+ DEFiRet;
+
+ assert(prsf != NULL);
+
+ /* find function by list walkthrough. */
+ pFound = NULL;
+ for(pEntry = funcRegRoot ; pEntry != NULL && pFound == NULL ; pEntry = pEntry->pNext)
+ if(!rsCStrCStrCmp(pEntry->pName, pcsName))
+ pFound = pEntry;
+
+ if(pFound == NULL)
+ ABORT_FINALIZE(RS_RET_UNKNW_FUNC);
+
+ *prsf = pFound->rsf;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* find the name of a RainerScript function whom's function pointer
+ * is known. This function returns the cstr_t object, which MUST NOT
+ * be modified by the caller.
+ * rgerhards, 2009-04-06
+ */
+static rsRetVal
+findRSFunctionName(prsf_t rsf, cstr_t **ppcsName)
+{
+ rsf_entry_t *pEntry;
+ rsf_entry_t *pFound;
+ DEFiRet;
+
+ assert(rsf != NULL);
+ assert(ppcsName != NULL);
+
+ /* find function by list walkthrough. */
+ pFound = NULL;
+ for(pEntry = funcRegRoot ; pEntry != NULL && pFound == NULL ; pEntry = pEntry->pNext)
+ if(pEntry->rsf == rsf)
+ pFound = pEntry;
+
+ if(pFound == NULL)
+ ABORT_FINALIZE(RS_RET_UNKNW_FUNC);
+
+ *ppcsName = pFound->pName;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* free the whole function registry
+ */
+static void
+rsfrRemoveAll(void)
+{
+ rsf_entry_t *pEntry;
+ rsf_entry_t *pEntryDel;
+
+ BEGINfunc
+ pEntry = funcRegRoot;
+ while(pEntry != NULL) {
+ pEntryDel = pEntry;
+ pEntry = pEntry->pNext;
+ rsCStrDestruct(&pEntryDel->pName);
+ free(pEntryDel);
+ }
+ funcRegRoot = NULL;
+ ENDfunc
+}
+
+
+/* ------------------------------ end function registry code and structures ------------------------------ */
+
/* ------------------------------ instruction set implementation ------------------------------ *
* The following functions implement the VM's instruction set.
@@ -331,7 +468,6 @@ CODESTARTop(PUSHSYSVAR)
finalize_it:
ENDop(PUSHSYSVAR)
-
/* The function call operation is only very roughly implemented. While the plumbing
* to reach this instruction is fine, the instruction itself currently supports only
* functions with a single argument AND with a name that we know.
@@ -341,20 +477,9 @@ ENDop(PUSHSYSVAR)
*/
BEGINop(FUNC_CALL) /* remember to set the instruction also in the ENDop macro! */
var_t *numOperands;
- var_t *operand1;
- int iStrlen;
CODESTARTop(FUNC_CALL)
vmstk.PopNumber(pThis->pStk, &numOperands);
- if(numOperands->val.num != 1)
- ABORT_FINALIZE(RS_RET_INVLD_NBR_ARGUMENTS);
- vmstk.PopString(pThis->pStk, &operand1); /* guess there's just one ;) */
- if(!rsCStrSzStrCmp(pOp->operand.pVar->val.pStr, (uchar*) "strlen", 6)) { /* only one supported so far ;) */
-RUNLOG_VAR("%s", rsCStrGetSzStr(operand1->val.pStr));
- iStrlen = strlen((char*) rsCStrGetSzStr(operand1->val.pStr));
-RUNLOG_VAR("%d", iStrlen);
- } else
- ABORT_FINALIZE(RS_RET_INVLD_FUNC);
- PUSHRESULTop(operand1, iStrlen); // TODO: dummy, FIXME
+ CHKiRet((*pOp->operand.rsf)(pThis->pStk, numOperands->val.num));
var.Destruct(&numOperands); /* no longer needed */
finalize_it:
ENDop(FUNC_CALL)
@@ -363,6 +488,89 @@ ENDop(FUNC_CALL)
/* ------------------------------ end instruction set implementation ------------------------------ */
+/* ------------------------------ begin built-in function implementation ------------------------------ */
+/* note: this shall probably be moved to a separate module, but for the time being we do it directly
+ * in here. This is on our way to get from a dirty to a clean solution via baby steps that are
+ * a bit less dirty each time...
+ *
+ * The advantage of doing it here is that we do not yet need to think about how to handle the
+ * exit case, where we must not unload function modules which functions are still referenced.
+ *
+ * CALLING INTERFACE:
+ * The function must pop its parameters off the stack and pop its result onto
+ * the stack when it is finished. The number of parameters the function was
+ * called with is provided to it. If the argument count is less then what the function
+ * expected, it may handle the situation with defaults (or return an error). If the
+ * argument count is greater than expected, returnung an error is highly
+ * recommended (use RS_RET_INVLD_NBR_ARGUMENTS for these cases).
+ *
+ * All function names are prefixed with "rsf_" (RainerScript Function) to have
+ * a separate "name space".
+ *
+ * rgerhards, 2009-04-06
+ */
+
+
+/* The strlen function, also probably a prototype of how all functions should be
+ * implemented.
+ * rgerhards, 2009-04-06
+ */
+static rsRetVal
+rsf_strlen(vmstk_t *pStk, int numOperands)
+{
+ DEFiRet;
+ var_t *operand1;
+ int iStrlen;
+
+ if(numOperands != 1)
+ ABORT_FINALIZE(RS_RET_INVLD_NBR_ARGUMENTS);
+
+ /* pop args and do operaton (trivial case here...) */
+ vmstk.PopString(pStk, &operand1);
+ iStrlen = strlen((char*) rsCStrGetSzStr(operand1->val.pStr));
+
+ /* Store result and cleanup */
+ var.SetNumber(operand1, iStrlen);
+ vmstk.Push(pStk, operand1);
+finalize_it:
+ RETiRet;
+}
+
+
+/* The "tolower" function, which converts its sole argument to lower case.
+ * Quite honestly, currently this is primarily a test driver for me...
+ * rgerhards, 2009-04-06
+ */
+static rsRetVal
+rsf_tolower(vmstk_t *pStk, int numOperands)
+{
+ DEFiRet;
+ var_t *operand1;
+ uchar *pSrc;
+ cstr_t *pcstr;
+ int iStrlen;
+
+ if(numOperands != 1)
+ ABORT_FINALIZE(RS_RET_INVLD_NBR_ARGUMENTS);
+
+ /* pop args and do operaton */
+ CHKiRet(rsCStrConstruct(&pcstr));
+ vmstk.PopString(pStk, &operand1);
+ pSrc = rsCStrGetSzStr(operand1->val.pStr);
+ iStrlen = strlen((char*)pSrc);
+ while(iStrlen--) {
+ CHKiRet(rsCStrAppendChar(pcstr, tolower(*pSrc++)));
+ }
+
+ /* Store result and cleanup */
+ CHKiRet(cstrFinalize(pcstr));
+ var.SetString(operand1, pcstr);
+ vmstk.Push(pStk, operand1);
+finalize_it:
+ RETiRet;
+}
+
+
/* Standard-Constructor
*/
BEGINobjConstruct(vm) /* be sure to specify the object type also in END macro! */
@@ -532,10 +740,23 @@ CODESTARTobjQueryInterface(vm)
pIf->PopBoolFromStack = PopBoolFromStack;
pIf->PopVarFromStack = PopVarFromStack;
pIf->SetMsg = SetMsg;
+ pIf->FindRSFunction = findRSFunction;
+ pIf->FindRSFunctionName = findRSFunctionName;
finalize_it:
ENDobjQueryInterface(vm)
+/* Exit the vm class.
+ * rgerhards, 2009-04-06
+ */
+BEGINObjClassExit(vm, OBJ_IS_CORE_MODULE) /* class, version */
+ rsfrRemoveAll();
+ objRelease(sysvar, CORE_COMPONENT);
+ objRelease(var, CORE_COMPONENT);
+ objRelease(vmstk, CORE_COMPONENT);
+ENDObjClassExit(vm)
+
+
/* Initialize the vm class. Must be called as the very first method
* before anything else is called inside this class.
* rgerhards, 2008-02-19
@@ -549,6 +770,11 @@ BEGINObjClassInit(vm, 1, OBJ_IS_CORE_MODULE) /* class, version */
/* set our own handlers */
OBJSetMethodHandler(objMethod_DEBUGPRINT, vmDebugPrint);
OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, vmConstructFinalize);
+
+ /* register built-in functions // TODO: move to its own module */
+ CHKiRet(rsfrAddFunction((uchar*)"strlen", rsf_strlen));
+ CHKiRet(rsfrAddFunction((uchar*)"tolower", rsf_tolower));
+
ENDObjClassInit(vm)
/* vi:set ai:
diff --git a/runtime/vm.h b/runtime/vm.h
index d2458220..cb3c69d0 100644
--- a/runtime/vm.h
+++ b/runtime/vm.h
@@ -55,8 +55,11 @@ BEGINinterface(vm) /* name must also be changed in ENDinterface macro! */
rsRetVal (*PopBoolFromStack)(vm_t *pThis, var_t **ppVar); /* there are a few cases where we need this... */
rsRetVal (*PopVarFromStack)(vm_t *pThis, var_t **ppVar); /* there are a few cases where we need this... */
rsRetVal (*SetMsg)(vm_t *pThis, msg_t *pMsg); /* there are a few cases where we need this... */
+ /* v2 (4.1.7) */
+ rsRetVal (*FindRSFunction)(cstr_t *pcsName, prsf_t *prsf); /* 2009-06-04 */
+ rsRetVal (*FindRSFunctionName)(prsf_t rsf, cstr_t **ppcsName); /* 2009-06-04 */
ENDinterface(vm)
-#define vmCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+#define vmCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */
/* prototypes */
diff --git a/runtime/vmop.c b/runtime/vmop.c
index a343481e..acacfc9e 100644
--- a/runtime/vmop.c
+++ b/runtime/vmop.c
@@ -32,10 +32,12 @@
#include "rsyslog.h"
#include "obj.h"
#include "vmop.h"
+#include "vm.h"
/* static data */
DEFobjStaticHelpers
DEFobjCurrIf(var)
+DEFobjCurrIf(vm)
/* forward definitions */
@@ -61,8 +63,10 @@ rsRetVal vmopConstructFinalize(vmop_t __attribute__((unused)) *pThis)
/* destructor for the vmop object */
BEGINobjDestruct(vmop) /* be sure to specify the object type also in END and CODESTART macros! */
CODESTARTobjDestruct(vmop)
- if(pThis->operand.pVar != NULL)
- var.Destruct(&pThis->operand.pVar);
+ if(pThis->opcode != opcode_FUNC_CALL) {
+ if(pThis->operand.pVar != NULL)
+ var.Destruct(&pThis->operand.pVar);
+ }
ENDobjDestruct(vmop)
@@ -72,13 +76,19 @@ BEGINobjDebugPrint(vmop) /* be sure to specify the object type also in END and C
cstr_t *pStrVar;
CODESTARTobjDebugPrint(vmop)
vmopOpcode2Str(pThis, &pOpcodeName);
- CHKiRet(rsCStrConstruct(&pStrVar));
- CHKiRet(rsCStrFinish(&pStrVar));
- if(pThis->operand.pVar != NULL) {
- CHKiRet(var.Obj2Str(pThis->operand.pVar, pStrVar));
+ if(pThis->opcode == opcode_FUNC_CALL) {
+ CHKiRet(vm.FindRSFunctionName(pThis->operand.rsf, &pStrVar));
+ assert(pStrVar != NULL);
+ } else {
+ CHKiRet(rsCStrConstruct(&pStrVar));
+ if(pThis->operand.pVar != NULL) {
+ CHKiRet(var.Obj2Str(pThis->operand.pVar, pStrVar));
+ }
}
+ CHKiRet(cstrFinalize(pStrVar));
dbgoprint((obj_t*) pThis, "%.12s\t%s\n", pOpcodeName, rsCStrGetSzStrNoNULL(pStrVar));
- rsCStrDestruct(&pStrVar);
+ if(pThis->opcode != opcode_FUNC_CALL)
+ rsCStrDestruct(&pStrVar);
finalize_it:
ENDobjDebugPrint(vmop)
@@ -98,6 +108,7 @@ static rsRetVal
Obj2Str(vmop_t *pThis, cstr_t *pstrPrg)
{
uchar *pOpcodeName;
+ cstr_t *pcsFuncName;
uchar szBuf[2048];
size_t lenBuf;
DEFiRet;
@@ -107,8 +118,13 @@ Obj2Str(vmop_t *pThis, cstr_t *pstrPrg)
vmopOpcode2Str(pThis, &pOpcodeName);
lenBuf = snprintf((char*) szBuf, sizeof(szBuf), "%s\t", pOpcodeName);
CHKiRet(rsCStrAppendStrWithLen(pstrPrg, szBuf, lenBuf));
- if(pThis->operand.pVar != NULL)
- CHKiRet(var.Obj2Str(pThis->operand.pVar, pstrPrg));
+ if(pThis->opcode == opcode_FUNC_CALL) {
+ CHKiRet(vm.FindRSFunctionName(pThis->operand.rsf, &pcsFuncName));
+ CHKiRet(rsCStrAppendCStr(pstrPrg, pcsFuncName));
+ } else {
+ if(pThis->operand.pVar != NULL)
+ CHKiRet(var.Obj2Str(pThis->operand.pVar, pstrPrg));
+ }
CHKiRet(rsCStrAppendChar(pstrPrg, '\n'));
finalize_it:
@@ -116,6 +132,23 @@ finalize_it:
}
+/* set function
+ * rgerhards, 2009-04-06
+ */
+static rsRetVal
+vmopSetFunc(vmop_t *pThis, cstr_t *pcsFuncName)
+{
+ prsf_t rsf; /* pointer to function */
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, vmop);
+ CHKiRet(vm.FindRSFunction(pcsFuncName, &rsf)); /* check if function exists and obtain pointer to it */
+ assert(rsf != NULL); /* just double-check, would be very hard to find! */
+ pThis->operand.rsf = rsf;
+finalize_it:
+ RETiRet;
+}
+
+
/* set operand (variant case)
* rgerhards, 2008-02-20
*/
@@ -248,6 +281,7 @@ CODESTARTobjQueryInterface(vmop)
pIf->ConstructFinalize = vmopConstructFinalize;
pIf->Destruct = vmopDestruct;
pIf->DebugPrint = vmopDebugPrint;
+ pIf->SetFunc = vmopSetFunc;
pIf->SetOpcode = vmopSetOpcode;
pIf->SetVar = vmopSetVar;
pIf->Opcode2Str = vmopOpcode2Str;
@@ -263,6 +297,7 @@ ENDobjQueryInterface(vmop)
BEGINObjClassInit(vmop, 1, OBJ_IS_CORE_MODULE) /* class, version */
/* request objects we use */
CHKiRet(objUse(var, CORE_COMPONENT));
+ CHKiRet(objUse(vm, CORE_COMPONENT));
OBJSetMethodHandler(objMethod_DEBUGPRINT, vmopDebugPrint);
OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, vmopConstructFinalize);
diff --git a/runtime/vmop.h b/runtime/vmop.h
index 938b08fd..67048c26 100644
--- a/runtime/vmop.h
+++ b/runtime/vmop.h
@@ -26,6 +26,7 @@
#define INCLUDED_VMOP_H
#include "ctok_token.h"
+#include "vmstk.h"
#include "stringbuf.h"
/* machine instructions types */
@@ -96,7 +97,8 @@ typedef struct vmop_s {
BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
opcode_t opcode;
union {
- var_t *pVar; /* for function call, this is the name (string) of function to be called */
+ var_t *pVar;
+ prsf_t rsf; /* pointer to function for "call" instruction */
} operand;
struct vmop_s *pNext; /* next operation or NULL, if end of program (logically this belongs to vmprg) */
} vmop_t;
@@ -112,8 +114,13 @@ BEGINinterface(vmop) /* name must also be changed in ENDinterface macro! */
rsRetVal (*SetVar)(vmop_t *pThis, var_t *pVar);
rsRetVal (*Opcode2Str)(vmop_t *pThis, uchar **ppName);
rsRetVal (*Obj2Str)(vmop_t *pThis, cstr_t *pstr);
+ /* v2 */
+ rsRetVal (*SetFunc)(vmop_t *pThis, cstr_t *pcsFuncName);
ENDinterface(vmop)
-#define vmopCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+#define vmopCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */
+/* interface changes, v1 -> v2
+ * added SetFuct after existing function pointers -- rgerhards, 2009-04-06
+ */
/* the remaining prototypes */
PROTOTYPEObj(vmop);
diff --git a/runtime/vmprg.c b/runtime/vmprg.c
index 75915025..07757b98 100644
--- a/runtime/vmprg.c
+++ b/runtime/vmprg.c
@@ -155,7 +155,6 @@ vmprgAddVarOperation(vmprg_t *pThis, opcode_t opcode, var_t *pVar)
/* construct and fill vmop */
CHKiRet(vmop.Construct(&pOp));
CHKiRet(vmop.ConstructFinalize(pOp));
- CHKiRet(vmop.ConstructFinalize(pOp));
CHKiRet(vmop.SetOpcode(pOp, opcode));
if(pVar != NULL)
CHKiRet(vmop.SetVar(pOp, pVar));
@@ -168,6 +167,32 @@ finalize_it:
}
+/* this is another shortcut for high-level callers. It is similar to vmprgAddVarOperation
+ * but adds a call operation. Among others, this include a check if the function
+ * is known.
+ */
+static rsRetVal
+vmprgAddCallOperation(vmprg_t *pThis, cstr_t *pcsName)
+{
+ DEFiRet;
+ vmop_t *pOp;
+
+ ISOBJ_TYPE_assert(pThis, vmprg);
+
+ /* construct and fill vmop */
+ CHKiRet(vmop.Construct(&pOp));
+ CHKiRet(vmop.ConstructFinalize(pOp));
+ CHKiRet(vmop.SetFunc(pOp, pcsName));
+ CHKiRet(vmop.SetOpcode(pOp, opcode_FUNC_CALL));
+
+ /* and add it to the program */
+ CHKiRet(vmprgAddOperation(pThis, pOp));
+
+finalize_it:
+ RETiRet;
+}
+
+
/* queryInterface function
* rgerhards, 2008-02-21
*/
@@ -189,6 +214,7 @@ CODESTARTobjQueryInterface(vmprg)
pIf->Obj2Str = Obj2Str;
pIf->AddOperation = vmprgAddOperation;
pIf->AddVarOperation = vmprgAddVarOperation;
+ pIf->AddCallOperation = vmprgAddCallOperation;
finalize_it:
ENDobjQueryInterface(vmprg)
diff --git a/runtime/vmprg.h b/runtime/vmprg.h
index c1042f7d..66f03913 100644
--- a/runtime/vmprg.h
+++ b/runtime/vmprg.h
@@ -57,8 +57,10 @@ BEGINinterface(vmprg) /* name must also be changed in ENDinterface macro! */
rsRetVal (*AddOperation)(vmprg_t *pThis, vmop_t *pOp);
rsRetVal (*AddVarOperation)(vmprg_t *pThis, opcode_t opcode, var_t *pVar);
rsRetVal (*Obj2Str)(vmprg_t *pThis, cstr_t *pstr);
+ /* v2 (4.1.7) */
+ rsRetVal (*AddCallOperation)(vmprg_t *pThis, cstr_t *pVar); /* added 2009-04-06 */
ENDinterface(vmprg)
-#define vmprgCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+#define vmprgCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */
/* prototypes */
diff --git a/runtime/vmstk.h b/runtime/vmstk.h
index 2d45ee4d..06657cf4 100644
--- a/runtime/vmstk.h
+++ b/runtime/vmstk.h
@@ -27,11 +27,11 @@
#define VMSTK_SIZE 256
/* the vmstk object */
-typedef struct vmstk_s {
+struct vmstk_s {
BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
var_t *vStk[VMSTK_SIZE];/* the actual stack */
int iStkPtr; /* stack pointer, points to next free location, grows from 0 --> topend */
-} vmstk_t;
+};
/* interfaces */