mirror of
https://github.com/rsyslog/rsyslog.git
synced 2025-12-18 08:40:42 +01:00
2414 lines
69 KiB
C
2414 lines
69 KiB
C
/* msg.c
|
|
* The msg object. Implementation of all msg-related functions
|
|
*
|
|
* File begun on 2007-07-13 by RGerhards (extracted from syslogd.c)
|
|
* This file is under development and has not yet arrived at being fully
|
|
* self-contained and a real object. So far, it is mostly an excerpt
|
|
* of the "old" message code without any modifications. However, it
|
|
* helps to have things at the right place one we go to the meat of it.
|
|
*
|
|
* Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH.
|
|
*
|
|
* This file is part of the rsyslog runtime library.
|
|
*
|
|
* The rsyslog runtime library is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* The rsyslog runtime library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* A copy of the GPL can be found in the file "COPYING" in this distribution.
|
|
* A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
|
|
*/
|
|
#include "config.h"
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#define SYSLOG_NAMES
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include "rsyslog.h"
|
|
#include "srUtils.h"
|
|
#include "stringbuf.h"
|
|
#include "template.h"
|
|
#include "msg.h"
|
|
#include "var.h"
|
|
#include "datetime.h"
|
|
#include "regexp.h"
|
|
#include "atomic.h"
|
|
|
|
/* static data */
|
|
DEFobjStaticHelpers
|
|
DEFobjCurrIf(var)
|
|
DEFobjCurrIf(datetime)
|
|
DEFobjCurrIf(regexp)
|
|
|
|
static syslogCODE rs_prioritynames[] =
|
|
{
|
|
{ "alert", LOG_ALERT },
|
|
{ "crit", LOG_CRIT },
|
|
{ "debug", LOG_DEBUG },
|
|
{ "emerg", LOG_EMERG },
|
|
{ "err", LOG_ERR },
|
|
{ "error", LOG_ERR }, /* DEPRECATED */
|
|
{ "info", LOG_INFO },
|
|
{ "none", INTERNAL_NOPRI }, /* INTERNAL */
|
|
{ "notice", LOG_NOTICE },
|
|
{ "panic", LOG_EMERG }, /* DEPRECATED */
|
|
{ "warn", LOG_WARNING }, /* DEPRECATED */
|
|
{ "warning", LOG_WARNING },
|
|
{ NULL, -1 }
|
|
};
|
|
|
|
#ifndef LOG_AUTHPRIV
|
|
# define LOG_AUTHPRIV LOG_AUTH
|
|
#endif
|
|
static syslogCODE rs_facilitynames[] =
|
|
{
|
|
{ "auth", LOG_AUTH },
|
|
{ "authpriv", LOG_AUTHPRIV },
|
|
{ "cron", LOG_CRON },
|
|
{ "daemon", LOG_DAEMON },
|
|
#if defined(LOG_FTP)
|
|
{"ftp", LOG_FTP},
|
|
#endif
|
|
{ "kern", LOG_KERN },
|
|
{ "lpr", LOG_LPR },
|
|
{ "mail", LOG_MAIL },
|
|
{ "news", LOG_NEWS },
|
|
{ "security", LOG_AUTH }, /* DEPRECATED */
|
|
{ "syslog", LOG_SYSLOG },
|
|
{ "user", LOG_USER },
|
|
{ "uucp", LOG_UUCP },
|
|
{ "local0", LOG_LOCAL0 },
|
|
{ "local1", LOG_LOCAL1 },
|
|
{ "local2", LOG_LOCAL2 },
|
|
{ "local3", LOG_LOCAL3 },
|
|
{ "local4", LOG_LOCAL4 },
|
|
{ "local5", LOG_LOCAL5 },
|
|
{ "local6", LOG_LOCAL6 },
|
|
{ "local7", LOG_LOCAL7 },
|
|
{ NULL, -1 }
|
|
};
|
|
|
|
/* some forward declarations */
|
|
static int getAPPNAMELen(msg_t *pM);
|
|
|
|
/* The following functions will support advanced output module
|
|
* multithreading, once this is implemented. Currently, we
|
|
* include them as hooks only. The idea is that we need to guard
|
|
* some msg objects data fields against concurrent access if
|
|
* we run on multiple threads. Please note that in any case this
|
|
* is not necessary for calls from INPUT modules, because they
|
|
* construct the message object and do this serially. Only when
|
|
* the message is in the processing queue, multiple threads may
|
|
* access a single object. Consequently, there are no guard functions
|
|
* for "set" methods, as these are called during input. Only "get"
|
|
* functions that modify important structures have them.
|
|
* rgerhards, 2007-07-20
|
|
* We now support locked and non-locked operations, depending on
|
|
* the configuration of rsyslog. To support this, we use function
|
|
* pointers. Initially, we start in non-locked mode. There, all
|
|
* locking operations call into dummy functions. When locking is
|
|
* enabled, the function pointers are changed to functions doing
|
|
* actual work. We also introduced another MsgPrepareEnqueue() function
|
|
* which initializes the locking structures, if needed. This is
|
|
* necessary because internal messages during config file startup
|
|
* processing are always created in non-locking mode. So we can
|
|
* not initialize locking structures during constructions. We now
|
|
* postpone this until when the message is fully constructed and
|
|
* enqueued. Then we know the status of locking. This has a nice
|
|
* side effect, and that is that during the initial creation of
|
|
* the Msg object no locking needs to be done, which results in better
|
|
* performance. -- rgerhards, 2008-01-05
|
|
*/
|
|
static void (*funcLock)(msg_t *pMsg);
|
|
static void (*funcUnlock)(msg_t *pMsg);
|
|
static void (*funcDeleteMutex)(msg_t *pMsg);
|
|
void (*funcMsgPrepareEnqueue)(msg_t *pMsg);
|
|
#if 1 /* This is a debug aid */
|
|
#define MsgLock(pMsg) funcLock(pMsg)
|
|
#define MsgUnlock(pMsg) funcUnlock(pMsg)
|
|
#else
|
|
#define MsgLock(pMsg) {dbgprintf("line %d\n - ", __LINE__); funcLock(pMsg);; }
|
|
#define MsgUnlock(pMsg) {dbgprintf("line %d - ", __LINE__); funcUnlock(pMsg); }
|
|
#endif
|
|
|
|
/* the next function is a dummy to be used by the looking functions
|
|
* when the class is not yet running in an environment where locking
|
|
* is necessary. Please note that the need to lock can (and will) change
|
|
* during a single run. Typically, this is depending on the operation mode
|
|
* of the message queues (which is operator-configurable). -- rgerhards, 2008-01-05
|
|
*/
|
|
static void MsgLockingDummy(msg_t __attribute__((unused)) *pMsg)
|
|
{
|
|
/* empty be design */
|
|
}
|
|
|
|
|
|
/* The following function prepares a message for enqueue into the queue. This is
|
|
* where a message may be accessed by multiple threads. This implementation here
|
|
* is the version for multiple concurrent acces. It initializes the locking
|
|
* structures.
|
|
* TODO: change to an iRet interface! -- rgerhards, 2008-07-14
|
|
*/
|
|
static void MsgPrepareEnqueueLockingCase(msg_t *pThis)
|
|
{
|
|
int iErr;
|
|
BEGINfunc
|
|
assert(pThis != NULL);
|
|
iErr = pthread_mutexattr_init(&pThis->mutAttr);
|
|
if(iErr != 0) {
|
|
dbgprintf("error initializing mutex attribute in %s:%d, trying to continue\n",
|
|
__FILE__, __LINE__);
|
|
}
|
|
iErr = pthread_mutexattr_settype(&pThis->mutAttr, PTHREAD_MUTEX_RECURSIVE);
|
|
if(iErr != 0) {
|
|
dbgprintf("ERROR setting mutex attribute to recursive in %s:%d, trying to continue "
|
|
"but we will probably either abort or hang soon\n",
|
|
__FILE__, __LINE__);
|
|
/* TODO: it makes very little sense to continue here,
|
|
* but it requires an iRet interface to gracefully shut
|
|
* down. We should do that over time. -- rgerhards, 2008-07-14
|
|
*/
|
|
}
|
|
pthread_mutex_init(&pThis->mut, &pThis->mutAttr);
|
|
|
|
/* we do no longer need the attribute. According to the
|
|
* POSIX spec, we can destroy it without affecting the
|
|
* initialized mutex (that used the attribute).
|
|
* rgerhards, 2008-07-14
|
|
*/
|
|
pthread_mutexattr_destroy(&pThis->mutAttr);
|
|
ENDfunc
|
|
}
|
|
|
|
|
|
/* ... and now the locking and unlocking implementations: */
|
|
static void MsgLockLockingCase(msg_t *pThis)
|
|
{
|
|
/* DEV debug only! dbgprintf("MsgLock(0x%lx)\n", (unsigned long) pThis); */
|
|
assert(pThis != NULL);
|
|
pthread_mutex_lock(&pThis->mut);
|
|
}
|
|
|
|
static void MsgUnlockLockingCase(msg_t *pThis)
|
|
{
|
|
/* DEV debug only! dbgprintf("MsgUnlock(0x%lx)\n", (unsigned long) pThis); */
|
|
assert(pThis != NULL);
|
|
pthread_mutex_unlock(&pThis->mut);
|
|
}
|
|
|
|
/* delete the mutex object on message destruction (locking case)
|
|
*/
|
|
static void MsgDeleteMutexLockingCase(msg_t *pThis)
|
|
{
|
|
assert(pThis != NULL);
|
|
pthread_mutex_destroy(&pThis->mut);
|
|
}
|
|
|
|
/* enable multiple concurrent access on the message object
|
|
* This works on a class-wide basis and can bot be undone.
|
|
* That is, if it is once enabled, it can not be disabled during
|
|
* the same run. When this function is called, no other thread
|
|
* must manipulate message objects. Then we would have race conditions,
|
|
* but guarding against this is counter-productive because it
|
|
* would cost additional time. Plus, it would be a programming error.
|
|
* rgerhards, 2008-01-05
|
|
*/
|
|
rsRetVal MsgEnableThreadSafety(void)
|
|
{
|
|
DEFiRet;
|
|
funcLock = MsgLockLockingCase;
|
|
funcUnlock = MsgUnlockLockingCase;
|
|
funcMsgPrepareEnqueue = MsgPrepareEnqueueLockingCase;
|
|
funcDeleteMutex = MsgDeleteMutexLockingCase;
|
|
RETiRet;
|
|
}
|
|
|
|
/* end locking functions */
|
|
|
|
|
|
/* "Constructor" for a msg "object". Returns a pointer to
|
|
* the new object or NULL if no such object could be allocated.
|
|
* An object constructed via this function should only be destroyed
|
|
* via "msgDestruct()".
|
|
*/
|
|
rsRetVal msgConstruct(msg_t **ppThis)
|
|
{
|
|
DEFiRet;
|
|
msg_t *pM;
|
|
|
|
assert(ppThis != NULL);
|
|
if((pM = calloc(1, sizeof(msg_t))) == NULL)
|
|
ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
|
|
|
|
/* initialize members that are non-zero */
|
|
pM->iRefCount = 1;
|
|
pM->iSeverity = -1;
|
|
pM->iFacility = -1;
|
|
datetime.getCurrTime(&(pM->tRcvdAt));
|
|
objConstructSetObjInfo(pM);
|
|
|
|
/* DEV debugging only! dbgprintf("msgConstruct\t0x%x, ref 1\n", (int)pM);*/
|
|
|
|
*ppThis = pM;
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
BEGINobjDestruct(msg) /* be sure to specify the object type also in END and CODESTART macros! */
|
|
int currRefCount;
|
|
CODESTARTobjDestruct(msg)
|
|
/* DEV Debugging only ! dbgprintf("msgDestruct\t0x%lx, Ref now: %d\n", (unsigned long)pM, pM->iRefCount - 1); */
|
|
# ifdef DO_HAVE_ATOMICS
|
|
currRefCount = ATOMIC_DEC_AND_FETCH(pThis->iRefCount);
|
|
# else
|
|
currRefCount = --pThis->iRefCount;
|
|
# endif
|
|
if(currRefCount == 0)
|
|
{
|
|
/* DEV Debugging Only! dbgprintf("msgDestruct\t0x%lx, RefCount now 0, doing DESTROY\n", (unsigned long)pThis); */
|
|
if(pThis->pszUxTradMsg != NULL)
|
|
free(pThis->pszUxTradMsg);
|
|
if(pThis->pszRawMsg != NULL)
|
|
free(pThis->pszRawMsg);
|
|
if(pThis->pszTAG != NULL)
|
|
free(pThis->pszTAG);
|
|
if(pThis->pszHOSTNAME != NULL)
|
|
free(pThis->pszHOSTNAME);
|
|
if(pThis->pszRcvFrom != NULL)
|
|
free(pThis->pszRcvFrom);
|
|
if(pThis->pszRcvFromIP != NULL)
|
|
free(pThis->pszRcvFromIP);
|
|
if(pThis->pszMSG != NULL)
|
|
free(pThis->pszMSG);
|
|
if(pThis->pszFacility != NULL)
|
|
free(pThis->pszFacility);
|
|
if(pThis->pszFacilityStr != NULL)
|
|
free(pThis->pszFacilityStr);
|
|
if(pThis->pszSeverity != NULL)
|
|
free(pThis->pszSeverity);
|
|
if(pThis->pszSeverityStr != NULL)
|
|
free(pThis->pszSeverityStr);
|
|
if(pThis->pszRcvdAt3164 != NULL)
|
|
free(pThis->pszRcvdAt3164);
|
|
if(pThis->pszRcvdAt3339 != NULL)
|
|
free(pThis->pszRcvdAt3339);
|
|
if(pThis->pszRcvdAt_SecFrac != NULL)
|
|
free(pThis->pszRcvdAt_SecFrac);
|
|
if(pThis->pszRcvdAt_MySQL != NULL)
|
|
free(pThis->pszRcvdAt_MySQL);
|
|
if(pThis->pszRcvdAt_PgSQL != NULL)
|
|
free(pThis->pszRcvdAt_PgSQL);
|
|
if(pThis->pszTIMESTAMP3164 != NULL)
|
|
free(pThis->pszTIMESTAMP3164);
|
|
if(pThis->pszTIMESTAMP3339 != NULL)
|
|
free(pThis->pszTIMESTAMP3339);
|
|
if(pThis->pszTIMESTAMP_SecFrac != NULL)
|
|
free(pThis->pszTIMESTAMP_SecFrac);
|
|
if(pThis->pszTIMESTAMP_MySQL != NULL)
|
|
free(pThis->pszTIMESTAMP_MySQL);
|
|
if(pThis->pszTIMESTAMP_PgSQL != NULL)
|
|
free(pThis->pszTIMESTAMP_PgSQL);
|
|
if(pThis->pszPRI != NULL)
|
|
free(pThis->pszPRI);
|
|
if(pThis->pCSProgName != NULL)
|
|
rsCStrDestruct(&pThis->pCSProgName);
|
|
if(pThis->pCSStrucData != NULL)
|
|
rsCStrDestruct(&pThis->pCSStrucData);
|
|
if(pThis->pCSAPPNAME != NULL)
|
|
rsCStrDestruct(&pThis->pCSAPPNAME);
|
|
if(pThis->pCSPROCID != NULL)
|
|
rsCStrDestruct(&pThis->pCSPROCID);
|
|
if(pThis->pCSMSGID != NULL)
|
|
rsCStrDestruct(&pThis->pCSMSGID);
|
|
funcDeleteMutex(pThis);
|
|
} else {
|
|
pThis = NULL; /* tell framework not to destructing the object! */
|
|
}
|
|
ENDobjDestruct(msg)
|
|
|
|
|
|
/* The macros below are used in MsgDup(). I use macros
|
|
* to keep the fuction code somewhat more readyble. It is my
|
|
* replacement for inline functions in CPP
|
|
*/
|
|
#define tmpCOPYSZ(name) \
|
|
if(pOld->psz##name != NULL) { \
|
|
if((pNew->psz##name = srUtilStrDup(pOld->psz##name, pOld->iLen##name)) == NULL) {\
|
|
msgDestruct(&pNew);\
|
|
return NULL;\
|
|
}\
|
|
pNew->iLen##name = pOld->iLen##name;\
|
|
}
|
|
|
|
/* copy the CStr objects.
|
|
* if the old value is NULL, we do not need to do anything because we
|
|
* initialized the new value to NULL via calloc().
|
|
*/
|
|
#define tmpCOPYCSTR(name) \
|
|
if(pOld->pCS##name != NULL) {\
|
|
if(rsCStrConstructFromCStr(&(pNew->pCS##name), pOld->pCS##name) != RS_RET_OK) {\
|
|
msgDestruct(&pNew);\
|
|
return NULL;\
|
|
}\
|
|
}
|
|
/* Constructs a message object by duplicating another one.
|
|
* Returns NULL if duplication failed. We do not need to lock the
|
|
* message object here, because a fully-created msg object is never
|
|
* allowed to be manipulated. For this, MsgDup() must be used, so MsgDup()
|
|
* can never run into a situation where the message object is being
|
|
* modified while its content is copied - it's forbidden by definition.
|
|
* rgerhards, 2007-07-10
|
|
*/
|
|
msg_t* MsgDup(msg_t* pOld)
|
|
{
|
|
msg_t* pNew;
|
|
|
|
assert(pOld != NULL);
|
|
|
|
BEGINfunc
|
|
if(msgConstruct(&pNew) != RS_RET_OK) {
|
|
return NULL;
|
|
}
|
|
|
|
/* now copy the message properties */
|
|
pNew->iRefCount = 1;
|
|
pNew->iSeverity = pOld->iSeverity;
|
|
pNew->iFacility = pOld->iFacility;
|
|
pNew->bParseHOSTNAME = pOld->bParseHOSTNAME;
|
|
pNew->msgFlags = pOld->msgFlags;
|
|
pNew->iProtocolVersion = pOld->iProtocolVersion;
|
|
memcpy(&pNew->tRcvdAt, &pOld->tRcvdAt, sizeof(struct syslogTime));
|
|
memcpy(&pNew->tTIMESTAMP, &pOld->tTIMESTAMP, sizeof(struct syslogTime));
|
|
tmpCOPYSZ(Severity);
|
|
tmpCOPYSZ(SeverityStr);
|
|
tmpCOPYSZ(Facility);
|
|
tmpCOPYSZ(FacilityStr);
|
|
tmpCOPYSZ(PRI);
|
|
tmpCOPYSZ(RawMsg);
|
|
tmpCOPYSZ(MSG);
|
|
tmpCOPYSZ(UxTradMsg);
|
|
tmpCOPYSZ(TAG);
|
|
tmpCOPYSZ(HOSTNAME);
|
|
tmpCOPYSZ(RcvFrom);
|
|
|
|
tmpCOPYCSTR(ProgName);
|
|
tmpCOPYCSTR(StrucData);
|
|
tmpCOPYCSTR(APPNAME);
|
|
tmpCOPYCSTR(PROCID);
|
|
tmpCOPYCSTR(MSGID);
|
|
|
|
/* we do not copy all other cache properties, as we do not even know
|
|
* if they are needed once again. So we let them re-create if needed.
|
|
*/
|
|
|
|
ENDfunc
|
|
return pNew;
|
|
}
|
|
#undef tmpCOPYSZ
|
|
#undef tmpCOPYCSTR
|
|
|
|
|
|
/* This method serializes a message object. That means the whole
|
|
* object is modified into text form. That text form is suitable for
|
|
* later reconstruction of the object by calling MsgDeSerialize().
|
|
* The most common use case for this method is the creation of an
|
|
* on-disk representation of the message object.
|
|
* We do not serialize the cache properties. We re-create them when needed.
|
|
* This saves us a lot of memory. Performance is no concern, as serializing
|
|
* is a so slow operation that recration of the caches does not count. Also,
|
|
* we do not serialize bParseHOSTNAME, as this is only a helper variable
|
|
* during msg construction - and never again used later.
|
|
* rgerhards, 2008-01-03
|
|
*/
|
|
static rsRetVal MsgSerialize(msg_t *pThis, strm_t *pStrm)
|
|
{
|
|
DEFiRet;
|
|
|
|
assert(pThis != NULL);
|
|
assert(pStrm != NULL);
|
|
|
|
CHKiRet(obj.BeginSerialize(pStrm, (obj_t*) pThis));
|
|
objSerializeSCALAR(pStrm, iProtocolVersion, SHORT);
|
|
objSerializeSCALAR(pStrm, iSeverity, SHORT);
|
|
objSerializeSCALAR(pStrm, iFacility, SHORT);
|
|
objSerializeSCALAR(pStrm, msgFlags, INT);
|
|
objSerializeSCALAR(pStrm, tRcvdAt, SYSLOGTIME);
|
|
objSerializeSCALAR(pStrm, tTIMESTAMP, SYSLOGTIME);
|
|
|
|
objSerializePTR(pStrm, pszRawMsg, PSZ);
|
|
objSerializePTR(pStrm, pszMSG, PSZ);
|
|
objSerializePTR(pStrm, pszUxTradMsg, PSZ);
|
|
objSerializePTR(pStrm, pszTAG, PSZ);
|
|
objSerializePTR(pStrm, pszHOSTNAME, PSZ);
|
|
objSerializePTR(pStrm, pszRcvFrom, PSZ);
|
|
objSerializePTR(pStrm, pszRcvFromIP, PSZ);
|
|
|
|
objSerializePTR(pStrm, pCSStrucData, CSTR);
|
|
objSerializePTR(pStrm, pCSAPPNAME, CSTR);
|
|
objSerializePTR(pStrm, pCSPROCID, CSTR);
|
|
objSerializePTR(pStrm, pCSMSGID, CSTR);
|
|
|
|
CHKiRet(obj.EndSerialize(pStrm));
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* Increment reference count - see description of the "msg"
|
|
* structure for details. As a convenience to developers,
|
|
* this method returns the msg pointer that is passed to it.
|
|
* It is recommended that it is called as follows:
|
|
*
|
|
* pSecondMsgPointer = MsgAddRef(pOrgMsgPointer);
|
|
*/
|
|
msg_t *MsgAddRef(msg_t *pM)
|
|
{
|
|
assert(pM != NULL);
|
|
# ifdef DO_HAVE_ATOMICS
|
|
ATOMIC_INC(pM->iRefCount);
|
|
# else
|
|
MsgLock(pM);
|
|
pM->iRefCount++;
|
|
MsgUnlock(pM);
|
|
# endif
|
|
/* DEV debugging only! dbgprintf("MsgAddRef\t0x%x done, Ref now: %d\n", (int)pM, pM->iRefCount);*/
|
|
return(pM);
|
|
}
|
|
|
|
|
|
/* This functions tries to aquire the PROCID from TAG. Its primary use is
|
|
* when a legacy syslog message has been received and should be forwarded as
|
|
* syslog-protocol (or the PROCID is requested for any other reason).
|
|
* In legacy syslog, the PROCID is considered to be the character sequence
|
|
* between the first [ and the first ]. This usually are digits only, but we
|
|
* do not check that. However, if there is no closing ], we do not assume we
|
|
* can obtain a PROCID. Take in mind that not every legacy syslog message
|
|
* actually has a PROCID.
|
|
* rgerhards, 2005-11-24
|
|
*/
|
|
static rsRetVal aquirePROCIDFromTAG(msg_t *pM)
|
|
{
|
|
register int i;
|
|
DEFiRet;
|
|
|
|
assert(pM != NULL);
|
|
if(pM->pCSPROCID != NULL)
|
|
return RS_RET_OK; /* we are already done ;) */
|
|
|
|
if(getProtocolVersion(pM) != 0)
|
|
return RS_RET_OK; /* we can only emulate if we have legacy format */
|
|
|
|
/* find first '['... */
|
|
i = 0;
|
|
while((i < pM->iLenTAG) && (pM->pszTAG[i] != '['))
|
|
++i;
|
|
if(!(i < pM->iLenTAG))
|
|
return RS_RET_OK; /* no [, so can not emulate... */
|
|
|
|
++i; /* skip '[' */
|
|
|
|
/* now obtain the PROCID string... */
|
|
CHKiRet(rsCStrConstruct(&pM->pCSPROCID));
|
|
rsCStrSetAllocIncrement(pM->pCSPROCID, 16);
|
|
while((i < pM->iLenTAG) && (pM->pszTAG[i] != ']')) {
|
|
CHKiRet(rsCStrAppendChar(pM->pCSPROCID, pM->pszTAG[i]));
|
|
++i;
|
|
}
|
|
|
|
if(!(i < pM->iLenTAG)) {
|
|
/* oops... it looked like we had a PROCID, but now it has
|
|
* turned out this is not true. In this case, we need to free
|
|
* the buffer and simply return. Note that this is NOT an error
|
|
* case!
|
|
*/
|
|
rsCStrDestruct(&pM->pCSPROCID);
|
|
FINALIZE;
|
|
}
|
|
|
|
/* OK, finaally we could obtain a PROCID. So let's use it ;) */
|
|
CHKiRet(rsCStrFinish(pM->pCSPROCID));
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* Parse and set the "programname" for a given MSG object. Programname
|
|
* is a BSD concept, it is the tag without any instance-specific information.
|
|
* Precisely, the programname is terminated by either (whichever occurs first):
|
|
* - end of tag
|
|
* - nonprintable character
|
|
* - ':'
|
|
* - '['
|
|
* - '/'
|
|
* The above definition has been taken from the FreeBSD syslogd sources.
|
|
*
|
|
* The program name is not parsed by default, because it is infrequently-used.
|
|
* If it is needed, this function should be called first. It checks if it is
|
|
* already set and extracts it, if not.
|
|
* A message object must be provided, else a crash will occur.
|
|
* rgerhards, 2005-10-19
|
|
*/
|
|
static rsRetVal aquireProgramName(msg_t *pM)
|
|
{
|
|
DEFiRet;
|
|
register int i;
|
|
|
|
assert(pM != NULL);
|
|
if(pM->pCSProgName == NULL) {
|
|
/* ok, we do not yet have it. So let's parse the TAG
|
|
* to obtain it.
|
|
*/
|
|
CHKiRet(rsCStrConstruct(&pM->pCSProgName));
|
|
rsCStrSetAllocIncrement(pM->pCSProgName, 33);
|
|
for( i = 0
|
|
; (i < pM->iLenTAG) && isprint((int) pM->pszTAG[i])
|
|
&& (pM->pszTAG[i] != '\0') && (pM->pszTAG[i] != ':')
|
|
&& (pM->pszTAG[i] != '[') && (pM->pszTAG[i] != '/')
|
|
; ++i) {
|
|
CHKiRet(rsCStrAppendChar(pM->pCSProgName, pM->pszTAG[i]));
|
|
}
|
|
CHKiRet(rsCStrFinish(pM->pCSProgName));
|
|
}
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* This function moves the HOSTNAME inside the message object to the
|
|
* TAG. It is a specialised function used to handle the condition when
|
|
* a message without HOSTNAME is being processed. The missing HOSTNAME
|
|
* is only detected at a later stage, during TAG processing, so that
|
|
* we already had set the HOSTNAME property and now need to move it to
|
|
* the TAG. Of course, we could do this via a couple of get/set methods,
|
|
* but it is far more efficient to do it via this specialised method.
|
|
* This is especially important as this can be a very common case, e.g.
|
|
* when BSD syslog is acting as a sender.
|
|
* rgerhards, 2005-11-10.
|
|
*/
|
|
void moveHOSTNAMEtoTAG(msg_t *pM)
|
|
{
|
|
assert(pM != NULL);
|
|
if(pM->pszTAG != NULL)
|
|
free(pM->pszTAG);
|
|
pM->pszTAG = pM->pszHOSTNAME;
|
|
pM->iLenTAG = pM->iLenHOSTNAME;
|
|
pM->pszHOSTNAME = NULL;
|
|
pM->iLenHOSTNAME = 0;
|
|
}
|
|
|
|
/* Access methods - dumb & easy, not a comment for each ;)
|
|
*/
|
|
void setProtocolVersion(msg_t *pM, int iNewVersion)
|
|
{
|
|
assert(pM != NULL);
|
|
if(iNewVersion != 0 && iNewVersion != 1) {
|
|
dbgprintf("Tried to set unsupported protocol version %d - changed to 0.\n", iNewVersion);
|
|
iNewVersion = 0;
|
|
}
|
|
pM->iProtocolVersion = iNewVersion;
|
|
}
|
|
|
|
int getProtocolVersion(msg_t *pM)
|
|
{
|
|
assert(pM != NULL);
|
|
return(pM->iProtocolVersion);
|
|
}
|
|
|
|
/* note: string is taken from constant pool, do NOT free */
|
|
char *getProtocolVersionString(msg_t *pM)
|
|
{
|
|
assert(pM != NULL);
|
|
return(pM->iProtocolVersion ? "1" : "0");
|
|
}
|
|
|
|
int getMSGLen(msg_t *pM)
|
|
{
|
|
return((pM == NULL) ? 0 : pM->iLenMSG);
|
|
}
|
|
|
|
|
|
char *getRawMsg(msg_t *pM)
|
|
{
|
|
if(pM == NULL)
|
|
return "";
|
|
else
|
|
if(pM->pszRawMsg == NULL)
|
|
return "";
|
|
else
|
|
return (char*)pM->pszRawMsg;
|
|
}
|
|
|
|
char *getUxTradMsg(msg_t *pM)
|
|
{
|
|
if(pM == NULL)
|
|
return "";
|
|
else
|
|
if(pM->pszUxTradMsg == NULL)
|
|
return "";
|
|
else
|
|
return (char*)pM->pszUxTradMsg;
|
|
}
|
|
|
|
char *getMSG(msg_t *pM)
|
|
{
|
|
if(pM == NULL)
|
|
return "";
|
|
else
|
|
if(pM->pszMSG == NULL)
|
|
return "";
|
|
else
|
|
return (char*)pM->pszMSG;
|
|
}
|
|
|
|
|
|
/* Get PRI value in text form */
|
|
char *getPRI(msg_t *pM)
|
|
{
|
|
if(pM == NULL)
|
|
return "";
|
|
|
|
MsgLock(pM);
|
|
if(pM->pszPRI == NULL) {
|
|
/* OK, we need to construct it...
|
|
* we use a 5 byte buffer - as of
|
|
* RFC 3164, it can't be longer. Should it
|
|
* still be, snprintf will truncate...
|
|
*/
|
|
if((pM->pszPRI = malloc(5)) == NULL) return "";
|
|
pM->iLenPRI = snprintf((char*)pM->pszPRI, 5, "%d",
|
|
LOG_MAKEPRI(pM->iFacility, pM->iSeverity));
|
|
}
|
|
MsgUnlock(pM);
|
|
|
|
return (char*)pM->pszPRI;
|
|
}
|
|
|
|
|
|
/* Get PRI value as integer */
|
|
int getPRIi(msg_t *pM)
|
|
{
|
|
assert(pM != NULL);
|
|
return (pM->iFacility << 3) + (pM->iSeverity);
|
|
}
|
|
|
|
|
|
char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt)
|
|
{
|
|
if(pM == NULL)
|
|
return "";
|
|
|
|
switch(eFmt) {
|
|
case tplFmtDefault:
|
|
MsgLock(pM);
|
|
if(pM->pszTIMESTAMP3164 == NULL) {
|
|
if((pM->pszTIMESTAMP3164 = malloc(16)) == NULL) {
|
|
MsgUnlock(pM);
|
|
return "";
|
|
}
|
|
datetime.formatTimestamp3164(&pM->tTIMESTAMP, pM->pszTIMESTAMP3164, 16);
|
|
}
|
|
MsgUnlock(pM);
|
|
return(pM->pszTIMESTAMP3164);
|
|
case tplFmtMySQLDate:
|
|
MsgLock(pM);
|
|
if(pM->pszTIMESTAMP_MySQL == NULL) {
|
|
if((pM->pszTIMESTAMP_MySQL = malloc(15)) == NULL) {
|
|
MsgUnlock(pM);
|
|
return "";
|
|
}
|
|
datetime.formatTimestampToMySQL(&pM->tTIMESTAMP, pM->pszTIMESTAMP_MySQL, 15);
|
|
}
|
|
MsgUnlock(pM);
|
|
return(pM->pszTIMESTAMP_MySQL);
|
|
case tplFmtPgSQLDate:
|
|
MsgLock(pM);
|
|
if(pM->pszTIMESTAMP_PgSQL == NULL) {
|
|
if((pM->pszTIMESTAMP_PgSQL = malloc(21)) == NULL) {
|
|
MsgUnlock(pM);
|
|
return "";
|
|
}
|
|
datetime.formatTimestampToPgSQL(&pM->tTIMESTAMP, pM->pszTIMESTAMP_PgSQL, 21);
|
|
}
|
|
MsgUnlock(pM);
|
|
return(pM->pszTIMESTAMP_PgSQL);
|
|
case tplFmtRFC3164Date:
|
|
MsgLock(pM);
|
|
if(pM->pszTIMESTAMP3164 == NULL) {
|
|
if((pM->pszTIMESTAMP3164 = malloc(16)) == NULL) {
|
|
MsgUnlock(pM);
|
|
return "";
|
|
}
|
|
datetime.formatTimestamp3164(&pM->tTIMESTAMP, pM->pszTIMESTAMP3164, 16);
|
|
}
|
|
MsgUnlock(pM);
|
|
return(pM->pszTIMESTAMP3164);
|
|
case tplFmtRFC3339Date:
|
|
MsgLock(pM);
|
|
if(pM->pszTIMESTAMP3339 == NULL) {
|
|
if((pM->pszTIMESTAMP3339 = malloc(33)) == NULL) {
|
|
MsgUnlock(pM);
|
|
return ""; /* TODO: check this: can it cause a free() of constant memory?) */
|
|
}
|
|
datetime.formatTimestamp3339(&pM->tTIMESTAMP, pM->pszTIMESTAMP3339, 33);
|
|
}
|
|
MsgUnlock(pM);
|
|
return(pM->pszTIMESTAMP3339);
|
|
case tplFmtSecFrac:
|
|
MsgLock(pM);
|
|
if(pM->pszTIMESTAMP_SecFrac == NULL) {
|
|
if((pM->pszTIMESTAMP_SecFrac = malloc(10)) == NULL) {
|
|
MsgUnlock(pM);
|
|
return ""; /* TODO: check this: can it cause a free() of constant memory?) */
|
|
}
|
|
datetime.formatTimestampSecFrac(&pM->tTIMESTAMP, pM->pszTIMESTAMP_SecFrac, 10);
|
|
}
|
|
MsgUnlock(pM);
|
|
return(pM->pszTIMESTAMP_SecFrac);
|
|
}
|
|
return "INVALID eFmt OPTION!";
|
|
}
|
|
|
|
char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt)
|
|
{
|
|
if(pM == NULL)
|
|
return "";
|
|
|
|
switch(eFmt) {
|
|
case tplFmtDefault:
|
|
MsgLock(pM);
|
|
if(pM->pszRcvdAt3164 == NULL) {
|
|
if((pM->pszRcvdAt3164 = malloc(16)) == NULL) {
|
|
MsgUnlock(pM);
|
|
return "";
|
|
}
|
|
datetime.formatTimestamp3164(&pM->tRcvdAt, pM->pszRcvdAt3164, 16);
|
|
}
|
|
MsgUnlock(pM);
|
|
return(pM->pszRcvdAt3164);
|
|
case tplFmtMySQLDate:
|
|
MsgLock(pM);
|
|
if(pM->pszRcvdAt_MySQL == NULL) {
|
|
if((pM->pszRcvdAt_MySQL = malloc(15)) == NULL) {
|
|
MsgUnlock(pM);
|
|
return "";
|
|
}
|
|
datetime.formatTimestampToMySQL(&pM->tRcvdAt, pM->pszRcvdAt_MySQL, 15);
|
|
}
|
|
MsgUnlock(pM);
|
|
return(pM->pszRcvdAt_MySQL);
|
|
case tplFmtPgSQLDate:
|
|
MsgLock(pM);
|
|
if(pM->pszRcvdAt_PgSQL == NULL) {
|
|
if((pM->pszRcvdAt_PgSQL = malloc(21)) == NULL) {
|
|
MsgUnlock(pM);
|
|
return "";
|
|
}
|
|
datetime.formatTimestampToPgSQL(&pM->tRcvdAt, pM->pszRcvdAt_PgSQL, 21);
|
|
}
|
|
MsgUnlock(pM);
|
|
return(pM->pszRcvdAt_PgSQL);
|
|
case tplFmtRFC3164Date:
|
|
MsgLock(pM);
|
|
if(pM->pszRcvdAt3164 == NULL) {
|
|
if((pM->pszRcvdAt3164 = malloc(16)) == NULL) {
|
|
MsgUnlock(pM);
|
|
return "";
|
|
}
|
|
datetime.formatTimestamp3164(&pM->tRcvdAt, pM->pszRcvdAt3164, 16);
|
|
}
|
|
MsgUnlock(pM);
|
|
return(pM->pszRcvdAt3164);
|
|
case tplFmtRFC3339Date:
|
|
MsgLock(pM);
|
|
if(pM->pszRcvdAt3339 == NULL) {
|
|
if((pM->pszRcvdAt3339 = malloc(33)) == NULL) {
|
|
MsgUnlock(pM);
|
|
return "";
|
|
}
|
|
datetime.formatTimestamp3339(&pM->tRcvdAt, pM->pszRcvdAt3339, 33);
|
|
}
|
|
MsgUnlock(pM);
|
|
return(pM->pszRcvdAt3339);
|
|
case tplFmtSecFrac:
|
|
MsgLock(pM);
|
|
if(pM->pszRcvdAt_SecFrac == NULL) {
|
|
if((pM->pszRcvdAt_SecFrac = malloc(10)) == NULL) {
|
|
MsgUnlock(pM);
|
|
return ""; /* TODO: check this: can it cause a free() of constant memory?) */
|
|
}
|
|
datetime.formatTimestampSecFrac(&pM->tRcvdAt, pM->pszRcvdAt_SecFrac, 10);
|
|
}
|
|
MsgUnlock(pM);
|
|
return(pM->pszRcvdAt_SecFrac);
|
|
}
|
|
return "INVALID eFmt OPTION!";
|
|
}
|
|
|
|
|
|
char *getSeverity(msg_t *pM)
|
|
{
|
|
if(pM == NULL)
|
|
return "";
|
|
|
|
MsgLock(pM);
|
|
if(pM->pszSeverity == NULL) {
|
|
/* we use a 2 byte buffer - can only be one digit */
|
|
if((pM->pszSeverity = malloc(2)) == NULL) { MsgUnlock(pM) ; return ""; }
|
|
pM->iLenSeverity =
|
|
snprintf((char*)pM->pszSeverity, 2, "%d", pM->iSeverity);
|
|
}
|
|
MsgUnlock(pM);
|
|
return((char*)pM->pszSeverity);
|
|
}
|
|
|
|
|
|
char *getSeverityStr(msg_t *pM)
|
|
{
|
|
syslogCODE *c;
|
|
int val;
|
|
char *name = NULL;
|
|
|
|
if(pM == NULL)
|
|
return "";
|
|
|
|
MsgLock(pM);
|
|
if(pM->pszSeverityStr == NULL) {
|
|
for(c = rs_prioritynames, val = pM->iSeverity; c->c_name; c++)
|
|
if(c->c_val == val) {
|
|
name = c->c_name;
|
|
break;
|
|
}
|
|
if(name == NULL) {
|
|
/* we use a 2 byte buffer - can only be one digit */
|
|
if((pM->pszSeverityStr = malloc(2)) == NULL) { MsgUnlock(pM) ; return ""; }
|
|
pM->iLenSeverityStr =
|
|
snprintf((char*)pM->pszSeverityStr, 2, "%d", pM->iSeverity);
|
|
} else {
|
|
if((pM->pszSeverityStr = (uchar*) strdup(name)) == NULL) { MsgUnlock(pM) ; return ""; }
|
|
pM->iLenSeverityStr = strlen((char*)name);
|
|
}
|
|
}
|
|
MsgUnlock(pM);
|
|
return((char*)pM->pszSeverityStr);
|
|
}
|
|
|
|
char *getFacility(msg_t *pM)
|
|
{
|
|
if(pM == NULL)
|
|
return "";
|
|
|
|
MsgLock(pM);
|
|
if(pM->pszFacility == NULL) {
|
|
/* we use a 12 byte buffer - as of
|
|
* syslog-protocol, facility can go
|
|
* up to 2^32 -1
|
|
*/
|
|
if((pM->pszFacility = malloc(12)) == NULL) { MsgUnlock(pM) ; return ""; }
|
|
pM->iLenFacility =
|
|
snprintf((char*)pM->pszFacility, 12, "%d", pM->iFacility);
|
|
}
|
|
MsgUnlock(pM);
|
|
return((char*)pM->pszFacility);
|
|
}
|
|
|
|
char *getFacilityStr(msg_t *pM)
|
|
{
|
|
syslogCODE *c;
|
|
int val;
|
|
char *name = NULL;
|
|
|
|
if(pM == NULL)
|
|
return "";
|
|
|
|
MsgLock(pM);
|
|
if(pM->pszFacilityStr == NULL) {
|
|
for(c = rs_facilitynames, val = pM->iFacility << 3; c->c_name; c++)
|
|
if(c->c_val == val) {
|
|
name = c->c_name;
|
|
break;
|
|
}
|
|
if(name == NULL) {
|
|
/* we use a 12 byte buffer - as of
|
|
* syslog-protocol, facility can go
|
|
* up to 2^32 -1
|
|
*/
|
|
if((pM->pszFacilityStr = malloc(12)) == NULL) { MsgUnlock(pM) ; return ""; }
|
|
pM->iLenFacilityStr =
|
|
snprintf((char*)pM->pszFacilityStr, 12, "%d", val >> 3);
|
|
} else {
|
|
if((pM->pszFacilityStr = (uchar*)strdup(name)) == NULL) { MsgUnlock(pM) ; return ""; }
|
|
pM->iLenFacilityStr = strlen((char*)name);
|
|
}
|
|
}
|
|
MsgUnlock(pM);
|
|
return((char*)pM->pszFacilityStr);
|
|
}
|
|
|
|
|
|
/* set flow control state (if not called, the default - NO_DELAY - is used)
|
|
* This needs no locking because it is only done while the object is
|
|
* not fully constructed (which also means you must not call this
|
|
* method after the msg has been handed over to a queue).
|
|
* rgerhards, 2008-03-14
|
|
*/
|
|
rsRetVal
|
|
MsgSetFlowControlType(msg_t *pMsg, flowControl_t eFlowCtl)
|
|
{
|
|
DEFiRet;
|
|
assert(pMsg != NULL);
|
|
assert(eFlowCtl == eFLOWCTL_NO_DELAY || eFlowCtl == eFLOWCTL_LIGHT_DELAY || eFlowCtl == eFLOWCTL_FULL_DELAY);
|
|
|
|
pMsg->flowCtlType = eFlowCtl;
|
|
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* rgerhards 2004-11-24: set APP-NAME in msg object
|
|
* TODO: revisit msg locking code!
|
|
*/
|
|
rsRetVal MsgSetAPPNAME(msg_t *pMsg, char* pszAPPNAME)
|
|
{
|
|
DEFiRet;
|
|
assert(pMsg != NULL);
|
|
if(pMsg->pCSAPPNAME == NULL) {
|
|
/* we need to obtain the object first */
|
|
CHKiRet(rsCStrConstruct(&pMsg->pCSAPPNAME));
|
|
rsCStrSetAllocIncrement(pMsg->pCSAPPNAME, 128);
|
|
}
|
|
/* if we reach this point, we have the object */
|
|
iRet = rsCStrSetSzStr(pMsg->pCSAPPNAME, (uchar*) pszAPPNAME);
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
static void tryEmulateAPPNAME(msg_t *pM); /* forward reference */
|
|
/* rgerhards, 2005-11-24
|
|
*/
|
|
char *getAPPNAME(msg_t *pM)
|
|
{
|
|
assert(pM != NULL);
|
|
MsgLock(pM);
|
|
if(pM->pCSAPPNAME == NULL)
|
|
tryEmulateAPPNAME(pM);
|
|
MsgUnlock(pM);
|
|
return (pM->pCSAPPNAME == NULL) ? "" : (char*) rsCStrGetSzStrNoNULL(pM->pCSAPPNAME);
|
|
}
|
|
|
|
|
|
/* rgerhards 2004-11-24: set PROCID in msg object
|
|
*/
|
|
rsRetVal MsgSetPROCID(msg_t *pMsg, char* pszPROCID)
|
|
{
|
|
DEFiRet;
|
|
ISOBJ_TYPE_assert(pMsg, msg);
|
|
if(pMsg->pCSPROCID == NULL) {
|
|
/* we need to obtain the object first */
|
|
CHKiRet(rsCStrConstruct(&pMsg->pCSPROCID));
|
|
rsCStrSetAllocIncrement(pMsg->pCSPROCID, 128);
|
|
}
|
|
/* if we reach this point, we have the object */
|
|
iRet = rsCStrSetSzStr(pMsg->pCSPROCID, (uchar*) pszPROCID);
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
/* rgerhards, 2005-11-24
|
|
*/
|
|
int getPROCIDLen(msg_t *pM)
|
|
{
|
|
assert(pM != NULL);
|
|
MsgLock(pM);
|
|
if(pM->pCSPROCID == NULL)
|
|
aquirePROCIDFromTAG(pM);
|
|
MsgUnlock(pM);
|
|
return (pM->pCSPROCID == NULL) ? 1 : rsCStrLen(pM->pCSPROCID);
|
|
}
|
|
|
|
|
|
/* rgerhards, 2005-11-24
|
|
*/
|
|
char *getPROCID(msg_t *pM)
|
|
{
|
|
char* pszRet;
|
|
|
|
ISOBJ_TYPE_assert(pM, msg);
|
|
MsgLock(pM);
|
|
if(pM->pCSPROCID == NULL)
|
|
aquirePROCIDFromTAG(pM);
|
|
pszRet = (pM->pCSPROCID == NULL) ? "-" : (char*) rsCStrGetSzStrNoNULL(pM->pCSPROCID);
|
|
MsgUnlock(pM);
|
|
return pszRet;
|
|
}
|
|
|
|
|
|
/* rgerhards 2004-11-24: set MSGID in msg object
|
|
*/
|
|
rsRetVal MsgSetMSGID(msg_t *pMsg, char* pszMSGID)
|
|
{
|
|
DEFiRet;
|
|
ISOBJ_TYPE_assert(pMsg, msg);
|
|
if(pMsg->pCSMSGID == NULL) {
|
|
/* we need to obtain the object first */
|
|
CHKiRet(rsCStrConstruct(&pMsg->pCSMSGID));
|
|
rsCStrSetAllocIncrement(pMsg->pCSMSGID, 128);
|
|
}
|
|
/* if we reach this point, we have the object */
|
|
iRet = rsCStrSetSzStr(pMsg->pCSMSGID, (uchar*) pszMSGID);
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
/* rgerhards, 2005-11-24
|
|
*/
|
|
#if 0 /* This method is currently not called, be we like to preserve it */
|
|
static int getMSGIDLen(msg_t *pM)
|
|
{
|
|
return (pM->pCSMSGID == NULL) ? 1 : rsCStrLen(pM->pCSMSGID);
|
|
}
|
|
#endif
|
|
|
|
|
|
/* rgerhards, 2005-11-24
|
|
*/
|
|
char *getMSGID(msg_t *pM)
|
|
{
|
|
return (pM->pCSMSGID == NULL) ? "-" : (char*) rsCStrGetSzStrNoNULL(pM->pCSMSGID);
|
|
}
|
|
|
|
|
|
/* Set the TAG to a caller-provided string. This is thought
|
|
* to be a heap buffer that the caller will no longer use. This
|
|
* function is a performance optimization over MsgSetTAG().
|
|
* rgerhards 2004-11-19
|
|
*/
|
|
void MsgAssignTAG(msg_t *pMsg, uchar *pBuf)
|
|
{
|
|
assert(pMsg != NULL);
|
|
pMsg->iLenTAG = (pBuf == NULL) ? 0 : strlen((char*)pBuf);
|
|
pMsg->pszTAG = (uchar*) pBuf;
|
|
}
|
|
|
|
|
|
/* rgerhards 2004-11-16: set TAG in msg object
|
|
*/
|
|
void MsgSetTAG(msg_t *pMsg, char* pszTAG)
|
|
{
|
|
assert(pMsg != NULL);
|
|
if(pMsg->pszTAG != NULL)
|
|
free(pMsg->pszTAG);
|
|
pMsg->iLenTAG = strlen(pszTAG);
|
|
if((pMsg->pszTAG = malloc(pMsg->iLenTAG + 1)) != NULL)
|
|
memcpy(pMsg->pszTAG, pszTAG, pMsg->iLenTAG + 1);
|
|
else
|
|
dbgprintf("Could not allocate memory in MsgSetTAG()\n");
|
|
}
|
|
|
|
|
|
/* This function tries to emulate the TAG if none is
|
|
* set. Its primary purpose is to provide an old-style TAG
|
|
* when a syslog-protocol message has been received. Then,
|
|
* the tag is APP-NAME "[" PROCID "]". The function first checks
|
|
* if there is a TAG and, if not, if it can emulate it.
|
|
* rgerhards, 2005-11-24
|
|
*/
|
|
static void tryEmulateTAG(msg_t *pM)
|
|
{
|
|
int iTAGLen;
|
|
uchar *pBuf;
|
|
assert(pM != NULL);
|
|
|
|
if(pM->pszTAG != NULL)
|
|
return; /* done, no need to emulate */
|
|
|
|
if(getProtocolVersion(pM) == 1) {
|
|
if(!strcmp(getPROCID(pM), "-")) {
|
|
/* no process ID, use APP-NAME only */
|
|
MsgSetTAG(pM, getAPPNAME(pM));
|
|
} else {
|
|
/* now we can try to emulate */
|
|
iTAGLen = getAPPNAMELen(pM) + getPROCIDLen(pM) + 3;
|
|
if((pBuf = malloc(iTAGLen * sizeof(char))) == NULL)
|
|
return; /* nothing we can do */
|
|
snprintf((char*)pBuf, iTAGLen, "%s[%s]", getAPPNAME(pM), getPROCID(pM));
|
|
MsgAssignTAG(pM, pBuf);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#if 0 /* This method is currently not called, be we like to preserve it */
|
|
static int getTAGLen(msg_t *pM)
|
|
{
|
|
if(pM == NULL)
|
|
return 0;
|
|
else {
|
|
tryEmulateTAG(pM);
|
|
if(pM->pszTAG == NULL)
|
|
return 0;
|
|
else
|
|
return pM->iLenTAG;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
char *getTAG(msg_t *pM)
|
|
{
|
|
char *ret;
|
|
|
|
if(pM == NULL)
|
|
ret = "";
|
|
else {
|
|
MsgLock(pM);
|
|
tryEmulateTAG(pM);
|
|
if(pM->pszTAG == NULL)
|
|
ret = "";
|
|
else
|
|
ret = (char*) pM->pszTAG;
|
|
MsgUnlock(pM);
|
|
}
|
|
return(ret);
|
|
}
|
|
|
|
|
|
int getHOSTNAMELen(msg_t *pM)
|
|
{
|
|
if(pM == NULL)
|
|
return 0;
|
|
else
|
|
if(pM->pszHOSTNAME == NULL)
|
|
return 0;
|
|
else
|
|
return pM->iLenHOSTNAME;
|
|
}
|
|
|
|
|
|
char *getHOSTNAME(msg_t *pM)
|
|
{
|
|
if(pM == NULL)
|
|
return "";
|
|
else
|
|
if(pM->pszHOSTNAME == NULL)
|
|
return "";
|
|
else
|
|
return (char*) pM->pszHOSTNAME;
|
|
}
|
|
|
|
|
|
char *getRcvFrom(msg_t *pM)
|
|
{
|
|
if(pM == NULL)
|
|
return "";
|
|
else
|
|
if(pM->pszRcvFrom == NULL)
|
|
return "";
|
|
else
|
|
return (char*) pM->pszRcvFrom;
|
|
}
|
|
|
|
|
|
uchar *getRcvFromIP(msg_t *pM)
|
|
{
|
|
if(pM == NULL)
|
|
return (uchar*) "";
|
|
else
|
|
if(pM->pszRcvFromIP == NULL)
|
|
return (uchar*) "";
|
|
else
|
|
return pM->pszRcvFromIP;
|
|
}
|
|
|
|
/* rgerhards 2004-11-24: set STRUCTURED DATA in msg object
|
|
*/
|
|
rsRetVal MsgSetStructuredData(msg_t *pMsg, char* pszStrucData)
|
|
{
|
|
DEFiRet;
|
|
ISOBJ_TYPE_assert(pMsg, msg);
|
|
if(pMsg->pCSStrucData == NULL) {
|
|
/* we need to obtain the object first */
|
|
CHKiRet(rsCStrConstruct(&pMsg->pCSStrucData));
|
|
rsCStrSetAllocIncrement(pMsg->pCSStrucData, 128);
|
|
}
|
|
/* if we reach this point, we have the object */
|
|
iRet = rsCStrSetSzStr(pMsg->pCSStrucData, (uchar*) pszStrucData);
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
/* get the length of the "STRUCTURED-DATA" sz string
|
|
* rgerhards, 2005-11-24
|
|
*/
|
|
#if 0 /* This method is currently not called, be we like to preserve it */
|
|
static int getStructuredDataLen(msg_t *pM)
|
|
{
|
|
return (pM->pCSStrucData == NULL) ? 1 : rsCStrLen(pM->pCSStrucData);
|
|
}
|
|
#endif
|
|
|
|
|
|
/* get the "STRUCTURED-DATA" as sz string
|
|
* rgerhards, 2005-11-24
|
|
*/
|
|
char *getStructuredData(msg_t *pM)
|
|
{
|
|
return (pM->pCSStrucData == NULL) ? "-" : (char*) rsCStrGetSzStrNoNULL(pM->pCSStrucData);
|
|
}
|
|
|
|
|
|
|
|
/* get the length of the "programname" sz string
|
|
* rgerhards, 2005-10-19
|
|
*/
|
|
int getProgramNameLen(msg_t *pM)
|
|
{
|
|
int iRet;
|
|
|
|
assert(pM != NULL);
|
|
MsgLock(pM);
|
|
if((iRet = aquireProgramName(pM)) != RS_RET_OK) {
|
|
dbgprintf("error %d returned by aquireProgramName() in getProgramNameLen()\n", iRet);
|
|
MsgUnlock(pM);
|
|
return 0; /* best we can do (consistent wiht what getProgramName() returns) */
|
|
}
|
|
MsgUnlock(pM);
|
|
|
|
return (pM->pCSProgName == NULL) ? 0 : rsCStrLen(pM->pCSProgName);
|
|
}
|
|
|
|
|
|
/* get the "programname" as sz string
|
|
* rgerhards, 2005-10-19
|
|
*/
|
|
char *getProgramName(msg_t *pM) /* this is the non-locking version for internal use */
|
|
{
|
|
int iRet;
|
|
char *pszRet;
|
|
|
|
assert(pM != NULL);
|
|
MsgLock(pM);
|
|
if((iRet = aquireProgramName(pM)) != RS_RET_OK) {
|
|
dbgprintf("error %d returned by aquireProgramName() in getProgramName()\n", iRet);
|
|
pszRet = ""; /* best we can do */
|
|
} else {
|
|
pszRet = (pM->pCSProgName == NULL) ? "" : (char*) rsCStrGetSzStrNoNULL(pM->pCSProgName);
|
|
}
|
|
|
|
MsgUnlock(pM);
|
|
return pszRet;
|
|
}
|
|
/* The code below was an approach without PTHREAD_MUTEX_RECURSIVE
|
|
* However, it turned out to be quite complex. So far, we use recursive
|
|
* locking, which is OK from a performance point of view, especially as
|
|
* we do not anticipate that multithreading msg objects is used often.
|
|
* However, we may re-think about using non-recursive locking and I leave this
|
|
* code in here to conserve the idea. -- rgerhards, 2008-01-05
|
|
*/
|
|
#if 0
|
|
static char *getProgramNameNoLock(msg_t *pM) /* this is the non-locking version for internal use */
|
|
{
|
|
int iRet;
|
|
|
|
assert(pM != NULL);
|
|
if((iRet = aquireProgramName(pM)) != RS_RET_OK) {
|
|
dbgprintf("error %d returned by aquireProgramName() in getProgramName()\n", iRet);
|
|
return ""; /* best we can do */
|
|
}
|
|
|
|
return (pM->pCSProgName == NULL) ? "" : (char*) rsCStrGetSzStrNoNULL(pM->pCSProgName);
|
|
}
|
|
char *getProgramName(msg_t *pM) /* this is the external callable version */
|
|
{
|
|
char *pszRet;
|
|
|
|
MsgLock(pM);
|
|
pszRet = getProgramNameNoLock(pM);
|
|
MsgUnlock(pM);
|
|
return pszRet;
|
|
}
|
|
/* an alternative approach has been: */
|
|
/* The macro below is used to generate external function definitions
|
|
* for such functions that may also be called internally (and thus have
|
|
* both a locking and non-locking implementation. Over time, we could
|
|
* reconsider how we handle that. -- rgerhards, 2008-01-05
|
|
*/
|
|
#define EXT_LOCKED_FUNC(fName, ret) \
|
|
ret fName(msg_t *pM) \
|
|
{ \
|
|
ret valRet; \
|
|
MsgLock(pM); \
|
|
valRet = fName##NoLock(pM); \
|
|
MsgUnlock(pM); \
|
|
return(valRet); \
|
|
}
|
|
EXT_LOCKED_FUNC(getProgramName, char*)
|
|
/* in this approach, the external function is provided by the macro and
|
|
* needs not to be writen.
|
|
*/
|
|
#endif /* #if 0 -- saved code */
|
|
|
|
|
|
/* This function tries to emulate APPNAME if it is not present. Its
|
|
* main use is when we have received a log record via legacy syslog and
|
|
* now would like to send out the same one via syslog-protocol.
|
|
*/
|
|
static void tryEmulateAPPNAME(msg_t *pM)
|
|
{
|
|
assert(pM != NULL);
|
|
if(pM->pCSAPPNAME != NULL)
|
|
return; /* we are already done */
|
|
|
|
if(getProtocolVersion(pM) == 0) {
|
|
/* only then it makes sense to emulate */
|
|
MsgSetAPPNAME(pM, getProgramName(pM));
|
|
}
|
|
}
|
|
|
|
|
|
/* rgerhards, 2005-11-24
|
|
*/
|
|
static int getAPPNAMELen(msg_t *pM)
|
|
{
|
|
assert(pM != NULL);
|
|
if(pM->pCSAPPNAME == NULL)
|
|
tryEmulateAPPNAME(pM);
|
|
return (pM->pCSAPPNAME == NULL) ? 0 : rsCStrLen(pM->pCSAPPNAME);
|
|
}
|
|
|
|
|
|
/* rgerhards 2004-11-16: set pszRcvFrom in msg object
|
|
*/
|
|
void MsgSetRcvFrom(msg_t *pMsg, char* pszRcvFrom)
|
|
{
|
|
assert(pMsg != NULL);
|
|
if(pMsg->pszRcvFrom != NULL)
|
|
free(pMsg->pszRcvFrom);
|
|
|
|
pMsg->iLenRcvFrom = strlen(pszRcvFrom);
|
|
if((pMsg->pszRcvFrom = malloc(pMsg->iLenRcvFrom + 1)) != NULL) {
|
|
memcpy(pMsg->pszRcvFrom, pszRcvFrom, pMsg->iLenRcvFrom + 1);
|
|
}
|
|
}
|
|
|
|
|
|
/* rgerhards 2005-05-16: set pszRcvFromIP in msg object */
|
|
rsRetVal
|
|
MsgSetRcvFromIP(msg_t *pMsg, uchar* pszRcvFromIP)
|
|
{
|
|
DEFiRet;
|
|
assert(pMsg != NULL);
|
|
if(pMsg->pszRcvFromIP != NULL) {
|
|
free(pMsg->pszRcvFromIP);
|
|
pMsg->iLenRcvFromIP = 0;
|
|
}
|
|
|
|
CHKmalloc(pMsg->pszRcvFromIP = (uchar*)strdup((char*)pszRcvFromIP));
|
|
pMsg->iLenRcvFromIP = strlen((char*)pszRcvFromIP);
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* Set the HOSTNAME to a caller-provided string. This is thought
|
|
* to be a heap buffer that the caller will no longer use. This
|
|
* function is a performance optimization over MsgSetHOSTNAME().
|
|
* rgerhards 2004-11-19
|
|
*/
|
|
void MsgAssignHOSTNAME(msg_t *pMsg, char *pBuf)
|
|
{
|
|
assert(pMsg != NULL);
|
|
assert(pBuf != NULL);
|
|
pMsg->iLenHOSTNAME = strlen(pBuf);
|
|
pMsg->pszHOSTNAME = (uchar*) pBuf;
|
|
}
|
|
|
|
|
|
/* rgerhards 2004-11-09: set HOSTNAME in msg object
|
|
* rgerhards, 2007-06-21:
|
|
* Does not return anything. If an error occurs, the hostname is
|
|
* simply not set. I have changed this behaviour. The only problem
|
|
* we can run into is memory shortage. If we have such, it is better
|
|
* to loose the hostname than the full message. So we silently ignore
|
|
* that problem and hope that memory will be available the next time
|
|
* we need it. The rest of the code already knows how to handle an
|
|
* unset HOSTNAME.
|
|
*/
|
|
void MsgSetHOSTNAME(msg_t *pMsg, char* pszHOSTNAME)
|
|
{
|
|
assert(pMsg != NULL);
|
|
if(pMsg->pszHOSTNAME != NULL)
|
|
free(pMsg->pszHOSTNAME);
|
|
|
|
pMsg->iLenHOSTNAME = strlen(pszHOSTNAME);
|
|
if((pMsg->pszHOSTNAME = malloc(pMsg->iLenHOSTNAME + 1)) != NULL)
|
|
memcpy(pMsg->pszHOSTNAME, pszHOSTNAME, pMsg->iLenHOSTNAME + 1);
|
|
else
|
|
dbgprintf("Could not allocate memory in MsgSetHOSTNAME()\n");
|
|
}
|
|
|
|
|
|
/* Set the UxTradMsg to a caller-provided string. This is thought
|
|
* to be a heap buffer that the caller will no longer use. This
|
|
* function is a performance optimization over MsgSetUxTradMsg().
|
|
* rgerhards 2004-11-19
|
|
*/
|
|
#if 0 /* This method is currently not called, be we like to preserve it */
|
|
static void MsgAssignUxTradMsg(msg_t *pMsg, char *pBuf)
|
|
{
|
|
assert(pMsg != NULL);
|
|
assert(pBuf != NULL);
|
|
pMsg->iLenUxTradMsg = strlen(pBuf);
|
|
pMsg->pszUxTradMsg = pBuf;
|
|
}
|
|
#endif
|
|
|
|
|
|
/* rgerhards 2004-11-17: set the traditional Unix message in msg object
|
|
*/
|
|
int MsgSetUxTradMsg(msg_t *pMsg, char* pszUxTradMsg)
|
|
{
|
|
assert(pMsg != NULL);
|
|
assert(pszUxTradMsg != NULL);
|
|
pMsg->iLenUxTradMsg = strlen(pszUxTradMsg);
|
|
if(pMsg->pszUxTradMsg != NULL)
|
|
free(pMsg->pszUxTradMsg);
|
|
if((pMsg->pszUxTradMsg = malloc(pMsg->iLenUxTradMsg + 1)) != NULL)
|
|
memcpy(pMsg->pszUxTradMsg, pszUxTradMsg, pMsg->iLenUxTradMsg + 1);
|
|
else
|
|
dbgprintf("Could not allocate memory for pszUxTradMsg buffer.");
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
/* rgerhards 2004-11-09: set MSG in msg object
|
|
*/
|
|
void MsgSetMSG(msg_t *pMsg, char* pszMSG)
|
|
{
|
|
assert(pMsg != NULL);
|
|
assert(pszMSG != NULL);
|
|
|
|
if(pMsg->pszMSG != NULL)
|
|
free(pMsg->pszMSG);
|
|
|
|
pMsg->iLenMSG = strlen(pszMSG);
|
|
if((pMsg->pszMSG = (uchar*) malloc(pMsg->iLenMSG + 1)) != NULL)
|
|
memcpy(pMsg->pszMSG, pszMSG, pMsg->iLenMSG + 1);
|
|
else
|
|
dbgprintf("MsgSetMSG could not allocate memory for pszMSG buffer.");
|
|
}
|
|
|
|
/* rgerhards 2004-11-11: set RawMsg in msg object
|
|
*/
|
|
void MsgSetRawMsg(msg_t *pMsg, char* pszRawMsg)
|
|
{
|
|
assert(pMsg != NULL);
|
|
if(pMsg->pszRawMsg != NULL)
|
|
free(pMsg->pszRawMsg);
|
|
|
|
pMsg->iLenRawMsg = strlen(pszRawMsg);
|
|
if((pMsg->pszRawMsg = (uchar*) malloc(pMsg->iLenRawMsg + 1)) != NULL)
|
|
memcpy(pMsg->pszRawMsg, pszRawMsg, pMsg->iLenRawMsg + 1);
|
|
else
|
|
dbgprintf("Could not allocate memory for pszRawMsg buffer.");
|
|
}
|
|
|
|
|
|
/* Decode a priority into textual information like auth.emerg.
|
|
* The variable pRes must point to a user-supplied buffer and
|
|
* pResLen must contain its size. The pointer to the buffer
|
|
* is also returned, what makes this functiona suitable for
|
|
* use in printf-like functions.
|
|
* Note: a buffer size of 20 characters is always sufficient.
|
|
* Interface to this function changed 2007-06-15 by RGerhards
|
|
*/
|
|
char *textpri(char *pRes, size_t pResLen, int pri)
|
|
{
|
|
syslogCODE *c_pri, *c_fac;
|
|
|
|
assert(pRes != NULL);
|
|
assert(pResLen > 0);
|
|
|
|
for (c_fac = rs_facilitynames; c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri)<<3); c_fac++);
|
|
for (c_pri = rs_prioritynames; c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
|
|
|
|
snprintf (pRes, pResLen, "%s.%s<%d>", c_fac->c_name, c_pri->c_name, pri);
|
|
|
|
return pRes;
|
|
}
|
|
|
|
|
|
/* This function returns the current date in different
|
|
* variants. It is used to construct the $NOW series of
|
|
* system properties. The returned buffer must be freed
|
|
* by the caller when no longer needed. If the function
|
|
* can not allocate memory, it returns a NULL pointer.
|
|
* Added 2007-07-10 rgerhards
|
|
*/
|
|
typedef enum ENOWType { NOW_NOW, NOW_YEAR, NOW_MONTH, NOW_DAY, NOW_HOUR, NOW_HHOUR, NOW_QHOUR, NOW_MINUTE } eNOWType;
|
|
#define tmpBUFSIZE 16 /* size of formatting buffer */
|
|
static uchar *getNOW(eNOWType eNow)
|
|
{
|
|
uchar *pBuf;
|
|
struct syslogTime t;
|
|
|
|
if((pBuf = (uchar*) malloc(sizeof(uchar) * tmpBUFSIZE)) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
datetime.getCurrTime(&t);
|
|
switch(eNow) {
|
|
case NOW_NOW:
|
|
snprintf((char*) pBuf, tmpBUFSIZE, "%4.4d-%2.2d-%2.2d", t.year, t.month, t.day);
|
|
break;
|
|
case NOW_YEAR:
|
|
snprintf((char*) pBuf, tmpBUFSIZE, "%4.4d", t.year);
|
|
break;
|
|
case NOW_MONTH:
|
|
snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.month);
|
|
break;
|
|
case NOW_DAY:
|
|
snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.day);
|
|
break;
|
|
case NOW_HOUR:
|
|
snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.hour);
|
|
break;
|
|
case NOW_HHOUR:
|
|
snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.minute / 30);
|
|
break;
|
|
case NOW_QHOUR:
|
|
snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.minute / 15);
|
|
break;
|
|
case NOW_MINUTE:
|
|
snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.minute);
|
|
break;
|
|
}
|
|
|
|
return(pBuf);
|
|
}
|
|
#undef tmpBUFSIZE /* clean up */
|
|
|
|
|
|
/* This function returns a string-representation of the
|
|
* requested message property. This is a generic function used
|
|
* to abstract properties so that these can be easier
|
|
* queried. Returns NULL if property could not be found.
|
|
* Actually, this function is a big if..elseif. What it does
|
|
* is simply to map property names (from MonitorWare) to the
|
|
* message object data fields.
|
|
*
|
|
* In case we need string forms of propertis we do not
|
|
* yet have in string form, we do a memory allocation that
|
|
* is sufficiently large (in all cases). Once the string
|
|
* form has been obtained, it is saved until the Msg object
|
|
* is finally destroyed. This is so that we save the processing
|
|
* time in the (likely) case that this property is requested
|
|
* again. It also saves us a lot of dynamic memory management
|
|
* issues in the upper layers, because we so can guarantee that
|
|
* the buffer will remain static AND available during the lifetime
|
|
* of the object. Please note that both the max size allocation as
|
|
* well as keeping things in memory might like look like a
|
|
* waste of memory (some might say it actually is...) - we
|
|
* deliberately accept this because performance is more important
|
|
* to us ;)
|
|
* rgerhards 2004-11-18
|
|
* Parameter "bMustBeFreed" is set by this function. It tells the
|
|
* caller whether or not the string returned must be freed by the
|
|
* caller itself. It is is 0, the caller MUST NOT free it. If it is
|
|
* 1, the caller MUST free 1. Handling this wrongly leads to either
|
|
* a memory leak of a program abort (do to double-frees or frees on
|
|
* the constant memory pool). So be careful to do it right.
|
|
* rgerhards 2004-11-23
|
|
* regular expression support contributed by Andres Riancho merged
|
|
* on 2005-09-13
|
|
* changed so that it now an be called without a template entry (NULL).
|
|
* In this case, only the (unmodified) property is returned. This will
|
|
* be used in selector line processing.
|
|
* rgerhards 2005-09-15
|
|
*/
|
|
char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
|
|
cstr_t *pCSPropName, unsigned short *pbMustBeFreed)
|
|
{
|
|
uchar *pName;
|
|
char *pRes; /* result pointer */
|
|
char *pBufStart;
|
|
char *pBuf;
|
|
int iLen;
|
|
short iOffs;
|
|
|
|
#ifdef FEATURE_REGEXP
|
|
/* Variables necessary for regular expression matching */
|
|
size_t nmatch = 10;
|
|
regmatch_t pmatch[10];
|
|
#endif
|
|
|
|
assert(pMsg != NULL);
|
|
assert(pbMustBeFreed != NULL);
|
|
|
|
if(pCSPropName == NULL) {
|
|
assert(pTpe != NULL);
|
|
pName = pTpe->data.field.pPropRepl;
|
|
} else {
|
|
pName = rsCStrGetSzStrNoNULL(pCSPropName);
|
|
}
|
|
*pbMustBeFreed = 0;
|
|
|
|
/* sometimes there are aliases to the original MonitoWare
|
|
* property names. These come after || in the ifs below. */
|
|
if(!strcmp((char*) pName, "msg")) {
|
|
pRes = getMSG(pMsg);
|
|
} else if(!strcmp((char*) pName, "rawmsg")) {
|
|
pRes = getRawMsg(pMsg);
|
|
} else if(!strcmp((char*) pName, "uxtradmsg")) {
|
|
pRes = getUxTradMsg(pMsg);
|
|
} else if(!strcmp((char*) pName, "fromhost")) {
|
|
pRes = getRcvFrom(pMsg);
|
|
} else if(!strcmp((char*) pName, "fromhost-ip")) {
|
|
pRes = (char*) getRcvFromIP(pMsg);
|
|
} else if(!strcmp((char*) pName, "source") || !strcmp((char*) pName, "hostname")) {
|
|
pRes = getHOSTNAME(pMsg);
|
|
} else if(!strcmp((char*) pName, "syslogtag")) {
|
|
pRes = getTAG(pMsg);
|
|
} else if(!strcmp((char*) pName, "pri")) {
|
|
pRes = getPRI(pMsg);
|
|
} else if(!strcmp((char*) pName, "pri-text")) {
|
|
pBuf = malloc(20 * sizeof(char));
|
|
if(pBuf == NULL) {
|
|
*pbMustBeFreed = 0;
|
|
return "**OUT OF MEMORY**";
|
|
} else {
|
|
*pbMustBeFreed = 1;
|
|
pRes = textpri(pBuf, 20, getPRIi(pMsg));
|
|
}
|
|
} else if(!strcmp((char*) pName, "iut")) {
|
|
pRes = "1"; /* always 1 for syslog messages (a MonitorWare thing;)) */
|
|
} else if(!strcmp((char*) pName, "syslogfacility")) {
|
|
pRes = getFacility(pMsg);
|
|
} else if(!strcmp((char*) pName, "syslogfacility-text")) {
|
|
pRes = getFacilityStr(pMsg);
|
|
} else if(!strcmp((char*) pName, "syslogseverity") || !strcmp((char*) pName, "syslogpriority")) {
|
|
pRes = getSeverity(pMsg);
|
|
} else if(!strcmp((char*) pName, "syslogseverity-text") || !strcmp((char*) pName, "syslogpriority-text")) {
|
|
pRes = getSeverityStr(pMsg);
|
|
} else if(!strcmp((char*) pName, "timegenerated")) {
|
|
pRes = getTimeGenerated(pMsg, pTpe->data.field.eDateFormat);
|
|
} else if(!strcmp((char*) pName, "timereported")
|
|
|| !strcmp((char*) pName, "timestamp")) {
|
|
pRes = getTimeReported(pMsg, pTpe->data.field.eDateFormat);
|
|
} else if(!strcmp((char*) pName, "programname")) {
|
|
pRes = getProgramName(pMsg);
|
|
} else if(!strcmp((char*) pName, "protocol-version")) {
|
|
pRes = getProtocolVersionString(pMsg);
|
|
} else if(!strcmp((char*) pName, "structured-data")) {
|
|
pRes = getStructuredData(pMsg);
|
|
} else if(!strcmp((char*) pName, "app-name")) {
|
|
pRes = getAPPNAME(pMsg);
|
|
} else if(!strcmp((char*) pName, "procid")) {
|
|
pRes = getPROCID(pMsg);
|
|
} else if(!strcmp((char*) pName, "msgid")) {
|
|
pRes = getMSGID(pMsg);
|
|
/* here start system properties (those, that do not relate to the message itself */
|
|
} else if(!strcmp((char*) pName, "$now")) {
|
|
if((pRes = (char*) getNOW(NOW_NOW)) == NULL) {
|
|
return "***OUT OF MEMORY***";
|
|
} else
|
|
*pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
|
|
} else if(!strcmp((char*) pName, "$year")) {
|
|
if((pRes = (char*) getNOW(NOW_YEAR)) == NULL) {
|
|
return "***OUT OF MEMORY***";
|
|
} else
|
|
*pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
|
|
} else if(!strcmp((char*) pName, "$month")) {
|
|
if((pRes = (char*) getNOW(NOW_MONTH)) == NULL) {
|
|
return "***OUT OF MEMORY***";
|
|
} else
|
|
*pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
|
|
} else if(!strcmp((char*) pName, "$day")) {
|
|
if((pRes = (char*) getNOW(NOW_DAY)) == NULL) {
|
|
return "***OUT OF MEMORY***";
|
|
} else
|
|
*pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
|
|
} else if(!strcmp((char*) pName, "$hour")) {
|
|
if((pRes = (char*) getNOW(NOW_HOUR)) == NULL) {
|
|
return "***OUT OF MEMORY***";
|
|
} else
|
|
*pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
|
|
} else if(!strcmp((char*) pName, "$hhour")) {
|
|
if((pRes = (char*) getNOW(NOW_HHOUR)) == NULL) {
|
|
return "***OUT OF MEMORY***";
|
|
} else
|
|
*pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
|
|
} else if(!strcmp((char*) pName, "$qhour")) {
|
|
if((pRes = (char*) getNOW(NOW_QHOUR)) == NULL) {
|
|
return "***OUT OF MEMORY***";
|
|
} else
|
|
*pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
|
|
} else if(!strcmp((char*) pName, "$minute")) {
|
|
if((pRes = (char*) getNOW(NOW_MINUTE)) == NULL) {
|
|
return "***OUT OF MEMORY***";
|
|
} else
|
|
*pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
|
|
} else {
|
|
/* there is no point in continuing, we may even otherwise render the
|
|
* error message unreadable. rgerhards, 2007-07-10
|
|
*/
|
|
dbgprintf("invalid property name: '%s'\n", pName);
|
|
return "**INVALID PROPERTY NAME**";
|
|
}
|
|
|
|
/* If we did not receive a template pointer, we are already done... */
|
|
if(pTpe == NULL) {
|
|
return pRes;
|
|
}
|
|
|
|
/* Now check if we need to make "temporary" transformations (these
|
|
* are transformations that do not go back into the message -
|
|
* memory must be allocated for them!).
|
|
*/
|
|
|
|
/* substring extraction */
|
|
/* first we check if we need to extract by field number
|
|
* rgerhards, 2005-12-22
|
|
*/
|
|
if(pTpe->data.field.has_fields == 1) {
|
|
size_t iCurrFld;
|
|
char *pFld;
|
|
char *pFldEnd;
|
|
/* first, skip to the field in question. The field separator
|
|
* is always one character and is stored in the template entry.
|
|
*/
|
|
iCurrFld = 1;
|
|
pFld = pRes;
|
|
while(*pFld && iCurrFld < pTpe->data.field.iToPos) {
|
|
/* skip fields until the requested field or end of string is found */
|
|
while(*pFld && (uchar) *pFld != pTpe->data.field.field_delim)
|
|
++pFld; /* skip to field terminator */
|
|
if(*pFld == pTpe->data.field.field_delim) {
|
|
++pFld; /* eat it */
|
|
++iCurrFld;
|
|
}
|
|
}
|
|
dbgprintf("field requested %d, field found %d\n", pTpe->data.field.iToPos, (int) iCurrFld);
|
|
|
|
if(iCurrFld == pTpe->data.field.iToPos) {
|
|
/* field found, now extract it */
|
|
/* first of all, we need to find the end */
|
|
pFldEnd = pFld;
|
|
while(*pFldEnd && *pFldEnd != pTpe->data.field.field_delim)
|
|
++pFldEnd;
|
|
--pFldEnd; /* we are already at the delimiter - so we need to
|
|
* step back a little not to copy it as part of the field. */
|
|
/* we got our end pointer, now do the copy */
|
|
/* TODO: code copied from below, this is a candidate for a separate function */
|
|
iLen = pFldEnd - pFld + 1; /* the +1 is for an actual char, NOT \0! */
|
|
pBufStart = pBuf = malloc((iLen + 1) * sizeof(char));
|
|
if(pBuf == NULL) {
|
|
if(*pbMustBeFreed == 1)
|
|
free(pRes);
|
|
*pbMustBeFreed = 0;
|
|
return "**OUT OF MEMORY**";
|
|
}
|
|
/* now copy */
|
|
memcpy(pBuf, pFld, iLen);
|
|
pBuf[iLen] = '\0'; /* terminate it */
|
|
if(*pbMustBeFreed == 1)
|
|
free(pRes);
|
|
pRes = pBufStart;
|
|
*pbMustBeFreed = 1;
|
|
if(*(pFldEnd+1) != '\0')
|
|
++pFldEnd; /* OK, skip again over delimiter char */
|
|
} else {
|
|
/* field not found, return error */
|
|
if(*pbMustBeFreed == 1)
|
|
free(pRes);
|
|
*pbMustBeFreed = 0;
|
|
return "**FIELD NOT FOUND**";
|
|
}
|
|
} else if(pTpe->data.field.iFromPos != 0 || pTpe->data.field.iToPos != 0) {
|
|
/* we need to obtain a private copy */
|
|
int iFrom, iTo;
|
|
char *pSb;
|
|
iFrom = pTpe->data.field.iFromPos;
|
|
iTo = pTpe->data.field.iToPos;
|
|
/* need to zero-base to and from (they are 1-based!) */
|
|
if(iFrom > 0)
|
|
--iFrom;
|
|
if(iTo > 0)
|
|
--iTo;
|
|
iLen = iTo - iFrom + 1; /* the +1 is for an actual char, NOT \0! */
|
|
pBufStart = pBuf = malloc((iLen + 1) * sizeof(char));
|
|
if(pBuf == NULL) {
|
|
if(*pbMustBeFreed == 1)
|
|
free(pRes);
|
|
*pbMustBeFreed = 0;
|
|
return "**OUT OF MEMORY**";
|
|
}
|
|
pSb = pRes;
|
|
if(iFrom) {
|
|
/* skip to the start of the substring (can't do pointer arithmetic
|
|
* because the whole string might be smaller!!)
|
|
*/
|
|
while(*pSb && iFrom) {
|
|
--iFrom;
|
|
++pSb;
|
|
}
|
|
}
|
|
/* OK, we are at the begin - now let's copy... */
|
|
while(*pSb && iLen) {
|
|
*pBuf++ = *pSb;
|
|
++pSb;
|
|
--iLen;
|
|
}
|
|
*pBuf = '\0';
|
|
if(*pbMustBeFreed == 1)
|
|
free(pRes);
|
|
pRes = pBufStart;
|
|
*pbMustBeFreed = 1;
|
|
#ifdef FEATURE_REGEXP
|
|
} else {
|
|
/* Check for regular expressions */
|
|
if (pTpe->data.field.has_regex != 0) {
|
|
if (pTpe->data.field.has_regex == 2)
|
|
/* Could not compile regex before! */
|
|
return "**NO MATCH** **BAD REGULAR EXPRESSION**";
|
|
|
|
dbgprintf("string to match for regex is: %s\n", pRes);
|
|
|
|
if(objUse(regexp, LM_REGEXP_FILENAME) == RS_RET_OK) {
|
|
short iTry = 0;
|
|
uchar bFound = 0;
|
|
iOffs = 0;
|
|
/* first see if we find a match, iterating through the series of
|
|
* potential matches over the string.
|
|
*/
|
|
while(!bFound) {
|
|
if(regexp.regexec(&pTpe->data.field.re, pRes + iOffs, nmatch, pmatch, 0) == 0) {
|
|
if(pmatch[0].rm_so == -1) {
|
|
dbgprintf("oops ... start offset of successful regexec is -1\n");
|
|
break;
|
|
}
|
|
if(iTry == pTpe->data.field.iMatchToUse) {
|
|
bFound = 1;
|
|
} else {
|
|
iOffs += pmatch[0].rm_eo;
|
|
++iTry;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if(!bFound) {
|
|
/* we got no match! */
|
|
if(pTpe->data.field.nomatchAction != TPL_REGEX_NOMATCH_USE_WHOLE_FIELD) {
|
|
if (*pbMustBeFreed == 1) {
|
|
free(pRes);
|
|
*pbMustBeFreed = 0;
|
|
}
|
|
if(pTpe->data.field.nomatchAction == TPL_REGEX_NOMATCH_USE_DFLTSTR)
|
|
return "**NO MATCH**";
|
|
else
|
|
return "";
|
|
}
|
|
} else {
|
|
/* Match- but did it match the one we wanted? */
|
|
/* we got no match! */
|
|
if(pmatch[pTpe->data.field.iSubMatchToUse].rm_so == -1) {
|
|
if(pTpe->data.field.nomatchAction != TPL_REGEX_NOMATCH_USE_WHOLE_FIELD) {
|
|
if (*pbMustBeFreed == 1) {
|
|
free(pRes);
|
|
*pbMustBeFreed = 0;
|
|
}
|
|
if(pTpe->data.field.nomatchAction == TPL_REGEX_NOMATCH_USE_DFLTSTR)
|
|
return "**NO MATCH**";
|
|
else
|
|
return "";
|
|
}
|
|
}
|
|
/* OK, we have a usable match - we now need to malloc pB */
|
|
int iLenBuf;
|
|
char *pB;
|
|
|
|
iLenBuf = pmatch[pTpe->data.field.iSubMatchToUse].rm_eo
|
|
- pmatch[pTpe->data.field.iSubMatchToUse].rm_so;
|
|
pB = (char *) malloc((iLenBuf + 1) * sizeof(char));
|
|
|
|
if (pB == NULL) {
|
|
if (*pbMustBeFreed == 1)
|
|
free(pRes);
|
|
*pbMustBeFreed = 0;
|
|
return "**OUT OF MEMORY ALLOCATING pBuf**";
|
|
}
|
|
|
|
/* Lets copy the matched substring to the buffer */
|
|
memcpy(pB, pRes + iOffs + pmatch[pTpe->data.field.iSubMatchToUse].rm_so, iLenBuf);
|
|
pB[iLenBuf] = '\0';/* terminate string, did not happen before */
|
|
|
|
if (*pbMustBeFreed == 1)
|
|
free(pRes);
|
|
pRes = pB;
|
|
*pbMustBeFreed = 1;
|
|
}
|
|
} else {
|
|
/* we could not load regular expression support. This is quite unexpected at
|
|
* this stage of processing (after all, the config parser found it), but so
|
|
* it is. We return an error in that case. -- rgerhards, 2008-03-07
|
|
*/
|
|
dbgprintf("could not get regexp object pointer, so regexp can not be evaluated\n");
|
|
if (*pbMustBeFreed == 1) {
|
|
free(pRes);
|
|
*pbMustBeFreed = 0;
|
|
}
|
|
return "***REGEXP NOT AVAILABLE***";
|
|
}
|
|
}
|
|
#endif /* #ifdef FEATURE_REGEXP */
|
|
}
|
|
|
|
if(*pRes) {
|
|
/* case conversations (should go after substring, because so we are able to
|
|
* work on the smallest possible buffer).
|
|
*/
|
|
if(pTpe->data.field.eCaseConv != tplCaseConvNo) {
|
|
/* we need to obtain a private copy */
|
|
int iBufLen = strlen(pRes);
|
|
char *pBStart;
|
|
char *pB;
|
|
char *pSrc;
|
|
pBStart = pB = malloc((iBufLen + 1) * sizeof(char));
|
|
if(pB == NULL) {
|
|
if(*pbMustBeFreed == 1)
|
|
free(pRes);
|
|
*pbMustBeFreed = 0;
|
|
return "**OUT OF MEMORY**";
|
|
}
|
|
pSrc = pRes;
|
|
while(*pSrc) {
|
|
*pB++ = (pTpe->data.field.eCaseConv == tplCaseConvUpper) ?
|
|
(char)toupper((int)*pSrc) : (char)tolower((int)*pSrc);
|
|
/* currently only these two exist */
|
|
++pSrc;
|
|
}
|
|
*pB = '\0';
|
|
if(*pbMustBeFreed == 1)
|
|
free(pRes);
|
|
pRes = pBStart;
|
|
*pbMustBeFreed = 1;
|
|
}
|
|
|
|
/* now do control character dropping/escaping/replacement
|
|
* Only one of these can be used. If multiple options are given, the
|
|
* result is random (though currently there obviously is an order of
|
|
* preferrence, see code below. But this is NOT guaranteed.
|
|
* RGerhards, 2006-11-17
|
|
* We must copy the strings if we modify them, because they may either
|
|
* point to static memory or may point into the message object, in which
|
|
* case we would actually modify the original property (which of course
|
|
* is wrong).
|
|
* This was found and fixed by varmojefkoj on 2007-09-11
|
|
*/
|
|
if(pTpe->data.field.options.bDropCC) {
|
|
int iLenBuf = 0;
|
|
char *pSrc = pRes;
|
|
char *pDstStart;
|
|
char *pDst;
|
|
char bDropped = 0;
|
|
|
|
while(*pSrc) {
|
|
if(!iscntrl((int) *pSrc++))
|
|
iLenBuf++;
|
|
else
|
|
bDropped = 1;
|
|
}
|
|
|
|
if(bDropped) {
|
|
pDst = pDstStart = malloc(iLenBuf + 1);
|
|
if(pDst == NULL) {
|
|
if(*pbMustBeFreed == 1)
|
|
free(pRes);
|
|
*pbMustBeFreed = 0;
|
|
return "**OUT OF MEMORY**";
|
|
}
|
|
for(pSrc = pRes; *pSrc; pSrc++) {
|
|
if(!iscntrl((int) *pSrc))
|
|
*pDst++ = *pSrc;
|
|
}
|
|
*pDst = '\0';
|
|
if(*pbMustBeFreed == 1)
|
|
free(pRes);
|
|
pRes = pDstStart;
|
|
*pbMustBeFreed = 1;
|
|
}
|
|
} else if(pTpe->data.field.options.bSpaceCC) {
|
|
char *pSrc;
|
|
char *pDstStart;
|
|
char *pDst;
|
|
|
|
if(*pbMustBeFreed == 1) {
|
|
/* in this case, we already work on dynamic
|
|
* memory, so there is no need to copy it - we can
|
|
* modify it in-place without any harm. This is a
|
|
* performance optiomization.
|
|
*/
|
|
for(pDst = pRes; *pDst; pDst++) {
|
|
if(iscntrl((int) *pDst))
|
|
*pDst = ' ';
|
|
}
|
|
} else {
|
|
pDst = pDstStart = malloc(strlen(pRes) + 1);
|
|
if(pDst == NULL) {
|
|
if(*pbMustBeFreed == 1)
|
|
free(pRes);
|
|
*pbMustBeFreed = 0;
|
|
return "**OUT OF MEMORY**";
|
|
}
|
|
for(pSrc = pRes; *pSrc; pSrc++) {
|
|
if(iscntrl((int) *pSrc))
|
|
*pDst++ = ' ';
|
|
else
|
|
*pDst++ = *pSrc;
|
|
}
|
|
*pDst = '\0';
|
|
pRes = pDstStart;
|
|
*pbMustBeFreed = 1;
|
|
}
|
|
} else if(pTpe->data.field.options.bEscapeCC) {
|
|
/* we must first count how many control charactes are
|
|
* present, because we need this to compute the new string
|
|
* buffer length. While doing so, we also compute the string
|
|
* length.
|
|
*/
|
|
int iNumCC = 0;
|
|
int iLenBuf = 0;
|
|
char *pB;
|
|
|
|
for(pB = pRes ; *pB ; ++pB) {
|
|
++iLenBuf;
|
|
if(iscntrl((int) *pB))
|
|
++iNumCC;
|
|
}
|
|
|
|
if(iNumCC > 0) { /* if 0, there is nothing to escape, so we are done */
|
|
/* OK, let's do the escaping... */
|
|
char *pBStart;
|
|
char szCCEsc[8]; /* buffer for escape sequence */
|
|
int i;
|
|
|
|
iLenBuf += iNumCC * 4;
|
|
pBStart = pB = malloc((iLenBuf + 1) * sizeof(char));
|
|
if(pB == NULL) {
|
|
if(*pbMustBeFreed == 1)
|
|
free(pRes);
|
|
*pbMustBeFreed = 0;
|
|
return "**OUT OF MEMORY**";
|
|
}
|
|
while(*pRes) {
|
|
if(iscntrl((int) *pRes)) {
|
|
snprintf(szCCEsc, sizeof(szCCEsc), "#%3.3d", *pRes);
|
|
for(i = 0 ; i < 4 ; ++i)
|
|
*pB++ = szCCEsc[i];
|
|
} else {
|
|
*pB++ = *pRes;
|
|
}
|
|
++pRes;
|
|
}
|
|
*pB = '\0';
|
|
if(*pbMustBeFreed == 1)
|
|
free(pRes);
|
|
pRes = pBStart;
|
|
*pbMustBeFreed = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Take care of spurious characters to make the property safe
|
|
* for a path definition
|
|
*/
|
|
if(pTpe->data.field.options.bSecPathDrop || pTpe->data.field.options.bSecPathReplace) {
|
|
if(pTpe->data.field.options.bSecPathDrop) {
|
|
int iLenBuf = 0;
|
|
char *pSrc = pRes;
|
|
char *pDstStart;
|
|
char *pDst;
|
|
char bDropped = 0;
|
|
|
|
while(*pSrc) {
|
|
if(*pSrc++ != '/')
|
|
iLenBuf++;
|
|
else
|
|
bDropped = 1;
|
|
}
|
|
|
|
if(bDropped) {
|
|
pDst = pDstStart = malloc(iLenBuf + 1);
|
|
if(pDst == NULL) {
|
|
if(*pbMustBeFreed == 1)
|
|
free(pRes);
|
|
*pbMustBeFreed = 0;
|
|
return "**OUT OF MEMORY**";
|
|
}
|
|
for(pSrc = pRes; *pSrc; pSrc++) {
|
|
if(*pSrc != '/')
|
|
*pDst++ = *pSrc;
|
|
}
|
|
*pDst = '\0';
|
|
if(*pbMustBeFreed == 1)
|
|
free(pRes);
|
|
pRes = pDstStart;
|
|
*pbMustBeFreed = 1;
|
|
}
|
|
} else {
|
|
char *pSrc;
|
|
char *pDstStart;
|
|
char *pDst;
|
|
|
|
if(*pbMustBeFreed == 1) {
|
|
/* here, again, we can modify the string as we already obtained
|
|
* a private buffer. As we do not change the size of that buffer,
|
|
* in-place modification is possible. This is a performance
|
|
* enhancement.
|
|
*/
|
|
for(pDst = pRes; *pDst; pDst++) {
|
|
if(*pDst == '/')
|
|
*pDst++ = '_';
|
|
}
|
|
} else {
|
|
pDst = pDstStart = malloc(strlen(pRes) + 1);
|
|
if(pDst == NULL) {
|
|
if(*pbMustBeFreed == 1)
|
|
free(pRes);
|
|
*pbMustBeFreed = 0;
|
|
return "**OUT OF MEMORY**";
|
|
}
|
|
for(pSrc = pRes; *pSrc; pSrc++) {
|
|
if(*pSrc == '/')
|
|
*pDst++ = '_';
|
|
else
|
|
*pDst++ = *pSrc;
|
|
}
|
|
*pDst = '\0';
|
|
/* we must NOT check if it needs to be freed, because we have done
|
|
* this in the if above. So if we come to hear, the pSrc string needs
|
|
* not to be freed (and we do not need to care about it).
|
|
*/
|
|
pRes = pDstStart;
|
|
*pbMustBeFreed = 1;
|
|
}
|
|
}
|
|
|
|
/* check for "." and ".." (note the parenthesis in the if condition!) */
|
|
if((*pRes == '.') && (*(pRes + 1) == '\0' || (*(pRes + 1) == '.' && *(pRes + 2) == '\0'))) {
|
|
char *pTmp = pRes;
|
|
|
|
if(*(pRes + 1) == '\0')
|
|
pRes = "_";
|
|
else
|
|
pRes = "_.";;
|
|
if(*pbMustBeFreed == 1)
|
|
free(pTmp);
|
|
*pbMustBeFreed = 0;
|
|
} else if(*pRes == '\0') {
|
|
if(*pbMustBeFreed == 1)
|
|
free(pRes);
|
|
pRes = "_";
|
|
*pbMustBeFreed = 0;
|
|
}
|
|
}
|
|
|
|
/* Now drop last LF if present (pls note that this must not be done
|
|
* if bEscapeCC was set!
|
|
*/
|
|
if(pTpe->data.field.options.bDropLastLF && !pTpe->data.field.options.bEscapeCC) {
|
|
int iLn = strlen(pRes);
|
|
char *pB;
|
|
if(iLn > 0 && *(pRes + iLn - 1) == '\n') {
|
|
/* we have a LF! */
|
|
/* check if we need to obtain a private copy */
|
|
if(*pbMustBeFreed == 0) {
|
|
/* ok, original copy, need a private one */
|
|
pB = malloc((iLn + 1) * sizeof(char));
|
|
if(pB == NULL) {
|
|
*pbMustBeFreed = 0;
|
|
return "**OUT OF MEMORY**";
|
|
}
|
|
memcpy(pB, pRes, iLn - 1);
|
|
pRes = pB;
|
|
*pbMustBeFreed = 1;
|
|
}
|
|
*(pRes + iLn - 1) = '\0'; /* drop LF ;) */
|
|
}
|
|
}
|
|
|
|
/*dbgprintf("MsgGetProp(\"%s\"): \"%s\"\n", pName, pRes); only for verbose debug logging */
|
|
return(pRes);
|
|
}
|
|
|
|
|
|
/* The returns a message variable suitable for use with RainerScript. Most importantly, this means
|
|
* that the value is returned in a var_t object. The var_t is constructed inside this function and
|
|
* MUST be freed by the caller.
|
|
* rgerhards, 2008-02-25
|
|
*/
|
|
rsRetVal
|
|
msgGetMsgVar(msg_t *pThis, cstr_t *pstrPropName, var_t **ppVar)
|
|
{
|
|
DEFiRet;
|
|
var_t *pVar;
|
|
uchar *pszProp = NULL;
|
|
cstr_t *pstrProp;
|
|
unsigned short bMustBeFreed = 0;
|
|
|
|
ISOBJ_TYPE_assert(pThis, msg);
|
|
ASSERT(pstrPropName != NULL);
|
|
ASSERT(ppVar != NULL);
|
|
|
|
/* make sure we have a var_t instance */
|
|
CHKiRet(var.Construct(&pVar));
|
|
CHKiRet(var.ConstructFinalize(pVar));
|
|
|
|
/* always call MsgGetProp() without a template specifier */
|
|
pszProp = (uchar*) MsgGetProp(pThis, NULL, pstrPropName, &bMustBeFreed);
|
|
|
|
/* now create a string object out of it and hand that over to the var */
|
|
CHKiRet(rsCStrConstructFromszStr(&pstrProp, pszProp));
|
|
CHKiRet(var.SetString(pVar, pstrProp));
|
|
|
|
/* finally store var */
|
|
*ppVar = pVar;
|
|
|
|
finalize_it:
|
|
if(bMustBeFreed)
|
|
free(pszProp);
|
|
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* This function can be used as a generic way to set properties.
|
|
* We have to handle a lot of legacy, so our return value is not always
|
|
* 100% correct (called functions do not always provide one, should
|
|
* change over time).
|
|
* rgerhards, 2008-01-07
|
|
*/
|
|
#define isProp(name) !rsCStrSzStrCmp(pProp->pcsName, (uchar*) name, sizeof(name) - 1)
|
|
rsRetVal MsgSetProperty(msg_t *pThis, var_t *pProp)
|
|
{
|
|
DEFiRet;
|
|
|
|
ISOBJ_TYPE_assert(pThis, msg);
|
|
assert(pProp != NULL);
|
|
|
|
if(isProp("iProtocolVersion")) {
|
|
setProtocolVersion(pThis, pProp->val.num);
|
|
} else if(isProp("iSeverity")) {
|
|
pThis->iSeverity = pProp->val.num;
|
|
} else if(isProp("iFacility")) {
|
|
pThis->iFacility = pProp->val.num;
|
|
} else if(isProp("msgFlags")) {
|
|
pThis->msgFlags = pProp->val.num;
|
|
} else if(isProp("pszRawMsg")) {
|
|
MsgSetRawMsg(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
|
|
} else if(isProp("pszMSG")) {
|
|
MsgSetMSG(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
|
|
} else if(isProp("pszUxTradMsg")) {
|
|
MsgSetUxTradMsg(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
|
|
} else if(isProp("pszTAG")) {
|
|
MsgSetTAG(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
|
|
} else if(isProp("pszRcvFromIP")) {
|
|
MsgSetRcvFromIP(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr));
|
|
} else if(isProp("pszRcvFrom")) {
|
|
MsgSetRcvFrom(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
|
|
} else if(isProp("pszHOSTNAME")) {
|
|
MsgSetHOSTNAME(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
|
|
} else if(isProp("pCSStrucData")) {
|
|
MsgSetStructuredData(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
|
|
} else if(isProp("pCSAPPNAME")) {
|
|
MsgSetAPPNAME(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
|
|
} else if(isProp("pCSPROCID")) {
|
|
MsgSetPROCID(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
|
|
} else if(isProp("pCSMSGID")) {
|
|
MsgSetMSGID(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr));
|
|
} else if(isProp("tRcvdAt")) {
|
|
memcpy(&pThis->tRcvdAt, &pProp->val.vSyslogTime, sizeof(struct syslogTime));
|
|
} else if(isProp("tTIMESTAMP")) {
|
|
memcpy(&pThis->tTIMESTAMP, &pProp->val.vSyslogTime, sizeof(struct syslogTime));
|
|
}
|
|
|
|
RETiRet;
|
|
}
|
|
#undef isProp
|
|
|
|
|
|
/* This is a construction finalizer that must be called after all properties
|
|
* have been set. It does some final work on the message object. After this
|
|
* is done, the object is considered ready for full processing.
|
|
* rgerhards, 2008-07-08
|
|
*/
|
|
static rsRetVal msgConstructFinalizer(msg_t *pThis)
|
|
{
|
|
MsgPrepareEnqueue(pThis);
|
|
return RS_RET_OK;
|
|
}
|
|
|
|
|
|
/* get the severity - this is an entry point that
|
|
* satisfies the base object class getSeverity semantics.
|
|
* rgerhards, 2008-01-14
|
|
*/
|
|
static rsRetVal
|
|
MsgGetSeverity(obj_t *pThis, int *piSeverity)
|
|
{
|
|
ISOBJ_TYPE_assert(pThis, msg);
|
|
assert(piSeverity != NULL);
|
|
*piSeverity = ((msg_t*) pThis)->iSeverity;
|
|
return RS_RET_OK;
|
|
}
|
|
|
|
|
|
/* dummy */
|
|
rsRetVal msgQueryInterface(void) { return RS_RET_NOT_IMPLEMENTED; }
|
|
|
|
/* Initialize the message class. Must be called as the very first method
|
|
* before anything else is called inside this class.
|
|
* rgerhards, 2008-01-04
|
|
*/
|
|
BEGINObjClassInit(msg, 1, OBJ_IS_CORE_MODULE)
|
|
/* request objects we use */
|
|
CHKiRet(objUse(var, CORE_COMPONENT));
|
|
CHKiRet(objUse(datetime, CORE_COMPONENT));
|
|
|
|
/* set our own handlers */
|
|
OBJSetMethodHandler(objMethod_SERIALIZE, MsgSerialize);
|
|
OBJSetMethodHandler(objMethod_SETPROPERTY, MsgSetProperty);
|
|
OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, msgConstructFinalizer);
|
|
OBJSetMethodHandler(objMethod_GETSEVERITY, MsgGetSeverity);
|
|
/* initially, we have no need to lock message objects */
|
|
funcLock = MsgLockingDummy;
|
|
funcUnlock = MsgLockingDummy;
|
|
funcDeleteMutex = MsgLockingDummy;
|
|
funcMsgPrepareEnqueue = MsgLockingDummy;
|
|
ENDObjClassInit(msg)
|
|
|
|
/*
|
|
* vi:set ai:
|
|
*/
|