mirror of
https://github.com/rsyslog/rsyslog.git
synced 2025-12-15 16:20:40 +01:00
Replace opaque/variadic callback usage with explicit, type-safe function signatures to reduce undefined behavior and clarify intent. Adapter helpers bridge the existing APIs without raw variadic casts, enabling the transition incrementally. Callback setup sites are standardized for consistent readability. This tightens the contract on callbacks, eases future refactoring, and makes their roles more self-documenting. Inspired by https://github.com/rsyslog/rsyslog/pull/5882 With AI support: Codex, Gemini
1361 lines
43 KiB
C
1361 lines
43 KiB
C
/* obj.c
|
|
*
|
|
* This file implements a generic object "class". All other classes can
|
|
* use the service of this base class here to include auto-destruction and
|
|
* other capabilities in a generic manner.
|
|
*
|
|
* As of 2008-02-29, I (rgerhards) am adding support for dynamically loadable
|
|
* objects. In essence, each object will soon be available via its interface,
|
|
* only. Before any object's code is accessed (including global static methods),
|
|
* the caller needs to obtain an object interface. To do so, it needs to provide
|
|
* the object name and the file where the object is expected to reside in. A
|
|
* file may not be given, in which case the object is expected to reside in
|
|
* the rsyslog core. The caller than receives an interface pointer which can
|
|
* be utilized to access all the object's methods. This method enables rsyslog
|
|
* to load library modules on demand. In order to keep overhead low, callers
|
|
* should request object interface only once in the object Init function and
|
|
* free them when they exit. The only exception is when a caller needs to
|
|
* access an object only conditional, in which case a pointer to its interface
|
|
* shall be acquired as need first arises but still be released only on exit
|
|
* or when there definitely is no further need. The whole idea is to limit
|
|
* the very performance-intense act of dynamically loading an objects library.
|
|
* Of course, it is possible to violate this suggestion, but than you should
|
|
* have very good reasoning to do so.
|
|
*
|
|
* Please note that there is one trick we need to do. Each object queries
|
|
* the object interfaces and it does so via objUse(). objUse, however, is
|
|
* part of the obj object's interface (implemented via the file you are
|
|
* just reading). So in order to obtain a pointer to objUse, we need to
|
|
* call it - obviously not possible. One solution would be that objUse is
|
|
* hardcoded into all callers. That, however, would bring us into slight
|
|
* trouble with actually dynamically loaded modules, as we should NOT
|
|
* rely on the OS loader to resolve symbols back to the caller (this
|
|
* is a feature not universally available and highly importable). Of course,
|
|
* we can solve this with a pHostQueryEtryPoint() call. It still sounds
|
|
* somewhat unnatural to call a regular interface function via a special
|
|
* method. So what we do instead is define a special function called
|
|
* objGetObjInterface() which delivers our own interface. That function
|
|
* than will be defined global and be queriable via pHostQueryEtryPoint().
|
|
* I agree, technically this is much the same, but from an architecture
|
|
* point of view it looks cleaner (at least to me).
|
|
*
|
|
* Please note that there is another egg-hen problem: we use a linked list,
|
|
* which is provided by the linkedList object. However, we need to
|
|
* initialize the linked list before we can provide the UseObj()
|
|
* functionality. That, in turn, would probably be required by the
|
|
* linkedList object. So the solution is to use a backdoor just to
|
|
* init the linked list and from then on use the usual interfaces.
|
|
*
|
|
* File begun on 2008-01-04 by RGerhards
|
|
*
|
|
* Copyright 2008-2024 Rainer Gerhards and Adiscon GmbH.
|
|
*
|
|
* This file is part of the rsyslog runtime library.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
* -or-
|
|
* see COPYING.ASL20 in the source distribution
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <assert.h>
|
|
#include <pthread.h>
|
|
|
|
#define DEV_DEBUG 0 /* set to 1 to enable very verbose developer debugging messages */
|
|
|
|
/* how many objects are supported by rsyslogd? */
|
|
#define OBJ_NUM_IDS 100
|
|
|
|
#include "rsyslog.h"
|
|
#include "syslogd-types.h"
|
|
#include "srUtils.h"
|
|
#include "obj.h"
|
|
#include "stream.h"
|
|
#include "modules.h"
|
|
#include "errmsg.h"
|
|
#include "cfsysline.h"
|
|
#include "unicode-helper.h"
|
|
#include "datetime.h"
|
|
|
|
/* static data */
|
|
DEFobjCurrIf(obj) /* we define our own interface, as this is expected by some macros! */
|
|
DEFobjCurrIf(var) DEFobjCurrIf(module)
|
|
DEFobjCurrIf(strm) static objInfo_t *arrObjInfo[OBJ_NUM_IDS]; /* array with object information pointers */
|
|
pthread_mutex_t mutObjGlobalOp; /* mutex to guard global operations of the object system */
|
|
|
|
|
|
/* cookies for serialized lines */
|
|
#define COOKIE_OBJLINE '<'
|
|
#define COOKIE_PROPLINE '+'
|
|
#define COOKIE_ENDLINE '>'
|
|
#define COOKIE_BLANKLINE '.'
|
|
|
|
/* forward definitions */
|
|
static rsRetVal FindObjInfo(const char *szObjName, objInfo_t **ppInfo);
|
|
|
|
/* methods */
|
|
|
|
/* This is a dummy method to be used when a standard method has not been
|
|
* implemented by an object. Having it allows us to simply call via the
|
|
* jump table without any NULL pointer checks - which gains quite
|
|
* some performance. -- rgerhards, 2008-01-04
|
|
*/
|
|
static rsRetVal objInfoNotImplementedDummy(void __attribute__((unused)) * pThis) {
|
|
return RS_RET_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/* and now the macro to check if something is not implemented
|
|
* must be provided an objInfo_t pointer.
|
|
*/
|
|
#define objInfoIsImplemented(pThis, method) \
|
|
(pThis->objMethods[method] != (rsRetVal(*)(void *, ...))objInfoNotImplementedDummy)
|
|
|
|
/* construct an object Info object. Each class shall do this on init. The
|
|
* resulting object shall be cached during the lifetime of the class and each
|
|
* object shall receive a reference. A constructor and destructor MUST be provided for all
|
|
* objects, thus they are in the parameter list.
|
|
* pszID is the identifying object name and must point to constant pool memory. It is never freed.
|
|
*/
|
|
static rsRetVal InfoConstruct(objInfo_t **ppThis,
|
|
uchar *pszID,
|
|
int iObjVers,
|
|
rsRetVal (*pConstruct)(void *),
|
|
rsRetVal (*pDestruct)(void *),
|
|
rsRetVal (*pQueryIF)(interface_t *),
|
|
modInfo_t *pModInfo) {
|
|
DEFiRet;
|
|
int i;
|
|
objInfo_t *pThis;
|
|
|
|
assert(ppThis != NULL);
|
|
|
|
if ((pThis = calloc(1, sizeof(objInfo_t))) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
|
|
|
|
pThis->pszID = pszID;
|
|
pThis->lenID = ustrlen(pszID);
|
|
pThis->pszName = ustrdup(pszID); /* it's OK if we have NULL ptr, GetName() will deal with that! */
|
|
pThis->iObjVers = iObjVers;
|
|
pThis->QueryIF = pQueryIF;
|
|
pThis->pModInfo = pModInfo;
|
|
|
|
pThis->objMethods[0] = (rsRetVal(*)(void *, ...))pConstruct;
|
|
pThis->objMethods[1] = (rsRetVal(*)(void *, ...))pDestruct;
|
|
for (i = 2; i < OBJ_NUM_METHODS; ++i) {
|
|
pThis->objMethods[i] = (rsRetVal(*)(void *, ...))objInfoNotImplementedDummy;
|
|
}
|
|
|
|
*ppThis = pThis;
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* destruct the objInfo object - must be done only when no more instances exist.
|
|
* rgerhards, 2008-03-10
|
|
*/
|
|
static rsRetVal InfoDestruct(objInfo_t **ppThis) {
|
|
DEFiRet;
|
|
objInfo_t *pThis;
|
|
|
|
assert(ppThis != NULL);
|
|
pThis = *ppThis;
|
|
assert(pThis != NULL);
|
|
|
|
free(pThis->pszName);
|
|
free(pThis);
|
|
*ppThis = NULL;
|
|
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* set a method handler */
|
|
static rsRetVal InfoSetMethod(objInfo_t *pThis, objMethod_t objMethod, rsRetVal (*pHandler)(void *)) {
|
|
pThis->objMethods[objMethod] = (rsRetVal(*)(void *, ...))pHandler;
|
|
return RS_RET_OK;
|
|
}
|
|
|
|
/* destruct the base object properties.
|
|
* rgerhards, 2008-01-29
|
|
*/
|
|
static rsRetVal DestructObjSelf(obj_t *pThis) {
|
|
DEFiRet;
|
|
|
|
ISOBJ_assert(pThis);
|
|
free(pThis->pszName);
|
|
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* --------------- object serializiation / deserialization support --------------- */
|
|
|
|
|
|
/* serialize the header of an object
|
|
* pszRecType must be either "Obj" (Object) or "OPB" (Object Property Bag)
|
|
*/
|
|
static rsRetVal objSerializeHeader(strm_t *pStrm, obj_t *pObj, uchar *pszRecType) {
|
|
DEFiRet;
|
|
|
|
ISOBJ_TYPE_assert(pStrm, strm);
|
|
ISOBJ_assert(pObj);
|
|
assert(!strcmp((char *)pszRecType, "Obj") || !strcmp((char *)pszRecType, "OPB"));
|
|
|
|
/* object cookie and serializer version (so far always 1) */
|
|
CHKiRet(strm.WriteChar(pStrm, COOKIE_OBJLINE));
|
|
CHKiRet(strm.Write(pStrm, (uchar *)pszRecType, 3)); /* record types are always 3 octets */
|
|
CHKiRet(strm.WriteChar(pStrm, ':'));
|
|
CHKiRet(strm.WriteChar(pStrm, '1'));
|
|
|
|
/* object type, version and string length */
|
|
CHKiRet(strm.WriteChar(pStrm, ':'));
|
|
CHKiRet(strm.Write(pStrm, pObj->pObjInfo->pszID, pObj->pObjInfo->lenID));
|
|
CHKiRet(strm.WriteChar(pStrm, ':'));
|
|
CHKiRet(strm.WriteLong(pStrm, objGetVersion(pObj)));
|
|
|
|
/* record trailer */
|
|
CHKiRet(strm.WriteChar(pStrm, ':'));
|
|
CHKiRet(strm.WriteChar(pStrm, '\n'));
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* begin serialization of an object
|
|
* rgerhards, 2008-01-06
|
|
*/
|
|
static rsRetVal BeginSerialize(strm_t *pStrm, obj_t *pObj) {
|
|
DEFiRet;
|
|
|
|
ISOBJ_TYPE_assert(pStrm, strm);
|
|
ISOBJ_assert(pObj);
|
|
|
|
CHKiRet(strm.RecordBegin(pStrm));
|
|
CHKiRet(objSerializeHeader(pStrm, pObj, (uchar *)"Obj"));
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* begin serialization of an object's property bag
|
|
* Note: a property bag is used to serialize some of an objects
|
|
* properties, but not necessarily all. A good example is the queue
|
|
* object, which at some stage needs to serialize a number of its
|
|
* properties, but not the queue data itself. From the object point
|
|
* of view, a property bag can not be used to re-instantiate an object.
|
|
* Otherwise, the serialization is exactly the same.
|
|
* rgerhards, 2008-01-11
|
|
*/
|
|
static rsRetVal BeginSerializePropBag(strm_t *pStrm, obj_t *pObj) {
|
|
DEFiRet;
|
|
|
|
ISOBJ_TYPE_assert(pStrm, strm);
|
|
ISOBJ_assert(pObj);
|
|
|
|
CHKiRet(strm.RecordBegin(pStrm));
|
|
CHKiRet(objSerializeHeader(pStrm, pObj, (uchar *)"OPB"));
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* append a property
|
|
*/
|
|
static rsRetVal SerializeProp(strm_t *pStrm, uchar *pszPropName, propType_t propType, void *pUsr) {
|
|
DEFiRet;
|
|
uchar *pszBuf = NULL;
|
|
size_t lenBuf = 0;
|
|
uchar szBuf[64];
|
|
varType_t vType = VARTYPE_NONE;
|
|
|
|
ISOBJ_TYPE_assert(pStrm, strm);
|
|
assert(pszPropName != NULL);
|
|
|
|
/*dbgprintf("objSerializeProp: strm %p, propName '%s', type %d, pUsr %p\n",
|
|
pStrm, pszPropName, propType, pUsr);*/
|
|
/* if we have no user pointer, there is no need to write this property.
|
|
* TODO: think if that's the righ point of view
|
|
* rgerhards, 2008-01-06
|
|
*/
|
|
if (pUsr == NULL) {
|
|
ABORT_FINALIZE(RS_RET_OK);
|
|
}
|
|
|
|
/* TODO: use the stream functions for data conversion here - should be quicker */
|
|
|
|
switch (propType) {
|
|
case PROPTYPE_PSZ:
|
|
pszBuf = (uchar *)pUsr;
|
|
lenBuf = ustrlen(pszBuf);
|
|
vType = VARTYPE_STR;
|
|
break;
|
|
case PROPTYPE_SHORT:
|
|
CHKiRet(srUtilItoA((char *)szBuf, sizeof(szBuf), (long)*((short *)pUsr)));
|
|
pszBuf = szBuf;
|
|
lenBuf = ustrlen(szBuf);
|
|
vType = VARTYPE_NUMBER;
|
|
break;
|
|
case PROPTYPE_INT:
|
|
CHKiRet(srUtilItoA((char *)szBuf, sizeof(szBuf), (long)*((int *)pUsr)));
|
|
pszBuf = szBuf;
|
|
lenBuf = ustrlen(szBuf);
|
|
vType = VARTYPE_NUMBER;
|
|
break;
|
|
case PROPTYPE_LONG:
|
|
CHKiRet(srUtilItoA((char *)szBuf, sizeof(szBuf), *((long *)pUsr)));
|
|
pszBuf = szBuf;
|
|
lenBuf = ustrlen(szBuf);
|
|
vType = VARTYPE_NUMBER;
|
|
break;
|
|
case PROPTYPE_INT64:
|
|
CHKiRet(srUtilItoA((char *)szBuf, sizeof(szBuf), *((int64 *)pUsr)));
|
|
pszBuf = szBuf;
|
|
lenBuf = ustrlen(szBuf);
|
|
vType = VARTYPE_NUMBER;
|
|
break;
|
|
case PROPTYPE_CSTR:
|
|
pszBuf = rsCStrGetSzStrNoNULL((cstr_t *)pUsr);
|
|
lenBuf = rsCStrLen((cstr_t *)pUsr);
|
|
vType = VARTYPE_STR;
|
|
break;
|
|
case PROPTYPE_SYSLOGTIME:
|
|
lenBuf =
|
|
snprintf((char *)szBuf, sizeof(szBuf), "%d:%d:%d:%d:%d:%d:%d:%d:%d:%c:%d:%d",
|
|
((syslogTime_t *)pUsr)->timeType, ((syslogTime_t *)pUsr)->year, ((syslogTime_t *)pUsr)->month,
|
|
((syslogTime_t *)pUsr)->day, ((syslogTime_t *)pUsr)->hour, ((syslogTime_t *)pUsr)->minute,
|
|
((syslogTime_t *)pUsr)->second, ((syslogTime_t *)pUsr)->secfrac,
|
|
((syslogTime_t *)pUsr)->secfracPrecision, ((syslogTime_t *)pUsr)->OffsetMode,
|
|
((syslogTime_t *)pUsr)->OffsetHour, ((syslogTime_t *)pUsr)->OffsetMinute);
|
|
if (lenBuf > sizeof(szBuf) - 1) ABORT_FINALIZE(RS_RET_PROVIDED_BUFFER_TOO_SMALL);
|
|
vType = VARTYPE_SYSLOGTIME;
|
|
pszBuf = szBuf;
|
|
break;
|
|
case PROPTYPE_NONE:
|
|
default:
|
|
dbgprintf("invalid PROPTYPE %d\n", propType);
|
|
break;
|
|
}
|
|
|
|
/* cookie */
|
|
CHKiRet(strm.WriteChar(pStrm, COOKIE_PROPLINE));
|
|
/* name */
|
|
CHKiRet(strm.Write(pStrm, pszPropName, ustrlen(pszPropName)));
|
|
CHKiRet(strm.WriteChar(pStrm, ':'));
|
|
/* type */
|
|
CHKiRet(strm.WriteLong(pStrm, (int)vType));
|
|
CHKiRet(strm.WriteChar(pStrm, ':'));
|
|
/* length */
|
|
CHKiRet(strm.WriteLong(pStrm, lenBuf));
|
|
CHKiRet(strm.WriteChar(pStrm, ':'));
|
|
|
|
/* data */
|
|
CHKiRet(strm.Write(pStrm, (uchar *)pszBuf, lenBuf));
|
|
|
|
/* trailer */
|
|
CHKiRet(strm.WriteChar(pStrm, ':'));
|
|
CHKiRet(strm.WriteChar(pStrm, '\n'));
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* end serialization of an object. The caller receives a
|
|
* standard C string, which he must free when no longer needed.
|
|
*/
|
|
static rsRetVal EndSerialize(strm_t *pStrm) {
|
|
DEFiRet;
|
|
|
|
assert(pStrm != NULL);
|
|
|
|
CHKiRet(strm.WriteChar(pStrm, COOKIE_ENDLINE));
|
|
CHKiRet(strm.Write(pStrm, (uchar *)"End\n", sizeof("END\n") - 1));
|
|
CHKiRet(strm.WriteChar(pStrm, COOKIE_BLANKLINE));
|
|
CHKiRet(strm.WriteChar(pStrm, '\n'));
|
|
|
|
CHKiRet(strm.RecordEnd(pStrm));
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* define a helper to make code below a bit cleaner (and quicker to write) */
|
|
#define NEXTC CHKiRet(strm.ReadChar(pStrm, &c)) /*;dbgprintf("c: %c\n", c)*/
|
|
|
|
|
|
/* de-serialize an embedded, non-octect-counted string. This is useful
|
|
* for deserializing the object name inside the header. The string is
|
|
* terminated by the first occurrence of the ':' character.
|
|
* rgerhards, 2008-02-29
|
|
*/
|
|
static rsRetVal objDeserializeEmbedStr(cstr_t **ppStr, strm_t *pStrm) {
|
|
DEFiRet;
|
|
uchar c;
|
|
cstr_t *pStr = NULL;
|
|
|
|
assert(ppStr != NULL);
|
|
|
|
CHKiRet(cstrConstruct(&pStr));
|
|
|
|
NEXTC;
|
|
while (c != ':') {
|
|
CHKiRet(cstrAppendChar(pStr, c));
|
|
NEXTC;
|
|
}
|
|
cstrFinalize(pStr);
|
|
|
|
*ppStr = pStr;
|
|
|
|
finalize_it:
|
|
if (iRet != RS_RET_OK && pStr != NULL) cstrDestruct(&pStr);
|
|
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* de-serialize a number */
|
|
static rsRetVal objDeserializeNumber(number_t *pNum, strm_t *pStrm) {
|
|
DEFiRet;
|
|
number_t i;
|
|
int bIsNegative;
|
|
uchar c;
|
|
|
|
assert(pNum != NULL);
|
|
|
|
NEXTC;
|
|
if (c == '-') {
|
|
bIsNegative = 1;
|
|
NEXTC;
|
|
} else {
|
|
bIsNegative = 0;
|
|
}
|
|
|
|
/* we check this so that we get more meaningful error codes */
|
|
if (!isdigit(c)) ABORT_FINALIZE(RS_RET_INVALID_NUMBER);
|
|
|
|
i = 0;
|
|
while (isdigit(c)) {
|
|
i = i * 10 + c - '0';
|
|
NEXTC;
|
|
}
|
|
|
|
if (c != ':') ABORT_FINALIZE(RS_RET_INVALID_DELIMITER);
|
|
|
|
if (bIsNegative) i *= -1;
|
|
|
|
*pNum = i;
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* de-serialize a string, length must be provided but may be 0 */
|
|
static rsRetVal objDeserializeStr(cstr_t **ppCStr, int iLen, strm_t *pStrm) {
|
|
DEFiRet;
|
|
int i;
|
|
uchar c;
|
|
cstr_t *pCStr = NULL;
|
|
|
|
assert(ppCStr != NULL);
|
|
assert(iLen >= 0);
|
|
|
|
CHKiRet(cstrConstruct(&pCStr));
|
|
|
|
NEXTC;
|
|
for (i = 0; i < iLen; ++i) {
|
|
CHKiRet(cstrAppendChar(pCStr, c));
|
|
NEXTC;
|
|
}
|
|
cstrFinalize(pCStr);
|
|
|
|
/* check terminator */
|
|
if (c != ':') {
|
|
/* Initialized to NULL */
|
|
*ppCStr = NULL;
|
|
ABORT_FINALIZE(RS_RET_INVALID_DELIMITER);
|
|
}
|
|
|
|
*ppCStr = pCStr;
|
|
|
|
finalize_it:
|
|
if (iRet != RS_RET_OK && pCStr != NULL) cstrDestruct(&pCStr);
|
|
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* de-serialize a syslogTime -- rgerhards,2008-01-08 */
|
|
#define GETVAL(var) \
|
|
CHKiRet(objDeserializeNumber(&l, pStrm)); \
|
|
pTime->var = l;
|
|
static rsRetVal objDeserializeSyslogTime(syslogTime_t *pTime, strm_t *pStrm) {
|
|
DEFiRet;
|
|
number_t l;
|
|
uchar c;
|
|
|
|
assert(pTime != NULL);
|
|
|
|
GETVAL(timeType);
|
|
GETVAL(year);
|
|
GETVAL(month);
|
|
GETVAL(day);
|
|
GETVAL(hour);
|
|
GETVAL(minute);
|
|
GETVAL(second);
|
|
GETVAL(secfrac);
|
|
GETVAL(secfracPrecision);
|
|
/* OffsetMode is a single character! */
|
|
NEXTC;
|
|
pTime->OffsetMode = c;
|
|
NEXTC;
|
|
if (c != ':') ABORT_FINALIZE(RS_RET_INVALID_DELIMITER);
|
|
GETVAL(OffsetHour);
|
|
GETVAL(OffsetMinute);
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
#undef GETVAL
|
|
|
|
/* de-serialize an object header
|
|
* rgerhards, 2008-01-07
|
|
*/
|
|
static rsRetVal objDeserializeHeader(uchar *pszRecType, cstr_t **ppstrID, int *poVers, strm_t *pStrm) {
|
|
DEFiRet;
|
|
number_t oVers;
|
|
uchar c;
|
|
|
|
assert(ppstrID != NULL);
|
|
assert(poVers != NULL);
|
|
assert(!strcmp((char *)pszRecType, "Obj") || !strcmp((char *)pszRecType, "OPB"));
|
|
|
|
/* check header cookie */
|
|
NEXTC;
|
|
if (c != COOKIE_OBJLINE) ABORT_FINALIZE(RS_RET_INVALID_HEADER);
|
|
NEXTC;
|
|
if (c != pszRecType[0]) ABORT_FINALIZE(RS_RET_INVALID_HEADER_RECTYPE);
|
|
NEXTC;
|
|
if (c != pszRecType[1]) ABORT_FINALIZE(RS_RET_INVALID_HEADER_RECTYPE);
|
|
NEXTC;
|
|
if (c != pszRecType[2]) ABORT_FINALIZE(RS_RET_INVALID_HEADER_RECTYPE);
|
|
NEXTC;
|
|
if (c != ':') ABORT_FINALIZE(RS_RET_INVALID_HEADER);
|
|
NEXTC;
|
|
if (c != '1') ABORT_FINALIZE(RS_RET_INVALID_HEADER_VERS);
|
|
NEXTC;
|
|
if (c != ':') ABORT_FINALIZE(RS_RET_INVALID_HEADER_VERS);
|
|
|
|
/* object type and version */
|
|
CHKiRet(objDeserializeEmbedStr(ppstrID, pStrm));
|
|
CHKiRet(objDeserializeNumber(&oVers, pStrm));
|
|
|
|
/* and now we skip over the rest until the delemiting \n */
|
|
NEXTC;
|
|
while (c != '\n') {
|
|
NEXTC;
|
|
}
|
|
|
|
*poVers = oVers;
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* Deserialize a single property. Pointer must be positioned at begin of line. Whole line
|
|
* up until the \n is read.
|
|
*/
|
|
rsRetVal objDeserializeProperty(var_t *pProp, strm_t *pStrm) {
|
|
DEFiRet;
|
|
number_t i;
|
|
number_t iLen;
|
|
uchar c;
|
|
int step = 0; /* which step was successful? */
|
|
int64 offs;
|
|
|
|
assert(pProp != NULL);
|
|
|
|
/* check cookie */
|
|
NEXTC;
|
|
if (c != COOKIE_PROPLINE) {
|
|
/* oops, we've read one char that does not belong to use - unget it first */
|
|
CHKiRet(strm.UnreadChar(pStrm, c));
|
|
ABORT_FINALIZE(RS_RET_NO_PROPLINE);
|
|
}
|
|
|
|
/* get the property name first */
|
|
CHKiRet(cstrConstruct(&pProp->pcsName));
|
|
|
|
NEXTC;
|
|
while (c != ':') {
|
|
CHKiRet(cstrAppendChar(pProp->pcsName, c));
|
|
NEXTC;
|
|
}
|
|
cstrFinalize(pProp->pcsName);
|
|
step = 1;
|
|
|
|
/* property type */
|
|
CHKiRet(objDeserializeNumber(&i, pStrm));
|
|
pProp->varType = i;
|
|
step = 2;
|
|
|
|
/* size (needed for strings) */
|
|
CHKiRet(objDeserializeNumber(&iLen, pStrm));
|
|
step = 3;
|
|
|
|
/* we now need to deserialize the value */
|
|
switch (pProp->varType) {
|
|
case VARTYPE_STR:
|
|
CHKiRet(objDeserializeStr(&pProp->val.pStr, iLen, pStrm));
|
|
break;
|
|
case VARTYPE_NUMBER:
|
|
CHKiRet(objDeserializeNumber(&pProp->val.num, pStrm));
|
|
break;
|
|
case VARTYPE_SYSLOGTIME:
|
|
CHKiRet(objDeserializeSyslogTime(&pProp->val.vSyslogTime, pStrm));
|
|
break;
|
|
case VARTYPE_NONE:
|
|
default:
|
|
dbgprintf("invalid VARTYPE %d\n", pProp->varType);
|
|
break;
|
|
}
|
|
step = 4;
|
|
|
|
/* we should now be at the end of the line. So the next char must be \n */
|
|
NEXTC;
|
|
if (c != '\n') ABORT_FINALIZE(RS_RET_INVALID_PROPFRAME);
|
|
|
|
finalize_it:
|
|
/* ensure the type of var is reset back to VARTYPE_NONE since
|
|
* the deconstruct method of var might free unallocated memory
|
|
*/
|
|
if (iRet != RS_RET_OK && iRet != RS_RET_NO_PROPLINE) {
|
|
if (step <= 2) {
|
|
pProp->varType = VARTYPE_NONE;
|
|
}
|
|
}
|
|
if (Debug && iRet != RS_RET_OK && iRet != RS_RET_NO_PROPLINE) {
|
|
strm.GetCurrOffset(pStrm, &offs);
|
|
dbgprintf("error %d deserializing property name, offset %lld, step %d\n", iRet, offs, step);
|
|
strmDebugOutBuf(pStrm);
|
|
if (step >= 1) {
|
|
dbgprintf("error property name: '%s'\n", rsCStrGetSzStrNoNULL(pProp->pcsName));
|
|
}
|
|
if (step >= 2) {
|
|
dbgprintf("error var type: '%d'\n", pProp->varType);
|
|
}
|
|
if (step >= 3) {
|
|
dbgprintf("error len: '%d'\n", (int)iLen);
|
|
}
|
|
if (step >= 4) {
|
|
switch (pProp->varType) {
|
|
case VARTYPE_STR:
|
|
dbgprintf("error data string: '%s'\n", rsCStrGetSzStrNoNULL(pProp->val.pStr));
|
|
break;
|
|
case VARTYPE_NUMBER:
|
|
dbgprintf("error number: %d\n", (int)pProp->val.num);
|
|
break;
|
|
case VARTYPE_SYSLOGTIME:
|
|
dbgprintf(
|
|
"syslog time was successfully parsed (but "
|
|
"is not displayed\n");
|
|
break;
|
|
case VARTYPE_NONE:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* de-serialize an object trailer. This does not get any data but checks if the
|
|
* format is ok.
|
|
* rgerhards, 2008-01-07
|
|
*/
|
|
static rsRetVal objDeserializeTrailer(strm_t *pStrm) {
|
|
DEFiRet;
|
|
uchar c;
|
|
|
|
/* check header cookie */
|
|
NEXTC;
|
|
if (c != COOKIE_ENDLINE) ABORT_FINALIZE(RS_RET_INVALID_TRAILER);
|
|
NEXTC;
|
|
if (c != 'E') ABORT_FINALIZE(RS_RET_INVALID_TRAILER);
|
|
NEXTC;
|
|
if (c != 'n') ABORT_FINALIZE(RS_RET_INVALID_TRAILER);
|
|
NEXTC;
|
|
if (c != 'd') ABORT_FINALIZE(RS_RET_INVALID_TRAILER);
|
|
NEXTC;
|
|
if (c != '\n') ABORT_FINALIZE(RS_RET_INVALID_TRAILER);
|
|
NEXTC;
|
|
if (c != COOKIE_BLANKLINE) ABORT_FINALIZE(RS_RET_INVALID_TRAILER);
|
|
NEXTC;
|
|
if (c != '\n') ABORT_FINALIZE(RS_RET_INVALID_TRAILER);
|
|
|
|
finalize_it:
|
|
if (Debug && iRet != RS_RET_OK) {
|
|
dbgprintf("objDeserializeTrailer fails with %d\n", iRet);
|
|
}
|
|
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* This method tries to recover a serial store if it got out of sync.
|
|
* To do so, it scans the line beginning cookies and waits for the object
|
|
* cookie. If that is found, control is returned. If the store is exhausted,
|
|
* we will receive an RS_RET_EOF error as part of NEXTC, which will also
|
|
* terminate this function. So we may either return with somehting that
|
|
* looks like a valid object or end of store.
|
|
* rgerhards, 2008-01-07
|
|
*/
|
|
static rsRetVal objDeserializeTryRecover(strm_t *pStrm) {
|
|
DEFiRet;
|
|
uchar c;
|
|
int bWasNL;
|
|
int bRun;
|
|
|
|
assert(pStrm != NULL);
|
|
bRun = 1;
|
|
bWasNL = 0;
|
|
|
|
while (bRun) {
|
|
NEXTC;
|
|
if (c == '\n')
|
|
bWasNL = 1;
|
|
else {
|
|
if (bWasNL == 1 && c == COOKIE_OBJLINE)
|
|
bRun = 0; /* we found it! */
|
|
else
|
|
bWasNL = 0;
|
|
}
|
|
}
|
|
|
|
CHKiRet(strm.UnreadChar(pStrm, c));
|
|
|
|
finalize_it:
|
|
dbgprintf("deserializer has possibly been able to re-sync and recover, state %d\n", iRet);
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* De-serialize the properties of an object. This includes processing
|
|
* of the trailer. Header must already have been processed.
|
|
* rgerhards, 2008-01-11
|
|
*/
|
|
static rsRetVal objDeserializeProperties(obj_t *pObj, rsRetVal (*objSetProperty)(void *, ...), strm_t *pStrm) {
|
|
DEFiRet;
|
|
var_t *pVar = NULL;
|
|
|
|
ISOBJ_assert(pObj);
|
|
ISOBJ_TYPE_assert(pStrm, strm);
|
|
|
|
CHKiRet(var.Construct(&pVar));
|
|
CHKiRet(var.ConstructFinalize(pVar));
|
|
|
|
iRet = objDeserializeProperty(pVar, pStrm);
|
|
while (iRet == RS_RET_OK) {
|
|
CHKiRet(objSetProperty(pObj, pVar));
|
|
/* re-init var object - TODO: method of var! */
|
|
rsCStrDestruct(&pVar->pcsName); /* no longer needed */
|
|
if (pVar->varType == VARTYPE_STR) {
|
|
if (pVar->val.pStr != NULL) rsCStrDestruct(&pVar->val.pStr);
|
|
}
|
|
iRet = objDeserializeProperty(pVar, pStrm);
|
|
}
|
|
|
|
if (iRet != RS_RET_NO_PROPLINE) FINALIZE;
|
|
|
|
CHKiRet(objDeserializeTrailer(pStrm)); /* do trailer checks */
|
|
finalize_it:
|
|
if (pVar != NULL) var.Destruct(&pVar);
|
|
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* De-Serialize an object.
|
|
* Params: Pointer to object Pointer (pObj) (like a obj_t**, but can not do that due to compiler warning)
|
|
* expected object ID (to check against), a fixup function that can modify the object before it is finalized
|
|
* and a user pointer that is to be passed to that function in addition to the object. The fixup function
|
|
* pointer may be NULL, in which case none is called.
|
|
* The caller must destruct the created object.
|
|
* rgerhards, 2008-01-07
|
|
*/
|
|
static rsRetVal Deserialize(
|
|
void *ppObj, uchar *pszTypeExpected, strm_t *pStrm, rsRetVal (*fFixup)(obj_t *, void *), void *pUsr) {
|
|
DEFiRet;
|
|
rsRetVal iRetLocal;
|
|
obj_t *pObj = NULL;
|
|
int oVers = 0; /* keep compiler happy, but it is totally useless but takes up some execution time... */
|
|
cstr_t *pstrID = NULL;
|
|
objInfo_t *pObjInfo;
|
|
|
|
assert(ppObj != NULL);
|
|
assert(pszTypeExpected != NULL);
|
|
ISOBJ_TYPE_assert(pStrm, strm);
|
|
|
|
/* we de-serialize the header. if all goes well, we are happy. However, if
|
|
* we experience a problem, we try to recover. We do this by skipping to
|
|
* the next object header. This is defined via the line-start cookies. In
|
|
* worst case, we exhaust the queue, but then we receive EOF return state,
|
|
* from objDeserializeTryRecover(), what will cause us to ultimately give up.
|
|
* rgerhards, 2008-07-08
|
|
*/
|
|
do {
|
|
iRetLocal = objDeserializeHeader((uchar *)"Obj", &pstrID, &oVers, pStrm);
|
|
if (iRetLocal != RS_RET_OK) {
|
|
dbgprintf("objDeserialize error %d during header processing - trying to recover\n", iRetLocal);
|
|
CHKiRet(objDeserializeTryRecover(pStrm));
|
|
}
|
|
} while (iRetLocal != RS_RET_OK);
|
|
|
|
if (rsCStrSzStrCmp(pstrID, pszTypeExpected, ustrlen(pszTypeExpected))) ABORT_FINALIZE(RS_RET_INVALID_OID);
|
|
|
|
CHKiRet(FindObjInfo((char *)cstrGetSzStrNoNULL(pstrID), &pObjInfo));
|
|
|
|
CHKiRet(pObjInfo->objMethods[objMethod_CONSTRUCT](&pObj));
|
|
|
|
/* we got the object, now we need to fill the properties */
|
|
CHKiRet(
|
|
objDeserializeProperties(pObj, (rsRetVal(*)(void *, ...))pObjInfo->objMethods[objMethod_SETPROPERTY], pStrm));
|
|
|
|
/* check if we need to call a fixup function that modifies the object
|
|
* before it is finalized. -- rgerhards, 2008-01-13
|
|
*/
|
|
if (fFixup != NULL) CHKiRet(fFixup(pObj, pUsr));
|
|
|
|
/* we have a valid object, let's finalize our work and return */
|
|
if (objInfoIsImplemented(pObjInfo, objMethod_CONSTRUCTION_FINALIZER))
|
|
CHKiRet(pObjInfo->objMethods[objMethod_CONSTRUCTION_FINALIZER](pObj));
|
|
|
|
*((obj_t **)ppObj) = pObj;
|
|
|
|
finalize_it:
|
|
if (iRet != RS_RET_OK && pObj != NULL) free(pObj); /* TODO: check if we can call destructor 2008-01-13 rger */
|
|
|
|
if (pstrID != NULL) rsCStrDestruct(&pstrID);
|
|
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* De-Serialize an object, with known constructur and destructor. Params like Deserialize().
|
|
* Note: this is for the queue subsystem, and optimized for its use.
|
|
* rgerhards, 2012-11-03
|
|
*/
|
|
rsRetVal objDeserializeWithMethods(void *ppObj,
|
|
uchar *pszTypeExpected,
|
|
int lenTypeExpected,
|
|
strm_t *pStrm,
|
|
rsRetVal (*fFixup)(obj_t *, void *),
|
|
void *pUsr,
|
|
rsRetVal (*objConstruct)(void **),
|
|
rsRetVal (*objConstructFinalize)(void *),
|
|
rsRetVal (*objDeserialize)(void *, strm_t *)) {
|
|
DEFiRet;
|
|
rsRetVal iRetLocal;
|
|
obj_t *pObj = NULL;
|
|
int oVers = 0; /* keep compiler happy, but it is totally useless but takes up some execution time... */
|
|
cstr_t *pstrID = NULL;
|
|
|
|
assert(ppObj != NULL);
|
|
assert(pszTypeExpected != NULL);
|
|
ISOBJ_TYPE_assert(pStrm, strm);
|
|
|
|
/* we de-serialize the header. if all goes well, we are happy. However, if
|
|
* we experience a problem, we try to recover. We do this by skipping to
|
|
* the next object header. This is defined via the line-start cookies. In
|
|
* worst case, we exhaust the queue, but then we receive EOF return state,
|
|
* from objDeserializeTryRecover(), what will cause us to ultimately give up.
|
|
* rgerhards, 2008-07-08
|
|
*/
|
|
do {
|
|
iRetLocal = objDeserializeHeader((uchar *)"Obj", &pstrID, &oVers, pStrm);
|
|
if (iRetLocal != RS_RET_OK) {
|
|
dbgprintf(
|
|
"objDeserialize error %d during header processing - "
|
|
"trying to recover\n",
|
|
iRetLocal);
|
|
CHKiRet(objDeserializeTryRecover(pStrm));
|
|
}
|
|
} while (iRetLocal != RS_RET_OK);
|
|
|
|
if (rsCStrSzStrCmp(pstrID, pszTypeExpected, lenTypeExpected)) ABORT_FINALIZE(RS_RET_INVALID_OID);
|
|
|
|
CHKiRet(objConstruct((void **)&pObj));
|
|
|
|
/* we got the object, now we need to fill the properties */
|
|
CHKiRet(objDeserialize(pObj, pStrm));
|
|
CHKiRet(objDeserializeTrailer(pStrm)); /* do trailer checks */
|
|
|
|
/* check if we need to call a fixup function that modifies the object
|
|
* before it is finalized. -- rgerhards, 2008-01-13
|
|
*/
|
|
if (fFixup != NULL) CHKiRet(fFixup(pObj, pUsr));
|
|
|
|
/* we have a valid object, let's finalize our work and return */
|
|
if (objConstructFinalize != NULL) {
|
|
CHKiRet(objConstructFinalize(pObj));
|
|
}
|
|
|
|
*((obj_t **)ppObj) = pObj;
|
|
|
|
finalize_it:
|
|
if (iRet != RS_RET_OK && pObj != NULL) free(pObj); /* TODO: check if we can call destructor 2008-01-13 rger */
|
|
|
|
if (pstrID != NULL) rsCStrDestruct(&pstrID);
|
|
|
|
if (Debug && iRet != RS_RET_OK) {
|
|
dbgprintf("objDeserializeWithMethods fails with %d, stream state:\n", iRet);
|
|
strmDebugOutBuf(pStrm);
|
|
}
|
|
|
|
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* De-Serialize an object property bag. As a property bag contains only partial properties,
|
|
* it is not instanciable. Thus, the caller must provide a pointer of an already-instanciated
|
|
* object of the correct type.
|
|
* Params: Pointer to object (pObj)
|
|
* Pointer to be passed to the function
|
|
* The caller must destruct the created object.
|
|
* rgerhards, 2008-01-07
|
|
*/
|
|
static rsRetVal DeserializePropBag(obj_t *pObj, strm_t *pStrm) {
|
|
DEFiRet;
|
|
rsRetVal iRetLocal;
|
|
cstr_t *pstrID = NULL;
|
|
int oVers;
|
|
objInfo_t *pObjInfo;
|
|
|
|
ISOBJ_assert(pObj);
|
|
ISOBJ_TYPE_assert(pStrm, strm);
|
|
|
|
/* we de-serialize the header. if all goes well, we are happy. However, if
|
|
* we experience a problem, we try to recover. We do this by skipping to
|
|
* the next object header. This is defined via the line-start cookies. In
|
|
* worst case, we exhaust the queue, but then we receive EOF return state
|
|
* from objDeserializeTryRecover(), what will cause us to ultimately give up.
|
|
* rgerhards, 2008-07-08
|
|
*/
|
|
do {
|
|
iRetLocal = objDeserializeHeader((uchar *)"OPB", &pstrID, &oVers, pStrm);
|
|
if (iRetLocal != RS_RET_OK) {
|
|
dbgprintf("objDeserializePropBag error %d during header - trying to recover\n", iRetLocal);
|
|
CHKiRet(objDeserializeTryRecover(pStrm));
|
|
}
|
|
} while (iRetLocal != RS_RET_OK);
|
|
|
|
if (rsCStrSzStrCmp(pstrID, pObj->pObjInfo->pszID, pObj->pObjInfo->lenID)) ABORT_FINALIZE(RS_RET_INVALID_OID);
|
|
|
|
CHKiRet(FindObjInfo((char *)cstrGetSzStrNoNULL(pstrID), &pObjInfo));
|
|
|
|
/* we got the object, now we need to fill the properties */
|
|
CHKiRet(
|
|
objDeserializeProperties(pObj, (rsRetVal(*)(void *, ...))pObjInfo->objMethods[objMethod_SETPROPERTY], pStrm));
|
|
|
|
finalize_it:
|
|
if (pstrID != NULL) rsCStrDestruct(&pstrID);
|
|
|
|
RETiRet;
|
|
}
|
|
|
|
#undef NEXTC /* undef helper macro */
|
|
|
|
|
|
/* --------------- end object serializiation / deserialization support --------------- */
|
|
|
|
|
|
/* set the object (instance) name
|
|
* rgerhards, 2008-01-29
|
|
* TODO: change the naming to a rsCStr obj! (faster)
|
|
*/
|
|
static rsRetVal SetName(obj_t *pThis, uchar *pszName) {
|
|
DEFiRet;
|
|
|
|
free(pThis->pszName);
|
|
CHKmalloc(pThis->pszName = ustrdup(pszName));
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* get the object (instance) name
|
|
* Note that we use a non-standard calling convention. Thus function must never
|
|
* fail, else we run into real big problems. So it must make sure that at least someting
|
|
* is returned.
|
|
* rgerhards, 2008-01-30
|
|
*/
|
|
uchar *ATTR_NONNULL() objGetName(obj_t *const pThis) {
|
|
uchar *ret;
|
|
uchar szName[128];
|
|
|
|
ISOBJ_assert(pThis);
|
|
|
|
if (pThis->pszName == NULL) {
|
|
snprintf((char *)szName, sizeof(szName), "%s %p", objGetClassName(pThis), pThis);
|
|
SetName(pThis, szName);
|
|
/* looks strange, but we NEED to re-check because if there was an
|
|
* error in objSetName(), the pointer may still be NULL
|
|
*/
|
|
if (pThis->pszName == NULL) {
|
|
ret = objGetClassName(pThis);
|
|
} else {
|
|
ret = pThis->pszName;
|
|
}
|
|
} else {
|
|
ret = pThis->pszName;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Find the objInfo object for the current object
|
|
* rgerhards, 2008-02-29
|
|
*/
|
|
static rsRetVal FindObjInfo(const char *const __restrict__ strOID, objInfo_t **ppInfo) {
|
|
DEFiRet;
|
|
int bFound;
|
|
int i;
|
|
|
|
bFound = 0;
|
|
i = 0;
|
|
while (!bFound && i < OBJ_NUM_IDS) {
|
|
if (arrObjInfo[i] != NULL && !strcmp(strOID, (const char *)arrObjInfo[i]->pszID)) {
|
|
bFound = 1;
|
|
break;
|
|
}
|
|
++i;
|
|
}
|
|
|
|
if (!bFound) ABORT_FINALIZE(RS_RET_NOT_FOUND);
|
|
|
|
*ppInfo = arrObjInfo[i];
|
|
|
|
finalize_it:
|
|
if (iRet != RS_RET_OK) {
|
|
dbgprintf("caller requested object '%s', not found (iRet %d)\n", strOID, iRet);
|
|
}
|
|
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* register a classes' info pointer, so that we can reference it later, if needed to
|
|
* (e.g. for de-serialization support).
|
|
* rgerhards, 2008-01-07
|
|
* In this function, we look for a free space in the object table. While we do so, we
|
|
* also detect if the same object has already been registered, which is not valid.
|
|
* rgerhards, 2008-02-29
|
|
*/
|
|
static rsRetVal RegisterObj(uchar *pszObjName, objInfo_t *pInfo) {
|
|
DEFiRet;
|
|
int bFound;
|
|
int i;
|
|
|
|
assert(pszObjName != NULL);
|
|
assert(pInfo != NULL);
|
|
|
|
bFound = 0;
|
|
i = 0;
|
|
while (!bFound && i < OBJ_NUM_IDS && arrObjInfo[i] != NULL) {
|
|
if (arrObjInfo[i] != NULL && !ustrcmp(arrObjInfo[i]->pszID, pszObjName)) {
|
|
bFound = 1;
|
|
break;
|
|
}
|
|
++i;
|
|
}
|
|
|
|
if (bFound) ABORT_FINALIZE(RS_RET_OBJ_ALREADY_REGISTERED);
|
|
if (i >= OBJ_NUM_IDS) ABORT_FINALIZE(RS_RET_OBJ_REGISTRY_OUT_OF_SPACE);
|
|
|
|
arrObjInfo[i] = pInfo;
|
|
#if DEV_DEBUG == 1
|
|
dbgprintf(
|
|
"object '%s' successfully registered with "
|
|
"index %d, qIF %p\n",
|
|
pszObjName, i, pInfo->QueryIF);
|
|
#endif
|
|
|
|
finalize_it:
|
|
if (iRet != RS_RET_OK) {
|
|
LogError(0, NO_ERRCODE, "registering object '%s' failed with error code %d", pszObjName, iRet);
|
|
}
|
|
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* deregister a classes' info pointer, usually called because the class is unloaded.
|
|
* After deregistration, the class can no longer be accessed, except if it is reloaded.
|
|
* rgerhards, 2008-03-10
|
|
*/
|
|
static rsRetVal UnregisterObj(uchar *pszObjName) {
|
|
DEFiRet;
|
|
int bFound;
|
|
int i;
|
|
|
|
assert(pszObjName != NULL);
|
|
|
|
bFound = 0;
|
|
i = 0;
|
|
while (!bFound && i < OBJ_NUM_IDS) {
|
|
if (arrObjInfo[i] != NULL && !ustrcmp(arrObjInfo[i]->pszID, pszObjName)) {
|
|
bFound = 1;
|
|
break;
|
|
}
|
|
++i;
|
|
}
|
|
|
|
if (!bFound) ABORT_FINALIZE(RS_RET_OBJ_NOT_REGISTERED);
|
|
|
|
InfoDestruct(&arrObjInfo[i]);
|
|
#if DEV_DEBUG == 1
|
|
dbgprintf("object '%s' successfully unregistered with index %d\n", pszObjName, i);
|
|
#endif
|
|
|
|
finalize_it:
|
|
if (iRet != RS_RET_OK) {
|
|
dbgprintf("unregistering object '%s' failed with error code %d\n", pszObjName, iRet);
|
|
}
|
|
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* This function shall be called by anyone who would like to use an object. It will
|
|
* try to locate the object, load it into memory if not already present and return
|
|
* a pointer to the objects interface.
|
|
* rgerhards, 2008-02-29
|
|
*/
|
|
static rsRetVal UseObj(const char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf) {
|
|
DEFiRet;
|
|
objInfo_t *pObjInfo;
|
|
|
|
|
|
#if DEV_DEBUG == 1
|
|
dbgprintf(
|
|
"source file %s requests object '%s', "
|
|
" ifIsLoaded %d\n",
|
|
srcFile, pObjName, pIf->ifIsLoaded);
|
|
#endif
|
|
pthread_mutex_lock(&mutObjGlobalOp);
|
|
|
|
if (pIf->ifIsLoaded == 1) {
|
|
ABORT_FINALIZE(RS_RET_OK); /* we are already set */
|
|
}
|
|
if (pIf->ifIsLoaded == 2) {
|
|
ABORT_FINALIZE(RS_RET_LOAD_ERROR); /* we had a load error and can not continue */
|
|
}
|
|
|
|
/* we must be careful that we do not enter in infinite loop if an error occurs during
|
|
* loading a module. ModLoad emits an error message in such cases and that potentially
|
|
* can trigger the same code here. So we initially set the module state to "load error"
|
|
* and set it to "fully initialized" when the load succeeded. It's a bit hackish, but
|
|
* looks like a good solution. -- rgerhards, 2008-03-07
|
|
*/
|
|
pIf->ifIsLoaded = 2;
|
|
|
|
iRet = FindObjInfo((const char *)pObjName, &pObjInfo);
|
|
if (iRet == RS_RET_NOT_FOUND) {
|
|
/* in this case, we need to see if we can dynamically load the object */
|
|
if (pObjFile == NULL) {
|
|
FINALIZE; /* no chance, we have lost... */
|
|
} else {
|
|
CHKiRet(module.Load(pObjFile, 0, NULL));
|
|
/* NOW, we must find it or we have a problem... */
|
|
CHKiRet(FindObjInfo((const char *)pObjName, &pObjInfo));
|
|
}
|
|
} else if (iRet != RS_RET_OK) {
|
|
FINALIZE; /* give up */
|
|
}
|
|
|
|
/* if we reach this point, we have a valid pObjInfo */
|
|
if (pObjFile != NULL) { /* NULL means core module */
|
|
module.Use(srcFile, pObjInfo->pModInfo); /* increase refcount */
|
|
}
|
|
|
|
CHKiRet(pObjInfo->QueryIF(pIf));
|
|
pIf->ifIsLoaded = 1; /* we are happy */
|
|
|
|
finalize_it:
|
|
pthread_mutex_unlock(&mutObjGlobalOp);
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* This function shall be called when a caller is done with an object. Its primary
|
|
* purpose is to keep the reference count correct, which is highly important for
|
|
* modules residing in loadable modules.
|
|
* rgerhards, 2008-03-10
|
|
*/
|
|
static rsRetVal ReleaseObj(const char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf) {
|
|
DEFiRet;
|
|
objInfo_t *pObjInfo;
|
|
|
|
/* dev debug only dbgprintf("source file %s releasing object '%s',
|
|
ifIsLoaded %d\n", srcFile, pObjName, pIf->ifIsLoaded); */
|
|
pthread_mutex_lock(&mutObjGlobalOp);
|
|
|
|
if (pObjFile == NULL) FINALIZE; /* if it is not a lodable module, we do not need to do anything... */
|
|
|
|
if (pIf->ifIsLoaded == 0) {
|
|
FINALIZE; /* we are not loaded - this is perfectly OK... */
|
|
} else if (pIf->ifIsLoaded == 2) {
|
|
pIf->ifIsLoaded = 0; /* clean up */
|
|
FINALIZE; /* we had a load error and can not/must not continue */
|
|
}
|
|
|
|
CHKiRet(FindObjInfo((const char *)pObjName, &pObjInfo));
|
|
|
|
/* if we reach this point, we have a valid pObjInfo */
|
|
module.Release(srcFile, &pObjInfo->pModInfo); /* decrease refcount */
|
|
|
|
pIf->ifIsLoaded = 0; /* indicated "no longer valid" */
|
|
|
|
finalize_it:
|
|
pthread_mutex_unlock(&mutObjGlobalOp);
|
|
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* queryInterface function
|
|
* rgerhards, 2008-02-29
|
|
*/
|
|
PROTOTYPEObjQueryInterface(obj);
|
|
BEGINobjQueryInterface(obj)
|
|
CODESTARTobjQueryInterface(obj);
|
|
if (pIf->ifVersion != objCURR_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->UseObj = UseObj;
|
|
pIf->ReleaseObj = ReleaseObj;
|
|
pIf->InfoConstruct = InfoConstruct;
|
|
pIf->DestructObjSelf = DestructObjSelf;
|
|
pIf->BeginSerializePropBag = BeginSerializePropBag;
|
|
pIf->InfoSetMethod = InfoSetMethod;
|
|
pIf->BeginSerialize = BeginSerialize;
|
|
pIf->SerializeProp = SerializeProp;
|
|
pIf->EndSerialize = EndSerialize;
|
|
pIf->RegisterObj = RegisterObj;
|
|
pIf->UnregisterObj = UnregisterObj;
|
|
pIf->Deserialize = Deserialize;
|
|
pIf->DeserializePropBag = DeserializePropBag;
|
|
pIf->SetName = SetName;
|
|
pIf->GetName = objGetName;
|
|
finalize_it:
|
|
ENDobjQueryInterface(obj)
|
|
|
|
|
|
/* This function returns a pointer to our own interface. It is used as the
|
|
* hook that every object (including dynamically loaded ones) can use to
|
|
* obtain a pointer to our interface which than can be used to obtain
|
|
* pointers to any other interface in the system. This function must be
|
|
* externally visible because of its special nature.
|
|
* rgerhards, 2008-02-29 [nice - will have that date the next time in 4 years ;)]
|
|
*/
|
|
rsRetVal objGetObjInterface(obj_if_t *pIf) {
|
|
DEFiRet;
|
|
assert(pIf != NULL);
|
|
objQueryInterface(pIf);
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* exit our class
|
|
* rgerhards, 2008-03-11
|
|
*/
|
|
rsRetVal objClassExit(void) {
|
|
DEFiRet;
|
|
/* release objects we no longer need */
|
|
objRelease(strm, CORE_COMPONENT);
|
|
objRelease(var, CORE_COMPONENT);
|
|
objRelease(module, CORE_COMPONENT);
|
|
|
|
/* TODO: implement the class exits! */
|
|
#if 0
|
|
cfsyslineExit(pModInfo);
|
|
varClassExit(pModInfo);
|
|
#endif
|
|
moduleClassExit();
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* initialize our own class
|
|
* Please note that this also initializes those classes that we rely on.
|
|
* Though this is a bit dirty, we need to do it - otherwise we can't get
|
|
* around that bootstrap problem. We need to face the fact the the obj
|
|
* class is a little different from the rest of the system, as it provides
|
|
* the core class loader functionality.
|
|
* rgerhards, 2008-02-29
|
|
*/
|
|
rsRetVal objClassInit(modInfo_t *pModInfo) {
|
|
pthread_mutexattr_t mutAttr;
|
|
int i;
|
|
DEFiRet;
|
|
|
|
/* first, initialize the object system itself. This must be done
|
|
* before any other object is created.
|
|
*/
|
|
for (i = 0; i < OBJ_NUM_IDS; ++i) {
|
|
arrObjInfo[i] = NULL;
|
|
}
|
|
|
|
/* the mutex must be recursive, because objects may call into other
|
|
* object identifiers recursively.
|
|
*/
|
|
pthread_mutexattr_init(&mutAttr);
|
|
pthread_mutexattr_settype(&mutAttr, PTHREAD_MUTEX_RECURSIVE);
|
|
pthread_mutex_init(&mutObjGlobalOp, &mutAttr);
|
|
|
|
/* request objects we use */
|
|
CHKiRet(objGetObjInterface(&obj)); /* get ourselves ;) */
|
|
|
|
/* init classes we use (limit to as few as possible!) */
|
|
CHKiRet(datetimeClassInit(pModInfo));
|
|
CHKiRet(cfsyslineInit());
|
|
CHKiRet(varClassInit(pModInfo));
|
|
CHKiRet(moduleClassInit(pModInfo));
|
|
CHKiRet(strmClassInit(pModInfo));
|
|
CHKiRet(objUse(var, CORE_COMPONENT));
|
|
CHKiRet(objUse(module, CORE_COMPONENT));
|
|
CHKiRet(objUse(strm, CORE_COMPONENT));
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|