summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRainer Gerhards <rgerhards@adiscon.com>2009-04-06 17:55:04 +0200
committerRainer Gerhards <rgerhards@adiscon.com>2009-04-06 17:55:04 +0200
commit8e3c5a9ca3732a41fbb8581b13c49acd699820da (patch)
treef75be0a65a7ab52fd3c35d240d0cc0446d3e883d
parent010060289a729dd930ac04b72237f0ca0db9ea1d (diff)
downloadrsyslog-8e3c5a9ca3732a41fbb8581b13c49acd699820da.tar.gz
rsyslog-8e3c5a9ca3732a41fbb8581b13c49acd699820da.tar.xz
rsyslog-8e3c5a9ca3732a41fbb8581b13c49acd699820da.zip
improved internal handling of RainerScript functions
- building the necessary plumbing to support more functions with decent runtime performance. This is also necessary towards the long-term goal of loadable library modules. - added new RainerScript function "tolower"
-rw-r--r--ChangeLog7
-rw-r--r--runtime/expr.c4
-rw-r--r--runtime/rsyslog.h4
-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
-rw-r--r--tests/3.rstest4
-rw-r--r--tools/syslogd.c1
12 files changed, 345 insertions, 32 deletions
diff --git a/ChangeLog b/ChangeLog
index 8d4191d3..24898dc7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+---------------------------------------------------------------------------
+Version 4.1.7 [DEVEL] (rgerhards), 2009-03-??
+- improved internal handling of RainerScript functions, building the
+ necessary plumbing to support more functions with decent runtime
+ performance. This is also necessary towards the long-term goal
+ of loadable library modules.
+- added new RainerScript function "tolower"
- bugfix: potential abort with DA queue after high watermark is reached
There exists a race condition that can lead to a segfault. Thanks
go to vbernetr, who performed the analysis and provided patch, which
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/rsyslog.h b/runtime/rsyslog.h
index 032d8c04..cea457d8 100644
--- a/runtime/rsyslog.h
+++ b/runtime/rsyslog.h
@@ -84,6 +84,8 @@ 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 vmstk_s vmstk_t;
+typedef rsRetVal (*prsf_t)(struct vmstk_s*, int); /* pointer to a RainerScript function */
/* some universal 64 bit define... */
typedef long long int64;
@@ -258,6 +260,8 @@ 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_DUP_FUNC_NAME = -2114, /**< duplicate function name (rainerscript) */
+ RS_RET_UNKNW_FUNC = -2115, /**< unkown function name (rainerscript) */
/* RainerScript error messages (range 1000.. 1999) */
RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */
diff --git a/runtime/vm.c b/runtime/vm.c
index a25476c2..41d3e483 100644
--- a/runtime/vm.c
+++ b/runtime/vm.c
@@ -26,6 +26,7 @@
#include "config.h"
#include <stdlib.h>
#include <assert.h>
+#include <ctype.h>
#include "rsyslog.h"
#include "obj.h"
@@ -39,6 +40,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.
@@ -330,7 +467,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.
@@ -340,20 +476,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)
@@ -362,6 +487,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(rsCStrFinish(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! */
@@ -531,10 +739,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
@@ -548,6 +769,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..3e001d27 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(rsCStrFinish(&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 */
diff --git a/tests/3.rstest b/tests/3.rstest
index 93cb941a..e75d9754 100644
--- a/tests/3.rstest
+++ b/tests/3.rstest
@@ -7,10 +7,10 @@ out:
00000000: push_msgvar msg[cstr]
00000001: push_const abc[cstr]
00000002: push_const 1[nbr]
-00000003: func_call strlen[cstr]
+00000003: func_call strlen
00000004: strconcat
00000005: push_const 1[nbr]
-00000006: func_call strlen[cstr]
+00000006: func_call strlen
00000007: push_const 20[nbr]
00000008: push_const 30[nbr]
00000009: add
diff --git a/tools/syslogd.c b/tools/syslogd.c
index b23c12a7..c85c4371 100644
--- a/tools/syslogd.c
+++ b/tools/syslogd.c
@@ -3091,6 +3091,7 @@ GlobalClassExit(void)
objRelease(net, LM_NET_FILENAME);/* TODO: the dependency on net shall go away! -- rgerhards, 2008-03-07 */
objRelease(conf, CORE_COMPONENT);
objRelease(expr, CORE_COMPONENT);
+ vmClassExit(); /* this is hack, currently core_modules do not get this automatically called */
objRelease(vm, CORE_COMPONENT);
objRelease(var, CORE_COMPONENT);
objRelease(datetime, CORE_COMPONENT);