mirror of
https://github.com/rsyslog/rsyslog.git
synced 2025-12-16 05:10:40 +01:00
This commit applies the new canonical formatting style using `clang-format` with custom settings (notably 4-space indentation), as part of our shift toward automated formatting normalization. ⚠️ No functional changes are included — only whitespace and layout modifications as produced by `clang-format`. This change is part of the formatting modernization strategy discussed in: https://github.com/rsyslog/rsyslog/issues/5747 Key context: - Formatting is now treated as a disposable view, normalized via tooling. - The `.clang-format` file defines the canonical style. - A fixup script (`devtools/format-code.sh`) handles remaining edge cases. - Formatting commits are added to `.git-blame-ignore-revs` to reduce noise. - Developers remain free to format code however they prefer locally.
1361 lines
42 KiB
C
1361 lines
42 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 *, ...)) {
|
|
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(&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;
|
|
}
|